Skip to content

Fix the remaining audit bugs and close the test gaps - #1421

Merged
Frotty merged 19 commits into
masterfrom
audit-fixes
Oct 9, 2026
Merged

Frotty merged 19 commits into
masterfrom
audit-fixes

Conversation

@Frotty

@Frotty Frotty commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Fixes the high and medium bugs left from the audit of the 7-9 October master commits, and closes the test gaps it found. None of the bugs came from those commits; each predates them. Every fix starts from a test which fails on master and passes here.

Bugs fixed

Lua: old-generics values bound to an enum or int (imtranslation/ExprTranslation)

  • colors.get() castTo int on a Box<Color> gave math.mininteger instead of 0: the cast's from-type was the erased ImAnyType, so Lua applied the generic-storage encoding. The cast now converts from the type argument's type, and the value is normalised as it leaves erased storage.
  • An unset enum entry read through an old generic was nil on Lua, not the first constant: wrapLua matched type names and isPrimitiveType left enums out. Enums are normalised like ints now.

Language server: the model went stale before the reconcile (ModelManagerImpl)

  • Jass declared outside a package in a .wurst/.jurst file is visible everywhere, but only .j files were treated so: editing or deleting such a file checked none of its users. Units with Jass declarations count as global now (Changes.jassNamesChanged carries it to the reconcile).
  • Removing or adding a unit marked its dependents unchecked but kept their attributes, so a run in the 3 s before the reconcile compiled stale bindings (a deleted function became an empty skeleton) and the full check certified the model. The dependents are cleared as on a replacement; the model's own attributes are cleared when a unit is added while every unit is unchecked.

Optimiser: an unread division which may stop the thread was dropped (Flatten, ImOptimizer, LocalMerger)

  • int unused = 10 div d lost the division in every variant: the effect was kept but flattened to nothing, as Jass and Lua have no expression statement. Such an operator becomes an assignment to a local which the garbage removal and the local merger keep (one shared rule, Flatten.mayStopTheThread); the round bound still holds.

Local optimisations: an uninitialised read pjass rejects (LocalMerger, ControlFlowGraph)

  • After a loop which always returns, the liveness saw no path to return a, removed every write to a and kept the read. The code no path reaches is removed first (ControlFlowGraph.unreachableStatements). Functions ending in endif without a final return are already in every map (stdlib Loglevel_getTag).

Lua dispatch: an interface default against a method inherited from outside the interface (LuaTranslator)

  • class C extends Base implements Omega called through Omega gave Base's m on Lua and Omega's default on Jass and in the interpreter, only because "Base" sorts before "Omega". Lua follows the rule Jass and the interpreter apply (a default passes over an implementation whose class is not below the interface), and where the call through the superclass reaches another implementation it gets its own slot. The NoOpState name tie-breaker is gone: the Lua of all affected test classes is byte-identical without it.

Jass and interpreter: an abstract interface method implemented by an inherited method (InterfaceTranslator)

  • C extends Base implements Omega, with Omega's m abstract and Base, which is not an Omega, declaring m: the Jass dispatch over Omega's implementations follows the classes below Omega and takes the method declared in each, so C kept the abstract method and landed in another implementor's branch (3 instead of 1); the interpreter called the abstract method. C gets a method of its own with Base's implementation, which its subclasses inherit, linked to the overrides below C (filled in once every class is translated). Where another interface gives C a default for m, that method runs the default, as a call through that interface does on every backend. Lua was already right for the plain case. Generic classes keep their previous behaviour (see below).

Local merger: assignments of a local to itself (LocalMerger)

  • Merging two locals turns the copy between them into x = x. The next run of the merger removed those, but nothing after the last run did: castle fight's optimised Lua had 103, zombie defense's 106. The merge removes them now (0 left). The later passes then see the code without those no-ops, which moves a few of their decisions: castle fight has 43 fewer assignments and 50 more local declarations (adjacent local x = nil compile to one LOADNIL in Lua), zombie defense 49 fewer and 35 more; both scripts are smaller.
  • Also: a dead assignment's effects are replaced by flat statements (a call, the assignment of a division which may stop the thread), not statement expressions, so the passes after the merger see flat IM (this was wrong for dead call assignments before too).

Test gaps closed

  • Six local-player OptimizerTests compared with Player(n), a native call, so they passed with an analysis which reports nothing. The native is read into a local now: all six fail with the analysis switched off. Nine LocalPlayerContextAnalyzerTests pin the edges no test caught when removed.
  • Inlined early returns next to break/continue, switch, for-in and tuple returns, compared against @noinline twins.
  • Optimised variants of the old-generics zero/round-trip tests on Lua.
  • Garbage removal: a preserved global and a blizzard.j global losing their last read in a later round; the IM-level round tests run in unit-test mode now; flatten of a class function changed deep inside; the liveness join of an if.
  • Dispatch determinism: FSM siblings in separate packages and same-named classes in several packages, in any compilation-unit order.
  • Model: a partly checked model, .j/.jurst files parsed ahead, a legacy compilation not certifying the model, the CLI failing on a type error from its single check.
  • LanguageWorkerTest: the wait behind the initial build is a 60 s hang guard (it took 8 s of 10 cold).

Checks

  • All touched classes: ModelManagerTests, UpdateModelTests, ParallelLoadTests, CliBuildMapTests, LanguageWorkerTest, BuildDiagnosticsTests, LuaBackendAuditTests, GenericsTests, GenericsWithTypeclassesTests, EnumTests, OptimizerTests, BugTests, TreeShakerTests, LuaTranslationTests, DeterministicChecks, InterfaceTests, ClassesTests, LocalPlayerContextAnalyzerTests: 1107 tests, all pass (one LanguageWorkerTest timeout under load, fixed above). After the interface fix: InterfaceTests, ClassesTests, LuaBackendAuditTests, LuaTranslationTests, DeterministicChecks, GenericsTests, GenericsWithTypeclassesTests, ModuleTests, NewFeatureTests, ClosureTests, BugTests, OptimizerTests, TreeShakerTests, FieldIterationTests: 1188 tests, all pass.
  • Castle fight and zombie defense (Lua, -inline -localOptimizations): byte-identical to master before the self-assignment fix; with it, the only differences are the removed x = x and the later passes' decisions described above.

