Skip to content

Record the four decisions, and settle div and mod - #1251

Closed
Frotty wants to merge 2 commits into
masterfrom
decisions/record-and-close-div-mod
Closed

Record the four decisions, and settle div and mod#1251
Frotty wants to merge 2 commits into
masterfrom
decisions/record-and-close-div-mod

Conversation

@Frotty

@Frotty Frotty commented Aug 17, 2026

Copy link
Copy Markdown
Member

The owner answered the four open questions. This writes them into the backlog so they survive the session, and closes the one that needed no code.

Settled: div and mod keep returning the left operand's type

So real r = 7 div 2 compiles, and it is meant to. Item 8 has been pending a yes or no since early in this work, with the behaviour pinned in both directions meanwhile.

The branch in AttrExprType now says so, rather than looking like an oversight beside caseMathOperation — which collapses two integer literals to int precisely so real r = 1 + 1 is an error. The asymmetry is deliberate: the division is integer either way and the result is then widened.

ExpressionTests.integerDivisionOfLiteralsIsStillAssignableToReal and OptimizerTests.realFormatting_consistent_fromIntOps both still pass, which is the point — the second opens with that assignment.

Recorded: module bounds (item 7)

The instantiation declares the module's type parameters only so a dispatch receiver has a name to resolve, and they stay out of type inference. Generic modules keep resolving theirs by matching the receiver type.

The alternative — letting inference see the declared parameters — collides with that mechanism and fails genericModuleInGenericClassGet with "Cannot infer type for type parameter T". This is the smaller change, at the honest price of the parameter meaning something narrower than it appears to.

The item now carries what the attempt reached, so it is resumable rather than rediscovered: the grammar change, the binding through WurstTypeBoundTypeParam, the dispatch widening, and the one step left — the requirement lookup does not follow a binding to the underlying parameter's bounds.

Recorded: the Lua erasure model (item 23)

Specialise only the paths which need a concrete type and leave the object erased. Generated scripts stay small, which is the reason; the cost is that it is more compiler work than not erasing at all.

The item spells out what that means for items 6 and 13 — the concrete type is threaded to the places which use it rather than to the object — and why it is worth doing rather than working around: two class shapes existing at once is what produced both field bugs in #1239.

Deferred, with the reason: instances for a family of types

It decides whether type class bounds stay a tool for new containers or become how Table, HashMap, HashList and HashSet work. Worth deciding when there is appetite for the language design, rather than alongside compiler work.

Green: ExpressionTests, OptimizerTests.

Frotty added 2 commits August 17, 2026 14:50
Item 8 is closed rather than pending: div and mod keep returning the left
operand's type, so real r = 7 div 2 compiles and is meant to. The branch in
AttrExprType says so now, instead of looking like an oversight beside
caseMathOperation, which collapses two literals to int precisely so
real r = 1 + 1 is an error. Both tests which depend on it still pass.

Item 7 records that the instantiation declares the module's type parameters only
so a dispatch receiver has a name to resolve, and that they stay out of type
inference - generic modules keep resolving theirs by matching the receiver type.
The alternative, letting inference see them, collides with that and fails
genericModuleInGenericClassGet; this is the smaller change at the price of the
parameter meaning something narrower than it looks. What is left of the attempt
is written down, including the widening the requirement lookup still needs.

Item 23 records that Lua specialises only the paths needing a concrete type and
leaves the object erased, because generated scripts should stay small. Items 6
and 13 both end there, and it removes the two-shapes-at-once arrangement which
produced both field bugs in #1239.

Instances for a family of types stay deferred, with why: it decides whether
bounds stay a tool for new containers or become how the existing ones work.
A dispatch slot's name is composed from the segment after the last underscore
of a method's mangled name, which is the declared name only when the declared
name has no underscore in it. get_it contributes it, which is nobody's method,
and the slot the call goes through is not the one the override was bound to.

Found by auditing the junk-slot rule for what its name comparison does to
unrelated methods, not by anyone hitting it, so it is worth pinning rather than
leaving to be met by surprise. The test asserts the failure it currently
produces and names what the fix has to be keyed on.

Asking the declaration for the name is not that fix. declaredName already reads
it off the trace, but two overloads share a declared name and mangle to Foo_bar
and Foo_bar_1, so the segment after the last underscore is also what keeps
their slots apart today. Pointing both composers at the declared name fixes the
underscore case and collapses overloaded slots instead. What a method and its
overrides share, and what tells two overloads apart, are different questions,
and the mangled name is answering both at once by accident.
@Frotty

Frotty commented Aug 17, 2026

Copy link
Copy Markdown
Member Author

Added the underscore dispatch finding here, since it came out of auditing the junk-slot rule rather than out of anything on this branch.

A dispatch slot's name is composed from the segment after the last underscore of a method's mangled name. That is the declared name only when the declared name has no underscore in it, so get_it contributes it - nobody's method - and an override named get_it in a generic hierarchy does not dispatch on Lua. The same shape without the underscore works, which is why nobody has hit it. Present on master, pinned as a known failure with what it currently produces.

I tried the obvious fix and it does not hold, which is the more useful half. LuaDispatchPreparation.declaredName already reads the name off the trace, so pointing both composers at it looked like a rename. But two overloads share a declared name and mangle to Foo_bar and Foo_bar_1, and the segment after the last underscore is also what currently keeps their slots apart - so that change fixes the underscore case and collapses overloaded slots instead. overloadedMethodsDoNotAliasInLuaDispatchTables and moduleProvidedOverloadedOverrideDoesNotCollapseLuaSlots both catch it.

What a method and its overrides share, and what tells two overloads apart, are two different questions, and the mangled name is answering both at once by accident. The replacement is keyed on the declared signature, which is a real change rather than a rename, so it is backlog item 26 with that reasoning written down rather than something bundled in here.

@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: 6d88a4ed9a

ℹ️ 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 segment after the last underscore is also what currently keeps their slots apart. Backlog
* item 26 carries what the replacement has to be keyed on.
*/
@Test(expectedExceptions = Error.class, expectedExceptionsMessageRegExp = ".*Succeed function not called.*")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Assert the actual Lua slot mismatch

If a future dispatch change makes this call return any other incorrect value, the condition still skips testSuccess() and this expected-error test remains green, so it does not pin the described call-site/table-binding defect or distinguish it from a new dispatch regression. Capture the emitted Lua and assert both the slot used by h.get_it() and the Doubler/Holder assignments that currently mismatch.

AGENTS.md reference: AGENTS.md:L262-L270

Useful? React with 👍 / 👎.

@Frotty

Frotty commented Aug 17, 2026

Copy link
Copy Markdown
Member Author

Closing into #1253, which carries the pinned dispatch failure and the backlog entry with the evidence from both failed attempts at the fix.

The div/mod decision and the other recorded decisions are in that branch's BACKLOG.md unchanged.

@Frotty Frotty closed this Aug 17, 2026
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