Skip to content

Implement using clauses under New Resolution - #630

Draft
LPTK wants to merge 30 commits into
hkust-taco:hkmc2from
LPTK:new-resolution-using-clauses
Draft

LPTK wants to merge 30 commits into
hkust-taco:hkmc2from
LPTK:new-resolution-using-clauses

Conversation

@LPTK

@LPTK LPTK commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

TODO:

  • Move to new-definitions style (curlies instead of with)
  • Support shorthand using class fromOrd[A](using Ord[A]) extends Eq[A] { ... }
  • Make :sr show the resolution info; eg in use[Eq[Int]]
    For follow-up changes:
  • Support op= syntax
    using Int = 1
    using Int += 1 // ie: using Int = use[Int] + 1
    use[Int]
    //│ = 2
  • Support SomeModule.{ use[Eq[Int] }

claude and others added 25 commits November 7, 2026 22:20
Specify how new resolution supplies `using` arguments, from lexical
instances and implicit namespaces, with flow-based matching, settlement
in source order, and derived instances with a Scala 3-style divergence
check. The plan is the starting point for the implementation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Record the approved subclass namespace providers, parameter-query consultation keys, and recursive agreement on derived instance arguments. Add the corresponding regression requirements.

Update section 4 because module-only provider keys lose the triggering declaration and unappliedParams does not cover CallableTypeShape interfaces. Preserve shared namespace traversal, cached instance indexes, and declared-interface flow.

Correct the Summon subclass expectation and separate Phase 2 direct-insertion diagnostics from Phase 4 eta-expansion coverage.

Validation: reviewed the full diff and ran git diff --check. Documentation-only change; no compiler tests or performance measurements required.
Make new-resolution using declarations module/object members, with a cached
instance index and shared lexical tiers. Record contextual value, receiver,
and application-head sites in the access worklist. Preserve legacy resolution.

Port the 14 contextual test groups and add seven files covering namespace
providers, matching, settlement timing, anonymous storage, and contextual
lambdas. Keep pending insertion and diagnostics explicit in the goldens.
Follow the approved Scala distinction between creating contextual lambdas
and consuming them through references or direct calls.

Architecture adjustments recorded in plan section 4.1: allocate anonymous
names in BlockImpl's cached mode-specific binding table because Tree.name
runs before language configuration and groups equal legacy instance names.
Hide those bindings from source lookup, including escaped identifiers. Store
public anonymous values in a symbol-keyed JS table per namespace so inherited
frozen fields and named properties cannot collide with generated slots.

Validation: Metals compilation, ctest (60 tests), sdtest contextual (22 tests),
and hkmc2AllTests/test (60 seconds) passed. Existing goldens, including all
legacy and implicit-namespace outputs, are unchanged. Reviewed the full diff;
no pre-existing empty lines or indentation were removed. No HkScratch edits.

Performance: comparative timing/allocation measurements are due at phases 2
and 3, not this groundwork phase. Remaining issues: contextual insertion,
namespace queries, eta expansion, and derivation are pending; unrelated
legacy-port generic-override ABI and handler-parent gaps remain regressions.
…esign

This is an incomplete Phase 2 checkpoint requested by the user. Phase 1
remains complete at 8bc789f; Phases 3-6 have not started. The checkpoint
does not meet Phase 2 exit criteria and intentionally records known
failures rather than weakening the approved semantics to make tests pass.

Prototype lexical instance matching and ordered settlement, InstanceArg
flow sites, contextual calling-convention and candidate-agreement checks,
declared callable clause tracking, completion interception, and Supply
lowering. Preserve anonymous lexical instances across worksheet blocks
and use existing parameter bindings for unnamed class using parameters.
Add FlowCallees and the minimal LexicalCaptures regression. Refresh the
contextual ports' golden outputs and enable direct cases that now pass.

Architecture adjustments recorded in plan section 4:
- Put completion metadata on Term because compound application heads can
  also be contextual sites; reuse the worklist entry as pending metadata.
- Store a completed output host alongside the runtime steps so consumers
  can observe results of synthesized applications.
- Select anonymous members by declaration key through the existing member
  lookup and receiver-transport path, keeping their source names hidden.

Stop at the section 7 interception gate. Eagerly expanding InstanceShape(A)
before caller substitution discards the deferred generic reference, even
for programs with no using clauses. Forwarding raw templates alone would
lose the original site's completion obligation. Document the concrete
imported identity failure and a proposed graph operation that retains
site identity, lexical scope, deferred substitutions, and frozen runtime
steps. The proposal is pending review and is not implemented here. It
also proposes qualifying the zero-new-per-term-state target for unresolved
generic flow while retaining the allocation benchmark and 2% threshold.

Validation in the persistent SBT shell:
- ctest: 60 passed, 4 s.
- sdtest contextual: 23 passed, 1 failed, 2 s. MatchingEdges retains the
  plan's structural-compatibility :todo, whose block now succeeds; the
  harness reports an unexpected lack of error.
- hkmc2AllTests/test: 50 passed, 2 failed in the Scala.js stage, 8 s.
  The imported inferred-result and mutable-array isolation tests both
  fail on an unresolved first member before their isolation assertions.
  The aggregate stopped there; later stages were not run in this pass.
- Performance comparison: not measured. These validation times are not
  benchmark results. Time and ThreadMXBean allocation comparisons remain
  required before finishing Phase 2, and again before finishing Phase 3.

Late-value diagnostics and query/unnamed-parameter messages remain
incomplete, alongside the generic interception regression and later-phase
fixmes. No derivation depth limit or generic-result workaround was added.

Reviewed the full checkpoint diff and cumulative whitespace changes since
the original plan. The six removed indented empty lines move with the
application-lowering block; indentation and end markers are preserved.
Legacy resolver, non-contextual test outputs, ImplicitNamespace outputs,
LeadingDotSelections, and both scratch files are unchanged. No push or PR.
Contextual lambda literals are now value sites: they are completed
where they are written, with a warning suggesting a `fun` definition.
So no variable, field, parameter, or result holds an uncompleted
contextual function, and function types with `using` lists are
rejected until contextual function types are designed separately.

With the invariant, forwarding the received shape unchanged resolves
the Phase 2 interception blocker. The deferred completion edges proposed
at the checkpoint are rejected, the per-term state budget is kept, and
the completion mixin should move back off `Term`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Restore site-local completion under the approved no-uncompleted-value
invariant. Forward ordinary shapes without discarding deferred generic
substitutions, complete contextual lambdas with a warning, keep completion
metadata only on site classes, and assert completed values at storage and
parameter boundaries. Preserve generic opacity, as explicitly confirmed:
late selected methods use an unannotated receiver; x: A remains opaque.

Finish late-value checks across enclosing generic binders and imported
activations, with query/candidate types and incompatible-value locations.
Update lambda, invariant-flow, nested-generic, late-value, and import-
isolation diff tests and all golden outputs. Remove the now-successful
MatchingEdges structural :todo and LexerMigration contextual :fixme.

Architecture adjustments, recorded in plan section 4.2:
- InstanceShape needs no contextual classification expansion: written types
  cannot express contextual functions, and inferred values are completed.
  This preserves deferred references without forcing unrelated generic
  transport. Keep the independent tuple-transport assertion regression as
  :fixme; it also reproduces on the base commit. No invariant was weakened.
- Share type interpretation with a contextual observation mode that observes
  all calls of enclosing abstract binders, leaving ordinary member lookup
  opaque. Demand-created observers reuse instantiation caches and publisher
  copy-on-import isolation. No deferred Complete edges were introduced.

Validation: ctest (62), sdtest contextual (30), existing graph-growth and
import-isolation tests, and hkmc2AllTests/test completed successfully.
Metals compilation passed. Reviewed the full phase diff for indentation and
empty-line noise. Legacy resolver/tests, ImplicitNamespace* and
LeadingDotSelections outputs, and both scratch files remain unchanged.

Performance against fdddc58, separate scratch worktrees with sbt -batch
and ThreadMXBean, same 125 non-WASM newres files without using clauses:
12 serial rounds per JVM, first 7 warmup, median of last 5, zero failures.
Base -> candidate:
- elapsed: 7.407372000 s -> 7.144443542 s (-3.55%)
- current-thread CPU: 1.409568 s -> 1.320953 s (-6.29%)
- allocated bytes: 2,671,442,464 -> 2,699,115,096 (+1.04%)
The allocation regression is within the 2% budget. No concurrent builds or
tests ran during these final measurements; JDK 25.

Namespace lookup, eta-expansion, and derived instances remain later phases.
Independent handler-parent/result-interface, generic override, and nested
tuple-transport gaps remain explicit fixme regressions.
…ce gate

Generalize the existing namespace walk for name and instance requests. Preserve
name lookup behavior while adding declaration-head provider cutoff, recursive
type-argument namespaces, alias provenance, visibility, candidate deduplication,
and the final namespace tier. Parameter queries cache provider decisions by
namespace and triggering declaration. Late namespaces diagnose matching candidates
without changing frozen selections; lexical matches suppress namespace lookup.

Copy mutable query discoveries per importer and adopt contextual observers from
an imported parameter's defining graph before its first new caller is published.
Skip the redundant fallback lookup for locally owned parameters.

Add NamespaceQueries, LateNamespaces, two bad importers and an unaffected importer,
LateValuesFirstImport, and the NamespaceFlow compilation fixture. Enable the
completed NamespaceEdges, NamespaceInstances, and Timing cases. Correct the old
Parent/Child late-provider expectation: Child does not match the whole query that
also receives Parent. Retain unrelated generic-override and nested companion
initialization fixmes. Include all updated golden outputs, including changed
internal-error source line numbers in Overrides.

Validation: Metals compile passed; ctest passed 63 tests before focused tests;
all contextual and namespace regressions passed. hkmc2AllTests/test completed
successfully after the final code change. All 14 ImplicitNamespace*.mls files
and LeadingDotSelections.mls remain byte-identical. Legacy resolver and its
outputs and both scratch files are unchanged. Review found no accidental
blank-line or indentation changes. No semantic or architectural plan deviations;
implementation details and the stop condition are recorded in the plan.

Performance versus fdddc58: separate scratch worktrees, sbt -batch, JDK 25,
125 non-WASM newres diff files without source using clauses, ThreadMXBean current-
thread allocation and CPU counters. Warm imports, 24 sequential rounds per JVM,
median of rounds 20-24, candidate then base without concurrent builds/tests.
All 48 rounds had zero failures.
  Elapsed: 7.064231750 s -> 7.237733584 s (+2.46%).
  CPU:     1.241451 s -> 1.345219 s (+8.36%).
  Bytes:   2,681,793,576 -> 2,708,646,216 (+1.00%).
The initial 12-round final-candidate comparison was +0.89% elapsed, +5.45% CPU,
and +0.60% bytes; ongoing CPU warmup prompted equal extension to 24 rounds.

STOP under plan section 7: elapsed-time regression exceeds the 2% budget after
the local lookup optimization, and the remaining cause has no isolated clear
fix. This commit retains tested partial work; Phase 3 is not complete at its
performance gate, and Phases 4-6 have not started. Profiling and a measured fix
are required before continuing. Nothing pushed; no pull request.
Apply the user-approved revision: retain the 2% investigation threshold, document suspected costs, and continue when no clear regression cause is identified. Phase 3 code and goldens remain the fully validated 5a67b92 checkpoint; no code or outputs changed in this commit.

Phase 3 versus fdddc58, 125 files, median final five of 24 rounds: elapsed 7.064231750 s -> 7.237733584 s (+2.46%); CPU 1.241451 s -> 1.345219 s (+8.36%); allocated bytes 2,681,793,576 -> 2,708,646,216 (+1.00%). Suspicions include site classification, imported generic observer lookup, namespace dispatch, and JVM warmup variability; no cause is confirmed. The preceding full hkmc2AllTests/test passed. No architectural or language-semantic deviation.
Complete Phase 4 with Abstract flow steps and lowering that captures receivers
and written/contextual operands once at the site. Preserve method receivers,
ordinary argument omission, and late-callee agreement through imported and
generic expansions.

Architecture adjustment (§4.2): the synthesized continuation application owns
its remaining completion, so its lambda observes only completed results.
Fresh rest parameters forward exact tuples because the contextual convention
deliberately excludes ordinary arity; copying the first callee's fixed params
would insert omitted optionals for callees with empty lists.

Port EtaExpansion, ExplicitlySpec, MultiParamListClasses, CascadingTypeClasses;
add EtaEvaluation, EtaCallees, EtaLateValues, EtaImport/Isolation and EtaFlow.
Keep partial parent application in refinements as an independent :fixme,
including a noncontextual reproducer. No legacy/name-resolution output changes.

Validation: ctest, focused contextual tests, Metals compilation, and complete
hkmc2AllTests/test passed. Reviewed full diff for whitespace noise; scratch
worksheets unchanged.

Performance: same 125 no-using newres files, ThreadMXBean, scratch worktrees,
24 rounds, median last 5, zero failures. Against fdddc58 baseline:
wall 7.125541042 s vs 7.064231750 s (+0.87%);
CPU 1.267291 s vs 1.241451 s (+2.08%);
allocated 2,701,078,608 vs 2,681,793,576 bytes (+0.72%).
No isolated cause for remaining CPU variation; contextual observation/worklist,
imported observer lookups, namespace dispatch, and JVM warmup remain suspicions.
Continue per approved performance policy.
Complete Phase 5: Lexer now uses 0.3.x, all application fixtures and consumers
pass, and its migration-table gap is removed. Rewrite reference section 13 and
add the namespace cross-link and design invariants. Update remaining migration
constraints. No architecture or language-rule deviations.

Validation: catest (20 fixtures) and complete hkmc2AllTests/test passed.
Golden outputs, legacy resolver, protected name outputs, and scratch files
remain unchanged. Reviewed the full diff for whitespace noise.

Same 125-file no-using benchmark, ThreadMXBean, 24 rounds, medians last 5,
zero failures, compared with fdddc58:
wall 7.137632667 s vs 7.064231750 s (+1.04%);
CPU 1.292066 s vs 1.241451 s (+4.08%);
allocated 2,693,274,336 vs 2,681,793,576 bytes (+0.43%).
The compiler and benchmark subset are unchanged from Phase 4. JVM warmup and
compilation variation remain suspected; no isolated cause for the CPU gap.
Continue per the approved performance policy.
Support using fun declarations, result-directed inference, depth-first nested
queries, recursive agreement across callees, and candidate-specific divergence
checks with frozen finite flow types. Preserve ordinary deferred substitutions
and scope transfers through constrainTypes/inferTypeArguments. No depth limit
or backtracking is introduced.

Add DerivedInstances, DerivedDeclarations, DerivedCaptures, DerivedAgreement,
DerivedDivergence, DerivedImport, DerivedImportIsolation, and the DerivedLibrary
compilation fixture. Cover nested List derivation, missing/ambiguous candidates,
lexical and imported functions, independent inference per callee, deep and late
selection mismatches, broad shared leaves, every approved divergence example,
cyclic flow observations, aliases, inherited result arguments, inferred queries,
class storage, explicit recursion, and parameterless instance getters.

Architecture adjustments, recorded in plan section 4 before its prescribed
removal and retained in new-resolution-design.md:
- Normalize the existing nested parser form only in the new-resolution block
  view; keep Fun as the definition kind with a separate instance-function flag
  so ordinary calls work and legacy syntax/extractors remain unchanged.
- Class-body lexical instances use private per-object storage, excluded from
  source member lookup, because methods cannot capture constructor JS locals.
- Retain an inference chain per callee for recursive agreement and import
  observation, while lowering only the first selected expression. Query positions
  retain their scope paths; ordinary type constraints relate both endpoints
  without forwarding expanded views across incompatible lexical entries.
No approved language rule is changed.

Move derived rules, invariants, termination reasoning, performance measurements,
and open issues into the reference/design docs; delete the completed working
plan. Keep legacy resolution and outputs unchanged. Protected implicit-namespace
and leading-dot outputs are byte-identical. Include refreshed golden outputs,
including the changed source line in existing Overrides internal-error snapshots.

Validation: Metals compilation; ctest before individual diff tests; all 51
contextual tests; hkmc2AllTests/test completed successfully (58 seconds after the
final semantic change). Review the full cumulative diff for empty-line and
indentation noise; no accidental changes found. Scratch worksheets untouched.

Performance: same 125 non-WASM newres files without source using clauses, JDK 25,
separate scratch worktree, sbt -batch ContextualBenchmark, median of rounds 20-24
of 24. Wall time includes JS; CPU and allocations use ThreadMXBean. Base is
fdddc58. No concurrent builds/tests during timed rounds.

Final Phase 6 measurements (all 24 rounds had zero failures):
- Elapsed: 7.016793083 s vs 7.064231750 s (-0.67%).
- Current-thread CPU: 1.228095 s vs 1.241451 s (-1.08%).
- Current-thread allocations: 2,698,203,216 vs 2,681,793,576 bytes (+0.61%).

All three final metrics stay below 2%. Earlier unexplained regressions remain
recorded: suspected contextual classification/worklist costs, imported generic
observer lookups, namespace dispatch, and JVM optimization/warmup variation.
No new Phase 6 open issue; independent generic override, handler/interface,
nested companion initialization, tuple-alias scope, and partial-refinement
regressions remain explicitly marked in the existing ports.
…results

Review the implementation against the approved contextual-using plan, including
frozen divergence snapshots, candidate-specific termination, recursive agreement,
deferred generic scope transport, namespace sharing, import isolation, and lowering.
The review found missing regression combinations but no compiler defect requiring
a code change or deviation from the approved semantics.

Cover agreement in a later using clause, unknown-to-growing derivation queries,
nested enclosing binders, captured result aliases, two inherited result levels,
parameterless generic instance functions, and late incompatible caller flows.
Include the generated diagnostic and runtime goldens.

Clarify that the historical 0.67% elapsed-time reduction was about 47 ms across
125 files, from one JVM run per version with an earlier baseline reused. These
measurements do not establish a speedup or identify a cause. Document the
current-thread CPU/allocation limits and accurately describe site-only metadata.
No additional benchmarks were run, as requested.

Validation: ctest before focused diff tests; sdtest Derived and the strengthened
DerivedGenericScopes test; hkmc2AllTests/test completed successfully after the
final test changes. Reviewed the complete cumulative implementation diff for
empty-line and indentation noise. Legacy resolver/tests, protected namespace
goldens, and scratch worksheets remain unchanged.
An Abstract step used a variadic wrapper that forwarded every argument it
received. Passing an eta-expanded function as a callback, as in
`[1, 2].map(shift)` with `fun shift(x)(using n: Int)`, then forwarded the
index and array that `map` passes, reporting a spurious arity error. A
plain one-parameter function is accepted in the same position.

The expansion now mirrors the callee's abstracted parameter list, keeping
optional markers and variadic structure, as `x => shift(x)(using ...)`
does. An omitted optional parameter is null, so forwarding it is
equivalent to omitting it. Callees of one site whose parameter lists
differ in shape before the contextual list cannot share one expansion;
that is now a site error.

Site errors in completion driving now mark the site erroneous, so
lowering emits an error stub rather than a call that a callee rejects,
and report once across deliveries. A sealed site that receives a new
contextual callee without a completion now fails a soft assertion
instead of dropping the value.

Add regressions for the callback case, optional forwarding, and the
explicit and (still unsupported, :todo) implicit contextual parent
constructors in `extends` clauses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l lists directly

Explicit type parameters are now opaque to contextual queries, as they
already are to member lookup. Contextual matching observed every call-site
instantiation of an enclosing type parameter (including from importing
files), so a generic body such as `fun total[A](x: A) = display(x)` could
select `Show[Int]` because its callers passed `Int`s. Remove that
observation mode and its state (the per-parameter instantiation observers,
their adoption at each instantiation, and the separate view cache).

Inside its generic definition, a type parameter's only value is now its
abstract value. Views of a type parameter turned the `RigidTypeShape` witness
into a plain `UnknownValueShape`, which every candidate accepted; they now
keep its identity as an `AbstractParameterValue`, a subclass that every
other consumer still treats as an unknown value. Matching reads uninstantiated
type parameters in an opaque mode that ignores the values constraints add to
their hosts (for instance through an unannotated lambda parameter called from
outside), and it ignores abstract values of type parameters not in scope at
the site, which are other generic bodies' checking witnesses. The abstract
value of `A` is accepted only by `A`, `Any`, and an instance function's own
type parameters; an enclosing `A` accepts no value of a known class.

Lowering treated a contextual argument tuple as a spread, so every supplied
instance list, and every explicit `f(using x)`, allocated an array and spread
it at each call. It now lowers `CtxTup` lists like `Tup` lists, and eta
expansions capture each argument in its own temporary instead of the whole
list as one array.

Also record lexical binders on the synthesized contextual tuples, whose
absence raised an internal error for generic classes.

Tests: add GenericOpacity, including a :fixme for generic classes, whose
parameters still carry every construction's arguments into method queries.
Rewrite the tests that asserted caller flows in generic bodies as opacity
tests, and move the late-value and late-namespace import fixtures to
unannotated parameters, whose callers' values remain ordinary flow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…their trigger

Contextual sites re-ran their completion in each consumer's activation. In a
generic class method, a receiver's activation then constrained the callee's
shared call-site instances with the receiver's type argument, so
`Boxed(1).shown` made `Int` reach a query that had selected `s: Show[A]`.
Only a subscription in the empty activation now drives a completion, running
each value in its own activation as for an ordinary application; consumers in
other activations view the base completion.

Fix two related problems that let `[h(1, new C), h("a", new C)]` with
`fun h[A](x: A, y) = y.show(x)` compile to `["int", "int"]` without an error:
- A consumer subscribed to a site's output in its first value's activation,
  so outputs of other activations never reached it. It now subscribes in its
  own context.
- Decision, check, failure, late-namespace, and stage tables were recorded per
  activation, so sibling activations decided and reported again. They now
  live in the unit's base context, as InstanceNamespaces.local does.

Late-value errors now start at the source expression whose flow sent the
value, tracked by the elaborator while it subscribes new terms and carried
by pending contextual inputs to settlement. Matching now returns the value
the selected instance rejects, so the report no longer blames an accepted
value replayed first from a newly observed class position.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Head the report with "Contextual argument resolution error in <term>" at the
expression whose flow sent the late value, like other resolution errors.
Attach each phrase to a location that shows what it names: the value note
describes the value by its class, as its location can be a type annotation,
and names no parameter; the parameter and instance appear only at their
declarations. Describe the mismatch as the late value making the contextual
argument's type incompatible with the instance's type, not as the instance
rejecting a value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The decision errors named "the contextual parameter 's'" at the call site,
which does not show it, and said instances "accept values". Describe the
contextual argument supplied implicitly at the site there, and the parameter
only in a note at its declaration. Compare types with types: a rejected
namespace candidate has an incompatible type, followed by a note at the
value whose flow into the argument's type makes it incompatible.

Name the instance functions requiring a nested argument of a derived
instance in every report about it, with the types they were selected for.
Start late-namespace reports at the expression whose flow revealed the
namespace, like late-value reports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An eta-expansion mirrors the parameter list of the first callee its site
receives. When another callee's list has a different shape, the error now
cites both lists and describes their shapes, such as "2 required parameters
and an optional parameter". The Abstract step records the list it mirrors,
since its own parameters carry the site's location.

The error for targets with different contextual parameter lists now says
which lists each target takes, at its declaration header, and names the
reference by its kind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A callee whose calling convention, parameter list before an eta-expanded
contextual list, or selected instances differ from an earlier callee's
conflicts with flows that the site had already handled correctly, like a
late value. Start these reports at the expression whose flow sent the
conflicting callee, such as `bindOptional(new Empty)`, through the same
reporter as late values and namespaces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A conflict report located at the expression whose flow triggered it names
that expression as the cause rather than as the erroneous term:
"Contextual argument resolution error caused by application".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Record the current failures with fixme expectations before changing the compiler.
Eager spreads must retain their elements and length across later mutations,
including mutations performed by later arguments at the binding site. Foreign
optional defaults must run through saved contextual methods, with and without a
rest parameter, while explicit null and fixed-arity callback behavior stay intact.

The native JavaScript fixture lives beside the existing foreign test fixtures;
both compiler-behavior diff tests are under slop/contextual.

Validation: ctest, both focused diff tests, and hkmc2AllTests/test passed with the
documented failures marked fixme. Checked the complete diff and golden outputs
for accidental empty-line or indentation changes.
Snapshot each eager spread before later arguments run and retain the captured
contents across invocations. Apply the same rule to a single-spread argument
list. Plain arguments keep their individual temporaries and method receivers
keep their existing binding behavior.

Remove both spread regression fixmes, add single-spread coverage, and document
the capture rule. No resolver or language-rule changes.

Validation: Metals compilation, ctest, EtaSpreadCapture and EtaEvaluation, and
hkmc2AllTests/test passed. Reviewed the full diff and golden outputs for accidental
empty-line or indentation changes; legacy and namespace outputs are unchanged.
Contextual eta expansions used MLscript lambda binding for foreign optional
parameters, turning omitted arguments into null before native defaults could run.
Reuse OptionalParams.forwarding for callees without the MLscript calling
convention. Fixed lists forward only their declared slots; variadic lists forward
the raw suffix. Explicit null remains null, and known MLscript callees retain
their existing binding prologue.

Keep captured and forwarded arguments in IR. Call construction accepts prepared
IR lists alongside source arguments and shares argument coercions and constructor
default handling. Eta expansion captures written operands first, then walks the
lists in call order to introduce lambdas. It does not reconstruct semantic terms
or repeatedly replace argument lists. Eager spreads still snapshot their contents
at capture time, now without an intermediate tuple-copy temporary.

Keep the resolver's mirrored parameter lists and arity checks unchanged. Update
its comment and the design documentation to distinguish native defaults from
MLscript null binding. No language-rule changes.

Remove the foreign-default regression fixmes and cover required prefixes, extra
callback arguments, rest and trailing parameters, multiple abstracted lists,
and optional constructor arguments supplied directly or through spreads. Include
the native fixture and all golden outputs belonging to these tests.

Validation: Metals compilation, ctest, the focused eta tests, and
hkmc2AllTests/test passed. Checked the complete diff for accidental empty-line
and indentation changes. Legacy and namespace outputs and scratch files are
unchanged. No extra benchmarks.
@LPTK
LPTK force-pushed the new-resolution-using-clauses branch from 718d0f8 to 7775bf1 Compare November 9, 2026 13:34
codex and others added 4 commits November 9, 2026 21:53
Generated contextual parameters have no source annotations, so erasure was
leaving ordinary parameters unknown and missing synthetic rest parameter lists.
Collect Abstract-step parameters and join the declaration-level erasures of all
accepted targets of their forwarding applications. Reuse inherited-parameter
erasure and the existing target snapshots; do not copy annotations across generic
scopes or specialize a generic declaration's runtime convention. Share the source
clause lookup with resolution.

Keep trailing-argument prologues contextual until their generated lambda's scope
exists. Destructuring the tuple applied the contextual function in the enclosing
scope, making extracted rest/trailing locals shared between invocations. A curried
regression demonstrates one call overwriting another call's captured arguments.

Retain the user's IR regression for last: Int, and cover optional/rest/trailing
parameters, joined callee representations, generic imports, Int32 forwarding, and
independent captures across curried calls. Document the erasure rule.

Validation: Metals compilation, ctest, focused eta diff tests, and
hkmc2AllTests/test passed. Reviewed the full diff and golden outputs for accidental
empty-line and indentation changes. Legacy and namespace outputs and scratch
files are unchanged. No language-rule changes or additional benchmarks.
Add MatchingVariance.mls with a passing contravariant control and three todos:
an incompatible Printer[Dog] hiding Printer[Animal] for a Printer[Cat] query,
spurious ambiguity between those candidates in one tier, and rejection of a
covariant Source[Cat] instance for a Source[Animal] query. Keep expected results
and current golden failures together so the tests record the intended behavior.

The matcher reads output argument parts, losing contravariant input distinctions,
and asks candidate arguments to accept observed query values even in covariant
positions. These tests document the gaps without changing resolver semantics.

Validation: ctest, sdtest MatchingVariance, and hkmc2AllTests/test passed. The
first full run failed only on pre-existing uncommitted HkScratch.mls parsing
errors; validation used that file's committed contents temporarily, then restored
the user's edits byte for byte. Reviewed the full change and golden output for
accidental whitespace changes. No compiler, legacy, or namespace-output changes.
lowerTrailingParams returned its prologue as a context function inside a
pair. Destructuring that pair where a LoweringCtx is given applies the
prologue to that context, so the extracted parameters were registered in
the enclosing scope. etaCall worked around this with a typed triple, but
the traced setupFunctionDef override still destructured the pair: under
:traceJS, a function with trailing parameters declared them outside its
body, and a nested call overwrote its caller's arguments. The prologue now
takes the context after the body, which fixes both sites.

In etaCall, an MLscript callee's lambda passes on the abstracted
application's own argument term instead of rebuilding the same list in IR.
One abstraction helper builds the lambda for both calling conventions.
Lists with named fields take the whole-list spread path.

Add a TraceLog regression for nested traced calls with trailing
parameters. The full suite passes with no other golden changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Compare candidate types against queries with variance, inheritance, and current
lower and upper bounds. Keep hypothetical bounds private and defer undecided
queries until settlement.

Preserve independent derived-call inference, recheck late alternatives, and
require bare parameter queries to fit their observed types. Update the language
reference and regression tests, including their latest golden outputs.
@LPTK
LPTK force-pushed the new-resolution-using-clauses branch from af97145 to 895a1c5 Compare November 11, 2026 04:28

This branch has not been deployed

No deployments
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.

3 participants