Found, not fixed here

  • Generics: a generic class's method overridden in a non-generic subclass and called through the base crashes the compiler (ConcurrentModificationException in EliminateGenerics.adaptSubmethods). Because of it, generic classes do not get the interface bridge above.
  • Jass and the interpreter disagree for A implements I (overriding the default), B extends A, C extends B implements I: interpreter 1, Jass 2 (Lua 2).
  • The trap rule counts integer div/mod; the tree shake also treats real division as possibly stopping the thread. Whether it does in game is not measured.

Frotty added 14 commits October 9, 2026 19:24
…clear what used a removed or added unit

(cherry picked from commit d3247f46edd8e7276654fec7c4594cca4b479743)
… CLI check gate

(cherry picked from commit ef10435588e8f340eb408441d8248dc4a5776d2a)
(cherry picked from commit f6139e934ade695bd8f93499f4c5395dd4a09d55)
(cherry picked from commit 62d7c22dc10fcbb2cfd18e031877c20b95aaa7ee)
(cherry picked from commit f9895fce93a1ce56f2604fe898316acfb373a75c)
…ip and the liveness join

(cherry picked from commit e852ff6fc6c3706fa500fdd8e75b77cc8da33c39)
(cherry picked from commit 95f58f9d4caa77541153636703e4affc355d1fef)
…nts only it reads

(cherry picked from commit 1f06d76c926fd6f5e468a86c57d2da35f9acb543)
…packages

(cherry picked from commit 9c1ac52b2a65efe7e89ff383543bf0ae5074d916)
…ide the interface, as on Jass

Through the interface, Jass and the interpreter never take the method a class inherits from a superclass which does not implement the interface; Lua chose between the two by class name. Calls through the superclass's method get a slot of their own where one class would need both answers under one key.

(cherry picked from commit e95142cddc9fafcdbb9348290a47b5fdcfc32f3b)
Since a class's own method is told by its class, it decides no binding: the Lua of LuaBackendAuditTests, LuaTranslationTests, DeterministicChecks, InterfaceTests and ClassesTests is byte-identical without it.

(cherry picked from commit 04056283c24b4632fc907a4ce5444c165403fb7c)
Six OptimizerTests compared with Player(n) in the second condition, a
native call, which the side-effect check refuses to move a statement
across whatever the analysis says: they passed with an analysis which
reports nothing. The native is read into a local first now, so each
fails when the analysis misses its edge.

Nine unit tests pin the edges which no test of the analysis caught when
removed: the return value's tree parent, an exit under a local branch,
vararg arguments and loop variables, every assigned left side, method
receivers and implementations, the control edge into a return, and the
stop at a nested loop.

(cherry picked from commit ddc1e101f97c4107d875946037f59971ba2cae3a)
No test inlined an early return next to break or continue, a return-free
loop after a returning one, a switch in a loop, a for-in loop closing
before the return or a tuple return. Each callee has an @noinline twin
with the same body; the program fails unless both give the same results
and the same trace of effects.

(cherry picked from commit fc8fa5d3fbc4eaba34c9c194692308f8049d3a64)
…tial build

The first case waits for a completion which comes after the server's
initial build; on a cold JVM it took 8 s of the 10 s limit and timed out
once in a run of many test classes. The limit only guards against a hang.
@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-09T18:56:59.317829Z bc56249 Manual request
ℹ️ 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.

…erits

C extends Base implements Omega, with Omega's m abstract and Base, which
is not an Omega, declaring m: the Jass dispatch over Omega's
implementations follows the classes below Omega and takes the method
declared in each, so C kept the abstract method and landed in another
implementor's branch, and the interpreter called the abstract method. C
gets a method of its own with Base's implementation now.
@Frotty

Frotty commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0616fa4518

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The method a class gets of its own for an abstract interface method it
inherits had no sub-methods, so a deeper override was reachable only
through the interface method, not from the bridge (AGENTS.md section 8).
Once every class is translated, the bridge takes the overrides of the
inherited method below its class. Generic classes keep what they did:
their methods are specialised with functions the class owns, and a
generic class with an override in a non-generic subclass does not
compile yet.
@Frotty

Frotty commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4ffb32a5f0

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

It put what a dead assignment's value does in a statement expression, so
the passes after it saw non-flat IM: a call (as before) and now a
division which may stop the thread inside an expression. The effects
become the statements a flatten makes of them; when that makes a local
(the division), the liveness is computed again before the merge.
@Frotty

Frotty commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e8baa23997

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Frotty added 2 commits October 9, 2026 20:50
…ives it

C extends Base implements Abstract, Default, with Abstract's m abstract
and Default's m a default: a default beats an inherited method, and Lua
binds one implementation for C to the calls through both interfaces, so
with C's own method running Base's m a call through Default ran Base's m
on Lua and the default on Jass. C's own method runs the default now, so
every backend runs it through either interface.
…makes

Merging two locals turns the copy between them into an assignment of the
local to itself. The next run of the merger removed those, but nothing
after the last run did: castle fight's optimised Lua had 103, zombie
defense's 106. The merge removes them now.
@Frotty

Frotty commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: bc56249856

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@Frotty
Frotty merged commit 1e842e4 into master 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