Skip to content

New generics: a static class sees the enclosing class's parameter as its own variable - #1424

Merged
Frotty merged 1 commit into
generic-interface-bridgefrom
static-class-captured-generics
Oct 9, 2026
Merged

Frotty merged 1 commit into
generic-interface-bridgefrom
static-class-captured-generics

Conversation

@Frotty

@Frotty Frotty commented Oct 9, 2026

Copy link
Copy Markdown
Member

Stacked on #1423 (base generic-interface-bridge). Merge this one into #1423's branch before #1423 is merged, or a roll-up PR to master follows.

Problem

A static class inside a generic class (class Outer<T:> with static class Holder) gets a type variable of its own for T. The translator did not use it consistently. Lua accepted these programs, but Jass failed to compile them:

  • Members of the static class. A member was translated with the type variables of whichever code asked for it first. A method that Outer calls took and returned Outer's T, while its body used the static class's own variable. The error was Type variable T not eliminated. Fields, constructors, ondestroy blocks and closures failed the same way.
  • Types of the static class. A type of the static class had no argument for that variable, so C extends Base gave C the superclass Base<>. The error was Rewrite generics with wrong sizes. The same happened with or without an interface. This is why Interface bridge: give generic classes a method of their own too #1423's static-class test ran on Lua only.
  • Static functions of the static class. Such a function is not in its class, but it used the class's variable, which it does not have.

Change

Members. A class's members (methods, constructors, ondestroy, fields) are now translated with the class's own type variables, whichever code asks for them first. In a static class, these include the captured variable. The type of this is the class with its own variables, and the class's variables no longer depend on where it is first used.

Types. A type of a static class carries an argument for its captured variable. The argument is one of two things:

  • what the type binds it to: new Outer<int>().make() returns a Base with T bound to int;
  • otherwise, the parameter as the surrounding code sees it: Outer's own variable inside Outer, and the captured one inside the static class.

This means C extends Base now gives C the superclass Base<T_C>. Super constructor and ondestroy calls pass the argument along.

Static functions. A static function of a static class takes the captured parameter as a type variable of its own, as a static function of the generic class already does.

Removed patch. EliminateGenerics no longer fills in missing class type arguments by matching type-variable names. Nothing produces the short types any more.

Interface bridge (#1423). typeArgumentsAsSeenFrom now translates the superclass's type as the class's own functions do, with the class's type variables. A static class that implements an interface through an inherited method over T is now dispatched on Jass too.

Checks

…its own variable

A static class inside a generic class (class Outer<T:> with static class
Holder) has a type variable of its own for T, but did not use it
consistently, so Jass failed to compile in several shapes which Lua
accepted:
- a member of the static class was translated with the type variables of
  whatever code first asked for it: a method Outer calls took and returned
  Outer's T while its body used the static class's ("Type variable T not
  eliminated"), and the same for fields, constructors and closures;
- a type of the static class had no argument for that variable, so
  C extends Base gave C the superclass Base<> ("Rewrite generics with
  wrong sizes");
- a static function of the static class, which is not in its class, used
  the class's variable, which it does not have.

The members of a class are now translated with the class's type variables
whoever asks for them first, and the type of this is the class with its
own variables. A type of a static class carries an argument for its
variable: what the type binds it to (new Outer<int>().make() returns a
Base with T bound to int), or else the parameter as the code sees it,
Outer's own in Outer and the captured one in the static class. Super
constructor and ondestroy calls pass it, and a static function of a static
class takes it as a type variable of its own, as a static function of the
generic class does. EliminateGenerics no longer fills in missing type
arguments by name.

The function a class gets for an inherited interface implementation
translates the superclass's type arguments the same way, with the class's
variables, so a static class implementing an interface with an inherited
method over T is dispatched on Jass too.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-09T21:49:59.560567Z 82cbee1 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@Frotty
Frotty merged commit c9b0034 into generic-interface-bridge Oct 9, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant