diff --git a/.codex/config.toml b/.codex/config.toml new file mode 100644 index 0000000000..a2e3c7e5ee --- /dev/null +++ b/.codex/config.toml @@ -0,0 +1,7 @@ +[mcp_servers.metals] +# Coursier (cs) and Git must be available on Codex's PATH. +# Resolve the checkout at launch time, including when Codex starts in a subdirectory. +command = "sh" +args = ["-c", 'workspace=$(git rev-parse --show-toplevel) && exec cs launch org.scalameta:metals-mcp_2.13:1.6.9 -- --workspace "$workspace" --transport stdio'] +startup_timeout_sec = 180 +tool_timeout_sec = 600 diff --git a/AGENTS.md b/AGENTS.md index 226ebd6837..88539b272c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,6 +3,14 @@ ## Workflow +When working on Scala code, use the Metals MCP tools when available for semantic +symbol searches, inspection, documentation, and finding usages, and for quick +file or module compilation feedback. Codex's project configuration in +`.codex/config.toml` can launch the headless Metals server automatically. +If Metals is unavailable, continue with source searches and SBT, and report the +connection problem. Metals compilation and test tools do not replace the SBT +test prerequisites and final validation below. + It is best to leave the SBT shell open (by just typing the `sbt` command line) and issue commands directly in that shell, as SBT startup is very slow. @@ -21,6 +29,11 @@ Any commit that does not include the latest changes to test outputs will fail th Please also read the files in `.github/skills/hkmc2-difftests`. +Use `.mls` diff tests for compiler behavior. Do not add Scala tests that duplicate +that coverage or merely pin internal representation or assertion details. Reserve +unit tests for properties diff tests cannot reliably observe, such as concurrency, +cache isolation, bounded graph growth, or algebraic laws; state the extra coverage. + When you create a commit, author it under your (the agent's) own identity, not under the user's identity. Set this identity for the commit command only @@ -73,6 +86,24 @@ When appropriate, explain the history of what led to the current implementation, especially if it involves non-obvious decisions/trade-offs or if alternative approaches were considered and rejected. +Comments must be understandable from the current code alone, without the conversation, +commit history, or an earlier version of the implementation. Explain the concrete +invariant, behavior, or failure being prevented. Name the relevant data and operations; +avoid vague claims about where a "lifecycle" or "responsibility" belongs. If history +or a rejected alternative matters, include enough context to explain the tradeoff +after commits are squashed. + + +## User-Facing Diagnostics + +Never use `showDbg`, `shwDbg`, raw AST/IR `toString` output, or other debug-only +representations in user-facing error messages, warnings, or diagnostic notes. +Use source-level names and user-facing descriptions, with source locations to +identify the relevant definitions or expressions. Do not expose internal symbol +IDs, resolution marks, or compiler representation details to users. +Debug representations belong only in internal logs and explicitly requested +debug output, not in ordinary diagnostics. + ## Editing Style @@ -97,4 +128,3 @@ to ensure that no needless empty-line changes are included in the PR. If you fin When working with IR representations, please refer to the "Important design notes" in `hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala`. - diff --git a/doc/new-resolution-design.md b/doc/new-resolution-design.md new file mode 100644 index 0000000000..082c503019 --- /dev/null +++ b/doc/new-resolution-design.md @@ -0,0 +1,240 @@ +NOTE: This document was written by Codex Astra and has not been deeply reviewed; +it is not meant to be official documentation and +is in fact likely to contain parts that are unintelligible to readers who lack sufficient context. + + +# New-resolution implementation invariants + +This note describes the resolver's implementation contracts. User-facing rules +are in the [language reference](reference.md#resolution-interfaces); outstanding +ports and implementation gaps belong in the [migration worklist](new-resolution-suite-migration.md). + +## Inference ownership and completed references + +[`NewResolverState`](../hkmc2/shared/src/main/scala/hkmc2/semantics/NewResolverState.scala) +owns the inference graph and caches. Listeners have type +`TermShape => NewResolverState ?=> Unit`, so imported callbacks access hosts +through the consuming state. Importing copies accessed hosts' candidates and +listeners lazily; caches consult individual source entries on demand. It must +not copy or enumerate an exporter's entire resolver maps. + +Type-parameter references retain their originating host. This lets exporter-private +inference on third-party parameters survive re-exporting. Legacy annotation +interpretations also remain local, rather than attaching consumer callbacks to +shared prelude syntax. + +Completion seals each file or diff-test block's recorded decisions and original +host data read by erasure/lowering. Symbol flow and elimination listeners remain +active against private host data, but cannot change completed reference targets. +This applies even between worksheet blocks sharing an elaborator state. An unknown +reaching a sealed elimination can still report an error without changing its target. + +[`PublisherTest`](../hkmc2/jvm/src/test/scala/hkmc2/PublisherTest.scala) +checks independent consumers and unchanged source candidates and listeners, +cycles, reentrant replay, transitive private inference, and bounded copying +in the presence of unrelated exporter hosts. `newres/GenericMethods.mls` covers +exported mapped arrays and imported generic functions and methods. + +## Captures and generic flow + +[Instance types and parameter constraints](new-resolution-type-value-flow.md) +specifies instance wrappers, input/output constraints, variance, canonical binder +instances, shared body views, and partial-signature inference. Its +[shape roles](new-resolution-type-value-flow.md#value-views-consumed-schemes-and-activation-events) +section distinguishes captured value substitutions, consumed callable schemes, +and inference-event activations, with examples and their representation invariants. +[Regular structural types](new-resolution-regular-types.md) specifies normalization +and the conservative recursion restriction. Do not duplicate those algorithms in +member lookup or value-flow handling. + +Context fragments compose from a definition to its consumer; argument flow +traverses that composition in reverse. A class and its constructor share one +resolution boundary. Methods capture their enclosing instance scope as well as +the enclosing function scopes. Alias qualification and structural type-field +projection introduce no value boundary; modules introduce no invocation boundary. + +[Lexical paths in resolution](new-resolution-scopes.md) derives the normal form, +boundary agreement, and depth bound from walks in the lexical scope tree. It also +states the endpoint obligations on producers and the separate requirement for +stable identities in recursive inference graphs. + +A normalized mark path contains entries followed by exits, with no repeated +lexical boundary in either direction. `Shape.scala` enables assertions for this +invariant. Checking each new tail costs linear time in its depth; it must not be +replaced by truncating or widening paths. In particular, wildcard exit followed +by wildcard entry is not an identity: it can discard a caller's activation ID. +Reference transport uses the same operations as ordinary value flow. + +The prelude uses its own `#lang(0.3.x)` directive. Its loader applies file +configuration before elaboration and erasure, and supplies the block's original +symbols for builtin lookup during bootstrap. Its signatures therefore carry the +same `Capture` syntax as other new-resolution declarations. + +Legacy consumers can read completed new-resolution signature symbols through +`legacyResolvedSym`. The lookup asserts that the referenced block is complete and +shares erasure's symbol-selection rules, including ambiguity and error checks. +It does not resolve imported syntax again or observe incomplete candidate sets. + +`newres/SpecializationCaptures.mls`, `RecursiveEnvironment.mls`, and +`ConstructorFieldRecovery.mls` exercise captured specialization, recursion, and +partial construction. `PrimitiveMembers.mls`, `Arrays.mls`, `GenericMethods.mls`, +and `MutableArrays.mls` exercise prelude signature captures and array member paths. +Receiver reconstruction and omitted-argument context precision are +[deferred improvements](new-resolution-future-work.md). + +## Declared interfaces and exposure checking + +Type interpretations retain arrows, type arguments, and lexical captures. +Generic aliases and inherited declared interfaces substitute their arguments; +declared member selections carry those contexts without reading implementation +value flow. An unannotated member read through a declared interface produces an +unknown shape, rather than consulting its initializer or method body. +Inferring these missing member types is a [deferred improvement](new-resolution-future-work.md#inferred-member-signatures) +that also requires override compatibility checks. +Callback parameter types constrain implementation parameters, and callback results +constrain inferred type arguments. `newres/DeclaredTypes.mls` covers these paths, +separate signatures, tuple constraints, and distinct generic instantiations. + +[`InterfaceExposure`](../hkmc2/shared/src/main/scala/hkmc2/semantics/InterfaceExposure.scala) +checks values reachable through a file's exposed interface before completion. +Worksheet blocks run this pass only under strict resolution. It seeds unannotated +parameters with `UnknownValueShape` and follows returned functions, record/tuple +contents, public instance members, and both interpretations of exposed overloads. +Callable result annotations constrain returned implementations through their +declared domains. Private helpers retain local inference unless their values escape. + +Discovery uses a queue and temporary observers of shape publishers. Each reached +shape is processed once, with listeners discovering subsequent candidates; +there is no repeated whole-graph scan. Observers detach before completion and +are never copied into consumer states. Imported new-resolution definitions have +already had their interfaces checked and are not recursively checked by importers. + +Unknown values and unresolved spreads carry shared, lazy provenance. Messages, +locations, and diagnostic chains are materialized only on error; provenance does +not affect shape equality. Diagnostics can identify the parameter and the storage +and return steps that expose it. `newres/InterfaceExposure.mls` covers discovery, +typed closures, overloads, and later exposure of a private definition from an +earlier block; its expected failures also document remaining precision gaps. + +## Structural and dynamic shapes + +Record fields have their own `BlockMemberSymbol`s. Lookup follows overwrites and +spreads in source order, preserving selected values' shapes and captures. Named +fields in term tuples are expressions, not type annotations. Tuple shapes group +them into one trailing record, matching lowering while retaining source evaluation +order, last-write-wins lookup, computed-key uncertainty, and property identities +across imports. See `newres/NamedFields.mls`, `ImportedNamedFields.mls`, and +`RecordInterfaces.mls`. + +Tuples preserve zero-based projections and expose the builtin Array interface. +An Array callback receives candidate shapes from every element. The +[language reference](reference.md#10-arrays) specifies mutable-array behavior and +the restriction imposed by explicit element annotations. + +For a mutable literal, all element reads and writes use the builtin `Array[T]` +parameter's symbol. Marks distinguish allocations and enclosing calls. Cache the +nominal view before subscribing to the initializer so empty arrays can receive +writes and recursive arrays can refer to themselves. Numeric projections, spreads, +patterns, and callbacks all use the same element endpoint. + +The nominal interface is located at the literal's use site. Its element reference +carries the allocation exit; the interface itself does not. Member lookup already +exits the nominal class scope. Adding the allocation exit to the entire view would +therefore cross that boundary twice. Initializer shapes enter the allocation +context before reaching the parameter host. + +When an array is returned or otherwise exposed to callers, `InterfaceExposure` +also follows its element shapes. A function stored in that array must be checked +for calls from outside the compilation unit, just like a directly returned +function. `newres/MutableArrays.mls` and `CompilerTest` cover element flow, +separation between calls and importing compilation units, and exposed functions. +See the [migration worklist](new-resolution-suite-migration.md) for the remaining +`splice` contract and storage-reassignment gaps. + +When an array's element shape is unknown, indexed access produces an unknown +shape; that does not authorize arbitrary member access on the result. +Tuple-pattern transfer handles leading, trailing, and rest elements, including +declared Array element types. See `newres/Arrays.mls` and `TuplePatternBindings.mls`. + +An unknown-length tuple segment retains a deferred element field, just as a +fixed tuple position does. Tuple views instantiate both through the same field +representation, and indexing, array interfaces, argument transfer, and patterns +observe that field through `listenTupleField`. Unknown length does not erase the +element's type arguments or captured environment. A positional selection after +a spread includes every field that can occupy that position, including a fixed +suffix when the spread is empty; positions before a spread remain precise. +`newres/ArraySpreadViews.mls` covers these cases and distinct generic callers. + +`DynShape` is distinct from `UnknownValueShape`: recursive widening, mutable reads, +and other losses of precision do not license dynamic lookup or calls. Known invalid +alternatives still report errors alongside dynamic candidates. Constructor patterns +narrow unknown inputs to declared class interfaces, retaining unknown type arguments +and provenance for unannotated fields instead of specializing them from local calls. + +Dynamic selections have no fabricated nominal definition. Lowering retains the +runtime receiver and property name. Dynamic values propagate through record/tuple +spreads and unique wildcard opens; competing wildcard receivers still need +disambiguation. Class projections, constructor patterns, and type references require +known, unambiguous identities. See `newres/Dynamic.mls`, `Records.mls`, +`SpreadCalls.mls`, and `loose/Targets.mls`. + +## Recursive getter results + +Parameterless definition bodies have a shared result host, cached by definition +and generic activation before following the body. Recursive references subscribe +to that host instead of traversing the body again. An unproductive cycle has no +normal result candidates; a productive branch publishes candidates through the +cycle, with the publisher suppressing duplicates. No unknown or dynamic fallback +is introduced to stop recursion. + +The host stores results at the body's lexical endpoint, including their activation +events. Each reference still applies its own exit and capture marks. Resolver-state +caches and consumer-local publisher copies preserve this graph across imports +without sharing mutable inference between consumers. `newres/RecursiveGetters` +checks cycles, later candidates, captures, generic specializations, imports, and +runtime getter effects. + +## Normal results of control flow + +Resolution must agree with lowering about which expressions produce values. +Assignments, `drop`, imperative conditionals, and loops return unit; their +operands or branch results must not supply the result's member interface. +A normally completed `try` returns its body's value, preserving its capture +marks, while the `finally` clause runs for effects. `throw` and `continue` supply +no normal result. `newres/ControlFlowResults.mls` checks these contracts, including +backtracking assignment and independent calls through cleanup blocks. + +## Overloads, foreign declarations, and lowering + +Ordinary values select function/constructor overloads; selection receivers select +companion modules. Preserve this distinction through nested captures and opens. +`newres/OverloadedCalls.mls` covers direct, generic, stored-function, module-member, +and captured uses. + +Bare classes can share their name with a function definition that has a parameter +list. The generated function owns the class reference, so JS uses the existing +`.class` property for references, selections, and imports. Arbitrary values, +parameterless functions, and term declarations cannot supply that companion: +their results may be primitive, aliased, or frozen, and must never be augmented. +Instance methods cannot supply per-instance companions either, because their +function objects are shared. Inlining may replace calls to a companion function, +but must preserve its binding while the class is used. WASM identifies constructor +functions by their class symbols, separately from explicit function companions. +Session imports follow the selected definition and cast target, rather than MIR +`freeVars`, which tracks lexical bindings introduced by `Scoped`. Importing a +class must not also import its function companion: the two have independent +signatures and can be shadowed independently in later worksheet blocks. + +Foreign declarations expose call and constructor capabilities explicitly. +The JS backend uses native class values for `new` and patterns without generated +MLscript `.class` storage. Bodyless foreign methods use foreign-call normalization +even when `declare` is on the enclosing class, so JavaScript `undefined` results +follow the existing unit normalization. Prelude declarations are parsed as one +compilation unit, permitting references across blank lines. + +Generated runtime-helper selections, such as `runtime.assertFail`, are synthetic; +they do not provide a fallback for unresolved source selections. Free-variable +collection and assignment lowering must handle resolved direct references and +retain the existing binding/assignment checks. Constructor-pattern resolution +discards every nested capture when recovering class identity; leaving one behind +can make generated patterns test a constructor function instead of its class. diff --git a/doc/new-resolution-future-work.md b/doc/new-resolution-future-work.md new file mode 100644 index 0000000000..4e725f9ca8 --- /dev/null +++ b/doc/new-resolution-future-work.md @@ -0,0 +1,139 @@ +NOTE: This document was written by Codex Astra and has not been deeply reviewed; +it is not meant to be official documentation and +is in fact likely to contain parts that are unintelligible to readers who lack sufficient context. + + +# Deferred resolution design work + +This reference records confirmed implementation gaps and deferred design work. +Current contracts are in [instance types and parameter constraints](new-resolution-type-value-flow.md); +remaining suite ports are in the [migration worklist](new-resolution-suite-migration.md). + +## Upper-bound interfaces + +This interface and checking-witness redesign is deferred future work. + +Instantiation installs `lower <: instance <: upper` with the complete binder +substitution, after recording explicit arguments. This covers callables and by-name +invocations; captured enclosing binders also specialize the bounds. Lower bounds +contribute inference candidates, and upper relations propagate obligations through +later candidates. Dependent and recursive bounds share the existing relation graph. + +Checking and observation gaps remain. An otherwise unconstrained result of +`[A extends Item] -> () -> A` does not expose `Item`'s members. A separately signed +implementation of `[A extends Item] -> A -> Int` cannot use that guaranteed +interface either. Installing an upper constraint does not give the parameter's +candidate host an observable shape. A recursive by-name getter with a lower bound +on a class parameter can also leak that parameter's abstract checking candidate +through inferred body constraints into a declared receiver. The same declared +receiver works when the body contributes no recursive checking edge. + +Do not fix this by publishing the upper bound as an additional lower candidate. +For `[A extends Base] -> A -> A`, inference from a `Child` input must still permit +`Child`-only operations on the result; a second `Base` candidate would incorrectly +forbid them. Conversely, a generic implementation must work for every permitted +`A`, and cannot use a lower bound's members as its checking interface. + +A design must distinguish a parameter's inferred candidates, its guaranteed upper +interface, and its rigid checking witness. It must specify how observation uses +those guarantees with delayed bounds and explicit arguments, without mixing the +checking witness into a real call or losing scope marks. Regression and precision +controls: `newres/QuantifiedBounds.mls`. + +## Receiver reconstruction + +```mlscript +class Box[T](val item: T) with + fun copy() = new Box[T](item) +``` + +Reconstruction needs to retain the old receiver's view of `T` separately from the +new constructor's view. Distinct receiver calls currently lose the returned field's +interface. The constructor-value form `Box[T](item)` has the same precision gap. +The unannotated control, `class Box(val item)` with `copy() = new Box(item)`, mixes +field candidates from distinct receivers. This therefore affects ordinary value +inference as well as explicit type parameters. + +A correction must preserve both receiver and new-allocation contexts using the +ordinary marks algebra. Correcting only a binder substitution does not establish +receiver separation. Regressions: `newres/ConstructorInstances.mls`. + +## Inferred member signatures + +Missing types on selected members should use the selected declaration's contextual +inference graph, consistently with partial function signatures: + +```mlscript +class Item(val value: Int) +class Box with + fun item() = Item(1) +private fun read(x: Box) = x.item().value +``` + +The desired result is to infer `Box.item`'s result and permit `.value`. The current +nominal view does not expose that missing result type. The same improvement applies +to missing parameter, constructor-field, and value-member types. + +Keep the actual receiver's contextual flow on these inferred connections while +using the written nominal type for lookup. A `Base` annotation must not expose +`Child`-only members. Annotated portions retain their written types, and selected +members retain their own schemes, including overloaded term alternatives. + +Override compatibility is part of this work: a base implementation returning +`Item` cannot justify `.value` on every dynamic dispatch result if an override can +return a different interface. Check overriding inputs and outputs against the +inherited interface before completing targets or exposing inferred dispatch +results. Preserve imported reference completion and consumer isolation. +Regressions: `newres/DeclaredTypes.mls` and `PartialSignatures.mls`. + +## Omitted-argument contexts + +Missing generic arguments are ordinary inference holes. Their current substitution +can lose caller separation, even without recursion: + +```mlscript +class First(val first: Int) +class Second(val second: Int) +class Pair[A, B](val value: [A, B]) +private fun second(pair: Pair[Int]) = pair.value.1 +[second(Pair([0, First(1)])).first, second(Pair([0, Second(2)])).second] +``` + +Both results receive both candidates and produce missing-member diagnostics. +Removing the parameter annotation avoids this template-substitution path and +preserves ordinary argument flow. Related cases use a shared alias-body omission +or a mutable `Array` annotation, with and without recursion. + +The hole belongs to the annotation inside `second`. Following the captured `Pair` +template rebases its argument outward with a wildcard exit, then projects it back +with a wildcard entry. Applied to incoming bounds, the exit consumes the call's +identified entry; re-entry cannot restore that ID. This is the specified marks +behavior, not permission to cancel those operations on references. + +A possible representation separates the template endpoint from the supplied +argument environment. Following a formal would use the argument's own endpoint; +following a free reference would use the template's context. This remains an open +design: verify deferred member projection, both constraint directions, polarity, +recursive aliases, and finite canonical reference keys. Dropping template transport +loses enclosing binders, while allocating per-call hole instances violates ordinary +inference. Regressions: `newres/InferenceHoles.mls`, `PartialSignatures.mls`, and +`AnnotationContexts.mls`. + +## More precise regularity analysis + +The current conservative check rejects some finite structural types. The +[regularity reference](new-resolution-regular-types.md#deferred-refinement-for-later-review) +contains two refinements for later review: input/output dependency analysis used +also for environment projection, and accounting for correlations between recursive +arguments after Boolean normalization. Neither is required for the current +conservative implementation. + +## Whole-graph convergence audit + +Binder allocation for fixed definition/site keys, source dependency analysis, +and relation replay have local bounds. A complete argument must also bound the +sites themselves, accepted contextual reference keys, nested environments, and +formula atoms, then establish listener convergence. +Retain marked parameter references and shared recursive edges; do not expand live +bounds into fresh type trees. Test delayed delivery, mutually recursive calls, +changing compound arguments, and independent consumers at the graph level. diff --git a/doc/new-resolution-regular-types.md b/doc/new-resolution-regular-types.md new file mode 100644 index 0000000000..f051433709 --- /dev/null +++ b/doc/new-resolution-regular-types.md @@ -0,0 +1,332 @@ +NOTE: This document was written by Codex Astra and has not been deeply reviewed; +it is not meant to be official documentation and +is in fact likely to contain parts that are unintelligible to readers who lack sufficient context. + + +# Regular structural types and canonical references + +This is an internal reference for structural recursion in new resolution. The +instance-wrapper and variance semantics are specified in +[instance types and parameter constraints](new-resolution-type-value-flow.md). + +## Guarded alias recursion + +Every recursive alias cycle must pass through a record, tuple, function arrow, +or nominal type. Aliases, type applications, unions, intersections, negation, +quantifiers, and wildcard bounds do not themselves supply a guard. An abstract +constructor is not assumed to guard its arguments. Reject unguarded cycles at +their definitions, including unused aliases and cycles through forward or mutually +recursive references. For example: + +```mlscript +type Loop = Loop // rejected +type Choice[A] = A | Choice[A] // rejected +type Left = Right // rejected with Right below +type Right = Left + +type Chain = {next: Chain} // guarded +type Wrap[A] = {next: A} +type Indirect = Wrap[Indirect] // guarded after forwarding A +``` + +Guardedness follows alias substitutions: `Identity[Loop]` forwards an unguarded +occurrence, whereas `Wrap[Loop]` above guards its argument. An unused argument +cannot create a cycle in the expanded alias. Repeated finite applications such as +`Identity[Identity[Int]]` are not recursive aliases. + +The check operates on the completed source graph before Boolean normalization; +absorption does not excuse a written unguarded cycle. The source dependency solver +also computes which formals are used without a structural guard. Alias applications +follow those actual arguments when checking for cycles, rather than treating every +argument as unguarded or every application as a guard. These summaries use the same +finite-set fixed point as free-binder discovery, but structural constructors stop +dependency propagation. The cycle check starts a fresh path inside each constructor +so a guarded use cannot hide an invalid alias's own definition. + +Diagnostics name the alias and point to the recursive reference and declaration. +The resolver does not choose least or greatest solutions for unguarded Boolean +equations. This rule is separate from the finite-representation check below: +a guarded alias can still grow its type arguments without bound. + +## Regularity requirement + +A structural type must unfold into a finite graph of distinct type components. +Check regularity after reducing aliases and normalizing unions/intersections; +textually changing an argument is not itself evidence of unbounded growth. + +```mlscript +type Chain[A] = {value: A, next: Chain[A]} +type Alternating[A, B] = {value: A, next: Alternating[B, A]} +type Reset[A] = {value: A, next: Reset[Array[Int]]} +type Saturating[A] = {value: A, next: Saturating[A | Int]} +type Growing[A] = {value: A, next: Growing[Array[A]]} +``` + +The first four have finite unfoldings. `Growing[Int]` exposes `Int`, `Array[Int]`, +`Array[Array[Int]]`, and so on. It receives a regularity diagnostic, +without widening the type or imposing an expansion-depth limit. + +Canonical references reduce applied aliases, including omitted arguments, and +normalize Boolean combinations and wildcard parts. The implemented check uses one +dependency node per original formal parameter and rejects constructor-bearing dependency cycles before observing the +structural interface. This is an intentionally conservative restriction: it can +reject regular types whose finite unfolding requires distinguishing argument parts +or correlations between successive substitutions. The diagnostic says that the +current check does not support the recursive type; it does not claim to prove +non-regularity. The more precise design below is deferred for later review. + +## Normalization + +`TypeFormula` represents a union of intersections as a set of sets of interpreted +references. It applies associativity, commutativity, idempotence, distribution, +and absorption. It does not inspect nominal subtyping, class disjointness, or the +current candidates of an inference host. For example: + +```text +(A | Int) | Int = A | Int +(A | Int) & Str = (A & Str) | (Int & Str) +A | (A & Array[A]) = A +((A | Int) & Str | Int) & Str = (A | Int) & Str +``` + +`TypeShape.Combined` retains the formula as one type. Supplying a union still +creates one instance wrapper. Constraint propagation approximates an upper union +by constraining every component, and observes lower intersection components +independently. Member lookup still merges record intersection interfaces. +Normalization itself does not turn one supplied union into several independently +supplied types. + +Formula atoms retain their lexical bindings, canonical binder instances, and +ordinary marks. Different written references to the same unbound parameter and +host share an atom within a formula. Ordinary annotation references retain their +own source locations for diagnostics. A live parameter remains a reference; +normalization does not freeze its current bounds. Combined nodes are interned in +the consuming resolution state, using the same inherited-cache discipline as other +type references. + +Applied aliases reduce before they become another type's argument. Both argument +polarities are interpreted in their caller environment, then declaration variance +retains the applicable parts and the alias's formals are substituted. The body +receives the enclosing polarity once; nominal applications compose that polarity +into their retained argument parts before exposing members. A guard on source alias +symbols stops unproductive recursive expansion. Nested arguments are reduced before adding +the outer alias to that guard, so `Identity[Identity[A]]` reduces to `A`. +Reduction stops at nominal, tuple, record, and function constructors. + +A captured alias uses the same application rule as deferred interpretation: + +1. Rebase supplied arguments to the template endpoint using the existing mark + operations. +2. Substitute the formals at that endpoint. +3. Transport the resulting reference back using the existing mark operations. + +This applies to `Chain[B | Identity[B]]` when `Identity` is captured by an enclosing +function. Retaining the captured application as a fresh argument layer on each +projection would prevent a fixed point. Conversely, cancelling its scope crossings +would change the meaning: a wildcard exit followed by wildcard entry can lose the +caller's ID and is **not** an identity. Eager reduction performs those operations; +it does not add any special mark simplification. + +Dependency discovery also follows the source graphs retained by synthetic formulas +and contextual references, without treating their saved bindings as ambient free +variables. Unresolved forward references therefore still delay interpretation. + +For a fixed finite atom set, the antichains of subsets used by `TypeFormula` are +finite. This bounds repeated union/intersection substitution, including alternating +operators. Formula normalization can be exponential in the number of atoms; this +is a representation bound, not a claim of linear complexity. No fresh inference +variables or binder instances are allocated by normalization. + +This does **not** by itself bound the atoms. Constructor growth, retained argument +parts that never become observable, and the existing requirements on finite marked +contexts remain separate obligations. + +Eager reduction and deferred interpretation share the same argument-binding rule. +Omitted positions reuse source-owned holes and follow the same scope transfers as +supplied arguments. For `type First[A, B] = A`, the recursive argument `First[A]` +therefore reduces to `A`; it does not retain a fresh environment on each recursive +constraint. A relevant omission, such as `Second[A]` for `type Second[A, B] = B`, +resets to its fixed source hole. Regressions: `TypeGraphPartialAliases.mls`. + +Written wildcard parts are normalized at their lexical polarities before the pair +is interned. Repeated `out A`, `in A`, or two-part substitutions then select existing +endpoints instead of retaining nested wildcard environments. The pair keeps its +written source so it still overrides declaration variance, and missing parts keep +that source for diagnostics. This does not discard unobserved parts: constructors +in either part still participate in the conservative growth check. + +## Conservative check + +The check connects original formals through alias applications in the reachable +source graph. An edge records whether the argument wraps its source formal in a +non-Boolean constructor. A growing edge on a dependency cycle causes rejection. +Alias and Boolean normalization precede edge construction; unused formals are +removed by existing source dependency analysis. + +Validation waits until the source dependency graph is complete, including forward +references. It runs both when an alias occurs in a written annotation and before +an interface is observed, so an unused annotation cannot avoid the check. Source +visitation and alias expansion use finite source-node and alias-symbol guards; +reachability uses the finite original-binder graph. Accepted and rejected results +are cached by source root in the consuming resolver state. A rejected root does +not reject an unrelated regular child. Omitted arguments reuse their source holes; +no ordinary mark operation is changed. + +That handles ordinary permutations and resets, including: + +```mlscript +type Left[A] = {value: A, next: Right[Array[A]]} +type Right[B] = {value: B, next: Left[Str]} +``` + +The wrapping edge is followed by a reset, so no growing dependency cycle remains. +It also distinguishes an absorbed `A | (A & Array[A])` from the growing +`A | Array[A]`. + +The conservative restriction also rejects this finite case: + +```mlscript +type Chain[A] = {value: A, next: Chain[in Array[A]]} +private fun read[A](chain: Chain[A]) = chain.next.value +``` + +At the positive occurrence `value: A`, substituting `in Array[A]` selects its +missing output part, `Any`. All later `value` components have that output type. +The constructor inside the input part is not an observable growing component of +this structural unfolding. A binder-only edge from `A` to `A` loses that fact. +The worksheet records the rejection explicitly as a current precision limitation. + +Constructor-free wildcard dependencies do not count as growing constructors. +For example, `next: Chain[out A]` is accepted and every positive `value` component +remains `A`. Both parts are still analyzed, so `Chain[in Array[A]]` remains +conservatively rejected. `TypeGraphTermination.mls` covers these boundaries, and +`TypeRelationTest` checks repeated wildcard substitution, marked early/late bounds, +and listener saturation. + +The representation needs the same distinction. Current free-binder projection +retains the whole bound argument whenever a formal is relevant, including parts +not selected by the structural body. Recursive observation can therefore accumulate +input-part environments even when the resulting structural type is regular. +Merely permitting this case in a rejection check would not establish termination. +The conservative check rejects this case before that growth can occur. + +## Deferred refinement for later review + +Track dependencies on argument parts, rather than only on binder symbols. The +analysis must distinguish lexical occurrence polarity and argument-part selection +from the direction of an instance-value constraint. A selected argument denotes +one fixed type for both subsequent input and output constraints. + +Let `I_p(T)` mean interpreting a type expression in its saved lexical polarity +`p`. A reference to alias formal `A` demands the `p` part of its bound argument. +For the parts produced by the current argument interpretation rules: + +| Argument | Input part | Output part | +| --- | --- | --- | +| Invariant `S` | `I_not-p(S)` | `I_p(S)` | +| Written `in T` | `I_not-p(T)` | `Any` | +| Written `out U` | `Nothing` | `I_p(U)` | +| Plain `S` with declaration `in` | `I_not-p(S)` | `Any` | +| Plain `S` with declaration `out` | `Nothing` | `I_p(S)` | + +Written wildcards override declaration variance. Written and declaration-site +input variance both reverse polarity. An invariant argument retains both +interpretations rather than freezing one endpoint before its two uses are known. + +For an invariant actual argument `B`, selecting a part of the callee's formal +selects the corresponding saved interpretation of `B` in its caller environment. +After selecting either argument part, both subsequent constraint directions use +that fixed type. Function inputs reverse lexical polarity; +record fields, tuple fields, and function results preserve it. Scope transport does +not change which argument part is selected and continues to use ordinary marks. + +The analysis can attach finite demand sets to source states consisting of a source +node, lexical polarity, and whether the operation requests the whole interpreted +type or a particular argument part. The demands name original binders and their +input/output parts. Alias application composes these demands with the argument table, +hiding the callee's formals. Forward references delay finalization. Equations range +over a finite set of source states and part demands, so monotone iteration terminates +without unfolding substituted recursive types or allocating inference variables. + +For `Chain[in Array[A]]` at positive polarity, the recursive output demand reaches +`Any`, ending that dependency. At negative polarity, selecting the written input +reverses the argument's saved polarity: its nested `A` demands the previous output +part, which is `Any`. Thus constructor depth does not grow indefinitely in either +polarity. A single binder node cannot express that dependency reset. + +The same demand summaries must govern the saved environments. A projected argument +view should retain references only for demanded parts, together with an explicit +mask recording which parts are available. Selecting an unretained part must assert +an invalid dependency summary; it must not silently invent `Any`, `Nothing`, or an +inference hole. Extreme types in the table come from actual variance semantics, +not from environment pruning. Keep both parts when either may be selected. Preserve +source-owned holes and live inference references without inspecting their current +candidates. Nominal member dependencies remain conservative until their required +parts are known. + +### A further limit of a simple growth graph + +Even part-sensitive dependency edges lose relationships between arguments. This +regular type requires absorption across successive recursive substitutions: + +```mlscript +type Stable[A, B] = {value: A, next: Stable[A | (B & Array[A]), A]} +``` + +Writing `X = A | (B & Array[A])`, its argument states are: + +```text +(A, B) -> (X, A) -> (X, X) -> (X, X) +``` + +The second step computes `X | (A & Array[X])`, which is `X` because `X` already +contains `A`. The next step absorbs `X & Array[X]`. A per-parameter growth graph +still contains a constructor cycle and rejects the definition under the current restriction. +`TypeGraphTermination.mls` includes a recursive observation of this example. + +Consequently, refining binder dependencies into part dependencies is necessary for +environment projection, but it is **not sufficient** to make constructor cycles +an exact regularity test after Boolean normalization. Such a graph provides a +sufficient acceptance criterion; a positive cycle justifies the conservative +restriction, but not a claim of non-regularity. A more precise procedure would need +to account for normalized substitution relationships while terminating on genuinely +growing cases. That design is deferred. Repeatedly unfolding until a cache stops +growing, imposing a depth limit, or assuming a constructor cycle proves growth +would not resolve it. + +Possible refinement steps, requiring review before implementation: + +1. Specify and test the finite, part-sensitive demand equations above, including + both polarities and alias substitution. This analysis itself has a finite + source-graph bound. +2. Use those summaries for checked environment projection, retaining marks and + live references. Verify the variance example and ordinary holes without claiming + that this supplies a complete regularity decision procedure. +3. Separately design a terminating rejection check that preserves correlations + needed by examples such as `Stable`. Use it to relax the current conservative + restriction without admitting unbounded reference environments. + +The whole resolver still needs a bound on accepted reference keys: finite source +and binder identities alone do not bound nested environments. Boolean normalization +bounds formulas only once their atom set is bounded. Existing finite-context +requirements on the marks algebra remain in force throughout. + +## Regression coverage + +`newres/TypeGraphTermination.mls` covers regular recursion, permutations, mutual +resets, unused arguments, transparent and captured forwarding aliases, forward +references, union/intersection saturation, rejected unguarded recursion, and the +variance counterexample. `newres/RecursiveBooleanInterfaces.mls` covers direct, +mutual, captured, and forwarding cycles alongside guarded wrapper controls. Rejected constructor cycles and the two documented precision +limitations use ordinary `:e` expectations. + +`TypeFormulaTest` checks normalization against Boolean truth tables. +`TypeRelationTest` checks a thousand repeated reductions, +unchanged binder allocation and source-listener counts, late bounds, independent +intersection propagation, and the loss of caller identity under captured alias +rebasing. These checks support normalization's local invariants; they do not +establish the whole resolver's termination argument. + +`newres/FunctionIntersectionConstraints.mls` checks that both function components +contribute result bounds and contravariant parameter bounds, with eager diagnostics +also observing each concrete result candidate. diff --git a/doc/new-resolution-scopes.md b/doc/new-resolution-scopes.md new file mode 100644 index 0000000000..e131704d1c --- /dev/null +++ b/doc/new-resolution-scopes.md @@ -0,0 +1,166 @@ +NOTE: This document was written by Codex Astra and has not been deeply reviewed; +it is not meant to be official documentation and +is in fact likely to contain parts that are unintelligible to readers who lack sufficient context. + + +# Lexical paths in resolution + +This note gives the scope calculus used by `Shape.scala`, the invariant needed +by its assertions, and the obligations on operations that construct paths. +The paths describe lexical environments, not the runtime call stack. + +## Scopes and endpoints + +Contract the source's scope tree to the boundaries represented by resolution: +function and method bodies, and class instance bodies. A constructor and its +class name the same boundary. Local blocks, anonymous lambdas, modules, and +objects do not add boundaries in this analysis; their bindings live at the +nearest represented scope. In particular, a type alias performs substitution +and has no value activation. `OuterCtx.resolutionBoundary` makes this decision +at elaboration, before either term or type references are constructed. + +Each inference host has one lexical endpoint. An ordinary parameter's host is +inside its function; a class parameter's host is inside its instance scope. +Instantiating a type parameter changes the host's identity, not this endpoint. +An argument reaches the parameter by traversing the reverse of the callable's +path to the caller. A result traverses that same path forward. Recursion goes +out of one activation and into another along the lexical tree; it does not +add another copy of the function to the lexical tree. + +A core shape describes a value at its origin. Its marks describe a walk from +that origin to the host observing it. A compound shape may contain references +with their own endpoints: tuple fields, declared type arguments, and the head +of an applied constructor are examples. Moving the compound delays movement of +those references until they are observed; it must not reinterpret their hosts +at the compound's destination. + +## Reduction and its proof + +Write `E(b, i)` for entering boundary `b` at static site `i`, and `X(b, i)` for +exiting it. An absent site is a wildcard, used for a lexical capture or a +declared interface that does not identify an activation. Here words are written +in execution order; the linked `Marks` representation stores that order reversed. + +There is one reduction rule: + +``` +E(b, i) X(b, j) = identity if i = j or either site is absent +E(b, i) X(b, j) = no flow otherwise +``` + +The rejected case is an ordinary result of inference, represented by `NoShape`. +It prevents a candidate supplied by one call from returning through an +incompatible call. An entry followed immediately by an exit must name the same +boundary: both steps traverse the edge between the current scope and its parent. +Different boundary names at this point are an incorrectly composed path, not +a reason to discard a possible source program. + +An exit followed by an entry is retained, even for two wildcard sites. On an +incoming activation stack it can remove an identified activation and replace +it with an unidentified one. Cancelling this pair would change subsequent +filtering. It also records the provenance of a closure or instance that left +one activation before entering another. + +For any finite, well-scoped walk: + +1. Each reduction shortens the word by two, so reduction terminates. +2. Removing an entry/exit pair preserves the walk's endpoints. Its stack action + is the identity when compatible; otherwise no activation can traverse it. +3. A nonempty reduced word has only exits followed by entries. Any later exit + after an entry would contain another adjacent entry/exit pair. +4. Its exits ascend a tree and its entries descend a tree. Neither run can + repeat a boundary. Its length is at most the source depth plus destination + depth, independently of runtime recursion depth. +5. Two reducible pairs cannot overlap, since a crossing cannot be both entry + and exit. Reductions of disjoint pairs commute, including rejection. + Thus normalization is independent of grouping, and composition is associative. + +These facts establish the three mark assertions for well-scoped walks: adjacent +entry/exit boundaries agree, and neither entries nor exits repeat a boundary. +They apply to arbitrary lexical depth, call-site labels, recursion, and scope +trees, not only to the programs in the test suite. `MarksTest` also compares +generated walks against a separate activation-stack interpreter, including +every prefix and the depth bound. Its purpose is to check the implementation +of the algebra, beyond what a worksheet's final result can observe. + +This proof has a necessary premise: edges must be composed at the same lexical +endpoint. Finite marks alone do not establish that premise for compiler code. +The assertions remain enabled to catch a violation by a producer. Suppressing +them, treating a boundary mismatch as `NoShape`, or truncating a repeated scope +would hide an incorrectly located host and could silently change resolution. + +## Maintaining the endpoint premise + +The producer rules are compositional: + +| Operation | Endpoint obligation | +| --- | --- | +| Lexical reference | Elaboration adds one `Capture` per represented boundary between the binding and the use. Explicit `this` follows the same rule. | +| Call or constructor application | Enter argument paths in reverse fragment order; leave results in forward order. Curried lists retain the original callable path. Class and constructor symbols share `ResolutionBoundary`. | +| Assignment | Undo the left-hand reference's captures before publishing to its binding host. | +| Inferred member | Observe its definition through the receiver's path; class fields keep their class parameter endpoint. | +| Declared member | Interpret the signature at its declaration, bind supplied class arguments there, then traverse the member/class exits and the path to the use. | +| Type reference | Retain the selected declaration, its receiver bindings, its substitution, and its path. Identity alone is sufficient for erasure, but not for interpreting free binders. | +| Type application | Move supplied arguments back to the template endpoint before substituting; move the interpreted result forward. Omitted arguments stay at their original inference host. | +| Deferred type projection | Compose transport on the `InstanceShape` reference before observation, including references nested in fields or arguments. | +| Import or activation view | Copy or instantiate hosts without moving their lexical endpoints. | + +For declared nominal members, distinguish the receiver's origin from the member's +declaration. A tuple literal can be created inside a function even though its +`Array` methods are declared globally. `splitReceiverPath` partitions the +receiver's leading upward journey: exits from scopes below the declaration's +enclosing scopes belong to the argument bindings' provenance. The remaining +path locates the declaration at the consumer. Tree ancestry makes this a prefix +split; a later exit cannot return to a deeper, unrelated scope. Moving the +member itself through the provenance portion would assign different endpoints +to the same method-parameter host for different receiver candidates. + +`TypeShape.Reference` is the common representation for a syntactic capture and +a qualified type selection. It is an **open** expression: the enclosing +declaration can still substitute its free binders. Receiver bindings override +those ambient bindings, and captured instances override ambient instances. +Interpretation closes the expression with `declaredType` and transports the +result with `transportType`. `TypeShape.Contextual`, in contrast, holds an +already interpreted `DeclaredType` with its own bindings and normalized path. +Confusing the two loses the enclosing substitution or applies it twice. + +For example, in a generic `make[A]`, a local module can declare `Parent` with a +field of type `A`. A nested `read(x: Types.Parent)` must retain the path from +`Types` to `read`. Dropping it reads `A` in `make` but later tries to leave +`read`, violating the endpoint premise. The same rule applies to an alias, +a nested class, a returned module, or a reference through an imported definition. +Type aliases must also be excluded at elaboration: ignoring an alias capture +only in type interpretation would leave the same fictitious boundary on the +term receiver used by a qualified selection. + +## Finite paths and finite inference graphs + +Bounded path length does not by itself prove that inference reaches a fixed +point. Core shapes and synthesized type references need stable identities too. +Publishers deduplicate candidates, and synthesized nodes must be memoized by +their semantic inputs before downstream observation can revisit them. + +Class-pattern refinement is one such operation. For an unknown input tested +against `Array`, it constructs a declared `Array` interface whose element is +unknown. A recursive `every` callback can send that element back to the same +test. Allocating a fresh element-type host on each visit produces infinitely +many different `Array` candidates even though all paths are bounded. + +`patternRefinements` therefore keys the synthesized type by the pattern's syntax +identity, selected class, constructor path, and input shape. The path is part +of the key because the same class can be reached through different captured +activations. For each such tuple there is one refinement graph, installed before +its interface is observed. Revisiting the tuple reuses the graph and publisher +deduplication closes the cycle. Diagnostic provenance does not create new +semantic candidates. The cache lives in `NewResolverState`, so imported graphs +retain the existing consumer-isolation rules. + +This is distinct from the regularity check for recursive declared types and +the widening of recursive tuple spreads. Those operations have their own +finiteness arguments; path truncation is not a substitute for either one. + +`newres/InheritedTypeArguments.mls`, `ScopePaths.mls`, and `NominalMemberPaths.mls` +exercise declaration endpoints and separate activations. `RecursiveEquality.mls` +checks both deep equality and mutually recursive array callbacks without +expected-failure markers. LazyFingerTree and the parser compilation fixtures +exercise the same rules in larger programs. diff --git a/doc/new-resolution-suite-migration.md b/doc/new-resolution-suite-migration.md new file mode 100644 index 0000000000..4fd3c3c16d --- /dev/null +++ b/doc/new-resolution-suite-migration.md @@ -0,0 +1,165 @@ +NOTE: This document was written by Codex Astra and has not been deeply reviewed; +it is not meant to be official documentation and +is in fact likely to contain parts that are unintelligible to readers who lack sufficient context. + + +# New-resolution suite migration + +This document must describe only the **current state** of the migration: active +configuration, remaining work, and validation requirements. Remove obsolete blockers +and historical progress notes when updating it; Git history records earlier states. + +## Scope and status + +Language rules are in the [language reference](reference.md#resolution-interfaces). +Resolver invariants are in the [design notes](new-resolution-design.md), and +backend constraints are in [WASM resolution](new-resolution-wasm.md). + +The table counts semantic worksheets, following their `:.` and `:..` configuration +imports. It excludes configuration files, shared declarations, dedicated `newres` +regressions, root scratch files, parser-only tests, the separate InvalML checker, +and the inactive `ucs/staging` directory. A migrated worksheet can import a legacy +compilation fixture; mixed-mode imports are supported. + +| Diff-test suite | New resolution | Legacy resolution | Total | +| --- | ---: | ---: | ---: | +| apps | 17 | 0 | 17 | +| backlog | 5 | 3 | 8 | +| basics | 79 | 11 | 90 | +| block-staging | 4 | 3 | 7 | +| codegen | 106 | 20 | 126 | +| ctx | 3 | 10 | 13 | +| dead-param-elim | 14 | 0 | 14 | +| deforest | 21 | 0 | 21 | +| flows | 0 | 7 | 7 | +| handlers | 9 | 29 | 38 | +| interop | 10 | 0 | 10 | +| lifter | 11 | 5 | 16 | +| meta | 7 | 0 | 7 | +| nofib | 38 | 0 | 38 | +| objbuf | 2 | 1 | 3 | +| opt | 30 | 0 | 30 | +| std | 8 | 2 | 10 | +| syntax | 11 | 0 | 11 | +| tailrec | 4 | 1 | 5 | +| ucs | 69 | 1 | 70 | +| ups | 60 | 11 | 71 | +| wasm | 23 | 0 | 23 | +| Total | 531 | 104 | 635 | + +| Compilation suite | New resolution | Legacy resolution | Total | +| --- | ---: | ---: | ---: | +| Main | 46 | 4 | 50 | +| Applications | 18 | 2 | 20 | +| Nofib | 38 | 1 | 39 | +| WASM | 1 | 0 | 1 | +| Total | 103 | 7 | 110 | + +`LegacyGenericLibrary` deliberately uses legacy resolution to test mixed-mode +imports. The other legacy compilation fixtures are listed below. Counts describe +source configuration, not the number of test cases reported by SBT. + +## Remaining compilation fixtures + +| Fixtures | Remaining work | +| --- | --- | +| `FingerTreeList` | New resolution exceeds the compilation time limit around recursive trees and tuple spreads. | +| `CSP`, `QuoteExample1` | Quasiquote type selections and wildcard-reference lowering are unsupported. | +| `parsing/Lexer` | Token constructors with trailing `using` parameters need automatic contextual argument insertion. `newres/LexerMigration` reproduces the missing behavior. | +| `parsing/ParseRule` | Recursive rule inference exceeds the compilation time limit when compiling consumers. Callback inputs exposed by `Iter.mapping` also leave `rule.map` with an unknown receiver. | +| `nofib/sorting` | New resolution exceeds the compilation time limit, including with the required `int_of_char(c: Str)` parameter annotation. | + +## Remaining worksheet constraints + +Legacy worksheets require more than enabling the language configuration. The +following constraints identify the current failing behavior and representative tests. + +- **Contextual calls and module checks:** `basics/CompanionModules_Classes` can + produce a function instead of the completed result because implicit `using` + arguments are not inserted. Several `ctx` and module worksheets also lose + required negative diagnostics. `basics/CyclicModuleForwarders` needs a decision + about nominal module-forwarding compatibility. Passing contextual arguments + explicitly would bypass the feature these tests exercise. +- **Constructor and anonymous-object interfaces:** `codegen/ParamClasses`, + `basics/ValMemberSymbols`, and `lifter/AnonClasses` exercise constructor members, partially + constructed objects, or anonymous refinements that lack the required interfaces. + `lifter/PrivateMutableFields` cannot resolve anonymous `new with` expressions. +- **Mutation and returned shapes:** `codegen/SetStmt` retains an initializer's + tuple length after reassignment (`newres/MutationFlow`). `basics/Records` needs + callable result precision across branches with different parameter lists. + `std/LazyFingerTreeTest` loses the private view interface through slicing helpers; + its general slice result also includes finger trees without `materialize`. +- **Generators:** `codegen/Generators` needs the iterator interface produced by + generator lowering. `newres/GeneratorResults` records the unresolved `.next`. +- **Handlers:** handler-generated receiver and member references remain unresolved + in `handlers`, `codegen/ScopedBlocksAndHandlers`, and handler cases in `lifter`. + Receiver flow, resumption arguments, and abortive results need distinct handling + (`newres/HandlerResults`). +- **Tail-call annotations and staged functions:** `tailrec/Annots` needs `@tailcall` + support for getter references and contains an unresolved handler receiver. + `block-staging/Functions` and `codegen/FirstClassFunctionTransform` encounter + unfinished generated symbols. +- **Legacy flow analysis:** the `flows` worksheets run a separate flow pass that + does not handle new-resolution selections and can throw on `NewSel` nodes. +- **Runtime instrumentation and privacy:** `codegen/ObjectMethodDebinding` loses + an expected runtime error. `codegen/PrivateMembers` loses privacy diagnostics + and required runtime errors. These worksheets must preserve their checks. +- **Pattern values and transformations:** `ucs/patterns/where`, `ups/MatchResult`, + `ups/SimpleTransform`, `std/RenderingTest`, and regex worksheets use pattern + values or direct `.unapply`/`.unapplyStringPrefix` access. Range and higher-order + transformations lack result interfaces in `ups/examples/Computation` and + `ups/examples/ListPredicates`. `ups/examples/EvaluationContext2` and the + Hindley–Milner worksheets additionally need recursive member interfaces. +- **Quasiquotes:** `codegen/Quasiquotes` needs type-selection and generated-reference + support. Pattern and quote lowering must preserve source targets and receiver paths. + +Focused coverage for capture identity and nominal interfaces is in `newres/CtxSens`, +`ValCtxSens`, `Projections`, `RecursiveEnvironment`, `SpreadCalls`, `ScopePaths`, +`InheritedTypeArguments`, and `InterfaceExposure`. These tests constrain fixes to +recursive and exposed interfaces; unknown shapes must not silently become dynamic. +`newres/RecursiveGetters` covers cyclic getter inference, productive results, +captured and generic activations, imports, and invalid member access. + +## Migration rules + +- Load the common language configuration before the first blank line. Most suites + use `:.` and a local `.mls` containing `:..`. Lifter worksheets use `:..` for the + language configuration and `:.` for their existing lifting flags. Preserve all + execution flags. `wasm/Binaryen` imports the parent language configuration with + `:..` because it tests JavaScript host tooling rather than the compiler's WASM backend. +- Add ordinary parameter and result interfaces before attributing a failure to + language design. Use shared `Indexable` and `IterableIndexable` aliases from + `decls/Prelude`; avoid invariant `Any` arguments and unnecessary casts. +- Keep host declarations accurate. Array callbacks receive three arguments for + `map`/`filter`/`forEach` and four for `reduce`; use `pass1`, `pass2`, or unused + rest parameters where appropriate. Static callbacks must retain these arities. +- Explicitly open names that shadow builtins, including operators and helpers such + as `equals`, `render`, and `Symbol`. Use dynamic selectors for deliberate host + reflection and undeclared property additions, preserving observable effects. + For tests of checked dynamic access, annotate receivers as `dyn` and keep ordinary + selections: raw `!` selectors omit checks that these tests need to exercise. +- Review values and logs as well as test-runner success. A tolerated error or + an uncalled function can hide a regression. Existing invalid operations may + need compilation-error expectations in addition to runtime-error expectations. +- Keep blocked worksheets on their existing configuration with their source and + goldens intact. Do not add failure suppressions or dynamic escapes to bypass + unresolved language features. + Reproduce compiler failures in focused `.mls` regressions. + +Host-interface precision still needs work for `Array.concat`, `Array.splice`, and +`Array.from`. `Array.reduce` without an initial accumulator also needs a signature +that includes the element type; declared methods cannot overload by arity. +`newres/MutableArrays` covers the missing insertion constraint for `splice`. + +## Validation + +Follow the [repository test workflow](../README.md#running-the-tests-1): + +1. Run `ctest` before worksheet tests to regenerate main runtime dependencies. + Use `catest`, `cntest`, and `cwtest` for application, Nofib, and WASM fixtures. +2. Run affected worksheets with `dtest`, `adtest`, `ndtest`, or `wdtest`; inspect + all rewritten goldens and retain existing successful results and negative intent. +3. Run `hkmc2AllTests/test` for the retained changes. Commit the final golden output + together with the migration, using the agent's own identity for agent commits. +4. Update this inventory and remove resolved blockers. Remove legacy configurations + only when every covered worksheet can migrate without lost coverage. diff --git a/doc/new-resolution-type-value-flow.md b/doc/new-resolution-type-value-flow.md new file mode 100644 index 0000000000..f14182e215 --- /dev/null +++ b/doc/new-resolution-type-value-flow.md @@ -0,0 +1,552 @@ +NOTE: This document was written by Codex Astra and has not been deeply reviewed; +it is not meant to be official documentation and +is in fact likely to contain parts that are unintelligible to readers who lack sufficient context. + + +# Instance types and parameter constraints + +This internal reference describes the current type-flow representation in new +resolution. User-facing rules are in the [language reference](reference.md#resolution-interfaces). +[Deferred improvements](new-resolution-future-work.md) cover receiver reconstruction, +inferred member signatures, omitted-argument contexts, and regularity precision. + +## Type arguments and instance bounds + +Supplying `Int` as the type argument for a contextual parameter `A` affects both +its input and output uses: + +```text +L <: A requires L <: Int +A <: U requires Int <: U +``` + +An ordinary integer argument to `x: A` contributes only a lower bound. For +`pair[A](x: A, y: A)`, integer and string arguments contribute two lower bounds; +neither argument must satisfy the other's type. + +If two distinct supplied types `Int` and `Str` reach the same activation of `A`, +each obligation applies to both. Resolution also approximates an upper union by +constraining both components: `L <: A | B` propagates into both `A` and `B`. A lower +intersection contributes every component separately when inspecting its interface +for constraints: `L1 & L2 <: R` installs both `L1 <: R` and `L2 <: R`. +For example, `(Int -> Int) & (Str -> Str) <: A -> B` constrains both arrows, +producing `A <: Int`, `Int <: B`, `A <: Str`, and `Str <: B`. +These rules collect possible resolution targets without solving +alternative constraint sets or forming products of intersection candidates. +They deliberately approximate type inference; diagnosing concrete incompatibilities +such as `Str <: Int` belongs to type checking, not resolution. + +## Instance wrappers and interface observations + +`InstanceShape(DeclaredType)` describes an instance of a type. Parameter/result +annotations, ascriptions, and annotated fields retain this wrapper until lookup, +application, or destructuring requests an interface through `listenInstanceViews`. +The type itself stays in the `TypeResolution`/`TypeShape` graph. A written interface +restricts observation even when more specific implementation values reach it. + +Member lookup merges the fields of record intersections. Constraint inspection +instead observes each component independently, including through aliases, type +parameters, and inference holes. The type-view cache distinguishes these two +observations so inferred arguments cannot change the interface of an annotation. + +A `DeclaredType` contains: + +- A source `TypeResolution` node. +- Bindings from original formals to contextual argument references. +- A flat substitution from original binders to canonical call-site instances. +- The lexical polarity at which to interpret substitutions. + +An interface view does not replace these references with their current candidates. +Late constraints must remain connected to the same endpoints. Nominal views retain +symbolic parameter endpoints for constructed receivers, including supplied arguments. +Suppliedness is recorded on the canonical parameter instance; marks choose which +activation's supplied bounds receive an obligation. An input obligation does not +add its implementation shape to a supplied parameter's output interface. + +## Finite call-site instantiation and marks + +Each explicitly declared binder is instantiated once per original definition and +**authoritative syntactic instantiation site**. Inline binders and separate +signatures follow the same policy: + +| Operation on an uninstantiated scheme | Authoritative site | +| --- | --- | +| Explicit term-level type application `f[T]` | That type application | +| By-name invocation `make` or `obj.make` | The reference or selection, unless an enclosing type application supplies arguments | +| Ordinary application `f(x)` | The first term application | +| Constructor application | Its explicit type application; otherwise its first term application, or saturated zero-list `new` | + +An annotation such as `Array[Int]` is a type expression, not an invocation. +An ordinary reference to a function with parameter lists can retain its scheme. +A by-name reference has already invoked the computation: retaining its consumed +scheme and independently instantiating it at later uses would misrepresent shared +state returned by that invocation. + +The cache maps `(original definition or source scheme, syntactic site)` to the +complete group of parameter instances. It contains no supplied types, incoming +bounds, marks, or instantiated-definition identities. Install a group before +subscribing to constraints so reentrant observation finds it. Recursion reuses +the group; marks distinguish enclosing activations of a shared inner site. +No other observation, projection, or recursive traversal allocates fresh binders. +`App.resSym`, `New.resSym`, and reference/selection sites provide stable identities; +`typeApplicationSite` memoizes one identity per original `TyApp`. Structural +field constraints likewise reuse one projection site per source field across +recursive and activated views (`TypeGraphProjectionSites.mls`). Class and +constructor views use the same original owner. An anonymous polymorphic annotation +uses its source scheme, and each alternative of an overload has its own owner. + +`TypeShape.Polymorphic` and `DeclaredTypeParameter` retain the original binders and +their bounds. Inputs, results, and nested callbacks receive the site's substitution +before interface expansion. The same substitution is applied to bounds, including +captured enclosing binders. Instantiation connects `lower <: instance <: upper` +after recording explicit supplied arguments, so a lower bound cannot widen a +supplied interface. By-name invocations install these relations when their scheme +is consumed. Recursive and dependent bounds share the existing relation graph. +Using an upper guarantee as an interface for an unconstrained result or a generic +checking body remains a [design issue](new-resolution-future-work.md#upper-bound-interfaces). +Enclosing binders remain lexical captures, not binders of the nested definition +being instantiated. + +### Partial application and explicit specialization + +`f[T]` consumes its scheme even before a term application occurs. `SpecializedShape` +retains the group for an inferred function; an instantiated complete callable view +removes that scheme. Stored aliases and remaining curried lists reuse the group. +A separately quantified result retains its independent scheme. + +For ordinary explicit applications, lexical captures are applied to the callable +before binding caller-supplied arguments. By-name invocation needs the arguments +before observing its result: the resolver records the reference's captures and +rebases those arguments through the ordinary mark operations into the invocation. +It records that invocation so its result is not specialized a second time. +Arity checks also apply to unused specializations. + +## Variance and substitution + +Follow InvalML's [`typeAndSubstType`](../hkmc2/shared/src/main/scala/hkmc2/invalml/InvalML.scala) +and [`constrainArgs`](../hkmc2/shared/src/main/scala/hkmc2/invalml/ConstraintSolver.scala). +Its [`TypeArg`](../hkmc2/shared/src/main/scala/hkmc2/invalml/types.scala) supplies +input and output parts: + +| Argument | Input part | Output part | +| --- | --- | --- | +| `S` | `S` | `S` | +| `in T` | `T` | `Any` | +| `out U` | `Nothing` | `U` | +| `in T out U` | `T` | `U` | + +Declaration variance applies to an unqualified argument. A written wildcard +supplies its own parts and overrides declaration variance. Comparing actual `a` +with expected `b` installs `a.output <: b.output` and `b.input <: a.input`. +Missing wildcard parts are actual top/bottom types, not inference holes. + +A subclass contributes the arguments of the requested nominal ancestor. Each +parent step preserves the child's binder substitution and the parent's scope +marks before installing both variance directions. This applies to constructed +receivers and declared nominal views, including multiple inheritance steps and +captured enclosing binders (`InheritedTypeArguments.mls`). + +A nominal annotation's parent is interpreted inside the subclass instance scope, +whereas the annotated value and its supplied type arguments are outside it. +Capture the arguments used by the parent before substitution, and leave the +subclass scope before applying the value's caller path. `nominalParent` shares +that exit between ancestor constraints and inherited member lookup. Constructed +receivers already carry the exit in their constructor context. + +### Compose substitution polarity through type arguments + +A formal bound to `in L out U` selects `U` positively and `L` negatively. +Function domains and type-argument input parts reverse polarity; results, +structural fields, and type-argument output parts preserve it. Write `I_p(S)` for +interpreting `S` at polarity `p`. For an unqualified argument `S`, an invariant +formal therefore retains both +`I_not-p(S)` as its input and `I_p(S)` as its output. These interpretations can +differ when `S` contains substituted parameters. Declaration-site `in` retains +the input interpretation and supplies `Any` as the output; declaration-site `out` +retains the output interpretation and supplies `Nothing` as the input. Written +wildcards use the same polarity composition and override declaration variance. + +For example, with `Child <: Base`: + +```mlscript +class Box[T](val item: T) +class Receiver[T] with + fun accept(box: Box[T]): () = () +``` + +On `Receiver[in Child out Base]`, `accept` expects `Box[in Base out Child]`. +The domain reverses polarity, and `Box`'s input part reverses it again, selecting +`Base`; its output part selects `Child`. Selecting `Child` once and using it for +both parts would be unsound: a `Receiver[Base]` can be viewed through this receiver +type, but an invariant `Box[Child]` is not a `Box[Base]`. A receiver that writes its +stored `Base` into an `Array[T]` exposes the runtime failure of that rule. + +Resolution propagates these obligations into inferred arguments and preserves +explicitly supplied interfaces. Checking concrete subtype compatibility belongs +to the later type checking phases: an `Array[Child]` annotation remains the read +interface during resolution, while the incompatible call must be rejected by the +type checker. The annotated-array case in `VarianceSoundness.mls` resolves +normally and, with type checking disabled, records the unsafe runtime write. +InvalML regressions check rejection of the corresponding concrete mismatch. + +Aliases bind both argument interpretations before interpreting their bodies at the +enclosing polarity. This avoids applying the enclosing reversal twice: +`Identity[T]`, for `type Identity[A] = A`, must behave like `T` in either polarity. +Captured enclosing binders retain their own bindings. Nominal applications instead +retain the argument pair composed with the application's polarity, since member +signatures subsequently interpret that pair from the member's positive interface. + +`DeclaredType.positive` records this lexical polarity, independently of a later +constraint's direction. `TypeShape.Wildcard` retains written parts; +`TypeShape.Argument` retains both contextual argument parts. +Neither transplants supplied syntax into the callee's binding environment. + +`TypeShape.SelectedArgument` saves one requested part for a scalar occurrence when +the argument is deferred. Both subsequent constraint directions use that same +selected type; an invariant argument makes separate selections for its two parts. +Selections are cached by argument reference and polarity; selecting an existing +selection is idempotent. Forwarding cycles retain subscriptions but stop repeated observation. +For a fixed argument-reference set, selection adds at most two nodes per reference. + +## Shared bodies and contextual constraints + +The inferred body is a shared graph. Instantiated views carry flat original-binder +to instance-symbol substitutions through deferred tuples, records, callbacks, and +closures. They do not copy expanded argument candidates into a new body graph. +Shape substitution is memoized so repeated observations reuse aggregate identities. + +### Value views, consumed schemes, and activation events + +The similarly named forms in +[`Shape.scala`](../hkmc2/shared/src/main/scala/hkmc2/semantics/Shape.scala) +answer different questions. A **scheme** is a callable's explicitly quantified +binders and their bounds. Consuming it chooses the canonical parameter instances +for an authoritative instantiation site; it does not imply that a term argument +list has been applied. + +| Form | Meaning | How it is consumed | +| --- | --- | --- | +| `ContextualShape(source, instances)` | Observe a shared value using these bindings for references inside it. This does not consume the value's own generic scheme. | Member/body observation uses the substitution; `shapeParts` extracts it before application. | +| `SpecializedShape(declaration, arguments, instances)` | This declaration's scheme has already been consumed by explicit type application. Retain its supplied type references and chosen instance group. | `shapeParts` retains the supplied arguments, so `appShape` reuses the consumed group. | +| `ActivatedShapeEvent(value, instances)` | Deliver an inference event to operations running in this body activation. The enclosed value retains its own, independent substitution. | `listen` checks compatibility, unwraps the envelope, and invokes the receiver in that activation. | + +All three `instances` fields map original binders to `TypeParameterInstance` +symbols, but the first two describe the value, whereas the third describes the +receiving operation. `ContextualSymShape` retains a value's substitution while +an overload remains a `SymShape`, before selection produces a term shape. +`ActivatedShapeEvent` accepts either a term shape or a symbolic shape as its payload. + +For a schematic nested definition: + +```text +outer[A](a: A) defines inner[B](b: B) = (a, b), and returns inner. +``` + +The returned `inner` captures an instance of `A`; its own `B` remains quantified. +A contextual view retains the captured `A` without selecting an instance for `B`. +Two later calls can instantiate `B` at their respective sites. Treating every +contextual view as specialized would prevent that instantiation. + +Conversely, in `let g = f[Int]`, the type application has already consumed `f`'s +scheme, even if `g` has not received term arguments. `SpecializedShape` preserves +that fact for an inferred declaration. Subsequent calls through `g` or its aliases +reuse the chosen group. Treating this as only a contextual view would let +`appShape` instantiate the declaration again. A complete annotated +`CallableTypeShape` records the same transition differently: `instantiateCallable` +substitutes its parameter/result references and clears `scheme`. Remaining curried +lists retain those references; a separately quantified result has its own scheme. + +The activation envelope is independent of both cases. Suppose a recursive +`f[A]` passes a value mentioning its caller's `A` into another call of `f`. +Write `A@p` and `A@q` for the instances chosen at two static sites (these are +explanatory names, not additional runtime identities). The incoming value must +still refer to `A@p`, while operations on the callee's shared body run with +`A -> A@q`. Schematically, delivery can therefore carry: + +```text +ActivatedShapeEvent( + ContextualShape(value, {A -> A@p}), + {A -> A@q}) +``` + +Replacing either map with the other would confuse the incoming value's type +references with the callee's parameters. Marks still distinguish lexical +activations when recursion revisits the same static site; these substitutions do +not replace, cancel, or otherwise change mark operations. + +`publishViewed` first applies the value substitution with `instantiateShape`, +then `publishActivated` records the current `NewResolverState.instances` on the +event. `listen` accepts an event when its map and the requested activation agree +on every shared key; absent keys do not conflict. A source listener with an empty +requested map therefore accepts all activations. On delivery, the receiver runs +with the combined compatible activation maps, while the enclosed value keeps its +own references. `NewResolverState.withInstances` shares consumer hosts, and +`inGraph` preserves the incoming activation when invoking an imported listener. +Publishers store `ShapeEvent`, a shared base with two alternatives: an ordinary +`Shape` or an `ActivatedShapeEvent`. The envelope is not a `Shape` or `TermShape`, +so it cannot enter member lookup, application, or mark transport. Its payload is +a `Shape`, preventing nested event envelopes. Semantic listeners receive shapes +only after dispatch. Ordinary shapes remain unwrapped when no activation is +attached; dispatch contextualizes those values using the observing state. + +Event equality includes both the payload and activation. Repeated publication of +one event is deduplicated, while the same shape in distinct activations remains +separate. Publisher replay and imported-host copying retain the complete events; +they do not replace saved activations with the state active during replay. + +### Related interfaces and representation invariants + +The view normal form is enforced by the Scala types. `CoreShape` excludes +`ContextualShape` and `SpecializedShape`, and `MarkedShape[T]` retains its core's +type parameter. `AppShape.receiver` has type +`CoreTermShape = CoreShape | MarkedShape[CoreShape]`: an application cannot contain +a contextual or specialized receiver, even beneath marks or earlier applications. +`ContextualShape.source` accepts only the shapes that defer substitution through +a view (`AppShape`, `NewShape`, `DefnShape`, `BaseShape`, and `IntroShape`). It +cannot contain another view, a marked shape, or a shape that stores substitutions +directly. `SpecializedShape.declaration` is a `DefnShape`. + +The symbolic counterpart follows the same rule: `ContextualSymShape.source` is +a `CoreSymShape` (plain or declared), so it cannot wrap another contextual symbol. +`CallableTypeShape.paramLists` is a `NELs[DeclaredParams]`: every callable view +has a next argument list, even when that list itself accepts zero arguments. +Consuming the last list exposes the result instead of constructing an empty +callable view. Mapping parameter types preserves nonemptiness with `ne_map`. + +Value captures and body activations use the opaque `TypeSubstitution`, whose +underlying immutable map remains available for reads. Its constructor derives +each key from the instance's original binder. `withOverrides` combines two valid +substitutions with the right operand taking precedence, and `without` removes +shadowed binders. Both preserve the substitution type; arbitrary map updates do +not. This rules out mismatched binder/instance pairs without runtime validation +at every `DeclaredType` construction. + +Consequently, `shapeParts` can decode one outer marking and one view with an +exhaustive match. It returns a `CoreTermShape`, its captured instances, and any +consumed type arguments. It preserves the application chain: peeling applications +would forget consumed term lists and could mistake a constructed object for a +constructor. Application, callback checking, constructor lookup, and constructor +patterns share this decoder. `ShapeViews.mls` exercises nested captures, +specialized curried values, callback constraints, and constructor-pattern controls. + +These roles must also be distinguished from interpreting an annotated instance: + +| Form | Role | +| --- | --- | +| `InstanceShape` | Preserve a `DeclaredType` reference in a value constraint, including its input and output uses. `listenInstanceViews` interprets it when an operation requests an interface. | +| `NominalInstanceView` | Expose a nominal type's declared members and inherited interface with their argument bindings. | +| `RecordTypeShape` | Expose a structural annotation's declared fields and their argument bindings. | +| `CallableTypeShape` | Expose a declared calling interface. `scheme` records whether quantified binders remain available for instantiation. | + +`ContextualShape` is the general deferred value view, not a mandatory wrapper +around every substituted shape. Tuples and records store their substitutions +directly; `InstanceShape` stores one inside its `DeclaredType`; declared interfaces +substitute their references. `instantiateShape` centralizes these cases and +memoizes their results. Applying another substitution composes flat maps rather +than nesting contextual wrappers. Entries already captured by a value take +precedence over a later observation's map. For an open `CallableTypeShape`, its +own quantified binders are excluded from capture substitution. + +The semantic requirements are independent value and activation substitutions, +and an explicit distinction between open and consumed schemes. The first two +value forms express scheme status separately; the event envelope belongs to the +publisher protocol and shares no value operations with them. + +Keeping both maps does not itself introduce a chain of environments. Their keys +are original binders and their values are canonical instance symbols, not further +substitutions. `withInstances` reuses a fixed base state; `listen` removes the event +envelope before handing its value to the operation. For a fixed finite set of +binders and instantiation sites, there are finitely many such maps and map pairs. +This is a local bound, not a proof of termination of the entire type graph; see +[canonical references and termination obligations](#canonical-references-and-termination-obligations). + +### Checking witnesses and constraint edges + +Generic bodies also receive a checking activation containing `RigidTypeShape` +witnesses. Those witnesses enforce generic opacity even in private/non-strict +code. Interface observation sees an unknown interface; call-site inference must +not copy a checking witness into its bounds. + +`constrainTypes` memoizes directed pairs of `ContextualType` endpoints before +installing subscriptions. Invariant arguments install both directions. A parameter +receives a symbolic instance wrapper; structured and concrete endpoints retain +listeners for future bounds. Delivery before and after edge creation must agree. +Relation replay adds no candidates, listeners, or parameter instances. + +## Scope transport + +See [Lexical paths in resolution](new-resolution-scopes.md) for the scope-tree +model, reduction proof, and producer obligations. Syntactic captures and qualified +type selections share `TypeShape.Reference`; both retain the declaration's +endpoint before the enclosing substitution is applied. + +`ContextualType` pairs a reference with an ordinary normalized mark path. +`transportType` uses the same mark operations as value flow and +flattens existing contextual nodes before interning the endpoint; references contain +one normalized path, not nested transport histories. `inverseMarks` reverses +directions for constraint transport; it is not a mathematical inverse +on candidates. A wildcard exit can consume an entry carrying a call-site ID. +Re-entering without an ID does not restore it. Exit followed by entry must not be +cancelled as an identity, including on deferred references. + +The relevant scope crossings are: + +- Function/method entry and exit, and lexical captures of enclosing definitions. +- Nominal member projection: capture argument references into the class and exit + the class when publishing its selected member view. Inherited members also exit + the class through which they are selected, since the parent reference is + captured into that class. +- Constructor invocation: use the same instance boundary for its class and + constructor, including later parameter lists. + +`Marks` stores the most recent crossing first. `shape.exit(path)` applies the +tail before the head; a list of path fragments is applied from left to right. +`shape.enter(fragments)` reverses both directions and fragment order. Thus +`shape.enter(p :: q :: Nil)` agrees with `shape.enter(q).enter(p)`. +For one boundary, entering at site `i` and then exiting at site `j` cancels when +either site is absent or the sites agree, and rejects the candidate otherwise. +Exiting and then entering retains both crossings. Associativity of composition +does not make these two operations mutual inverses. + +Alias qualification and structural type-field projection introduce no value scope. +Modules introduce no invocation boundary. Transport must follow the source +reference's scope, including references nested inside structured types; inspecting +only whether an outer shape is marked cannot decide this. `transportShape` +composes paths on deferred instance references before delivering them at projection +and result boundaries. Leaving a wildcard entry outside a wrapper could consume a +call exit before the wrapper's own path was considered. + +A declared member is located at its class's definition, where its signature's +`Capture` nodes start. A receiver path starts where the value was created or +annotated instead. Its innermost exits from scopes that do not enclose the class's +definition are the receiver's provenance: they transport the class's argument +bindings, but not the member's own scope. Otherwise a member's scope would move +with each receiver's origin. Receivers created at different depths then disagree +about the scopes that enclose shared nodes such as a method's type-parameter +instance at one call site, and a candidate published through one receiver crosses +a scope twice when read through another. `NewResolver.nominalMember` performs this +split using the enclosing scopes that elaboration records for each type definition. +`newres/Arrays.mls` and `GenericMethods.mls` cover recursive and nested receivers. + +New-resolution syntax records lexical `Capture` nodes, including in the prelude. +Imported type interpretations retain these source references in the consuming +graph; they do not reconstruct capture paths from selected member parameters. + +For mutable array literals, the nominal interface lives at the literal's use site. +The allocation context belongs to its element-parameter reference. Attaching that +exit to the whole interface would duplicate the class exit during member lookup. + +## Partial signatures and inference holes + +Unannotated function parameters and results use ordinary marked inference. +Omitted generic arguments use one `TypeShape.Hole` host per source type-use and +formal position. They receive constraints from arguments, results, and ascriptions; +no invocation allocates another hole or a quantified binder for it. For example, +`Pair[Int]` omits the second formal of `Pair[A, B]`, while bare `Pair` omits both. +Distinct source occurrences have distinct holes; revisiting one occurrence or +expanding its alias reuses it. Declaration variance applies to omitted arguments, +and excess arguments are rejected, including in unused annotations. An empty hole +waits for evidence instead of immediately publishing an unknown candidate. + +Written fragments continue to restrict the interface. Abstract types remain +`TypeShape.Abstract`; variance extremes are not holes. Interface exposure adds +unknown values to genuinely missing external inputs, including callback results. +Those unknown candidates remain alongside any local evidence. + +Current limitations are [omitted-argument context precision](new-resolution-future-work.md#omitted-argument-contexts) +and [missing selected member types](new-resolution-future-work.md#inferred-member-signatures). + +## Canonical references and termination obligations + +Source dependency analysis computes free original binders over the finite +`TypeResolution` graph. Alias/quantifier edges hide their bound formals; an alias +application forwards argument dependencies only for formals used by its body. +Monotone finite-set equations reach a least fixed point. Nominal arguments remain +relevant, and nominal declarations conservatively retain all enclosing explicit +binders recorded by elaboration, including dependencies through local aliases. + +No summary is finalized while a reachable source target is unresolved. Observations, +constraints, and hole exposure wait on shared dependency hosts, then project their +own saved environments. Synthetic formula/argument/selection nodes follow their +saved references instead of reading an ambient binding map. This removes irrelevant +bindings without freezing inference candidates or treating forward references as closed. + +[Regular structural types](new-resolution-regular-types.md) specifies guarded alias +recursion, alias reduction, Boolean normalization, and the conservative +constructor-cycle rejection check. +Accepted recursive references must share graph edges rather than grow substituted +environments. Structural recursion and recursive generic function constraints are +different: a call can add an edge to a reusable parameter instance without eagerly +unfolding its accumulated bounds. + +For a fixed set of instantiation sites, the binder cache allocates finitely many +instances, so flat substitutions over the original binders also have a finite range. +Source holes have stable identities. Normalized paths contain distinct lexical +boundaries in each direction; their bounded length gives finitely many paths only +when their site labels also range over a finite set. +Formula normalization is finite for a fixed atom set. These bounds do not alone +establish finiteness of nested binding environments or the atom set. + +Structural field constraints now reuse source projection sites, and partial +forwarding aliases share deferred interpretation's source-hole binding rules. +Wildcard normalization similarly retains canonical argument parts rather than +nested substitution histories. Their regressions establish these local bounds; +a [whole-graph termination argument](new-resolution-future-work.md#whole-graph-convergence-audit) +must still cover all accepted contextual references, formula atoms, and listener +convergence. Depth limits and dropped marks do not establish a fixed point. + +These representations are internal to resolution. Lowering consumes completed +targets and value shapes; runtime values acquire no type-argument objects. + +## Async fulfillment interfaces + +`Promise[A]` retains its settlement value type. The intrinsic `Awaited[A]` +projects the eventual fulfillment interface, recursively adopting nested promises. +Both `await` and `.then` observe this projection; constructors may receive a promise +through their resolve callback. Factories and async results expose +`Promise[Awaited[A]]`, while `Promise.all` applies the projection to its element type. + +The Promise declarations follow TypeScript's library where the current language +can express the signatures. Executor, resolve, reject, and finalizer callbacks use +`undefined` for TypeScript's `void`; only rejection reasons are unrestricted `Any`. +`then` keeps separate fulfillment and rejection result parameters, `catch` includes +the original fulfillment type, and `finally` preserves it. `Nothing` is the empty +fulfillment type of a rejected promise, including through type aliases. + +Optional first callbacks, zero-argument factories, and default type arguments still +require language support. The rejection callback of `then` uses the existing rest +parameter convention; it, `catch`, and `finally` accept explicit `undefined` or +`null`. Promise inputs currently support native promises; structural +`PromiseLike` values, general JS +iterables, and tuple-mapped `all` results remain outside these signatures. + +An async definition's result annotation describes its body before the async wrapper. +Inferred bodies feed a shared deferred type edge, preserving their lexical marks. +The `Awaited` application stays symbolic until the caller supplies its substitution: +expanding it at the definition would lose inferred generic result parameters. +Fulfillment observers are interned before following nested promises, retaining +late bounds and sharing recursive edges. Exposing a promise also exposes callbacks +inside its payload, so their parameter obligations cannot escape checking. + +Structural thenables currently produce an unknown fulfillment interface. Treating +the original object's fields as the fulfilled value would be incorrect because +JavaScript can replace that object with the value passed to its `then` callback. +`Promises.mls` retains an explicit regression for the unsupported callback inference. + +## Validation + +Graph tests cover bounded instance allocation and replay (`TypeInstantiationTest`), directed +relations, delayed targets and consumer isolation (`TypeRelationTest`), and Boolean +normalization, including substitutions that identify atoms (`TypeFormulaTest`). +`MarksTest` checks activation matching, normalized composition, regrouping, reverse +transport, and wildcard identity loss across nested and sibling scopes. +These algebraic checks cover combinations that worksheet examples cannot exhaust. +`PublisherTest` checks exporter immutability. + +Worksheet coverage under `newres` includes `MutableArrays`, `ContextualInference`, +`InstantiationSites`, `StoredSpecializations`, `SpecializationCaptures`, +`TypeArgumentVariance`, `VarianceSubstitution`, `VarianceSoundness`, `VarianceAliases`, +`AnnotationContexts`, and `TypeGraphTermination`. `Promises` covers async fulfillment, +nested adoption, constructor inference, generic call isolation, and escaping payloads. Deferred cases +retain explicit regression expectations; +see the [future-work reference](new-resolution-future-work.md). diff --git a/doc/new-resolution-wasm.md b/doc/new-resolution-wasm.md new file mode 100644 index 0000000000..e0f38e2901 --- /dev/null +++ b/doc/new-resolution-wasm.md @@ -0,0 +1,46 @@ +NOTE: This document was written by Codex Astra and has not been deeply reviewed; +it is not meant to be official documentation and +is in fact likely to contain parts that are unintelligible to readers who lack sufficient context. + + +# New resolution and WASM + +The [migration worklist](new-resolution-suite-migration.md) tracks remaining ports. +The general [resolver invariants](new-resolution-design.md) also apply to WASM. + +## Configuration and lowering contracts + +`hkmc2/shared/src/test/mlscript/wasm/.mls` inherits the common non-strict +`#lang(0.3.x)` configuration and enables WASM execution. Migrated worksheets +start with `:.` before any blank line so the flags apply throughout the file; +no `:global` is needed. The common language configuration disables JS execution. + +The synthetic Predef import explicitly targets JS. WASM emission checks the +compilation target as well as `:wasm`, so placing the directive at the top of a +file does not send the host-side bootstrap import to WASM. +`newres/wasm/FileWideFlags.mls` covers this across two blocks. + +Type interpretation must finish before erased signatures are consumed, including +forward definitions, aliases, and wildcard names. Eager legacy symbol queries on +new-resolution annotations violate this ordering. Declared parameter types provide +interfaces independently of calls; they do not reveal unannotated field initializer +shapes. `newres/wasm/DeclaredTypes.mls` covers annotated receivers and tuple reads +and writes, which use indexed lowering. + +## Remaining migration and backend gaps + +Nineteen of the 21 existing worksheets use new resolution. The remaining files +retain their legacy configuration and goldens: + +- `wasm/Basics.mls`: the last trial reached a capture assertion in the unannotated + initializer `class Foo(val x) with val y = this.x`. Annotated-receiver regressions + already pass; annotation opacity is a settled rule. +- `wasm/Binaryen.mls`: `WebAssembly.Instance.exports` needs a declared interface. + +The compilation fixture `mlscript-compile/wasm/Wasm.mls` also needs the exports +interface and an interface for its exposed `wasmInst.imports` receiver. + +The callback example and calls through tuple elements in +`newres/wasm/DeclaredTypes.mls` remain expected failures: WASM lacks the required +first-class function representation and anonymous-function lowering. Coordinate +cross-block method changes with the separate implementation work. diff --git a/doc/reference.md b/doc/reference.md index ed47194fb5..c401322e00 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -517,6 +517,10 @@ data class Some[T](value: T) extends Option[T] object None extends Option[Nothing] ``` +Under new resolution, classes, objects, and modules without an explicit parent +inherit Object's declared member interface. Explicit parents and members declared +on the receiver take precedence. + ### Class with Multiple Parameter Lists ```mlscript @@ -673,6 +677,11 @@ f(a: 0) // calls f({a: 0}) f of a: 0, b: 1 // calls f({a: 0, b: 1}) ``` +With positional and named arguments mixed, positional values are followed by +one record containing the named fields. Expressions still evaluate in source +order, and later occurrences of a named field overwrite earlier ones. +Here `a: expression` is a field value, not a type annotation. + --- ## 10. Arrays @@ -682,10 +691,15 @@ f of a: 0, b: 1 // calls f({a: 0, b: 1}) mut [1, 2, 3] // mutable array xs.[0] // index access (uses .at() internally) +xs![0] // dynamic index access xs.(0) // dynamic index access set xs.[0] = v // index assignment ``` +Under new resolution, `xs.[i]` is checked like the array's methods: `xs` must be +an array, and the result has its element type. A dynamic access such as `xs![i]` +is only checked at runtime, and so are operations on its result. + **Spread syntax:** ```mlscript [1, ..xs, 4] // lazy spread β€” xs is expanded in the middle @@ -712,6 +726,30 @@ fun g(x, ...rest) = rest f(1, 2, 3) // xs = [1, 2, 3] ``` +Under new resolution, an annotation on a rest parameter describes the whole rest +array. Tuple values support precise zero-based projections such as `xs.0` and +inherit the declared Array interface. + +Mutable array literals collect element shapes from their initializer and later +writes. Statically resolved element reads are checked against all those shapes. +For example, +after `let xs = mut [First(1)]` followed by `xs.push(Second(2))`, resolution +considers both `First` and `Second` when checking `xs.0`, even though the runtime +value at index 0 is still a `First`. Overwriting or removing an element does not +remove its shape from consideration. Resolution does not track individual +positions or the array's length. + +An explicit annotation such as `Array[Base]` makes element reads use the members +declared by `Base`. Storing a `Child` that extends `Base` does not permit access +to `Child`-only members through that annotation. + +Array callbacks such as `map` receive the element, index, and array; their +declared function interfaces must account for these arguments, for example with +unused or rest parameters. `reduce` also passes the accumulator first, and must be +given an initial value: `xs.reduce((acc, x, ...) => acc + x, 0)`. Without one, +JavaScript uses the first element as the initial accumulator, which the declared +signature does not describe. + --- ## 11. The Ultimate Conditional Syntax (UCS) @@ -946,6 +984,13 @@ if r is { 'x: x } then x ### `as` Pattern Alias +With `newResolution` enabled, capitalization distinguishes binding from matching: +`p as name` binds the result of `p` to the lowercase name, even if that name is +already in scope. `p as Name` matches the result of `p` against the uppercase +constructor name; it does not introduce a binding. Constructor names follow the +usual lookup rules, including selective and wildcard opens. An unknown uppercase +name is an error. + ```mlscript fun map(f) = case Some(x as n) then Some(f(n)) @@ -1265,6 +1310,49 @@ fun f(x: Int): Str = String(x) val v: Option[Int] = Some(1) ``` +### Resolution Interfaces + +With new resolution (`#lang(0.3.x)`), annotations determine the interface available +to member lookup and calls. A receiver annotated `Base` exposes Base's declared +members and preserves virtual dispatch; concrete arguments cannot add subclass +members to that interface. The same rule applies to parameters, result annotations, +separate signatures, and constructor fields. Reading an unannotated member through +an annotated interface currently does not infer its shape from its initializer or +method body. Inference of missing selected-member types is a planned improvement. +Structural record annotations likewise expose only their declared fields. + +Compilation files check that their exposed functions and values work for inputs +admitted by their interfaces. A public `fun read(x) = x.a` needs an interface for +`x` even if local callers all provide `{a: 1}`. A private helper can use local +inference, but returning it or storing it in an exposed record or tuple makes its +interface subject to the same check. Callable result annotations provide the +declared parameter interface when checking returned functions. + +Worksheet blocks perform this exposed-interface check only under +`#lang(strictResolution: true)`. Non-strict resolution allows multiple selection +or call targets, but still rejects missing targets and known invalid operations. + +Generic parameters remain opaque regardless of strict mode, visibility, or local +callers. For example, `private fun read[A](x: A) = x.a` is invalid because `A` +does not supply member `a`, even when every call supplies a record with that field. +Explicit and inferred type arguments still substitute into result interfaces: +an identity function with parameter and result type `A` preserves its caller's type. + +### Dynamic Values + +JavaScript imports (including package imports), `globalThis`, and explicit dynamic +selection or instantiation introduce dynamic values. Ordinary selections and +calls on those values are checked at runtime and produce dynamic values. +Use `value as dyn` for an explicit dynamic result or `fun useValue(x: dyn)` +for a dynamic parameter. Type aliases can also denote `dyn`; dynamic construction +uses `new!`. + +These rules apply in both strict and non-strict resolution. A value whose interface +is unknown is not automatically dynamic. If inference includes both a dynamic +candidate and a known candidate with an invalid operation, the known error is +still reported. Dynamic values also do not remove the need for an unambiguous +class or type identity in constructor patterns, class projections, or type references. + ### Generic Type Parameters Covariant, contravariant, invariant: @@ -1279,12 +1367,83 @@ Bounded type parameters: fun f[A <: Num](x: A): A = x ``` +Under new resolution, supplying a type argument affects both inputs and outputs. +For an invariant `Array[A]`, supplying `Int` means that lower bounds of `A` are +checked against `Int`, and upper bounds must accept `Int`. Passing an ordinary +integer value to `x: A` instead contributes only a lower bound; it does not fix +all uses of `A` to `Int`. + +Use-site type arguments have input and output parts: + +| Argument | Input | Output | +| --- | --- | --- | +| `S` | `S` | `S` | +| `in T` | `T` | `Any` | +| `out U` | `Nothing` | `U` | +| `in T out U` | `T` | `U` | + +For an unqualified argument, the declaration's `in` or `out` annotation selects +the corresponding form. An explicitly written use-site variance supplies its own +parts. Substitution selects the output part in positive positions and the input +part in negative positions. Function inputs and type-argument input parts reverse +polarity; results and type-argument output parts preserve it. An invariant argument +must be interpreted in both polarities before substitution selects its bounds. +For example, a method input `Box[T]` on `Receiver[in Child out Base]` becomes +`Box[in Base out Child]` when `Box` is invariant. Declaration-site `in` reverses +polarity just like a written input part; declaration-site `out` preserves it. + +Each explicit generic parameter is instantiated once at the expression that +specializes or invokes its definition: `f[T]`, a by-name reference/selection, or +otherwise the first term application. Stored specializations and later curried +argument lists share that instantiation. A by-name reference invokes its body even +without parentheses, so later uses of its result share any state it created. +An ordinary reference to a function with parameter lists can retain its generic +scheme until specialization or invocation. + +Missing parameter/result annotations use inference. Omitted generic arguments +likewise count as inference holes rather than anonymous generic parameters; written +parts continue to restrict the interface. Currently an omitted argument inside a +shared annotation or alias can mix inferred types from different callers. These +annotations therefore do not always preserve the precision of a wholly unannotated +parameter. Concrete type-mismatch diagnostics are also incomplete during the +new-resolution migration. + +Type-argument inference approximates a union in an expected type by propagating +constraints into every component. An intersection in an actual type contributes +each component separately: `L1 & L2 <: R` installs both `L1 <: R` and `L2 <: R`. +Resolution uses these approximations to collect member targets without searching +alternative constraint sets. Ordinary member +lookup still combines the fields of record intersections. + ### Structural Record Types ```mlscript type Point = { x: Num, y: Num } ``` +Under new resolution, recursive type aliases must be guarded: every recursive +cycle must pass through a record, tuple, function arrow, or nominal type. +`type Loop = Loop` and `type Choice[A] = A | Choice[A]` are rejected, even when +unused. Alias applications follow the guards in the alias body: a wrapper defined +as `type Wrap[A] = {next: A}` permits `type Loop = Wrap[Loop]`. + +Recursive structural types must also have a finite graph representation of their +unfolding. The current check conservatively rejects constructor-bearing cycles +between used type parameters, after alias and union/intersection normalization. +For example: + +```mlscript +type Chain[A] = {value: A, next: Chain[A]} +type Reset[A] = {value: A, next: Reset[Array[Int]]} +type Saturating[A] = {value: A, next: Saturating[A | Int]} +type Growing[A] = {value: A, next: Growing[Array[A]]} +``` + +The first three are supported. `Growing[Int]` would expose `Int`, `Array[Int]`, +`Array[Array[Int]]`, and so on, and is rejected. The check also rejects some finite +types whose regularity depends on variance or relationships between recursive +arguments. Its diagnostic indicates a limitation of the current check. + ### Function Types ```mlscript @@ -1336,6 +1495,32 @@ open Iter // open all members do open M; ... // locally open ``` +With `newResolution` enabled, wildcard opens are consulted only when a name has +no explicit binding in the current scope or any enclosing scope. Explicit +bindings include local variables, parameters, member definitions, imports, and +selective opens such as `open Stack { Cons, Nil }`. An inner wildcard open does +not shadow an explicit binding in an outer scope. Moving a wildcard open before +or after an explicit binding does not give it priority over that binding. + +For example, inside the function below, `x` refers to the explicitly bound `x`, +even though `M` also provides a member named `x`: + +```mlscript +val x = 1 +module M { val x = 2; val y = 3 } +fun example() = + open M + x + y // explicit x; y from M +``` + +When no explicit binding exists, lookup considers the wildcard opens visible at +that occurrence. These rules apply to the name itself, before choosing its term, +class, or pattern interpretation, so an explicit binding also takes precedence +over an overloaded class or function supplied by a wildcard open. + +If wildcard lookup finds several distinct members with the requested name, the +reference is ambiguous; the order of the opens does not choose a winner. + ### `open M in` Scope ```mlscript diff --git a/hkmc2/js/src/test/scala/hkmc2/CompilerTest.scala b/hkmc2/js/src/test/scala/hkmc2/CompilerTest.scala index d97c9cec72..12dc9e4eca 100644 --- a/hkmc2/js/src/test/scala/hkmc2/CompilerTest.scala +++ b/hkmc2/js/src/test/scala/hkmc2/CompilerTest.scala @@ -271,6 +271,223 @@ class CompilerTest extends AnyFunSuite: assert(valueDefn.flatMap(_.inlinerBodySummary |> Option.apply).exists(_ is summary), "The second importer should reuse the published summary instead of replacing it") + test("generic method inference does not mutate shared prelude parameters"): + val fs = new InMemoryFileSystem(loadStandardLibrary()) + given cctx: CompilerCtx = CompilerCtx.fresh(fs, paths, Config.default(io.Path("/"))) + given DebugPrinter = new DebugPrinter + given TL = new TraceLogger: + override def doTrace = false + given Raise = diagnostic => fail(diagnostic.toString) + val prelude = cctx.getPrelude(paths.preludeFile).ctx + val map = prelude.builtins.Array.defn.get.body.members("map").asTrm.get.defn.get + val parameter = map.tparams.get.head.sym + val originalShapes = parameter.shapes.toVector + val originalListeners = parameter.shapeListeners.length + val callbackSignature = map.params.head.params.head.sign.get + val originalInterpretation = callbackSignature.typeInterpretation + + fs.write("/Generic.mls", """module Generic with + | fun identity[A](x: A): A = x + |""".stripMargin) + fs.write("/First.mls", """#lang(0.3.x, strictResolution: true) + |import "./Generic.mls" + |class Item(val first: Int) + |[0].map((x, ...) => Generic.identity(Item(1))) + | .map((x, ...) => x.first) + |""".stripMargin) + fs.write("/Second.mls", """#lang(0.3.x, strictResolution: true) + |import "./Generic.mls" + |class Item(val second: Int) + |[0].map((x, ...) => Generic.identity[Item](Item(2))) + | .map((x, ...) => x.second) + |""".stripMargin) + val compiler = new MLsCompiler(_ => summon[Raise]) + compiler.compileModule(Path("/First.mls")) + compiler.compileModule(Path("/Second.mls")) + + assert(parameter.shapes.toVector == originalShapes, + "Consumer inference must not publish into the shared prelude parameter") + assert(parameter.shapeListeners.length == originalListeners, + "Consumer inference must not attach listeners to the shared prelude parameter") + assert(callbackSignature.typeInterpretation == originalInterpretation, + "Interpreting a legacy signature must not cache consumer listeners on shared syntax") + + test("imported inferred results leave the cached definition's listeners and candidates unchanged"): + val fs = new InMemoryFileSystem(loadStandardLibrary()) + given cctx: CompilerCtx = CompilerCtx.fresh(fs, paths, Config.default(io.Path("/"))) + given DebugPrinter = new DebugPrinter + given TL = new TraceLogger: + override def doTrace = false + given Raise = diagnostic => fail(diagnostic.toString) + fs.write("/Generic.mls", """#lang(0.3.x, strictResolution: true) + |module Generic with + | class Known(val known: Int) + | val mapped = [Known(4)].map((item, ...) => Known(5)) + | fun identity[A](x: A) = x + |""".stripMargin) + val compiler = new MLsCompiler(_ => summon[Raise]) + compiler.compileModule(Path("/Generic.mls")) + val prelude = cctx.getPrelude(paths.preludeFile).ctx + val library = cctx.getElaboratedBlock(io.Path("/Generic.mls"), prelude) + val module = library.compilationUnit.defaultExport.get.asModOrObj.get.defn.get + val identity = module.body.members("identity").asTrm.get.defn.get + val parameters = identity.params.flatMap(_.params.map(_.sym)) ::: identity.tparams.get.map(_.sym) + val before = parameters.map(p => (p.shapes.toVector, p.shapeListeners.toVector)) + List("first", "second").foreach: field => + val path = s"/$field.mls" + fs.write(path, s"""#lang(0.3.x, strictResolution: true) + |import "./Generic.mls" + |class Item(val $field: Int) + |Generic.identity(Item(1)).$field + |Generic.mapped.map((item, ...) => item.known) + |""".stripMargin) + compiler.compileModule(Path(path)) + assert(parameters.map(p => (p.shapes.toVector, p.shapeListeners.toVector)) == before, + "A consumer must not change the cached definition's inference graph") + + test("imported recursive structural aliases preserve their type arguments"): + val fs = new InMemoryFileSystem(loadStandardLibrary()) + given cctx: CompilerCtx = CompilerCtx.fresh(fs, paths, Config.default(io.Path("/"))) + given DebugPrinter = new DebugPrinter + given TL = new TraceLogger: + override def doTrace = false + given Raise = diagnostic => fail(diagnostic.toString) + fs.write("/Types.mls", """#lang(0.3.x, strictResolution: true) + |module Types with + | type Chain[A] = {value: A, next: Chain[A]} + | fun step[A](chain: Chain[A]): Chain[A] = chain.next + |""".stripMargin) + val compiler = new MLsCompiler(_ => summon[Raise]) + compiler.compileModule(Path("/Types.mls")) + List("first", "second").foreach: field => + val path = s"/$field.mls" + fs.write(path, s"""#lang(0.3.x, strictResolution: true) + |import "./Types.mls" + |class Item(val $field: Int) + |private fun read(chain: Types.Chain[Item]): Int = + | Types.step[Item](chain).next.value.$field + |""".stripMargin) + compiler.compileModule(Path(path)) + + test("a supplied compound type stays inside one instance wrapper"): + import semantics.{InstanceShape, Marked, Statement, Term, TermShape, TypeShape} + val fs = new InMemoryFileSystem(loadStandardLibrary()) + given cctx: CompilerCtx = CompilerCtx.fresh(fs, paths, Config.default(io.Path("/"))) + given DebugPrinter = new DebugPrinter + given TL = new TraceLogger: + override def doTrace = false + given Raise = diagnostic => fail(diagnostic.toString) + fs.write("/Types.mls", """#lang(0.3.x, strictResolution: true) + |module Types with + | fun identity[A](x: A) = x + |Types.identity[Int | Str](1) + |""".stripMargin) + val compiler = new MLsCompiler(_ => summon[Raise]) + compiler.compileModule(Path("/Types.mls")) + val prelude = cctx.getPrelude(paths.preludeFile).ctx + val unit = cctx.getElaboratedBlock(io.Path("/Types.mls"), prelude) + val module = unit.compilationUnit.defaultExport.get.asModOrObj.get.defn.get + val definition = module.body.members("identity").asTrm.get.defn.get + val parameter = definition.tparams.get.head.sym + def applications(statement: Statement): List[Term.App] = + val here = statement match + case app: Term.App => app :: Nil + case _ => Nil + here ::: statement.subStatements.toList.flatMap(applications) + val calls = applications(unit.term) + assert(calls.length == 1) + val state = unit.state.newResolverState + assert(state.allocatedTypeInstanceCount == 1) + val site = calls.head.lhs match + case application: Term.TyApp => state.typeApplicationSite(application) + case _ => fail("Expected an explicit type application") + val instance = state.instantiateTypeParameters(definition.tsym, site, parameter :: Nil)(parameter) + assert(state.allocatedTypeInstanceCount == 1, "Observation must reuse the specialization's binder") + assert(!parameter.shapes.exists: + case Marked(_: InstanceShape, _) => true + case _ => false + , "Specialization must not publish supplied arguments into the source binder") + val supplied = instance.currentShapes(using state).toList.flatMap: + case value: TermShape => value match + case Marked(instance: InstanceShape, _) => instance.tpe.resolution :: Nil + case _ => Nil + case _ => Nil + assert(supplied.length == 1, "A union argument must not become two supplied type arguments") + assert(supplied.head.shapes.toList match + case TypeShape.Combined(formula) :: Nil => + formula.clauses.size == 2 && formula.clauses.forall(_.size == 1) && + formula.atoms.flatMap(_.resolution.shapes.collect { case TypeShape.Nominal(cls) => cls.sym.nme }) == Set("Int", "Str") + case _ => false) + + test("mutable array parameter flow stays private to each importer"): + val fs = new InMemoryFileSystem(loadStandardLibrary()) + given cctx: CompilerCtx = CompilerCtx.fresh(fs, paths, Config.default(io.Path("/"))) + given DebugPrinter = new DebugPrinter + given TL = new TraceLogger: + override def doTrace = false + given Raise = diagnostic => fail(diagnostic.toString) + val prelude = cctx.getPrelude(paths.preludeFile).ctx + val parameter = prelude.builtins.Array.defn.get.tparams.head.sym + val originalShapes = parameter.shapes.toVector + val originalListeners = parameter.shapeListeners.toVector + fs.write("/Arrays.mls", """#lang(0.3.x, strictResolution: true) + |module Arrays with + | fun empty() = mut [] + | fun singleton[A](x: A) = mut [x] + |""".stripMargin) + val compiler = new MLsCompiler(_ => summon[Raise]) + List("first", "second").foreach: field => + val path = s"/$field.mls" + fs.write(path, s"""#lang(0.3.x, strictResolution: true) + |import "./Arrays.mls" + |class Item(val $field: Int) + |let xs = Arrays.empty() + |xs.push(Item(1)) + |xs.0.$field + |Arrays.singleton(Item(2)).0.$field + |""".stripMargin) + compiler.compileModule(Path(path)) + assert(parameter.shapes.toVector == originalShapes, + "Mutable element inference must not publish into the shared Array parameter") + assert(parameter.shapeListeners.toVector == originalListeners, + "Mutable element inference must not attach listeners to the shared Array parameter") + + test("an exported generic selection is rejected before a consumer can specialize it"): + val fs = new InMemoryFileSystem(loadStandardLibrary()) + given cctx: CompilerCtx = CompilerCtx.fresh(fs, paths, Config.default(io.Path("/"))) + given DebugPrinter = new DebugPrinter + given TL = new TraceLogger: + override def doTrace = false + val errors = scala.collection.mutable.ArrayBuffer.empty[Diagnostic] + given Raise = errors += _ + fs.write("/Unsafe.mls", """#lang(0.3.x, strictResolution: true) + |module Unsafe with + | fun foo[A](x: A) = x.a + |Unsafe.foo({a: 1}) + |""".stripMargin) + val compiler = new MLsCompiler(_ => summon[Raise]) + compiler.compileModule(Path("/Unsafe.mls")) + assert(errors.exists(_.theMsg.contains("Resolution error"))) + assert(errors.exists(_.allMsgs.exists(_._1.show.contains("Type parameter 'A'")))) + assert(!errors.exists(_.allMsgs.exists(_._1.show.contains("exposed by this compilation unit")))) + + test("non-strict files still check their exported interfaces"): + val fs = new InMemoryFileSystem(loadStandardLibrary()) + given cctx: CompilerCtx = CompilerCtx.fresh(fs, paths, Config.default(io.Path("/"))) + given DebugPrinter = new DebugPrinter + given TL = new TraceLogger: + override def doTrace = false + val errors = scala.collection.mutable.ArrayBuffer.empty[Diagnostic] + given Raise = errors += _ + fs.write("/Unsafe.mls", """#lang(0.3.x, strictResolution: false) + |module Unsafe with + | fun foo(x) = x.a + |Unsafe.foo({a: 1}) + |""".stripMargin) + val compiler = new MLsCompiler(_ => summon[Raise]) + compiler.compileModule(Path("/Unsafe.mls")) + assert(errors.exists(_.allMsgs.exists(_._1.show.contains("exposed by this compilation unit")))) + test("compiler can report errors"): val (fs, compiler) = createCompiler() diff --git a/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunnerBase.scala b/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunnerBase.scala index 4b2b8cd6c4..3cd762bbdf 100644 --- a/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunnerBase.scala +++ b/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunnerBase.scala @@ -31,12 +31,11 @@ abstract class CompileTestRunnerBase( val mainTestDir: os.Path = TestFolders.mainTestDir(workingDir) - val validExt = Set("mls") for dir <- compileDirs do { val allFiles = os.walk(dir) .filter(_.toIO.isFile) - .filter(_.ext in validExt) + .filter(TestFolders.isTestSource) lazy val compileTestFiles = allFiles.filter: file => file.segments.contains("mlscript-compile") diff --git a/hkmc2/jvm/src/test/scala/hkmc2/CompilerCacheTest.scala b/hkmc2/jvm/src/test/scala/hkmc2/CompilerCacheTest.scala index e0a2a344b4..6fabb90896 100644 --- a/hkmc2/jvm/src/test/scala/hkmc2/CompilerCacheTest.scala +++ b/hkmc2/jvm/src/test/scala/hkmc2/CompilerCacheTest.scala @@ -1,6 +1,6 @@ package hkmc2 -import java.util.concurrent.{ConcurrentLinkedQueue, CountDownLatch, Executors, ThreadFactory, TimeUnit} +import java.util.concurrent.{ConcurrentLinkedQueue, CountDownLatch, Executors, ThreadFactory, TimeUnit, TimeoutException} import java.util.concurrent.atomic.AtomicInteger import collection.concurrent.TrieMap import scala.concurrent.{Await, ExecutionContext, Future} @@ -15,6 +15,11 @@ import hkmc2.CompilerCache.{ActiveDependencyGraph, ArtifactCache} class CompilerCacheTest extends AnyFunSuite: private given ExecutionContext = ExecutionContext.global + // Allow more time for both worker startup and completion on shared CI runners. + private val (startupTimeout, completionTimeout) = + if sys.env.contains("CI") then (15.seconds, 15.seconds) + else (5.seconds, 10.seconds) + private final class Entry private def newCache(): ArtifactCache[Entry] = @@ -35,9 +40,9 @@ class CompilerCacheTest extends AnyFunSuite: val requests = List(request(io.Path("/A.mls")), request(io.Path("/B.mls"))) val overlapped = - try bothStarted.await(5, TimeUnit.SECONDS) + try bothStarted.await(startupTimeout.toMillis, TimeUnit.MILLISECONDS) finally release.countDown() - Await.result(Future.sequence(requests), 10.seconds) + Await.result(Future.sequence(requests), completionTimeout) assert(overlapped, "Building one path should not hold the cache monitor while it evaluates") @@ -58,11 +63,11 @@ class CompilerCacheTest extends AnyFunSuite: }) val first = request() - assert(buildStarted.await(5, TimeUnit.SECONDS), "The first artifact build should start") + assert(buildStarted.await(startupTimeout.toMillis, TimeUnit.MILLISECONDS), "The first artifact build should start") val second = request() release.countDown() - val firstResult = Await.result(first, 10.seconds) - val secondResult = Await.result(second, 10.seconds) + val firstResult = Await.result(first, completionTimeout) + val secondResult = Await.result(second, completionTimeout) assert(buildCount.get() == 1, "Concurrent requesters should share one build for a path") assert(firstResult eq secondResult, "Concurrent requesters should observe one artifact identity") @@ -128,7 +133,7 @@ class CompilerCacheTest extends AnyFunSuite: def read(path: io.Path): String = if synchronizedRoots.contains(path) then bothRootsLocked.countDown() - assert(bothRootsLocked.await(5, TimeUnit.SECONDS), + assert(bothRootsLocked.await(startupTimeout.toMillis, TimeUnit.MILLISECONDS), "Both root compilations should reach source parsing while holding their path locks") underlyingFs.read(path) @@ -137,9 +142,11 @@ class CompilerCacheTest extends AnyFunSuite: def getLastChangedTimestamp(path: io.Path): Long = underlyingFs.getLastChangedTimestamp(path) + val workerThreads = new ConcurrentLinkedQueue[Thread] val threadFactory: ThreadFactory = runnable => val thread = new Thread(runnable, "compiler-cache-deadlock-reproducer") thread.setDaemon(true) + workerThreads.add(thread) thread val executor = Executors.newFixedThreadPool(2, threadFactory) val executionContext = ExecutionContext.fromExecutorService(executor) @@ -160,7 +167,11 @@ class CompilerCacheTest extends AnyFunSuite: compiler.compileModule(file) })(using executionContext) - Await.result(Future.sequence(List(compile(fileA), compile(fileB))), 10.seconds) + try Await.result(Future.sequence(List(compile(fileA), compile(fileB))), completionTimeout) + catch case timeout: TimeoutException => + val workerStacks = workerThreads.asScala.map: thread => + s"${thread.getName}: ${thread.getState}\n${thread.getStackTrace.mkString("\n")}" + fail(s"Concurrent circular imports did not finish:\n${workerStacks.mkString("\n\n")}", timeout) assert(diagnostics.asScala.exists(_.theMsg.contains("Circular imports")), "A circular import diagnostic should be reported") finally diff --git a/hkmc2/jvm/src/test/scala/hkmc2/MarksTest.scala b/hkmc2/jvm/src/test/scala/hkmc2/MarksTest.scala new file mode 100644 index 0000000000..86d751456d --- /dev/null +++ b/hkmc2/jvm/src/test/scala/hkmc2/MarksTest.scala @@ -0,0 +1,131 @@ +package hkmc2 +package semantics + +import org.scalatest.funsuite.AnyFunSuite +import hkmc2.syntax.{Fun, Tree} +import hkmc2.utils.*, shorthands.* + + +/** Algebraic laws over paths and activation sites complement worksheet examples: + * in particular, check all wildcard/identified combinations and regroupings. + */ +class MarksTest extends AnyFunSuite: + private class Harness: + given Elaborator.State = new Elaborator.State + given DebugPrinter = new DebugPrinter + given TraceLogger = new TraceLogger: + override def doTrace: Boolean = false + val value = DynShape() + val outer = ResolutionBoundary(TermSymbol(Fun, N, Tree.Ident("outer"))) + val left = ResolutionBoundary(TermSymbol(Fun, N, Tree.Ident("left"))) + val right = ResolutionBoundary(TermSymbol(Fun, N, Tree.Ident("right"))) + val scopes = List(Nil, List(outer), List(outer, left), List(outer, right)) + val sites = List(N, S(FlowSymbol.app()), S(FlowSymbol.app())) + def entries(scopes: List[ResolutionBoundary], site: Opt[FlowSymbol]): Marks = + scopes.foldLeft[Marks](NoMarks)((rest, boundary) => EntryMark(boundary, site, rest)) + def exits(scopes: List[ResolutionBoundary], site: Opt[FlowSymbol]): Marks = + scopes.foldRight[ExitMarks](NoMarks)((boundary, rest) => ExitMark(boundary, site, rest)) + def path(from: List[ResolutionBoundary], to: List[ResolutionBoundary], + exitSite: Opt[FlowSymbol], entrySite: Opt[FlowSymbol]): List[Marks] = + List(exits(from, exitSite), entries(to, entrySite)) + def normalized(path: List[Marks]): List[Marks] = value.exit(path) match + case Marked(_, NoMarks) => Nil + case Marked(_, marks) => List(marks) + case NoShape => fail("A path with no cancellable entry cannot reject its source") + + test("scope transport agrees with matching activation stacks"): + val h = new Harness + import h.given + for from <- h.scopes; to <- h.scopes; origin <- h.sites; leaving <- h.sites; arriving <- h.sites do + val input = h.value.exit(h.entries(from, origin)) + val path = h.path(from, to, leaving, arriving) + // Exiting consumes the entire input stack. A capture matches either site; + // otherwise different identified activations have disjoint value flow. + val compatible = from.isEmpty || origin.isEmpty || leaving.isEmpty || origin == leaving + val expected = if compatible then h.value.exit(h.entries(to, arriving)) else NoShape + assert(input.exit(path) == expected) + assert(input.exit(h.normalized(path)) == expected) + + test("normalizing fragments preserves composition and argument transport reverses their order"): + val h = new Harness + import h.given + for from <- h.scopes; via <- h.scopes; to <- h.scopes; first <- h.sites; second <- h.sites do + val p = h.path(from, via, first, first) + val q = h.path(via, to, second, second) + val parts = h.normalized(p) ::: h.normalized(q) + val input = h.value.exit(h.entries(from, first)) + assert(input.exit(p ::: q) == input.exit(parts)) + assert(input.exit(parts) == input.exit(h.normalized(p)).exit(h.normalized(q))) + val output = h.value.exit(h.entries(to, second)) + assert(output.enter(parts) == output.enter(h.normalized(q)).enter(h.normalized(p))) + assert(output.enter(p ::: q) == output.enter(parts)) + + test("regrouping three normalized paths preserves filtering"): + val h = new Harness + import h.given + def compose(value: TermShape | NoShape, path: TermShape | NoShape): TermShape | NoShape = path match + case NoShape => NoShape + case Marked(_, marks) => value.exit(marks) + for start <- h.scopes; middle <- h.scopes; finish <- h.scopes + a <- h.sites; b <- h.sites; c <- h.sites do + val p = h.value.exit(h.path(start, middle, a, b)) + val q = h.value.exit(h.path(middle, finish, c, a)) + val r = h.value.exit(h.path(finish, start, b, c)) + assert(compose(compose(p, q), r) == compose(p, compose(q, r))) + + test("wildcard exit followed by entry preserves provenance but loses the consumed site"): + val h = new Harness + import h.given + val scope = List(h.outer) + val source = h.value.exit(h.entries(scope, h.sites(1))) + val transferred = source.exit(h.path(scope, scope, N, N)) + assert(transferred != source) + assert(source.exit(h.exits(scope, h.sites(2))) == NoShape) + assert(transferred.exit(h.exits(scope, h.sites(2))) == h.value) + val uncancelled = h.value.exit(h.path(scope, scope, N, N)) + assert(uncancelled != h.value) + assert(uncancelled.exit(h.exits(scope, h.sites(2))) == h.value.exit(h.exits(scope, N))) + + test("arbitrary lexical walks preserve stack semantics and have a lexical depth bound"): + val h = new Harness + import h.given + case class Crossing(entering: Boolean, boundary: ResolutionBoundary, site: Opt[FlowSymbol]) + type Stack = List[(ResolutionBoundary, Opt[FlowSymbol])] + // Interpret the unreduced walk independently, on a complete activation + // stack. Diff tests cannot observe this equivalence for every path prefix. + def step(stack: Opt[Stack], crossing: Crossing): Opt[Stack] = stack.flatMap: stack => + if crossing.entering then S((crossing.boundary, crossing.site) :: stack) + else stack match + case (boundary, site) :: rest => + assert(boundary == crossing.boundary) + if site.isEmpty || crossing.site.isEmpty || site == crossing.site then S(rest) else N + case Nil => fail("A lexical walk cannot exit the root") + def crossings(marks: Marks): List[Crossing] = marks match + case NoMarks => Nil + case EntryMark(boundary, site, rest) => crossings(rest) :+ Crossing(true, boundary, site) + case ExitMark(boundary, site, rest) => crossings(rest) :+ Crossing(false, boundary, site) + val random = new scala.util.Random(0L) + for trial <- 0 until 400 do + val start = h.scopes(random.nextInt(h.scopes.size)) + val inputs = h.sites.map(site => start.reverse.map(_ -> site)) + var expected = inputs.map(S(_): Opt[Stack]) + var scope = start + var reduced: TermShape | NoShape = h.value + for _ <- 0 until 80 do + val children = h.scopes.filter(s => s.length == scope.length + 1 && s.startsWith(scope)) + val entering = scope.isEmpty || (children.nonEmpty && random.nextBoolean()) + val boundary = if entering then children(random.nextInt(children.size)).last else scope.last + // Half the walks never filter: repeated recursion must remain bounded + // even when every possible return site is accepted. + val site = if trial % 2 == 0 then N else h.sites(random.nextInt(h.sites.size)) + val crossing = Crossing(entering, boundary, site) + expected = expected.map(step(_, crossing)) + scope = if entering then scope :+ boundary else scope.init + val mark = if entering then EntryMark(boundary, site, NoMarks) else ExitMark(boundary, site, NoMarks) + reduced = reduced.exit(mark) + reduced match + case NoShape => assert(expected.forall(_.isEmpty)) + case Marked(_, marks) => + val normal = crossings(marks) + assert(normal.length <= start.length + scope.length) + assert(inputs.map(input => normal.foldLeft[Opt[Stack]](S(input))(step)) == expected) diff --git a/hkmc2/jvm/src/test/scala/hkmc2/PublisherTest.scala b/hkmc2/jvm/src/test/scala/hkmc2/PublisherTest.scala new file mode 100644 index 0000000000..8e17022e6a --- /dev/null +++ b/hkmc2/jvm/src/test/scala/hkmc2/PublisherTest.scala @@ -0,0 +1,195 @@ +package hkmc2 + +import org.scalatest.funsuite.AnyFunSuite +import scala.collection.mutable.ArrayBuffer + +import hkmc2.utils.* +import hkmc2.semantics.{Elaborator, NewResolverState, Term, VarSymbol, DynShape, OpaqueTypeShape, TermShape} + + +class PublisherTest extends AnyFunSuite: + private class IntHost extends Host[Int]: + def showDbg(using DebugPrinter): String = "integer shapes" + def publish(shape: Int)(using NewResolverState): Unit = + if currentShapes.add(shape) then notifyShapeListeners(shape) + + test("subscribing during notification replays each shape exactly once"): + given NewResolverState = new Elaborator.State().newResolverState + val host = new IntHost + val original = ArrayBuffer.empty[Int] + val added = ArrayBuffer.empty[Int] + val last = ArrayBuffer.empty[Int] + host.subscribeToShapes: shape => + original += shape + if shape == 1 then host.subscribeToShapes(added += _) + host.subscribeToShapes(last += _) + host.publish(1) + host.publish(2) + assert(original.toList == List(1, 2)) + assert(added.toList == List(1, 2)) + assert(last.toList == List(1, 2)) + + test("publishing during replay does not replay the newly published shape again"): + given NewResolverState = new Elaborator.State().newResolverState + val host = new IntHost + host.publish(1) + host.publish(2) + val received = ArrayBuffer.empty[Int] + host.subscribeToShapes: shape => + received += shape + if shape == 1 then host.publish(3) + host.publish(4) + assert(received.toList == List(1, 3, 2, 4)) + + test("imported listeners route captured destinations through each consumer state"): + val original = new Elaborator.State().newResolverState + val first = new Elaborator.State().newResolverState + val second = new Elaborator.State().newResolverState + val source = new IntHost + val target = new IntHost + locally: + given NewResolverState = original + // Include a cycle: each consumer must install a host copy before invoking + // listeners that can immediately return to that same host. + source.subscribeToShapes(target.publish) + target.subscribeToShapes(source.publish) + source.publish(1) + val sourceListeners = source.shapeListeners.size + val targetListeners = target.shapeListeners.size + source.publish(2)(using first) + source.publish(3)(using second) + assert(source.currentShapes(using original).toList == List(1)) + assert(target.currentShapes(using original).toList == List(1)) + assert(target.currentShapes(using first).toList == List(1, 2)) + assert(target.currentShapes(using second).toList == List(1, 3)) + assert(source.shapeListeners.size == sourceListeners) + assert(target.shapeListeners.size == targetListeners) + + test("imports lazily retain exporter-private inference on third-party hosts"): + val declaration = new Elaborator.State().newResolverState + val exporter = new Elaborator.State().newResolverState + val consumer = new Elaborator.State().newResolverState + val source = new IntHost + val target = new IntHost + source.publish(1)(using declaration) + source.subscribeToShapes(target.publish)(using exporter) + source.publish(2)(using exporter) + source.publish(3)(using consumer) + // Unrelated exporter state must not be copied merely to import this host. + (1 to 1000).foreach: n => + val unrelated = new IntHost + unrelated.publish(n)(using exporter) + val exported = source.inferenceHost(using exporter) + assert(consumer.copiedHostCount == 1) + exported.publish(4)(using consumer) + assert(source.currentShapes(using declaration).toList == List(1)) + assert(source.currentShapes(using exporter).toList == List(1, 2)) + assert(source.currentShapes(using consumer).toSet == Set(1, 3)) + assert(consumer.local(exported).shapes.toSet == Set(1, 2, 4)) + assert(target.currentShapes(using consumer).toSet == Set(1, 2, 4)) + assert(consumer.copiedHostCount == 3) + assert(target.currentShapes(using exporter).toList == List(1, 2)) + + test("re-exported listeners retain intermediate exporters' private destinations"): + val declaration = new Elaborator.State().newResolverState + val exporter = new Elaborator.State().newResolverState + val middle = new Elaborator.State().newResolverState + val consumer = new Elaborator.State().newResolverState + val source = new IntHost + val target = new IntHost + val referencedTarget = new IntHost + val reference = referencedTarget.inferenceHost(using declaration) + locally: + given NewResolverState = declaration + source.subscribeToShapes: shape => + target.publish(shape) + reference.publish(shape) + source.publish(1)(using declaration) + source.publish(2)(using exporter) + val exportedSource = source.inferenceHost(using exporter) + val exportedTarget = target.inferenceHost(using exporter) + val exportedReference = referencedTarget.inferenceHost(using exporter) + exportedSource.publish(3)(using middle) + val forwarded = middle.local(exportedSource) + forwarded.publish(4)(using consumer) + assert(source.currentShapes(using declaration).toList == List(1)) + assert(target.currentShapes(using exporter).toList == List(1, 2)) + assert(middle.local(exportedTarget).shapes.toList == List(1, 2, 3)) + assert(consumer.local(middle.local(exportedTarget)).shapes.toList == List(1, 2, 3, 4)) + assert(consumer.local(middle.local(exportedReference)).shapes.toList == List(1, 2, 3, 4)) + + test("completing a block freezes its nodes while retaining symbol and elimination flow"): + given owner: Elaborator.State = new Elaborator.State + given state: NewResolverState = owner.newResolverState + val variable = new VarSymbol(new syntax.Tree.Ident("x")) + val use = Term.NewSel(Term.SimpleRef(variable)(new syntax.Tree.Ident("x")), + new syntax.Tree.Ident("value"), None)(semantics.FlowSymbol.sel("value")) + use.hasDynamicTarget = true + variable.subscribeToShapes: shape => + if use.currentShapes.add(shape) then use.notifyShapeListeners(shape) + def publish(shape: TermShape)(using NewResolverState): Unit = + if variable.currentShapes.add(shape) then variable.notifyShapeListeners(shape) + val before = DynShape() + val after = OpaqueTypeShape(Term.UnitVal())(semantics.TypeInterfaceReason.Unavailable(Term.UnitVal())) + publish(before) + val originalListeners = use.shapeListeners.toVector + state.completeBlock(use) + publish(after) + assert(variable.currentShapes.toSet == Set(before, after)) + assert(use.currentShapes.toSet == Set(before, after)) + assert(use.shapes.toSet == Set(before)) + assert(use.shapeListeners.toVector == originalListeners) + intercept[AssertionError]: + state.recordResolution(use, false)(use.tupleIndex = Some(0)) + assert(use.tupleIndex.isEmpty && use.hasDynamicTarget) + // A new node in the same worksheet state is still open for resolution. + val next = Term.UnitVal() + next.inferenceHost + assert(state.canResolve(next)) + state.recordResolution(use, true)(fail("An unchanged decision must not be written again")) + + test("temporary observers replay once, detach, and are not inherited by consumers"): + given original: NewResolverState = new Elaborator.State().newResolverState + val host = new IntHost + val observed = ArrayBuffer.empty[Int] + val routed = ArrayBuffer.empty[Int] + host.publish(1) + host.subscribeToShapes(routed += _) + val detach = host.inferenceHost.observe(observed += _) + host.publish(2) + val consumer = new Elaborator.State().newResolverState + host.publish(3)(using consumer) + assert(observed.toList == List(1, 2)) + assert(routed.toList == List(1, 2, 3)) + detach() + host.publish(4) + assert(observed.toList == List(1, 2)) + assert(routed.toList == List(1, 2, 3, 4)) + + test("unknown provenance is lazy and does not affect shape identity"): + import semantics.{ShapeProvenance, UnknownValueShape, RecordShape} + import Message.MessageContext + var evaluated = 0 + def provenance = ShapeProvenance { + evaluated += 1 + Nil + } + val source = Term.UnitVal() + val first = UnknownValueShape(source, fromPublicInterface = false)(provenance) + val second = UnknownValueShape(source, fromPublicInterface = false)(provenance) + assert(Set(first, second).size == 1) + assert(RecordShape.Unknown(first) == RecordShape.Unknown(second)) + assert(evaluated == 0) + first.provenance.diagnostic(msg"Operation failed", None) + first.provenance.diagnostic(msg"Operation failed", None) + assert(evaluated == 1) + var noteEvaluated = 0 + val extended = first.provenance.via { + noteEvaluated += 1 + msg"A deferred provenance step" -> None + } + assert(noteEvaluated == 0) + extended.diagnostic(msg"Operation failed", None) + extended.diagnostic(msg"Operation failed", None) + assert(noteEvaluated == 1) + assert(evaluated == 1) diff --git a/hkmc2/jvm/src/test/scala/hkmc2/TestFolders.scala b/hkmc2/jvm/src/test/scala/hkmc2/TestFolders.scala index 46a0a8f63c..94739a7bc3 100644 --- a/hkmc2/jvm/src/test/scala/hkmc2/TestFolders.scala +++ b/hkmc2/jvm/src/test/scala/hkmc2/TestFolders.scala @@ -8,6 +8,10 @@ import hkmc2.utils.*, shorthands.* * `mlscript-compile/` subdirectories (for compile tests) that it owns. */ object TestFolders: + /** Directory configuration files named `.mls` are not standalone test sources. */ + def isTestSource(file: os.BasePath): Bool = + file.ext === "mls" && file.baseName.nonEmpty + /** Helper to check whether `file` is inside (i.e. a descendant of) `dir`. */ def isInDir(file: os.Path, dir: os.Path): Bool = file.startsWith(dir) diff --git a/hkmc2/jvm/src/test/scala/hkmc2/TypeFormulaTest.scala b/hkmc2/jvm/src/test/scala/hkmc2/TypeFormulaTest.scala new file mode 100644 index 0000000000..e43f39fd1e --- /dev/null +++ b/hkmc2/jvm/src/test/scala/hkmc2/TypeFormulaTest.scala @@ -0,0 +1,54 @@ +package hkmc2 +package semantics + +import org.scalatest.funsuite.AnyFunSuite + + +class TypeFormulaTest extends AnyFunSuite: + test("lattice normalization preserves Boolean meanings and identifies equal formulas"): + val a = TypeFormula.atom(0) + val b = TypeFormula.atom(1) + val c = TypeFormula.atom(2) + val atoms = List(a, b, c, TypeFormula.top[Int], TypeFormula.bottom[Int]) + val formulas = (atoms ++ (for left <- atoms; right <- atoms; union <- List(false, true) + yield if union then left.union(right) else left.intersection(right))).distinct + def truthTable(formula: TypeFormula[Int]): List[Boolean] = (0 until 8).toList.map: assignment => + formula.clauses.exists(_.forall(atom => (assignment & (1 << atom)) != 0)) + val results = for left <- formulas; right <- formulas yield + val union = left.union(right) + val intersection = left.intersection(right) + assert(truthTable(union) == truthTable(left).zip(truthTable(right)).map(_ || _)) + assert(truthTable(intersection) == truthTable(left).zip(truthTable(right)).map(_ && _)) + List(union, intersection) + results.flatten.groupBy(truthTable).values.foreach(equal => assert(equal.distinct.size == 1)) + assert(a.union(a.intersection(b)) == a) + assert(a.intersection(b.union(c)) == a.intersection(b).union(a.intersection(c))) + assert(a.union(b) == b.union(a)) + assert(a.union(b).hashCode == b.union(a).hashCode) + assert(a.union(b).orderedAtoms == Vector(0, 1)) + assert(b.union(a).orderedAtoms == Vector(1, 0)) + assert(a.union(a.intersection(b)).orderedAtoms == Vector(0)) + + test("all three-atom formulas retain their meaning when substitution identifies atoms"): + // Enumerate every DNF over three atoms, including the empty clause (top) + // and no clauses (bottom). This checks absorption after a non-injective map, + // which recursive substitutions can cause even if the source was reduced. + val clauses = (0 until 8).map: bits => + (0 until 3).filter(atom => (bits & (1 << atom)) != 0).foldLeft(TypeFormula.top[Int]): (clause, atom) => + clause.intersection(TypeFormula.atom(atom)) + val formulas = (0 until 256).map: bits => + clauses.zipWithIndex.filter((_, index) => (bits & (1 << index)) != 0).foldLeft(TypeFormula.bottom[Int]): + case (formula, (clause, _)) => formula.union(clause) + .distinct + assert(formulas.size == 20) + for formula <- formulas; a <- 0 until 3; b <- 0 until 3; c <- 0 until 3 do + val substitution = Vector(a, b, c) + val mapped = formula.map(substitution) + assert(mapped.orderedAtoms.toSet == mapped.atoms) + assert(mapped.orderedAtoms.distinct == mapped.orderedAtoms) + val second = Vector(1, 1, 0) + assert(mapped.map(second) == formula.map(atom => second(substitution(atom)))) + for assignment <- 0 until 8 do + val expected = formula.clauses.exists(_.forall(atom => (assignment & (1 << substitution(atom))) != 0)) + val actual = mapped.clauses.exists(_.forall(atom => (assignment & (1 << atom)) != 0)) + assert(actual == expected) diff --git a/hkmc2/jvm/src/test/scala/hkmc2/TypeInstantiationTest.scala b/hkmc2/jvm/src/test/scala/hkmc2/TypeInstantiationTest.scala new file mode 100644 index 0000000000..7befe21689 --- /dev/null +++ b/hkmc2/jvm/src/test/scala/hkmc2/TypeInstantiationTest.scala @@ -0,0 +1,138 @@ +package hkmc2 + +import org.scalatest.funsuite.AnyFunSuite + +import hkmc2.utils.* +import hkmc2.semantics.* + + +class TypeInstantiationTest extends AnyFunSuite: + test("a definition and static site allocate their binder group once"): + given owner: Elaborator.State = new Elaborator.State + val state = owner.newResolverState + val scheme = new TypeResolution(Term.UnitVal(), _ => fail("Unexpected type error")) + val otherScheme = new TypeResolution(Term.UnitVal(), _ => fail("Unexpected type error")) + val a = new VarSymbol(new syntax.Tree.Ident("A")) + val b = new VarSymbol(new syntax.Tree.Ident("B")) + val firstSite = FlowSymbol.app() + val secondSite = FlowSymbol.app() + val first = state.instantiateTypeParameters(scheme, firstSite, List(a, b)) + (1 to 1000).foreach: _ => + val repeated = state.instantiateTypeParameters(scheme, firstSite, List(a, b)) + assert(repeated(a) eq first(a)) + assert(repeated(b) eq first(b)) + assert(state.allocatedTypeInstanceCount == 2) + val second = state.instantiateTypeParameters(scheme, secondSite, List(a, b)) + val other = state.instantiateTypeParameters(otherScheme, firstSite, List(a, b)) + assert(!(second(a) eq first(a))) + assert(!(other(a) eq first(a))) + assert(first(a).origin eq a) + assert(first(b).origin eq b) + assert(state.allocatedTypeInstanceCount == 6) + intercept[AssertionError]: + state.instantiateTypeParameters(scheme, firstSite, List(a)) + + test("instances do not copy checking candidates or listeners and cannot be reinstantiated"): + given owner: Elaborator.State = new Elaborator.State + given state: NewResolverState = owner.newResolverState + val scheme = new TypeResolution(Term.UnitVal(), _ => fail("Unexpected type error")) + val parameter = new VarSymbol(new syntax.Tree.Ident("A")) + parameter.inferenceHost.publish(DynShape()) + parameter.subscribeToShapes(_ => ()) + val instance = state.instantiateTypeParameters(scheme, FlowSymbol.app(), List(parameter))(parameter) + assert(instance.currentShapes.isEmpty) + assert(instance.shapeListeners.isEmpty) + assert(parameter.currentShapes.nonEmpty) + assert(parameter.shapeListeners.nonEmpty) + intercept[IllegalArgumentException]: + state.instantiateTypeParameters(scheme, FlowSymbol.app(), List(instance)) + + test("structural projections bound allocation across activation and importer views"): + // Diff tests check recursive getter behavior; this checks that repeated + // projections and imported activations allocate only one binder group. + given owner: Elaborator.State = new Elaborator.State + val source = owner.newResolverState + val consumer = new Elaborator.State().newResolverState + val field = BlockMemberSymbol("next", Nil) + val site = source.fieldProjectionSite(field) + assert(consumer.inGraph(source).fieldProjectionSite(field) eq site) + val scheme = new TypeResolution(Term.UnitVal(), _ => fail("Unexpected type error")) + val parameter = new VarSymbol(new syntax.Tree.Ident("A")) + val instances = consumer.instantiateTypeParameters(scheme, site, List(parameter)) + val views = List(consumer, consumer.withInstances(instances), consumer.inGraph(source), + consumer.withInstances(instances).inGraph(source)) + (1 to 1000).foreach: _ => + views.foreach: view => + assert(view.fieldProjectionSite(field) eq site) + assert(view.instantiateTypeParameters(scheme, view.fieldProjectionSite(field), List(parameter)) == instances) + assert(consumer.allocatedTypeInstanceCount == 1) + assert(source.allocatedTypeInstanceCount == 0) + + test("consumer instantiation leaves another consumer and the source untouched"): + given owner: Elaborator.State = new Elaborator.State + val source = owner.newResolverState + val first = new Elaborator.State().newResolverState + val second = new Elaborator.State().newResolverState + val scheme = new TypeResolution(Term.UnitVal(), _ => fail("Unexpected type error")) + val parameter = new VarSymbol(new syntax.Tree.Ident("A")) + val site = FlowSymbol.app() + val left = first.instantiateTypeParameters(scheme, site, List(parameter))(parameter) + val right = second.instantiateTypeParameters(scheme, site, List(parameter))(parameter) + left.inferenceHost(using first).publish(DynShape())(using first) + assert(left.currentShapes(using first).nonEmpty) + assert(right.currentShapes(using second).isEmpty) + assert(parameter.currentShapes(using source).isEmpty) + assert(source.allocatedTypeInstanceCount == 0) + + test("activation views compose flatly and share their consumer's hosts"): + given owner: Elaborator.State = new Elaborator.State + val source = owner.newResolverState + val scheme = new TypeResolution(Term.UnitVal(), _ => fail("Unexpected type error")) + val parameter = new VarSymbol(new syntax.Tree.Ident("A")) + val local = new VarSymbol(new syntax.Tree.Ident("value")) + val first = source.instantiateTypeParameters(scheme, FlowSymbol.app(), List(parameter)) + val second = source.instantiateTypeParameters(scheme, FlowSymbol.app(), List(parameter)) + val left = source.withInstances(first) + val right = source.withInstances(second) + (1 to 1000).foreach: _ => + assert(left.withInstances(second) eq right) + assert(right.withInstances(first) eq left) + assert(left.withInstances(TypeSubstitution.empty) eq source) + assert(left.inGraph(right) eq left) + assert(local.inferenceHost(using left) eq local.inferenceHost(using right)) + assert(source.allocatedTypeInstanceCount == 2) + + test("imported activation views retain consumer substitutions without mutating exporter hosts"): + given owner: Elaborator.State = new Elaborator.State + val source = owner.newResolverState + val consumer = new Elaborator.State().newResolverState + val other = new Elaborator.State().newResolverState + val scheme = new TypeResolution(Term.UnitVal(), _ => fail("Unexpected type error")) + val parameter = new VarSymbol(new syntax.Tree.Ident("A")) + val local = new VarSymbol(new syntax.Tree.Ident("value")) + val sourceSubstitution = source.instantiateTypeParameters(scheme, FlowSymbol.app(), List(parameter)) + val substitution = consumer.instantiateTypeParameters(scheme, FlowSymbol.app(), List(parameter)) + local.inferenceHost(using source).publish(UnknownValueShape.at(Term.UnitVal()))(using source) + val contextual = consumer.withInstances(substitution) + val imported = contextual.inGraph(source.withInstances(sourceSubstitution)) + assert(imported.instances == substitution) + assert(imported.withInstances(TypeSubstitution.empty) eq consumer.inGraph(source)) + (1 to 1000).foreach: _ => + assert(contextual.inGraph(source) eq imported) + assert(imported.inGraph(source) eq imported) + local.inferenceHost(using imported).publish(DynShape())(using imported) + assert(local.currentShapes(using imported).size == 2) + assert(local.currentShapes(using source).size == 1) + assert(local.currentShapes(using other.inGraph(source)).size == 1) + assert(consumer.allocatedTypeInstanceCount == 1) + + test("imported callbacks return to the consumer without growing graph-view chains"): + val exporter = new Elaborator.State().newResolverState + val middle = new Elaborator.State().newResolverState + val consumer = new Elaborator.State().newResolverState + val imported = consumer.inGraph(middle.inGraph(exporter)) + // A worksheet can observe successful values without detecting an extra + // import-view layer per callback. Check the graph's boundedness directly. + (1 to 1000).foreach: _ => + assert(imported.inGraph(consumer) eq consumer) + assert(imported.inGraph(imported) eq imported) diff --git a/hkmc2/jvm/src/test/scala/hkmc2/TypeRelationTest.scala b/hkmc2/jvm/src/test/scala/hkmc2/TypeRelationTest.scala new file mode 100644 index 0000000000..7496253eda --- /dev/null +++ b/hkmc2/jvm/src/test/scala/hkmc2/TypeRelationTest.scala @@ -0,0 +1,580 @@ +package hkmc2 +package semantics + +import org.scalatest.funsuite.AnyFunSuite +import scala.collection.mutable.ArrayBuffer +import hkmc2.syntax.{Cls, Fun, Tree} +import hkmc2.utils.*, shorthands.* + + +class TypeRelationTest extends AnyFunSuite: + private class Harness: + import io.PlatformPath.given + given owner: Elaborator.State = new Elaborator.State + given state: NewResolverState = owner.newResolverState + given Raise = diagnostic => fail(diagnostic.theMsg) + given DebugPrinter = new DebugPrinter + given trace: TraceLogger = new TraceLogger: + override def doTrace: Boolean = false + given Config = Config.default(TestFolders.mainTestDir(os.pwd)) + given CompilerCtx = CompilerCtx.fresh(io.FileSystem.default, TestFolders.compilerPaths(os.pwd), summon[Config]) + val resolver = Elaborator(trace, TestFolders.mainTestDir(os.pwd), Elaborator.Ctx.empty) + def tpe(shape: TypeShape): DeclaredType = + val resolution = new TypeResolution(Term.UnitVal(), _ => fail("Unexpected type error")) + resolution.publish(shape) + DeclaredType(resolution, Map.empty, TypeSubstitution.empty, true)(N) + def parameter(name: String): (VarSymbol, DeclaredType) = + val symbol = VarSymbol(Tree.Ident(name)) + (symbol, tpe(TypeShape.Parameter(symbol, symbol.inferenceHost))) + def observe(reference: ContextualType): ArrayBuffer[TermShape] = + val values = ArrayBuffer.empty[TermShape] + InstanceShape(reference.tpe).exit(reference.marks) match + case value: TermShape => resolver.listenInstanceViews(value)(values += _) + case NoShape => fail("A type reference cannot be unreachable") + values + + test("symbolic cycles retain early and late bounds without adding listeners on replay"): + val h = new Harness + import h.given + val (a, at) = h.parameter("A") + val (b, bt) = h.parameter("B") + val ar = ContextualType(at, Nil) + val br = ContextualType(bt, Nil) + val first = IntroShape(Term.UnitVal(), N) + val second = DynShape() + h.resolver.publishParameter(a, first) + h.resolver.constrainTypes(ar, br) + h.resolver.constrainTypes(br, ar) + val seen = h.observe(br) + h.resolver.publishParameter(a, second) + assert(seen.toSet == Set(first, second)) + val counts = (a.inferenceHost.listeners.size, b.inferenceHost.listeners.size) + (1 to 1000).foreach: _ => + h.resolver.constrainTypes(ar, br) + h.resolver.constrainTypes(br, ar) + h.resolver.publishParameter(a, first) + assert((a.inferenceHost.listeners.size, b.inferenceHost.listeners.size) == counts) + assert(seen.toList == List(first, second)) + + test("reference exit and reentry agree with value transport for wildcard and explicit sites"): + // Check both publication orders and every combination of wildcard/explicit + // exit, reentry, and consumer sites against the ordinary value operations. + for exitSite <- 0 to 2; entrySite <- 0 to 2; consumerSite <- 0 to 2 do + val h = new Harness + import h.given + val (a, at) = h.parameter("A") + val owner = ResolutionBoundary(TermSymbol(Fun, N, Tree.Ident("owner"))) + val sites = Vector(N, S(FlowSymbol.app()), S(FlowSymbol.app())) + val exit = ExitMark(owner, sites(exitSite), NoMarks) + val entry = EntryMark(owner, sites(entrySite), NoMarks) + val consumer = ExitMark(owner, sites(consumerSite), NoMarks) + val first = MarkedShape.enter(IntroShape(Term.UnitVal(), N), owner, sites(1)) + val second = MarkedShape.enter(DynShape(), owner, sites(2)) + val outside = h.resolver.transportType(at, exit :: Nil) + val inside = h.resolver.transportType(outside, entry :: Nil) + h.resolver.publishParameter(a, first) + val observed = h.observe(ContextualType(inside, Nil)) + def expected(values: List[TermShape], path: List[Marks]): Set[TermShape] = + values.flatMap: value => + value.exit(path) match + case shape: TermShape => Some(shape) + case NoShape => None + .toSet + assert(observed.toSet == expected(List(first), List(exit, entry))) + h.resolver.publishParameter(a, second) + assert(observed.toSet == expected(List(first, second), List(exit, entry))) + val consumed = h.resolver.transportType(inside, consumer :: Nil) + assert(h.observe(ContextualType(consumed, Nil)).toSet == + expected(List(first, second), List(exit, entry, consumer))) + + test("alias argument normalization reaches a fixed point without changing its marks"): + val h = new Harness + import h.given + val (formal, ft) = h.parameter("X") + val symbol = TypeAliasSymbol(Tree.Ident("Identity")) + val member = BlockMemberSymbol("Identity", Nil) + symbol.defn = S(TypeDef(symbol, member, List(TyParam(FldFlags.empty, N, formal)(N)), + S(ft.resolution.source), N, Nil)(N)) + val alias = h.tpe(TypeShape.Alias(symbol, S(ft.resolution))) + val (argument, at) = h.parameter("A") + val application = h.tpe(TypeShape.Applied(alias.resolution, List(at.resolution))) + val (target, tt) = h.parameter("Target") + val owner = ResolutionBoundary(TermSymbol(Fun, N, Tree.Ident("owner"))) + val path = EntryMark(owner, S(FlowSymbol.app()), NoMarks) + val original = h.resolver.transportType(tt, path :: Nil) + val seen = h.observe(ContextualType(original, Nil)) + val before = target.inferenceHost.listeners.size + var current = original + (1 to 1000).foreach: _ => + current = h.resolver.declaredType(application.resolution, Map(argument -> current)) + assert(current == original) + val value = DynShape() + h.resolver.publishParameter(target, value) + assert(seen.toList == List(MarkedShape.enter(value, owner, path.id))) + assert(target.inferenceHost.listeners.size == before) + assert(h.state.allocatedTypeInstanceCount == 0) + + test("partial captured alias reduction uses ordinary exit and entry including caller identity loss"): + val h = new Harness + import h.given + val (formal, ft) = h.parameter("X") + val (unused, _) = h.parameter("Unused") + val symbol = TypeAliasSymbol(Tree.Ident("Identity")) + val member = BlockMemberSymbol("Identity", Nil) + symbol.defn = S(TypeDef(symbol, member, List(TyParam(FldFlags.empty, N, formal)(N), TyParam(FldFlags.empty, N, unused)(N)), + S(ft.resolution.source), N, Nil)(N)) + val alias = h.tpe(TypeShape.Alias(symbol, S(ft.resolution))) + val scope = TermSymbol(Fun, N, Tree.Ident("owner")) + val owner = ResolutionBoundary(scope) + val captured = h.tpe(TypeShape.Reference(alias.resolution, + EntryMark(owner, N, NoMarks) :: Nil, Map.empty, TypeSubstitution.empty)) + val (argument, at) = h.parameter("A") + val application = h.tpe(TypeShape.Applied(captured.resolution, List(at.resolution))) + val (target, tt) = h.parameter("Target") + val original = h.resolver.transportType(tt, EntryMark(owner, S(FlowSymbol.app()), NoMarks) :: Nil) + val outside = h.resolver.transportType(original, ExitMark(owner, N, NoMarks) :: Nil) + val expected = h.resolver.transportType(outside, EntryMark(owner, N, NoMarks) :: Nil) + assert(expected != original) + val actual = h.resolver.declaredType(application.resolution, Map(argument -> original)) + assert(actual == expected) + val early = IntroShape(Term.UnitVal(), N) + val late = DynShape() + h.resolver.publishParameter(target, early) + val seen = h.observe(ContextualType(actual, Nil)) + h.resolver.publishParameter(target, late) + assert(seen.toList == List(early, late).map(MarkedShape.enter(_, owner, N))) + val count = target.inferenceHost.listeners.size + (1 to 1000).foreach: _ => + assert(h.resolver.declaredType(application.resolution, Map(argument -> actual)) == expected) + assert(target.inferenceHost.listeners.size == count) + assert(h.state.allocatedTypeInstanceCount == 0) + + test("wildcard normalization bounds recursive environments while retaining late marked bounds"): + // Unlike a finite number of source projections, this measures saturation of + // the saved argument graph and its listeners under repeated substitution. + for input <- List(false, true); output <- List(false, true) do + val h = new Harness + import h.given + val (formal, ft) = h.parameter("A") + val (target, tt) = h.parameter("Target") + val scope = ResolutionBoundary(TermSymbol(Fun, N, Tree.Ident("owner"))) + val site = S(FlowSymbol.app()) + val original = h.resolver.transportType(tt, EntryMark(scope, site, NoMarks) :: Nil) + val wildcard = h.tpe(TypeShape.Wildcard(Option.when(input)(ft.resolution), Option.when(output)(ft.resolution))) + val first = h.resolver.declaredType(wildcard.resolution, Map(formal -> original)) + val observed = h.observe(ContextualType(first, Nil)) + val inputObserved = h.observe(ContextualType(h.resolver.selectArgument(first, false), Nil)) + val before = target.inferenceHost.listeners.size + var current = first + (1 to 1000).foreach: _ => + current = h.resolver.declaredType(wildcard.resolution, Map(formal -> current)) + assert(current eq first) + val value = DynShape() + h.resolver.publishParameter(target, value) + if output then assert(observed.toList == List(MarkedShape.enter(value, scope, site))) + else assert(!observed.exists(_.applicationHead._1.isInstanceOf[DynShape])) + if input then assert(inputObserved.toList == List(MarkedShape.enter(value, scope, site))) + else assert(inputObserved.isEmpty) + assert(target.inferenceHost.listeners.size == before) + assert(h.state.allocatedTypeInstanceCount == 0) + + test("recursive Boolean arguments share a fixed point and preserve each atom's marks and late bounds"): + val h = new Harness + import h.given + val (a, at) = h.parameter("A") + val (b, bt) = h.parameter("B") + val (target, tt) = h.parameter("Target") + val owner = ResolutionBoundary(TermSymbol(Fun, N, Tree.Ident("owner"))) + val path = EntryMark(owner, S(FlowSymbol.app()), NoMarks) + val original = h.resolver.transportType(tt, path :: Nil) + val source = h.tpe(TypeShape.Union(at.resolution, bt.resolution)) + val expected = h.resolver.declaredType(source.resolution, Map(a -> original)) + val early = IntroShape(Term.UnitVal(), N) + val late = DynShape() + h.resolver.publishParameter(target, early) + val seen = h.observe(ContextualType(expected, Nil)) + val counts = (target.inferenceHost.listeners.size, b.inferenceHost.listeners.size) + var current = expected + (1 to 1000).foreach: _ => + current = h.resolver.declaredType(source.resolution, Map(a -> current)) + assert(current eq expected) + h.resolver.listenInstanceViews(InstanceShape(current))(_ => ()) + h.resolver.publishParameter(b, late) + assert(seen.toSet == Set(MarkedShape.enter(early, owner, path.id), late)) + assert((target.inferenceHost.listeners.size, b.inferenceHost.listeners.size) == counts) + assert(h.state.allocatedTypeInstanceCount == 0) + + test("recursive structural projections preserve bounds and reuse their listeners"): + val h = new Harness + import h.given + val (a, at) = h.parameter("A") + val (b, bt) = h.parameter("B") + def recursiveRecord(element: DeclaredType): DeclaredType = + val resolution = new TypeResolution(Term.UnitVal(), _ => fail("Unexpected type error")) + val value = Term.UnitVal() + value.typeInterpretation = S(element.resolution) + val next = Term.UnitVal() + next.typeInterpretation = S(resolution) + val valueField = RcdField.signature(Term.Lit(Tree.StrLit("value")), value) + val nextField = RcdField.signature(Term.Lit(Tree.StrLit("next")), next) + val source: Term.Rcd = Term.Rcd(false, List(valueField, nextField)) + resolution.publish(TypeShape.Record(source, List(valueField -> element.resolution, nextField -> resolution))) + DeclaredType(resolution, Map.empty, TypeSubstitution.empty, true)(N) + val left = ContextualType(recursiveRecord(at), Nil) + val right = ContextualType(recursiveRecord(bt), Nil) + val first = IntroShape(Term.UnitVal(), N) + val later = DynShape() + h.resolver.publishParameter(a, first) + h.resolver.constrainTypes(left, right) + val seen = h.observe(ContextualType(bt, Nil)) + assert(seen.toList == List(first)) + def counts = List(left.tpe.resolution.inferenceHost.listeners.size, right.tpe.resolution.inferenceHost.listeners.size, + a.inferenceHost.listeners.size, b.inferenceHost.listeners.size) + val before = counts + h.resolver.publishParameter(a, later) + assert(seen.toList == List(first, later)) + (1 to 1000).foreach: _ => + h.resolver.constrainTypes(left, right) + assert(counts == before) + assert(h.state.allocatedTypeInstanceCount == 0) + + test("forward type dependencies retain each observer's substitution until the target is known"): + val h = new Harness + import h.given + val (a, at) = h.parameter("A") + val first = IntroShape(Term.UnitVal(), N) + val second = DynShape() + val early = new TypeResolution(Term.UnitVal(), _ => fail("Unexpected type error")) + val late = new TypeResolution(Term.UnitVal(), _ => fail("Unexpected type error")) + early.publish(TypeShape.Tuple(List(at.resolution, late))) + val left = h.resolver.declaredType(early, Map(a -> h.tpe(TypeShape.Inferred(first)))) + val right = h.resolver.declaredType(early, Map(a -> h.tpe(TypeShape.Inferred(second)))) + val leftSeen = h.observe(ContextualType(left, Nil)) + val rightSeen = h.observe(ContextualType(right, Nil)) + assert(leftSeen.isEmpty && rightSeen.isEmpty) + // Closing a recursive dependency must wake both observers without merging + // their environments or allocating a parameter for the forward reference. + late.publish(TypeShape.Tuple(List(at.resolution, early))) + def firstField(values: ArrayBuffer[TermShape]): Set[TermShape] = values.toList match + case (tuple: TupleShape) :: Nil => tuple.elements.head match + case TupleShape.TypedField(tpe, marks) => h.observe(ContextualType(tpe, marks)).toSet + case _ => fail("Expected a declared tuple field") + case _ => fail("Expected one declared tuple") + assert(firstField(leftSeen) == Set(first)) + assert(firstField(rightSeen) == Set(second)) + (1 to 1000).foreach: _ => + h.resolver.constrainTypes(ContextualType(left, Nil), ContextualType(right, Nil)) + val saturated = (early.inferenceHost.listeners.size, late.inferenceHost.listeners.size) + (1 to 1000).foreach: _ => + h.resolver.constrainTypes(ContextualType(left, Nil), ContextualType(right, Nil)) + assert((early.inferenceHost.listeners.size, late.inferenceHost.listeners.size) == saturated) + assert(h.state.allocatedTypeInstanceCount == 0) + + test("delayed argument selection fixes one endpoint for both constraint directions"): + val h = new Harness + import h.given + val (input, inputType) = h.parameter("Input") + val (output, outputType) = h.parameter("Output") + val resolution = new TypeResolution(Term.UnitVal(), _ => fail("Unexpected type error")) + val argument = DeclaredType(resolution, Map.empty, TypeSubstitution.empty, true)(N) + val negative = h.resolver.selectArgument(argument, false) + val positive = h.resolver.selectArgument(argument, true) + val neg = ContextualType(negative, Nil) + val pos = ContextualType(positive, Nil) + val negativeValues = h.observe(neg) + val positiveValues = h.observe(pos) + val first = IntroShape(Term.UnitVal(), N) + val second = DynShape() + val lower = ContextualType(h.tpe(TypeShape.Inferred(first)), Nil) + h.resolver.constrainTypes(lower, neg) + assert(negativeValues.isEmpty && positiveValues.isEmpty) + resolution.publish(TypeShape.Argument(TypeArgument(inputType, outputType))) + assert(negativeValues.toList == List(first)) + assert(positiveValues.isEmpty) + h.resolver.publishParameter(output, second) + assert(positiveValues.toList == List(second)) + val counts = (resolution.inferenceHost.listeners.size, input.inferenceHost.listeners.size, output.inferenceHost.listeners.size) + (1 to 1000).foreach: _ => + // Re-selecting an interpreted reference must not change its meaning or + // allocate another node, even when the later use has opposite polarity. + assert(h.resolver.selectArgument(argument, false) eq negative) + assert(h.resolver.selectArgument(argument, true) eq positive) + assert(h.resolver.selectArgument(negative, true) eq negative) + assert(h.resolver.selectArgument(positive, false) eq positive) + h.resolver.constrainTypes(lower, neg) + assert((resolution.inferenceHost.listeners.size, input.inferenceHost.listeners.size, output.inferenceHost.listeners.size) == counts) + assert(negativeValues.toList == List(first)) + assert(positiveValues.toList == List(second)) + assert(h.state.allocatedTypeInstanceCount == 0) + + test("cyclic argument selections wait for productive bounds without expanding the cycle"): + val h = new Harness + import h.given + val resolution = new TypeResolution(Term.UnitVal(), _ => fail("Unexpected type error")) + val argument = DeclaredType(resolution, Map.empty, TypeSubstitution.empty, true)(N) + val selected = h.resolver.selectArgument(argument, true) + val reference = ContextualType(selected, Nil) + val seen = h.observe(reference) + resolution.publish(TypeShape.Argument(TypeArgument(selected, selected))) + assert(seen.isEmpty) + val (parameter, bound) = h.parameter("Later") + resolution.publish(TypeShape.Argument(TypeArgument(bound, bound))) + val value = IntroShape(Term.UnitVal(), N) + h.resolver.publishParameter(parameter, value) + assert(seen.toList == List(value)) + h.resolver.constrainTypes(reference, reference) + val counts = (resolution.inferenceHost.listeners.size, parameter.inferenceHost.listeners.size) + (1 to 1000).foreach: _ => + h.resolver.constrainTypes(reference, reference) + assert(h.resolver.selectArgument(argument, true) eq selected) + assert((resolution.inferenceHost.listeners.size, parameter.inferenceHost.listeners.size) == counts) + assert(seen.toList == List(value)) + assert(h.state.allocatedTypeInstanceCount == 0) + + test("cyclic hole constraints saturate without allocating parameter instances"): + val h = new Harness + import h.given + val (parameter, _) = h.parameter("T") + val first = h.resolver.omittedType(h.tpe(TypeShape.Abstract).resolution, parameter) + val second = h.resolver.omittedType(h.tpe(TypeShape.Abstract).resolution, parameter) + val left = ContextualType(first, Nil) + val right = ContextualType(second, Nil) + h.resolver.constrainTypes(left, right) + h.resolver.constrainTypes(right, left) + val seen = h.observe(right) + assert(seen.isEmpty) + val bound = IntroShape(Term.UnitVal(), N) + val input = ContextualType(h.tpe(TypeShape.Inferred(bound)), Nil) + h.resolver.constrainTypes(input, left) + def listeners(tpe: DeclaredType): Int = tpe.resolution.currentShapes.toList match + case TypeShape.Hole(host) :: Nil => host.listeners.size + case _ => fail("An omitted argument must retain its inference host") + val counts = (listeners(first), listeners(second)) + (1 to 1000).foreach: _ => + h.resolver.constrainTypes(left, right) + h.resolver.constrainTypes(right, left) + h.resolver.constrainTypes(input, left) + assert(seen.toList == List(bound)) + assert((listeners(first), listeners(second)) == counts) + assert(h.state.allocatedTypeInstanceCount == 0) + + test("importers share hole identities while keeping inferred bounds private"): + val h = new Harness + import h.given + val (parameter, _) = h.parameter("T") + val source = h.tpe(TypeShape.Abstract).resolution + val hole = h.resolver.omittedType(source, parameter) + val output = new TermShapeHost + h.resolver.listenInstanceViews(InstanceShape(hole))(output.publish) + val exporter = ArrayBuffer.empty[TermShape] + val detach = output.inferenceHost.observe(exporter += _) + val left = new Elaborator.State().newResolverState.inGraph(h.state) + val right = new Elaborator.State().newResolverState.inGraph(h.state) + assert(h.resolver.omittedType(source, parameter)(using left) eq hole) + assert(h.resolver.omittedType(source, parameter)(using right) eq hole) + val leftValues = ArrayBuffer.empty[TermShape] + val rightValues = ArrayBuffer.empty[TermShape] + h.resolver.listenInstanceViews(InstanceShape(hole))(leftValues += _)(using left) + h.resolver.listenInstanceViews(InstanceShape(hole))(rightValues += _)(using right) + val first = IntroShape(Term.UnitVal(), N) + val second = DynShape() + h.resolver.constrainTypes(ContextualType(h.tpe(TypeShape.Inferred(first)), Nil), ContextualType(hole, Nil))(using left) + h.resolver.constrainTypes(ContextualType(h.tpe(TypeShape.Inferred(second)), Nil), ContextualType(hole, Nil))(using right) + assert(leftValues.toList == List(first)) + assert(rightValues.toList == List(second)) + assert(exporter.isEmpty) + assert(output.currentShapes.isEmpty) + assert(h.state.allocatedTypeInstanceCount == 0) + assert(left.allocatedTypeInstanceCount == 0) + assert(right.allocatedTypeInstanceCount == 0) + detach() + + test("closed nominal candidates do not multiply along generic substitution paths"): + // A worksheet can check the selected member, but cannot detect redundant + // candidates that multiply when a constraint cycle crosses generic callers. + val h = new Harness + import h.given + val name: Tree.Ident = Tree.Ident("Closed") + val symbol = ClassSymbol(Tree.TypeDef(Cls, name, N), name) + val defn = ClassDef.Plain(N, Cls, symbol, BlockMemberSymbol("Closed", Nil), Nil, N, + ObjBody(Term.Blk(Nil, Term.UnitVal())), N, Nil, Nil, N)(N) + h.state.lexicalTypeBinders(symbol) = Set.empty + val original = NominalInstanceView(defn, Map.empty, N)(N, ShapeProvenance.empty)(h.resolver) + val scheme = h.tpe(TypeShape.Top).resolution + val substitutions = (1 to 16).map: index => + val (parameter, _) = h.parameter(s"A$index") + h.state.instantiateTypeParameters(scheme, FlowSymbol.app(), List(parameter)) + val host = new TermShapeHost + var current: TermShape = original + (1 to 1000).foreach: index => + current = h.resolver.instantiateShape(current, substitutions(index % substitutions.size)) + host.publish(current) + assert(host.currentShapes.size == 1) + assert(h.state.allocatedTypeInstanceCount == substitutions.size) + + test("intersection inputs propagate independently and reuse their subscriptions"): + // Diff tests cover inferred interfaces. This also checks that one intersection + // component can propagate before the other has any candidates, and that replay + // of the relation does not grow subscriptions or allocate parameter instances. + val h = new Harness + import h.given + val (a, at) = h.parameter("A") + val (b, bt) = h.parameter("B") + val (_, result) = h.parameter("Result") + val input = ContextualType(h.tpe(TypeShape.Intersection(at.resolution, bt.resolution)), Nil) + val expected = ContextualType(h.tpe(TypeShape.Tuple(result.resolution :: Nil)), Nil) + val seen = h.observe(ContextualType(result, Nil)) + def tuple(value: TermShape): TermShape = + InstanceShape(h.tpe(TypeShape.Tuple(h.tpe(TypeShape.Inferred(value)).resolution :: Nil))) + val first = IntroShape(Term.UnitVal(), N) + val second = DynShape() + val left = tuple(first) + val right = tuple(second) + h.resolver.constrainTypes(input, expected) + h.resolver.publishParameter(a, left) + assert(seen.toList == List(first)) + h.resolver.publishParameter(b, right) + assert(seen.toSet == Set(first, second)) + val counts = (a.inferenceHost.listeners.size, b.inferenceHost.listeners.size) + (1 to 1000).foreach: _ => + h.resolver.constrainTypes(input, expected) + h.resolver.publishParameter(a, left) + h.resolver.publishParameter(b, right) + assert((a.inferenceHost.listeners.size, b.inferenceHost.listeners.size) == counts) + assert(seen.size == 2) + assert(h.state.allocatedTypeInstanceCount == 0) + + test("supplied arguments receive input obligations instead of discarding them"): + val h = new Harness + import h.given + val (a, at) = h.parameter("A") + val one = h.tpe(TypeShape.Unit) + val two = h.tpe(TypeShape.Abstract) + val first = IntroShape(Term.UnitVal(), N) + val second = DynShape() + h.state.markExplicitTypeArgument(a) + h.resolver.constrainTypes(ContextualType(h.tpe(TypeShape.Inferred(first)), Nil), ContextualType(at, Nil)) + h.resolver.publishParameter(a, InstanceShape(one)) + h.resolver.publishParameter(a, InstanceShape(two)) + h.resolver.constrainTypes(ContextualType(h.tpe(TypeShape.Inferred(second)), Nil), ContextualType(at, Nil)) + for target <- List(one, two); bound <- List(first, second) do + assert(h.state.typeConstraints((target, bound, Nil))) + assert(a.currentShapes.toSet == Set(InstanceShape(one), InstanceShape(two))) + + + test("supplied parameter references forward input obligations across marked contexts"): + val h = new Harness + import h.given + val (a, at) = h.parameter("A") + val (b, bt) = h.parameter("B") + val source = ResolutionBoundary(TermSymbol(Fun, N, Tree.Ident("source"))) + val outer = ResolutionBoundary(TermSymbol(Fun, N, Tree.Ident("outer"))) + val target = ResolutionBoundary(TermSymbol(Fun, N, Tree.Ident("target"))) + val site = FlowSymbol.app() + val sourceMarks = ExitMark(source, S(site), NoMarks) :: ExitMark(outer, S(site), NoMarks) :: Nil + val targetMarks = ExitMark(target, S(site), NoMarks) :: Nil + val concrete = h.tpe(TypeShape.Unit) + val bound = IntroShape(Term.UnitVal(), N) + h.state.markExplicitTypeArgument(a) + h.state.markExplicitTypeArgument(b) + h.resolver.publishParameter(a, InstanceShape(bt).exit(sourceMarks).enter(targetMarks)) + h.resolver.publishParameter(b, InstanceShape(concrete).enter(sourceMarks)) + h.resolver.constrainTypes(ContextualType(h.tpe(TypeShape.Inferred(bound)), Nil), ContextualType(at, targetMarks)) + assert(h.state.typeConstraints((concrete, bound, Nil))) + assert(b.currentShapes.toSet == Set(InstanceShape(concrete).enter(sourceMarks))) + + test("a supplied argument receives only obligations for its marked activation"): + val h = new Harness + import h.given + val (a, at) = h.parameter("A") + val owner = ResolutionBoundary(TermSymbol(Fun, N, Tree.Ident("owner"))) + val firstMarks = ExitMark(owner, S(FlowSymbol.app()), NoMarks) :: Nil + val secondMarks = ExitMark(owner, S(FlowSymbol.app()), NoMarks) :: Nil + val one = h.tpe(TypeShape.Unit) + val two = h.tpe(TypeShape.Abstract) + val bound = IntroShape(Term.UnitVal(), N) + h.state.markExplicitTypeArgument(a) + h.resolver.publishParameter(a, InstanceShape(one).enter(firstMarks)) + h.resolver.publishParameter(a, InstanceShape(two).enter(secondMarks)) + h.resolver.constrainTypes(ContextualType(h.tpe(TypeShape.Inferred(bound)), Nil), ContextualType(at, firstMarks)) + assert(h.state.typeConstraints((one, bound, Nil))) + assert(!h.state.typeConstraints((two, bound, Nil))) + + test("supplied reference cycles saturate without expanding their arguments"): + val h = new Harness + import h.given + val (a, at) = h.parameter("A") + val (b, bt) = h.parameter("B") + val concrete = h.tpe(TypeShape.Unit) + val bound = IntroShape(Term.UnitVal(), N) + val lower = ContextualType(h.tpe(TypeShape.Inferred(bound)), Nil) + h.state.markExplicitTypeArgument(a) + h.state.markExplicitTypeArgument(b) + h.resolver.publishParameter(a, InstanceShape(bt)) + h.resolver.publishParameter(b, InstanceShape(at)) + h.resolver.constrainTypes(lower, ContextualType(at, Nil)) + h.resolver.publishParameter(b, InstanceShape(concrete)) + assert(h.state.typeConstraints((concrete, bound, Nil))) + val counts = (a.inferenceHost.listeners.size, b.inferenceHost.listeners.size) + (1 to 1000).foreach: _ => + h.resolver.constrainTypes(lower, ContextualType(at, Nil)) + h.resolver.publishParameter(a, InstanceShape(bt)) + h.resolver.publishParameter(b, InstanceShape(at)) + assert((a.inferenceHost.listeners.size, b.inferenceHost.listeners.size) == counts) + assert(a.currentShapes.toSet == Set(InstanceShape(bt))) + assert(b.currentShapes.toSet == Set(InstanceShape(at), InstanceShape(concrete))) + + test("importers extend a cyclic relation without mutating the exporter or one another"): + val h = new Harness + import h.given + val (a, at) = h.parameter("A") + val (b, bt) = h.parameter("B") + val ar = ContextualType(at, Nil) + val br = ContextualType(bt, Nil) + h.resolver.constrainTypes(ar, br) + h.resolver.constrainTypes(br, ar) + val output = new TermShapeHost + h.resolver.listenInstanceViews(InstanceShape(bt))(output.publish) + val exporter = ArrayBuffer.empty[TermShape] + val detach = output.inferenceHost.observe(exporter += _) + val left = new Elaborator.State().newResolverState.inGraph(h.state) + val right = new Elaborator.State().newResolverState.inGraph(h.state) + val leftValues = ArrayBuffer.empty[TermShape] + val rightValues = ArrayBuffer.empty[TermShape] + h.resolver.listenInstanceViews(InstanceShape(bt))(leftValues += _)(using left) + h.resolver.listenInstanceViews(InstanceShape(bt))(rightValues += _)(using right) + val first = IntroShape(Term.UnitVal(), N) + val second = DynShape() + h.resolver.publishParameter(a, first)(using left) + h.resolver.publishParameter(a, second)(using right) + assert(leftValues.toList == List(first)) + assert(rightValues.toList == List(second)) + assert(exporter.isEmpty) + assert(output.currentShapes.isEmpty) + detach() + + test("recursive record intersections reuse type views and stop adding graph edges"): + // Diff tests check member behavior; this measures graph growth and object + // reuse after saturation, which their golden output cannot observe. + val h = new Harness + import h.given + val left = new TypeResolution(Term.UnitVal(), _ => fail("Unexpected type error")) + val right = new TypeResolution(Term.UnitVal(), _ => fail("Unexpected type error")) + def recursive(resolution: TypeResolution): Unit = + val next = RcdField.signature(Term.Lit(Tree.StrLit("next")), Term.UnitVal()) + val source: Term.Rcd = Term.Rcd(false, List(next)) + resolution.publish(TypeShape.Record(source, List(next -> resolution))) + recursive(left) + recursive(right) + val intersection = h.tpe(TypeShape.Intersection(left, right)) + def record(tpe: DeclaredType): RecordTypeShape = h.observe(ContextualType(tpe, Nil)).toList match + case (value: RecordTypeShape) :: Nil => value + case _ => fail("Expected one merged record") + val first = record(intersection) + val next = first.fields("next") + val saturated = record(next) + val counts = (left.inferenceHost.listeners.size, right.inferenceHost.listeners.size) + (1 to 1000).foreach: _ => + assert(record(saturated.fields("next")) eq saturated) + assert((left.inferenceHost.listeners.size, right.inferenceHost.listeners.size) == counts) + assert(h.state.allocatedTypeInstanceCount == 0) diff --git a/hkmc2/shared/src/main/scala/hkmc2/CompilerCtx.scala b/hkmc2/shared/src/main/scala/hkmc2/CompilerCtx.scala index 5f277a130d..4f556c4e54 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/CompilerCtx.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/CompilerCtx.scala @@ -89,11 +89,13 @@ class CompilerCtx( ParserSetup(file) given Elaborator.Ctx = prelude val artifactCtx = derive(parse.origin.fileName, dependencies) + val parsed = parse.resultBlk + val elaborationConfig = Config.elaborationConfig(parsed) val elab = given CompilerCtx = artifactCtx + given Config = elaborationConfig Elaborator(tl, file.up, prelude) - val parsed = parse.resultBlk val nme = file.baseName val exportedSymbol = parsed.definedSymbols.find(_._1 === nme).map(_._2) def collectCompilationUnitSymbols(program: codegen.Program): Set[codegen.BoundSymbol] = @@ -103,15 +105,18 @@ class CompilerCtx( case sym: BlockMemberSymbol => sym .toSet case _ => Set.empty - val (blk0, _) = elab.importFrom(parsed) - if file.toString === paths.runtimeSourceFile.toString then - state.initRuntimeSymbolsFromBlock(blk0) - else + val isRuntime = file.toString === paths.runtimeSourceFile.toString + // Elaboration synthesizes references to runtime definitions (e.g. assertFail). + // The runtime itself supplies these symbols once its own body is elaborated. + if !isRuntime then state.initRuntimeSymbolsFromFile(paths.runtimeSourceFile, prelude)( using tl, summon[Raise], artifactCtx) + val (blk0, _) = elaborationConfig.givenIn(elab.importFrom(parsed, exportedSymbol.toList)) + if isRuntime then + state.initRuntimeSymbolsFromBlock(blk0) val artifactConfig = Config.extractConfigFromStats(blk0) - artifactConfig.givenIn: + if !artifactConfig.language.useNewResolution then artifactConfig.givenIn: given Elaborator.State = state val resolver = Resolver(backendTL) resolver.traverseBlock(blk0)(using Resolver.ICtx.empty) @@ -159,6 +164,7 @@ class CompilerCtx( val ir = artifactConfig.givenIn: given Elaborator.State = state + given codegen.Erasure = codegen.Erasure(blk)(using artifactConfig, prelude, state) val low = backendTL.givenIn: new codegen.Lowering()(using artifactConfig, backendTL, summon[Raise], state, prelude, summon[SymbolPrinter]) optimize(low.program(blk, Set.empty)) @@ -196,8 +202,9 @@ class CompilerCtx( // The prelude context is shared so every compilation unit sees the same prelude // symbols. Callers still elaborate their own files with a fresh State; the frozen // State remains the owner captured by the prelude symbols themselves. - // The prelude is elaborated once per context, under its root configuration: were it - // elaborated per requester, cached compilation units would keep referring to whichever + // The prelude is elaborated once per context, under its own file directives applied + // to the root configuration. Were it elaborated per requester, cached compilation + // units would keep referring to whichever // elaboration came first, and a body inlined across units would then carry prelude symbols // that the importing file does not recognize. val lastMod = fs.getLastChangedTimestamp(file) @@ -213,9 +220,19 @@ class CompilerCtx( given Config = rootConfig given CompilerCtx = this val parse = ParserSetup(file) - val elab = Elaborator(tl, file.up, Ctx.empty) - val initCtx = State.init.nestLocal("prelude") - val (blk, ctx) = elab.importFrom(parse.resultBlk)(using initCtx) + val elaborationConfig = Config.elaborationConfig(parse.resultBlk) + // The prelude defines the builtins consulted during its own resolution. + // Seed lookup with the block's original symbols, as block elaboration does, + // so those lookups share the declarations that are being elaborated. + val initCtx = State.init.nestLocal("prelude").withMembers(parse.resultBlk.definedSymbols) + val elab = + given Config = elaborationConfig + Elaborator(tl, file.up, initCtx) + val (blk, ctx) = elaborationConfig.givenIn(elab.importFrom(parse.resultBlk, Nil)(using initCtx)) + // Prelude declarations have no executable program, but their signatures and nominal hierarchy + // must be erased before any compilation unit can use them. + given Ctx = ctx + elaborationConfig.givenIn(codegen.Erasure(blk)) PreludeArtifact(parse.resultBlk, blk, ctx, state, rootConfig, lastMod), ) diff --git a/hkmc2/shared/src/main/scala/hkmc2/Config.scala b/hkmc2/shared/src/main/scala/hkmc2/Config.scala index a3cd035e58..b3d1ef2b4a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/Config.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/Config.scala @@ -95,6 +95,9 @@ object Config: case class Language( allowUnresolvedAccesses: Bool, useNewResolution: Bool, + strictResolution: Bool, // Reject multiple resolved selection targets as well as missing targets + // Unknown inputs admitted by a public interface are checked regardless of this flag. + eagerResolution: Bool, // Reject selections whose prefixes have shapes that do not support them, even though the selection might have other valid targets typeCheck: Opt[TypeChecking], )(val versionName: Str) @@ -103,6 +106,8 @@ object Config: val v0_2_x = Language( typeCheck = N, useNewResolution = false, + strictResolution = false, + eagerResolution = false, allowUnresolvedAccesses = true, )( versionName = "0.2.x", @@ -111,6 +116,8 @@ object Config: val v0_3_x = Language( typeCheck = N, useNewResolution = true, + strictResolution = false, + eagerResolution = false, allowUnresolvedAccesses = false, )( versionName = "0.3.x", @@ -216,6 +223,19 @@ object Config: * Normally, we avoid inlining into @inline functions as that could lead to unexpected code bloat. */ case class Inliner(inlineThreshold: Int, altSmallThreshold: Int = 2) + /** File directives apply to the whole compilation unit, including elaboration. + * Read them before constructing the elaborator so its resolver agrees with lowering. + * Elaboration still records SetConfig statements and reports directive errors once. */ + def elaborationConfig(prgm: syntax.Tree.Block)(using Config): Config = + import syntax.Tree.* + given Raise = _ => () + def modifier(tree: syntax.Tree): Config => Config = tree match + case App(Directive(prefix, _), args) => modifier(Directive(prefix, args)) + case Directive(Ident("config"), Tup(args)) => ConfigParser.parseOverrides(args) + case Directive(Ident("lang"), Tup(args)) => ConfigParser.parseLanguageDirective(args) + case _ => identity + prgm.desugStmts.foldLeft(config)((cfg, tree) => modifier(tree)(cfg)) + def extractConfigFromStats(prgm: semantics.Term.Blk)(using Config) = // Extract cumulative config modifications from SetConfig statements val configModify = prgm.stats.collect: @@ -378,20 +398,6 @@ object ConfigParser: source = Diagnostic.Source.Compilation)) N - private def withLanguage( - base: Config.Language, - allowUnresolvedAccesses: Bool, - useNewResolution: Bool, - typeCheck: Opt[Config.TypeChecking], - ): Config.Language = - Config.Language( - allowUnresolvedAccesses, - useNewResolution, - typeCheck, - )( - base.versionName, - ) - private def parsedLanguageModifier[A]( value: Tree, )( @@ -404,13 +410,19 @@ object ConfigParser: private def parseLanguageFieldModifier(tree: Tree)(using Raise): Opt[Config.Language => Config.Language] = tree match case NamedArg("allowUnresolvedAccesses", value) => parsedLanguageModifier(value)(parseBool): v => - language => withLanguage(language, v, language.useNewResolution, language.typeCheck) + lang => lang.copy(allowUnresolvedAccesses = v)(lang.versionName) case NamedArg("useNewResolution", value) => parsedLanguageModifier(value)(parseBool): v => - language => withLanguage(language, language.allowUnresolvedAccesses, v, language.typeCheck) + lang => lang.copy(useNewResolution = v)(lang.versionName) + case NamedArg("strictResolution", value) => + parsedLanguageModifier(value)(parseBool): v => + lang => lang.copy(strictResolution = v)(lang.versionName) + case NamedArg("eagerResolution", value) => + parsedLanguageModifier(value)(parseBool): v => + lang => lang.copy(eagerResolution = v)(lang.versionName) case NamedArg("typeCheck", value) => parsedLanguageModifier(value)(tree => parseOpt(tree)(parseTypeChecking)): v => - language => withLanguage(language, language.allowUnresolvedAccesses, language.useNewResolution, v) + lang => lang.copy(typeCheck = v)(lang.versionName) case other => unsupported("Language", other) N diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index c8de517df4..09305a9d04 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -29,6 +29,10 @@ This means that if a program fragment containing definitions is rewritten at all then the rewritten version must be kept as the "currently valid" version, and the old one must no longer be reused, as a given symbol can never correspond to more than one IR definition. +Class-like symbols also retain a nominal `ClassHeader`: erasure publishes these before executable bodies, +including for external declarations, and rewritten IR class definitions refresh them. This lets inheritance +queries operate solely on IR even when a parent has no executable definition or has not been lowered yet. + Moreover, we generally assume that in a given program, no symbol is every bound more than once by any binding form, such as Scoped, Label, Define. (This is currently not completely enforced, though.) This means that processes that duplicate binding forms within a program (eg, inlining) @@ -63,7 +67,10 @@ type ValueSymbol = SimpleSymbol | TermSymbol | BlockMemberSymbol | InnerSymbol */ type BoundSymbol = ScopedSymbol | TermSymbol -/** Symbols that may occur in MIR free-variable sets. +/** Symbols of lexical bindings required by MIR, including assignment destinations. + * A `MemberRef` requires its block member binding regardless of the selected definition: + * `Scoped` introduces that binding, and JS can store its class on the companion function. + * Analyses needing individual definitions must instead inspect the reference's `disamb`. */ type FreeSymbol = SimpleSymbol | BlockMemberSymbol | LabelSymbol @@ -845,6 +852,11 @@ object ValDefn: ValDefn(tsym = TermSymbol(k, owner, Tree.Ident(sym.nme), erasedType = rhs.erasedValueType), sym, rhs)(configOverride, annotations) +/** The nominal hierarchy of a class, available independently of its executable body. + * An absent header means an unknown parent; a header with no parent denotes a root. + */ +final case class ClassHeader(parent: Opt[TypeSymbol]) + /* The following explains the difference between paramsOpt, auxParams, privateFields and publicFields. @@ -900,6 +912,12 @@ final case class ClsLikeDefn( val annotations: Ls[Annot], ) extends Defn: require(k isnt syntax.Mod) + isym match + case sym: ClassLikeSymbol => + sym.irClassHeader = parentPath match + case N => S(ClassHeader(N)) + case S(parent) => parent.targetSymbol.flatMap(_.asTpe).map(p => ClassHeader(S(p))) + case _ => () methods.foreach: m => m.dSym.irDefn = S(m) companion.foreach: c => @@ -917,6 +935,9 @@ final case class ClsLikeBody( ctor: Block, annotations: Ls[Annot], ): + isym match + case sym: ModuleOrObjectSymbol => sym.irClassHeader = S(ClassHeader(N)) + case _ => () def isStaged: Bool = annotations.exists: case Annot.Modifier(Keyword.`staged`) => true case _ => false @@ -994,6 +1015,7 @@ sealed abstract class Result extends Located, HasErasedType: case Value.This(sym) => s"this[${sym.showDbg}]" case Value.Lit(lit) => lit.idStr case Select(q, n) => s"Select(${q.showDbg}, ${n.showDbg})" + case SuperSelect(q, base, n) => s"SuperSelect(${q.showDbg}, ${base.showDbg}, ${n.showDbg})" case DynSelect(q, fld, arrayIdx) => s"DynSelect(${q.showDbg}, ${fld.showDbg}, $arrayIdx)" case Call(fun, argss) => s"Call(${fun.showDbg}, [${ argss.map(_.map(a => a.value.showDbg).mkString("[", ", ", "]")).mkString(", ")}])" @@ -1022,6 +1044,7 @@ sealed abstract class Result extends Located, HasErasedType: case _: Value => true case sel @ Select(q, n) => q.isPure && sel.symbol.exists(_.isPure) + case sel @ SuperSelect(q, base, _) => q.isPure && base.isPure && sel.symbol.isPure case c @ Call(fun, ass) if c.isKnownUnsaturatedCall => fun.isPure && ass.forall(_.forall(a => a.spread.isEmpty && a.value.isPure)) case Call(Value.SimpleRef(bs: BuiltinSymbol), ass) if bs.isPure => @@ -1038,6 +1061,7 @@ sealed abstract class Result extends Located, HasErasedType: case Call(fun, argss) => fun.subBlocks ::: argss.flatten.flatMap(_.value.subBlocks) case Instantiate(mut, cls, argss) => argss.flatten.flatMap(_.value.subBlocks) case Select(qual, name) => qual.subBlocks + case SuperSelect(qual, base, _) => qual.subBlocks ::: base.subBlocks case Lambda(params, body) => body :: Nil case Tuple(mut, elems) => elems.flatMap(_.value.subBlocks) case _ => Nil @@ -1047,6 +1071,7 @@ sealed abstract class Result extends Located, HasErasedType: case Instantiate(mut, cls, argss) => cls.freeVars ++ argss.flatten.flatMap(_.value.freeVars).toSet case Cast(value, _, _) => value.freeVars case Select(qual, name) => qual.freeVars + case SuperSelect(qual, base, _) => qual.freeVars ++ base.freeVars case Lambda(params, body) => body.freeVars -- params.paramSyms case Tuple(mut, elems) => elems.flatMap(_.value.freeVars).toSet case Record(mut, args) => @@ -1062,6 +1087,7 @@ sealed abstract class Result extends Located, HasErasedType: case Instantiate(mut, cls, argss) => cls.size + argss.iterator.flatten.map(_.value.size).sum case Cast(value, _, _) => value.size case Select(qual, name) => qual.size + case SuperSelect(qual, base, _) => qual.size + base.size case Lambda(params, body) => 1 + body.size case Tuple(mut, elems) => elems.iterator.map(_.value.size).sum case Record(mut, args) => args.iterator.map(arg => arg.idx.fold(0)(_.size) + arg.value.size).sum @@ -1107,6 +1133,7 @@ sealed abstract class Result extends Located, HasErasedType: case _ => N // * A resolved selection has the type of the member it refers to (e.g. `this.field`); an // * unresolved selection (dynamic field access) stays unknown. + case sel: SuperSelect => sel.symbol.erasedType case sel @ Select(_, _) => sel.symbol match case S(ts: TermSymbol) => ts.erasedType // * A class reference is the class object, so it stays unknown. @@ -1310,6 +1337,7 @@ sealed abstract class Path extends TrivialResult: def targetSymbol: Opt[DefinitionSymbol[?]] = this match case ref: Value.MemberRef => S(ref.disamb) case sel: Select => sel.symbol + case sel: SuperSelect => S(sel.symbol) case _ => N /** @@ -1319,6 +1347,14 @@ case class Select(qual: Path, name: Tree.Ident)(val symbol: Opt[DefinitionSymbol def withLoc(loc: Opt[Loc]): Select = if loc == toLoc then this else copy()(symbol, loc)(sanitize) def extraInfo(using DebugPrinter): Str = symbol.map(s => s"sym=${s.showAsPlain}").mkString +/** Direct lookup on the lexical parent's prototype, with the original receiver as `this`. + * `base` is the same stable parent operand used by the class definition: a direct class + * reference or a previously evaluated value. No extends expression is re-evaluated at a + * super call. Both operands are explicit so lifting and inlining preserve their bindings. + */ +case class SuperSelect(qual: Path, base: Path, name: Tree.Ident)(val symbol: TermSymbol, val toLoc: Opt[Loc]) extends Path: + def withLoc(loc: Opt[Loc]): SuperSelect = if loc == toLoc then this else copy()(symbol, loc) + case class DynSelect(qual: Path, fld: Path, arrayIdx: Bool)(val toLoc: Opt[Loc]) extends Path: def withLoc(loc: Opt[Loc]): DynSelect = if loc == toLoc then this else copy()(loc) @@ -1390,6 +1426,7 @@ object TermSymbolPath: case selection: Select => selection.symbol match case S(sym: TermSymbol) => S(sym) case _ => N + case selection: SuperSelect => S(selection.symbol) case _ => N case class Arg(spread: Opt[SpreadKind], value: Path) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockSimplifier.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockSimplifier.scala index fab852731a..eee15c7cd9 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockSimplifier.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockSimplifier.scala @@ -1538,6 +1538,10 @@ class BlockSimplifier def canBeInlineEliminated: Bool = isPrivate && !isMethod && !defn.noInline && useCount <= 1 && !disallowElimination && !isLoopBreaker && !isPatternHelper && !hasDuplicateBindings + // Class/module references still need the function object that owns their `.class`. + // Term-only use counts cannot establish that this shared binding is dead; + // ordinary DCE can remove it once neither interpretation is referenced. + && defn.sym.asClsOrMod.isEmpty // false def inlineCost(newBlk: Block, threshold: Int): Opt[Int] = diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala index 0f863beadc..cb27059c40 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala @@ -180,6 +180,12 @@ class BlockTransformer(subst: SymbolSubst): case p: Path => applyPath(p)(k) def applyPath(p: Path)(k: Path => Block): Block = p match + case p @ SuperSelect(qual, base, name) => + applyPath(qual): qual2 => + applyPath(base): base2 => + val sym2 = p.symbol.subst + k(if (qual2 is qual) && (base2 is base) && (sym2 is p.symbol) then p + else SuperSelect(qual2, base2, name)(sym2, p.toLoc)) case DynSelect(qual, fld, arrayIdx) => applyPath(qual): qual2 => applyPath(fld): fld2 => diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTraverser.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTraverser.scala index ecfe9446b0..da0de4c039 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTraverser.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTraverser.scala @@ -69,6 +69,8 @@ class BlockTraverser: case p: Path => applyPath(p) def applyPath(p: Path): Unit = p match + case p @ SuperSelect(qual, base, _) => + applyPath(qual); applyPath(base); p.symbol.traverse case DynSelect(qual, fld, arrayIdx) => applyPath(qual); applyPath(fld) case p @ Select(qual, name) => diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/ErasedType.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/ErasedType.scala index 15dbef9a55..76b2d3dfcc 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/ErasedType.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/ErasedType.scala @@ -157,18 +157,8 @@ object ErasedType: * - `S(N)` for a root class with no parent. * - `N` for a class whose parent chain is not available in the IR (e.g. an unlinked import). */ - private def parentOf(sym: BaseTypeSymbol)(using Ctx, State): Opt[Opt[TypeSymbol]] = - sym.asClsOrMod.flatMap: sym => - sym.irClsLikeDefn.flatMap: defn => - defn.parentPath match - case S(parent) => parent.targetSymbol.collect { case s: TypeSymbol => s }.map(S(_)) - case N => S(N) - .orElse: - // FIXME: remove this fallback once imported classes have their `irClsLikeDefn` properly linked - sym.defn.flatMap: defn => - defn.ext match - case S(parent) => parent.cls.resolvedSym.flatMap(_.asClsOrMod).map(S(_)) - case N => S(N) + private def parentOf(sym: BaseTypeSymbol): Opt[Opt[TypeSymbol]] = + sym.asClsOrMod.flatMap(_.irClassHeader.map(_.parent)) /** A symbol's ancestors, nearest first, starting with the symbol itself and following its single parent chain. * @@ -247,10 +237,11 @@ object ErasedType: /** Erases a type-annotated term to an [[ErasedType]]. * - * Note that the resulting erased type is **not** canonicalized to avoid using `ctx.builtins` during elaboration - * of `Prelude`. + * Used by the legacy resolver after resolution. Canonicalization is separate and may inspect the + * nominal hierarchy published by Erasure. */ - def eraseSign(sign: Term): Opt[ErasedValueType] = sign match + def eraseSign(sign: Term)(using Erasure): Opt[ErasedValueType] = sign match + case Capture(base, _) => eraseSign(base) case CompType(lhs, rhs, true) => // * A union is kept as a transient `Union` surface form; `canonicalize` collapses it to the members' LUB. for @@ -260,6 +251,7 @@ object ErasedType: // * An intersection is never decomposed: narrowing to one member would call for a GLB, which this lattice // * cannot express. case CompType(_, _, false) => S(ErasedType.Unknown) + case DynTy() => S(ErasedType.Unknown) case UnitVal() => S(ErasedType.Unit) // * A written arrow denotes a function value, and every function value is a `Function`. case FunTy(_, _, _) => S(ErasedType.Function(rsc = S(false))) @@ -510,6 +502,22 @@ trait HasErasedType: /** Similar to `erasedValueType`, but coerces to the top type if the specific erased value type is not known. */ lazy val erasedValueType_! : ErasedValueType = erasedValueType.getOrElse(ErasedType.Unknown) +/** Erased types of source declarations are initialized by Erasure after resolution finishes. + * `null` means no representation has been assigned; `N` is an initialized, unknown representation. + * The checked getter also protects HasErasedType's lazy derived values from caching an early `N`. + * IR-generated symbols may initialize the same slot directly at construction. + */ +trait HasDelayedErasedType[T <: ErasedType] extends HasErasedType: + private var assignedErasedType: Opt[T] = null + final def isErased: Bool = assignedErasedType != null + final def erasedType: Opt[T] = + assert(isErased, "Erased type read before erasure") + assignedErasedType + final def erasedType_=(value: Opt[T]): Unit = + assert(!isErased, "Erased type initialized more than once") + assert(value != null, "An initialized erased type cannot be null") + assignedErasedType = value + /** A [[HasErasedType]] whose erased type can be populated exactly once post-construction. */ trait HasOnceMutableErasedType extends HasErasedType: // Implementation Note: Provided for overriding classes to implement `erasedType` directly as an `override var` diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Erasure.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Erasure.scala new file mode 100644 index 0000000000..15686a31d7 --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Erasure.scala @@ -0,0 +1,272 @@ +package hkmc2 +package codegen + +import scala.collection.mutable + +import hkmc2.utils.*, shorthands.* +import semantics.* +import semantics.Elaborator.{Ctx, State} +import semantics.Term.* +import hkmc2.Message.MessageContext + +/** The capability to inspect completed resolution. Created only after elaboration/resolution; + * executable lowering additionally requires this instance's whole-program pass to have finished. + */ +final class Erasure private (using Config, Ctx, State)(using Raise): + private given Erasure = this + private var completed: Opt[Statement] = N + + def requireCompleted(statement: Statement): Unit = + assert(completed.exists(_ is statement), "Lowering requires completed erasure of this program") + + /** Erase an interpretation registered during elaboration, without installing new listeners. */ + private def erase(resolution: TypeResolution, seen: Set[TypeResolution]): ErasedValueType = + import TypeShape.* + if seen(resolution) || resolution.hasErrors then ErasedType.Unknown + else + val next = seen + resolution + resolution.shapes.toList match + case Nominal(defn) :: Nil => defn.sym match + case symbol: (ClassSymbol | ModuleOrObjectSymbol) => ErasedType.ValueLike(S(false), symbol) + case _ => ErasedType.Unknown + case Alias(_, rhs) :: Nil => rhs.fold(ErasedType.Unknown)(erase(_, next)) + case Union(left, right) :: Nil => ErasedType.union(erase(left, next), erase(right, next)) + case Reference(base, _, _, _) :: Nil => erase(base, next) + case Applied(base, _) :: Nil => erase(base, next) + case Function(_, _) :: Nil => ErasedType.Function(S(false)) + case Polymorphic(_, _, body) :: Nil => erase(body, next) + case Unit :: Nil => ErasedType.Unit + case _ => ErasedType.Unknown + + private def eraseSignature(sign: Term): Opt[ErasedValueType] = + if config.language.useNewResolution then + assert(sign.typeInterpretation.isDefined, "Signature was not registered during elaboration") + S(erase(sign.typeInterpretation.get, Set.empty)) + else ErasedType.eraseSign(sign) + + private def declaredType(sign: Opt[Term], modulefulness: Modulefulness): Opt[ErasedValueType] = + modulefulness.msym match + case S(msym) => S(ErasedType.ValueLike(rsc = S(false), msym)) + case N => sign.flatMap(eraseSignature) + + /** Splits written arrows, retaining an unknown arity when a parameter tuple contains a spread. */ + private def splitSignature(sign: Term): Opt[(Ls[Ls[Opt[ErasedValueType]]], Term)] = + def paramsOf(lhs: Term): Opt[Ls[Opt[ErasedValueType]]] = lhs match + case Tup(fields) => + val noParams: Opt[Ls[Opt[ErasedValueType]]] = S(Nil) + fields.foldRight(noParams): (fld, acc) => + (fld, acc) match + case (Fld(_, t, _), S(rest)) => S(eraseSignature(t) :: rest) + case _ => N + case single => S(eraseSignature(single) :: Nil) + sign match + case Forall(_, _, body) => splitSignature(body) + case FunTy(lhs, rhs, _) => paramsOf(lhs).map: ps => + splitSignature(rhs) match + case S((rest, ret)) => (ps :: rest, ret) + case N => (ps :: Nil, rhs) + case _ => N + + private def definitionType(td: TermDefinition): Opt[ErasedType] = + val declared = Annot.declareModifierOf(td.annotations).isDefined + // A declared function with no written parameters obtains its calling convention from its arrows. + // An ordinary paramless function instead returns the value denoted by the entire signature. + val sigShape = + if (td.k is syntax.Fun) && td.params.isEmpty && declared then td.sign.flatMap(splitSignature) + else N + val resultSign = sigShape.map(_._2).orElse(td.resultSignature) + def fullResult(res: TypeResolution, count: Int, seen: Set[TypeResolution]): ErasedValueType = + if seen(res) then ErasedType.Unknown + else if count == 0 then erase(res, Set.empty) + else res.shapes.toList match + case TypeShape.Alias(_, S(rhs)) :: Nil => fullResult(rhs, count, seen + res) + case TypeShape.Reference(base, _, _, _) :: Nil => fullResult(base, count, seen + res) + case TypeShape.Applied(base, _) :: Nil => fullResult(base, count, seen + res) + case TypeShape.Polymorphic(_, _, body) :: Nil => fullResult(body, count, seen + res) + case TypeShape.Function(_, ret) :: Nil => fullResult(ret, count - 1, seen + res) + case _ => ErasedType.Unknown + val resultType = + if config.language.useNewResolution && td.sign.nonEmpty && (td.k is syntax.Fun) + && !td.flags.hasResultAnnotation && td.params.nonEmpty && !td.modulefulness.isModuleful + then S(fullResult(td.sign.get.typeInterpretation.get, td.params.length, Set.empty)) + else declaredType(resultSign, td.modulefulness) + td.k match + case syntax.Fun => + val paramLists = sigShape match + case S((ps, _)) => ps + case N => td.params.map(_.params.map(_.sym.erasedType)) + // Block-level nullary functions take an implicit empty list and references auto-invoke them. + // Member getters and global declarations denote their result directly. + val physicalParamLists = + if paramLists.isEmpty && td.owner.isEmpty && !declared then Nil :: Nil else paramLists + if physicalParamLists.isEmpty then resultType + else S(ErasedType.FuncRef(rsc = S(false), physicalParamLists, resultType)) + case _: syntax.Val => resultType + case _ => N + + private def run(root: Statement): Unit = + val visited = mutable.Set.empty[Identity[Statement]] + val statements = mutable.ArrayBuffer.empty[Statement] + val parameters = mutable.ArrayBuffer.empty[Param] + val parentConstructions = mutable.Set.empty[Identity[Statement]] + def parentHead(term: Term): Unit = if parentConstructions.add(Identity(term)) then term match + // Legacy resolution can expand a parent reference to an implicit constructor call. + // Follow only the class head; constructions in argument expressions are real allocations. + case ref: Resolvable if ref.expanded isnt ref => parentHead(ref.expanded) + case New(cls, _, _) => parentHead(cls) + case App(lhs, _) => parentHead(lhs) + case TyApp(lhs, _) => parentHead(lhs) + case _ => () + def params(ps: ParamList): Unit = ps.foreach(parameters += _) + // Handler continuations and rejected class rest parameters can survive as references after their + // parameter list has been removed. The symbol retains the source declaration needed for erasure. + def referencedParameter(sym: VarSymbol): Unit = sym.decl match + case S(p: Param) => + parameters += p + p.subTerms.foreach(visit) + case _ => () + def visit(statement: Statement): Unit = if visited.add(Identity(statement)) then + statements += statement + statement match + case td: TermDefinition => td.params.foreach(params); td.tparams.foreach(_.foreach(parameters += _)) + case cls: ClassLikeDef => + cls.paramsOpt.foreach(params) + cls.auxParams.foreach: ps => + params(ps) + ps.subTerms.foreach(visit) + cls.ext.foreach: parent => + parentHead(parent) + visit(parent) + cls match + case pat: PatternDef => + parameters ++= pat.parameters + pat.parameters.flatMap(_.subTerms).foreach(visit) + pat.pattern.subTerms.foreach(visit) + case _ => () + case Lam(ps, _) => params(ps) + case Handle(_, _, _, _, definitions, _) => definitions.foreach: d => + referencedParameter(d.resumeSym) + visit(d.td) + case Ref(sym: VarSymbol) => referencedParameter(sym) + case SimpleRef(sym: VarSymbol) => referencedParameter(sym) + case New(_, _, refinement) => refinement.foreach: (symbol, body) => + symbol.defn.fold(visit(body.blk))(visit) + case Rcd(_, stats) => stats.foreach(visit) + case _ => () + statement.subStatements.foreach(visit) + // Legacy resolution may insert executable terms (for example implicit applications). + // Expanded nodes may share their children with the source tree; visit their identities only once. + statement match + case term: Resolvable => visit(term.expanded) + case _ => () + visit(root) + + def parentClasses(parent: New): Ls[ClassLikeDef] = + (parent.cls.resolvedSym.flatMap(_.asClsOrMod).flatMap(_.defn).toList ::: + ClassValueShape.targetsOf(parent).map(ClassValueShape.definitionOf)).distinct + def parentClass(parent: New): Opt[ClassLikeDef] = parentClasses(parent) match + case cls :: Nil => S(cls) + case _ => N + + // No erased representation is read until every interpretation has been validated and every + // nominal parent header has been published, including forward and external declarations. + statements.foreach: + case term: Term => term.typeInterpretation.foreach(_.validate(Set.empty)) + case _ => () + statements.foreach: + case cls: ClassLikeDef => + cls.ext.foreach: extension => + parentClasses(extension).foreach(Inheritance.validateParent(cls, _, extension)) + cls.sym match + case sym: ClassLikeSymbol if sym.irClassHeader.isEmpty => + sym.irClassHeader = cls.ext match + case N => S(ClassHeader(N)) + case S(parent) => parentClass(parent).flatMap(_.sym.asClsOrMod).map(p => ClassHeader(S(p))) + case _ => () + case _ => () + + // Public members replace inherited names; private members retain their declaration identity. + // A subclass cannot implement a private declaration by introducing the same name. + type MemberKey = Either[TermSymbol, Str] + // Definitions are keyed by identity: as case classes, they would otherwise + // be hashed and compared through their entire bodies on every lookup. + val effectiveMembers = mutable.Map.empty[Identity[ClassLikeDef], Map[MemberKey, TermSymbol]] + def checkConstruction(term: Term & PossiblyErroneous, cls: ClassDef): Unit = + if Inheritance.hasModifier(cls, syntax.Keyword.`abstract`) then + term.isErroneous = true + raise(ErrorReport( + msg"Cannot instantiate abstract class '${cls.sym.nme}'" -> term.toLoc :: + (msg"Abstract class defined here" -> cls.toLoc) :: Nil, source = Diagnostic.Source.Compilation)) + def members(cls: ClassLikeDef, visiting: Set[Identity[ClassLikeDef]]): Map[MemberKey, TermSymbol] = + effectiveMembers.getOrElseUpdate(Identity(cls), + if visiting(Identity(cls)) then + raise(ErrorReport(msg"Inheritance cycle involving '${cls.sym.nme}'" -> cls.toLoc :: Nil, + source = Diagnostic.Source.Compilation)) + Map.empty + else + val inherited = cls.ext.flatMap(parentClass) + .fold(Map.empty[MemberKey, TermSymbol])(members(_, visiting + Identity(cls))) + inherited ++ cls.body.members.toList.flatMap: (name, member) => + member.asTrm.map: symbol => + val key: MemberKey = if symbol.isPrivate then Left(symbol) else Right(name) + key -> symbol + ) + statements.foreach: + case td: TermDefinition => Inheritance.validateMember(td) + case cls: ClassLikeDef if !Inheritance.isExternal(cls) && + !Inheritance.hasModifier(cls, syntax.Keyword.`abstract`) => + val missing = members(cls, Set.empty).values.toList + .distinct.filter(_.defn.exists(Inheritance.isAbstract)) + .sortBy(_.nme) + if missing.nonEmpty then raise(ErrorReport( + msg"${cls.sym.describeKind.capitalize} '${cls.sym.nme}' has unimplemented members" -> cls.toLoc :: + missing.map(sym => msg"Member '${sym.nme}' requires an implementation" -> sym.toLoc), + source = Diagnostic.Source.Compilation)) + case nw: New if nw.rft.isEmpty && !parentConstructions(Identity(nw)) => + parentClass(nw).collect { case cls: ClassDef => cls }.foreach(checkConstruction(nw, _)) + case app: App if !parentConstructions(Identity(app)) => + // Constructor functions can flow through variables and parameters; their call shapes + // retain the declaration even when the callee has no single resolved symbol. + def constructed(event: ShapeEvent): Ls[ClassDef] = event match + case ActivatedShapeEvent(value, _) => constructed(value) + case MarkedShape(value, _) => constructed(value) + case ContextualShape(value, _) => constructed(value) + case shape: AppShape if shape.src is app => shape.applicationHead._1 match + case definition: DefnShape => definition.defn match + case td: TermDefinition => td.tsym match + case ctor: ClassCtorSymbol => ctor.associatedCls.defn.toList + case _ => Nil + case cls: ClassDef => cls :: Nil + case _ => Nil + case _ => Nil + case _ => Nil + val direct = app.lhs.resolvedSym.toList.flatMap: + case ctor: ClassCtorSymbol => ctor.associatedCls.defn.toList + case _ => Nil + (direct ::: app.shapes.toList.flatMap(constructed)).distinct.foreach(checkConstruction(app, _)) + case _ => () + + parameters.foreach: p => + if !p.sym.isErased then p.sym.erasedType = declaredType(p.sign, p.modulefulness) + // Public fields have a BlockMemberSymbol; private/auxiliary constructor fields have a TermSymbol. + // Their source signatures belong to the parameter, not to the synthesized field definition. + val field = p.fldSym.flatMap: + case sym: TermSymbol => S(sym) + case sym: BlockMemberSymbol => sym.tsym + case _ => N + field.foreach: sym => + if !sym.isErased then sym.erasedType = p.sym.erasedType + statements.foreach: + case td: TermDefinition if !td.tsym.isErased => td.tsym.erasedType = definitionType(td) + case _ => () + statements.foreach: + case LetDecl(sym: TermSymbol, _) if !sym.isErased => sym.erasedType = N + case _ => () + completed = S(root) + +object Erasure: + /** Prelude declarations use the same pass even though they have no executable program. */ + def apply(root: Statement)(using Config, Ctx, State)(using Raise): Erasure = + val erasure = new Erasure + erasure.run(root) + erasure diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 9bddf3a37d..6e3981a290 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -212,6 +212,14 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): // search for unliftable classes and build the extends graph new BlockTraverser: this.applyScopedObject(s.obj) + override def applyPath(p: Path): Unit = p match + // A direct superclass reference, like an extends or Instantiate operand, does not + // let the class escape as a value. Its receiver still needs ordinary capture analysis. + case sel @ SuperSelect(qual, RefOfDefn(S(cls: ClassSymbol), _), _) => + applyPath(qual) + cls.traverse + sel.symbol.traverse + case _ => super.applyPath(p) override def applyCase(cse: Case): Unit = cse match case Case.Cls(cls: (ClassSymbol | ModuleOrObjectSymbol), _) => @@ -1181,7 +1189,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): val cls = obj.cls val flattenedSym = BlockMemberSymbol(obj.cls.sym.nme + "$", Nil, true) - val flattenedDSym = TermSymbol.fromFunBms(flattenedSym, N, erasedType = N) + val flattenedDSym = TermSymbol(syntax.Fun, N, Tree.Ident(flattenedSym.nme)) // Contains *all* parameters, and applies them all at once in a single `Instantiate` def mkFlattenedDefn: FunDefn = @@ -1199,7 +1207,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): // * The flattened definition takes every parameter list at once and returns an instance of the // * class, so its erased type is only known here, once the parameter lists are assembled. - flattenedDSym.populateErasedType(ErasedType.FuncRef( + flattenedDSym.erasedType = S(ErasedType.FuncRef( rsc = S(false), paramLists = allParamLists.map(_.params.map(_.sym.erasedType)), ret = cls.instanceType, diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index ded1cae497..6131961693 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -21,6 +21,7 @@ import semantics.Elaborator.{State, Ctx, ctx} import syntax.{Literal, Tree, SpreadKind} import hkmc2.syntax.{Fun, Keyword, LetBind, MutVal} +import sem.flow.SelectionTarget abstract class TailOp(val transfersControl: Bool) extends (Result => Block) @@ -40,6 +41,7 @@ class LoweringCtx( val mayRet: Bool, // For rewriting while loop into tail recursive function, represent whether an explicit return is legal in the current block private val definedSymsDuringLowering: collection.mutable.Set[ScopedSymbol], // used to create Scoped blocks val returnType: Opt[ErasedType], // the declared return type of the enclosing function, used to coerce `return`s + val superBases: Map[InnerSymbol, Path], // evaluated parents of the enclosing class definitions ): val map = initMap def collectScopedSym(s: ScopedSymbol) = definedSymsDuringLowering.add(s) @@ -63,11 +65,13 @@ class LoweringCtx( object LoweringCtx: def loweringCtx(using sub: LoweringCtx): LoweringCtx = sub def empty = - LoweringCtx(Map.empty, mayRet = false, collection.mutable.Set.empty, returnType = N) + LoweringCtx(Map.empty, mayRet = false, collection.mutable.Set.empty, returnType = N, superBases = Map.empty) def nestFunc(returnType: Opt[ErasedType])(using sub: LoweringCtx): LoweringCtx = - LoweringCtx(sub.map, mayRet = true, sub.definedSymsDuringLowering, returnType) + LoweringCtx(sub.map, mayRet = true, sub.definedSymsDuringLowering, returnType, sub.superBases) def nestScoped(using sub: LoweringCtx): LoweringCtx = - LoweringCtx(sub.map, sub.mayRet, collection.mutable.Set.empty, sub.returnType) + LoweringCtx(sub.map, sub.mayRet, collection.mutable.Set.empty, sub.returnType, sub.superBases) + def withSuperBase(owner: InnerSymbol, parent: Path)(using sub: LoweringCtx): LoweringCtx = + LoweringCtx(sub.map, sub.mayRet, sub.definedSymsDuringLowering, sub.returnType, sub.superBases + (owner -> parent)) end LoweringCtx import LoweringCtx.loweringCtx @@ -84,7 +88,30 @@ object Lowering: import Lowering.* -class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): +class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter)(using Erasure): + + val newResolution: Bool = config.language.useNewResolution + val strictResolution: Bool = config.language.strictResolution + + /** Lower the parent before the class body and share its value with every super reference. + * Definition references are stable; classOf already saves computed class values in temporaries. + * Other paths (notably a class selected through a mutable receiver) must be read here, once, + * so changing that receiver after the class definition cannot redirect its super calls. + */ + private def withClassParent(parent: Path)(k: Path => Block)(using LoweringCtx): Block = + def save(path: Path)(k: Path => Block): Block = + val saved = loweringCtx.registerTempSymbol(N, erasedType = N, "superBase") + Assign(saved, path, k(saved.asSimpleRef)) + parent match + case Value.MemberRef(_, _: ClassSymbol) | Value.SimpleRef(_: TempSymbol) => k(parent) + case sel: Select if sel.symbol.exists(_.isInstanceOf[ClassSymbol]) => + // The selected class declaration is immutable, but its receiver might be a variable. + // Save that receiver while retaining the class symbol for nominal IR analyses. + sel.qual match + case _: Value.This | Value.MemberRef(_, _: ClassSymbol | _: ModuleOrObjectSymbol) | + Value.SimpleRef(_: TempSymbol) => k(sel) + case qual => save(qual)(p => k(sel.copy(qual = p)(sel.symbol, sel.toLoc)(sel.sanitize))) + case _ => save(parent)(k) extension (t: Term) def instantiated = t match @@ -184,13 +211,13 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): new TailOp(transfersControl = true): override def apply(k: Result): Block = Ret(castTo(k, returnType, N)) - def parentConstructor(parentClsPath: Path, cls: Term, args: Ls[Term])(using LoweringCtx) = + def parentConstructor(ctorParamLists: Ls[ParamList], args: Ls[Term], loc: Opt[Loc])(using LoweringCtx) = lowerSuperCtorCall( - parentClsPath, + ctorParamLists, State.builtinOpsMap("super").asSimpleRef, isMlsFun = true, args, - N, // TODO: location? + loc, )(c => Assign(NoSymbol, c, End())) // * Used to work around Scala's @tailrec annotation for those few calls that are not in tail position. @@ -233,7 +260,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): case L((mut, flds, loc)) => subTerm(bod): l => blockImpl(stats, L((mut, RcdArg(N, l) :: flds, loc))) - case RcdField(lhs, rhs) :: stats => + case RcdField(lhs, rhs, _) :: stats => res match case R(_) => wat("RcdField in non-Rcd context", res) case L((mut, flds, loc)) => @@ -288,7 +315,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): case Annot.Config(modify) => modify(config) Define(FunDefn(td.owner, td.sym, td.tsym, paramLists, bodyBlock)(cfgOverride, td.annotations), blockImpl(stats, res)) - case syntax.LetBind | syntax.HandlerBind => fail: + case syntax.LetBind | syntax.HandlerBind | syntax.RecordField => fail: ErrorReport( msg"Unexpected declaration kind '${td.k.str}' in lowering" -> td.toLoc :: Nil, source = Diagnostic.Source.Compilation) @@ -324,15 +351,8 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): )(mod.toLoc) case _ => _defn reportAnnotations(defn, defn.extraAnnotations) - val bufferableAnnots = defn.annotations.flatMap: - case Annot.Trm(trm: SynthSel) => - if trm.sym.contains(ctx.builtins.annotations.buffered) then - S(false -> trm) - else if trm.sym.contains(ctx.builtins.annotations.bufferable) then - S(true -> trm) - else - N - case _ => N + val bufferableAnnots = defn.annotations.collect: + case annot @ Annot.Bufferable(keepInstances) => keepInstances -> annot if bufferableAnnots.length > 1 then raise(ErrorReport( msg"Only one of bufferable annotation is allowed." -> Loc(bufferableAnnots.map(_._2)) :: Nil, @@ -352,81 +372,123 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): msg"Buffered classes must not have a main parameter list; use `constructor(...)` syntax instead." -> defn.toLoc :: Nil, source = Diagnostic.Source.Compilation )) - val (mtds, publicFlds, privateFlds, ctor) = defn match - case pd: PatternDef => - // Compile the pattern definition into `unapply` and `unapplyStringPrefix` - // methods using the `SplitCompiler`, which transliterate the pattern into - // UCS splits that backtrack without any optimizations. - val compiler = new ups.SplitCompiler - val methods = compiler.compilePattern(pd) - // We only need `owner`, `sym`, `params` and `body` - val mtds = methods.map: - case (sym, params, split) => - val paramLists = params :: Nil - val bodyBlock = inScopedBlock(ucs.Normalization(this)(split)(Ret)) - FunDefn.withFreshSymbol(N, sym, paramLists, bodyBlock)(configOverride = N, annotations = Nil) - // The return type is intended to be consistent with `gatherMembers` - (mtds, Nil, Nil, End()) - case _ => gatherMembers(defn.body) - val mod = defn.companion match - case S(sym) => - sym.defn match - case S(mod: ModuleOrObjectDef) => - reportAnnotations(mod, mod.extraAnnotations) - mod.ext match - case S(ext) => fail: - ErrorReport( - msg"Modules cannot have an extension clause." -> ext.toLoc :: Nil, - source = Diagnostic.Source.Compilation - ) - case N => - val (mtds, publicFlds, privateFlds, ctor) = - gatherMembers(mod.body) - S(ClsLikeBody(mod.sym, mtds, privateFlds, publicFlds, ctor, mod.annotations)) - case _ => N - case _ => N + def defineClass(parent: Opt[Path], parentParams: Ls[ParamList]): Block = + val base = parent.getOrElse( + Select(State.globalThisSymbol.asThis, Tree.Ident("Object"))(S(ctx.builtins.Object), defn.toLoc)(false)) + val clsDef = LoweringCtx.withSuperBase(defn.sym, base).givenIn: + val (mtds, publicFlds, privateFlds, ctor) = defn match + case pd: PatternDef => + // Compile the pattern definition into `unapply` and `unapplyStringPrefix` + // methods using the `SplitCompiler`, which transliterate the pattern into + // UCS splits that backtrack without any optimizations. + val compiler = new ups.SplitCompiler + val methods = compiler.compilePattern(pd) + // We only need `owner`, `sym`, `params` and `body` + val mtds = methods.map: + case (sym, params, split) => + val paramLists = params :: Nil + val bodyBlock = inScopedBlock(ucs.Normalization(this)(split)(Ret)) + FunDefn.withFreshSymbol(N, sym, paramLists, bodyBlock)(configOverride = N, annotations = Nil) + // The return type is intended to be consistent with `gatherMembers` + (mtds, Nil, Nil, End()) + case _ => gatherMembers(defn.body) + val mod = defn.companion match + case S(sym) => + sym.defn match + case S(mod: ModuleOrObjectDef) => + reportAnnotations(mod, mod.extraAnnotations) + mod.ext match + case S(ext) => fail: + ErrorReport( + msg"Modules cannot have an extension clause." -> ext.toLoc :: Nil, + source = Diagnostic.Source.Compilation + ) + case N => + val (mtds, publicFlds, privateFlds, ctor) = + gatherMembers(mod.body) + S(ClsLikeBody(mod.sym, mtds, privateFlds, publicFlds, ctor, mod.annotations)) + case _ => N + case _ => N + val pctor = defn.ext.fold[Block](End()): ext => + inScopedBlock(parentConstructor(parentParams, ext.args, ext.toLoc)) + val cfgOverride = defn.extraAnnotations.collectFirst: + case Annot.Config(modify) => modify(config) + ClsLikeDefn(defn.owner, defn.sym, defn.bsym, defn.ctorSym, defn.kind, defn.paramsOpt, defn.auxParams, parent, + mtds, privateFlds, publicFlds, pctor, ctor, mod, bufferable)(cfgOverride, defn.annotations) + Define(clsDef, blockImpl(stats, res)) defn.ext match - case N => - val cfgOverride = defn.extraAnnotations.collectFirst: - case Annot.Config(modify) => modify(config) - Define( - ClsLikeDefn(defn.owner, defn.sym, defn.bsym, defn.ctorSym, defn.kind, defn.paramsOpt, defn.auxParams, N, - mtds, - privateFlds, - publicFlds, - End(), - ctor, - mod, - bufferable, - )(cfgOverride, defn.annotations), - blockImpl(stats, res)) + case N => defineClass(N, Nil) case S(ext) => assert(k isnt syntax.Mod) // modules can't extend things and can't have super calls - val cfgOverride = defn.extraAnnotations.collectFirst: - case Annot.Config(modify) => modify(config) - subTerm(ext.cls): clsp => - val pctor = inScopedBlock(parentConstructor(clsp, ext.cls, ext.args)) - Define( - ClsLikeDefn( - defn.owner, defn.sym, defn.bsym, defn.ctorSym, defn.kind, defn.paramsOpt, defn.auxParams, S(clsp), - mtds, privateFlds, publicFlds, pctor, ctor, mod, bufferable, - )(cfgOverride, defn.annotations), - blockImpl(stats, res) - ) + classOf(ext.cls, ext): (clsp, params) => + withClassParent(clsp)(parent => defineClass(S(parent), params)) case td: TypeDef => // * Type definitions are erased blockImpl(stats, res) blockImpl(imps ::: funs ::: rest, res) + def classOf(trm: Term, nw: PossiblyErroneous)(k: (Path, Ls[ParamList]) => Block)(using LoweringCtx): Block = + def classValue: Block = nw match + case term: Term if !nw.isErroneous => + ClassValueShape.targetsOf(term) match + case target :: Nil => + val cls = ClassValueShape.definitionOf(target) + subTerm(trm): value => + val classPath = target match + case _: ClassCtorSymbol => value.selSN("class") + case _ => value + // Read a stored class before evaluating arguments or building a + // partial constructor, even if its binding is assigned later. + val saved = loweringCtx.registerTempSymbol(S(trm), erasedType = N, "classValue") + Assign(saved, classPath, k(saved.asSimpleRef, classParamLists(cls))) + case Nil => fail: + ErrorReport(msg"Cannot resolve the class referenced here" -> trm.toLoc :: Nil, + source = Diagnostic.Source.Compilation) + case targets => fail: + ErrorReport(msg"The class interpretation here is ambiguous" -> trm.toLoc :: + targets.map(target => msg"${target.describeKind} '${target.nme}' defined here" -> target.toLoc), + source = Diagnostic.Source.Compilation) + case _ => compError + if newResolution && nw.isErroneous then compError + else if newResolution then + trm.classHead match + case resolved @ Resolved(_, _: ClassSymbol) => + // Synthesized runtime constructors already carry an explicit class target. + subTerm(resolved)(p => k(p, getClassParamLists(p))) + case _: SimpleRef => classValue + case resl: NewResolvable => + resl.resolvedTargets.distinct match + case cls :: Nil => + cls.defn match + case S(clsDef: ClassLikeDef) => + subTerm(resl)(p => k(p, classParamLists(clsDef))) + case _ => classValue + case Nil => + if !resl.isErroneous then raise: + ErrorReport( + msg"Cannot resolve the class instantiated here" -> trm.toLoc :: Nil, + source = Diagnostic.Source.Compilation) + compError + case targets => + fail: + ErrorReport(msg"The class instantiated here is ambiguous" -> trm.toLoc :: + targets.map(target => msg"target: ${target.describeKind} '${target.nme}'" -> target.toLoc), + source = Diagnostic.Source.Compilation) + // Calls and other computed values retain their class interpretation on + // the enclosing selection or construction, just like local variables. + case _ => classValue + else subTerm(trm)(p => k(p, getClassParamLists(p))) + + private def classParamLists(cls: ClassLikeDef): Ls[ParamList] = cls.paramsOpt.toList ::: cls.auxParams + def getClassParamLists(cls: Path): Ls[ParamList] = cls.targetSymbol match case S(clsSym: ClassSymbol) => - clsSym.defn.map(clsDef => clsDef.paramsOpt.toList ::: clsDef.auxParams).getOrElse(Nil) + clsSym.defn.map(classParamLists).getOrElse(Nil) case _ => Nil // * Lowers the `super(...)(...)` call we get from the `extends C(...)(...)` syntax - def lowerSuperCtorCall(parentClsPth: Path, fr: Path, isMlsFun: Bool, args: List[Term], loc: Opt[Loc])(k: Result => Block)(using LoweringCtx): Block = - val ctorParamLists = getClassParamLists(parentClsPth) + def lowerSuperCtorCall(ctorParamLists: Ls[ParamList], fr: Path, isMlsFun: Bool, args: List[Term], loc: Opt[Loc])(k: Result => Block)(using LoweringCtx): Block = args match case _ :: _ => def zipArgs(remainingParamss: Ls[ParamList], args: Ls[Term], acc: Ls[Ls[Arg]]): Block = (remainingParamss, args) match @@ -497,7 +559,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): * when they correspond to constructor parameter lists of the same class. * If fewer argument lists are provided than constructor parameter lists, eta-expands * the missing ones with fresh lambdas (avoiding reliance on mutable JS class curry semantics). */ - def lowerMultiInstantiate(mut: Bool, cls: Path, args: Ls[Term], annotations: Ls[Annot], loc: Opt[Loc])(k: Result => Block)(using LoweringCtx): Block = + def lowerMultiInstantiate(mut: Bool, cls: Path, ctorParamLists: Ls[ParamList], args: Ls[Term], annotations: Ls[Annot], loc: Opt[Loc])(k: Result => Block)(using LoweringCtx): Block = // Nullary instantiations are represented with one empty argument list, matching existing `Instantiate` usage. def buildInstantiate(argss: Ls[Ls[Arg]]): Instantiate = Instantiate(mut, cls, if argss.isEmpty then Nil :: Nil else argss)(InstantiateMetadata(annotations), loc) @@ -530,12 +592,6 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): val freshArgs = freshSyms.map(s => Arg(N, s.asSimpleRef)) Lambda(freshParamList, Return(etaExpand(rest, accArgss :+ freshArgs)))(Nil, loc) k(etaExpand(remainingParamss, acc.reverse)) - // * Resolve the class definition to get the constructor param lists. - // * The class path typically resolves to a TermSymbol (the constructor function), - // * so we also look up the owner InnerSymbol via TermDefinition#owner. - // * Note: apparently, this can also be accessed through TermDefinition#companionClass - // * (what Copilot initially used), which is weird. - val ctorParamLists = getClassParamLists(cls) if ctorParamLists.isEmpty then // * Need to specially handle no-param classes args match @@ -611,7 +667,143 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): case td: TermDefinition => (td.k is syntax.Fun) && td.params.isEmpty case _ => false - def ref(ref: st.Ref, annots: List[Annot], disamb: Opt[DefinitionSymbol[?]], inStmtPos: Bool)(k: Result => Block)(using LoweringCtx): Block = + /** Consume wildcard lookup results, retaining the receiver selected by resolution. */ + private def openSelection(ref: UnresolvedRef)(k: (Term, Opt[BlockMemberSymbol], Opt[DefinitionSymbol[?]]) => Block)(using LoweringCtx): Block = + // A dynamic candidate also covers known fields on the same receiver. Keep + // other receivers distinct: a wildcard name must identify which object to read. + val dynamic = ref.dynamicPrefixes.distinct + val candidates = ref.resolvedMembers.distinct.filterNot((prefix, _) => dynamic.contains(prefix)) + .map((prefix, member) => prefix -> S(member)) ::: dynamic.map(_ -> N) + if ref.isErroneous then compError else candidates match + case (prefix, N) :: Nil => k(prefix, N, N) + case (prefix, member @ S(_)) :: Nil => ref.resolvedTargets.distinct match + case target :: Nil => k(prefix, member, S(target)) + case targets if targets.nonEmpty && !strictResolution => k(prefix, member, N) + case targets => fail: + ErrorReport(msg"Wildcard-open reference '${ref.id.name}' requires one resolved definition" -> ref.toLoc :: + targets.map(target => msg"target: ${target.describeKind}" -> target.toLoc), + source = Diagnostic.Source.Compilation) + case Nil => fail: + ErrorReport(msg"Name not found in wildcard opens: ${ref.id.name}" -> ref.toLoc :: Nil, + source = Diagnostic.Source.Compilation) + case candidates => fail: + ErrorReport(msg"Wildcard-open reference '${ref.id.name}' is ambiguous" -> ref.toLoc :: + candidates.flatMap: (prefix, member) => + member.toList.map(member => msg"candidate: ${member.describe} '${member.nme}' defined here" -> member.toLoc) ::: + (msg"Opened here" -> prefix.toLoc) :: Nil + , + source = Diagnostic.Source.Compilation) + + /** Validate source selection once, then let reads, calls, and writes consume + * its runtime receiver, property name, and optional static definition. */ + private def newSelection(sel: NewSel)(k: (Term, Tree.Ident, Opt[DefinitionSymbol[?]]) => Block)(using LoweringCtx): Block = + val NewSel(prefix, id, _) = sel + if !checkProjection(sel) || sel.isErroneous then compError + else if sel.tupleIndex.nonEmpty && sel.resolvedTargets.nonEmpty then fail: + ErrorReport(msg"This selection has both tuple and nominal member targets" -> sel.toLoc :: Nil, + source = Diagnostic.Source.Compilation) + else if sel.hasDynamicTarget || sel.tupleIndex.nonEmpty then k(prefix, memberIdent(id, N), N) + else Inheritance.commonMember(sel.resolvedTargets).fold(sel.resolvedTargets.distinct)(_ :: Nil) match + case Nil => fail: + ErrorReport(msg"This selection of member '${id.name}' has no resolved target" -> sel.toLoc :: Nil, + source = Diagnostic.Source.Compilation) + case target :: Nil => + // For `r = {make: C}`, the pattern head `r.make` resolves to class C, + // but the generated property read must use `make`. resolvedMembers holds + // the property names; target identifies the definition used by the pattern. + sel.resolvedMembers.map(_.nme).distinct match + case name :: Nil => + k(prefix, new Tree.Ident(name).withLocOf(id), S(target)) + case Nil => k(prefix, definitionIdent(id, target), S(target)) + case _ => fail: + ErrorReport(msg"This selection of member '${id.name}' has multiple resolved member names" -> sel.toLoc :: + sel.resolvedMembers.map(member => msg"candidate: ${member.describe} '${member.nme}'" -> member.toLoc), + source = Diagnostic.Source.Compilation) + case targets => + if strictResolution then fail: + ErrorReport( + msg"This selection of member '${id.name}' is ambiguous, as it has multiple resolved targets" -> sel.toLoc :: + targets.map: target => + val owner = target.asTrm.flatMap(_.owner).fold("")(o => s" in ${o.asBlkMember.get.describe} '${o.nme}'") + msg"target: ${target.describeKind} '${target.nme}'${owner}" -> target.toLoc + , + source = Diagnostic.Source.Compilation) + else k(prefix, memberIdent(id, N), N) + + private def selectionPath(sel: NewSel, prefix: Path, name: Tree.Ident, + target: Opt[DefinitionSymbol[?]]): Path = sel.tupleIndex match + case S(index) => DynSelect(prefix, Value.Lit(Tree.IntLit(BigInt(index)))(N), true)(sel.toLoc) + case N => Select(prefix, name)(target, sel.toLoc)(false) + + private def superReference(sup: st.Super)(k: Path => Block)(using LoweringCtx): Block = + if sup.isErroneous then compError else sup.resolvedTargets.distinct match + case (target: TermSymbol) :: Nil if !target.isPrivate && target.defn.exists(d => !Inheritance.isAbstract(d)) => + subTerm_nonTail(sup.receiver): receiver => + // Fields live on the receiver; only methods/getters have a base implementation + // on the prototype. In particular Reflect.get(base.prototype, ...) cannot read fields. + if target.k is syntax.Fun then + val base = loweringCtx.superBases.getOrElse(sup.owner, + lastWords("Superclass must be lowered before its members")) + k(SuperSelect(receiver, base, target.id)(target, sup.toLoc)) + else k(Select(receiver, target.id)(S(target), sup.toLoc)(false)) + case targets => fail(ErrorReport( + msg"Super reference '${sup.id.name}' requires one inherited implementation" -> sup.toLoc :: + targets.map(target => msg"Candidate '${target.nme}' is declared here" -> target.toLoc), + source = Diagnostic.Source.Compilation)) + + /** A projection must identify its class as well as its member: different + * classes can inherit the same definition. Captures do not disambiguate classes. */ + private def checkProjection(sel: NewSel): Bool = sel.cls match + case N => true + case S(cls) => + if sel.isErroneous then false + else if sel.hasAmbiguousClass then + // Qualifiers denote class declarations; their runtime receivers are irrelevant. + raise: + ErrorReport(msg"The projection class is ambiguous" -> cls.toLoc :: + sel.projectionClasses.map(sym => msg"class: '${sym.nme}'" -> sym.toLoc), + source = Diagnostic.Source.Compilation) + false + else if sel.projectionClasses.isEmpty then + raise: + ErrorReport(msg"Cannot resolve the projection class" -> cls.toLoc :: Nil, + source = Diagnostic.Source.Compilation) + false + else true + + def selSymbol(sel: AnySelTerm): Opt[DefinitionSymbol[?]] = + sel.validResolvedTargets match + // sel.resolvedTargets match + case Nil => + if newResolution && !sel.isErroneous then raise: + ErrorReport( + msg"This selection of member '${sel.nme.name}' has no resolved target" -> sel.toLoc :: + Nil, S(sel), source = Diagnostic.Source.Compilation) + N + case SelectionTarget.ObjectMember(sym: DefinitionSymbol[?]) :: Nil => + S(sym) + case SelectionTarget.ObjectMember(sym: BlockMemberSymbol) :: Nil => + // TODO: instead, make SelectionTarget a listener that resolves to a more precise target + sym.asTrm orElse sym.asModOrObj match + case s @ S(mod) => s + case N => + if newResolution && !sel.isErroneous then raise: + ErrorReport( + msg"Selection target ${sym.describe} '${sym.nme}' cannot be used as a term" -> sel.toLoc :: + Nil, S(sel), source = Diagnostic.Source.Compilation) + N + case ts => + if newResolution && strictResolution && !sel.isErroneous then raise: + // println(ts) + // ??? + ErrorReport( + msg"Selection of member '${sel.nme.name}' is ambiguous, as it has multiple resolved targets" -> sel.toLoc :: + ts.map: t => + msg"target: ${t.describe}" -> t.loc + , S(sel), source = Diagnostic.Source.Compilation) + N + + def ref(ref: AnyRef_, annots: List[Annot], disamb: Opt[DefinitionSymbol[?]], inStmtPos: Bool)(k: Result => Block)(using LoweringCtx): Block = def warnStmt = if inStmtPos then warnPureExprInStmtPos(ref.toLoc, S(ref)) val sym = ref.sym @@ -742,7 +934,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): N, )(trm.toLoc) val rewritten = st.App( - st.SynthSel(State.runtimeSymbol.ref(), Tree.Ident("toJsAsync"))(N, FlowSymbol.sel("toJsAsync"), N, N), + State.runtimeSymbol.ref().synthSel(State.toJsAsyncSymbol), st.Tup(PlainFld(st.Blk(td :: Nil, bms.ref(ident).resolved(dsym))) :: Nil)(Tree.DummyTup) )(Tree.DummyApp, N, FlowSymbol.app()) extractAnnots(rewritten, acc) @@ -823,6 +1015,31 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): case st.CtxTup(fs) => // * This case is currently triggered for code such as `f(using 42)` args(fs, Nil)(args => k(Tuple(mut = false, args)(trm.toLoc))) + case t @ st.SimpleRef(sym) => + ref(t, annots, N, inStmtPos = inStmtPos)(k) + case t @ st.SelfRef(sym) => + ref(t, annots, N, inStmtPos = inStmtPos)(k) + case t @ st.MemberRef(bms) => + if t.isErroneous then compError else t.resolvedTargets.distinct match + case Nil => + fail: + ErrorReport(msg"Member reference '${bms.nme}' has no resolved target" -> t.toLoc :: Nil, + source = Diagnostic.Source.Compilation) + case trgt :: Nil => + ref(t, annots, S(trgt), inStmtPos = inStmtPos)(k) + case ts => + if strictResolution then fail: + ErrorReport( + msg"Member reference '${bms.nme}' is ambiguous, as it has multiple resolved targets" -> t.toLoc :: + ts.map: t => + msg"target: ${t.describeKind}" -> t.toLoc + , S(t), source = Diagnostic.Source.Compilation) + else ref(t, annots, N, inStmtPos = inStmtPos)(k) + case ref: UnresolvedRef => + openSelection(ref)((prefix, member, target) => + setupNamedSelection(prefix, memberIdent(ref.id, member), target, ref.toLoc)(k)) + case Capture(base, thru) => + term(base, inStmtPos = inStmtPos)(k) case t @ st.Ref(sym) => ref(t, annots, N, inStmtPos = inStmtPos)(k) case st.Resolved(t @ st.Ref(bsym), sym) => @@ -882,7 +1099,8 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): msg"Unexpected arguments for builtin symbol '${sym.nme}'" -> arg.toLoc :: Nil, S(arg), source = Diagnostic.Source.Compilation) case st.TyApp(f, ts) => term(f)(k) // * Type arguments are erased - case st.App(f, arg) => + case app @ st.App(f, arg) => + if app.isErroneous then return compError // Collect chains of App nodes: `f(a)(b)(c)` β†’ (f, [a, b, c]) // This allows lowering curried calls as a single `Call` with multiple arg lists. @@ -899,7 +1117,11 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): case sym: sem.BlockMemberSymbol => sym.trmImplTree.fold(sym.clsTree.isDefined)(_.k is syntax.Fun) case sym: sem.TermSymbol => - (sym.k is syntax.Fun) && sym.defn.forall(!_.hasDeclareModifier.isDefined) + // Methods inside a declared class can have no local `declare` modifier. + // Only implementations (and generated constructors) promise the MLscript + // calling convention; foreign declarations need undefined normalization. + (sym.k is syntax.Fun) && sym.defn.exists(d => !d.hasDeclareModifier.isDefined && + (d.body.isDefined || sym.isInstanceOf[ClassCtorSymbol])) // Do not perform safety check on `MatchSuccess` and `MatchFailure`. case sym => (sym is State.matchSuccessClsSymbol) || (sym is State.matchFailureClsSymbol) @@ -971,9 +1193,17 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): instantiated match // * Due to whacky JS semantics, we need to make sure that selections leading to a call // * are preserved in the call and not moved to a temporary variable. + case sup: st.Super => superReference(sup)(conclude) + case sel: NewSel if sel.isClassValue => classOf(sel.prefix, sel)((p, _) => conclude(p)) + case sel: NewSel => newSelection(sel): (prefix, name, target) => + subTerm_nonTail(prefix): p => + conclude(selectionPath(sel, p, name, target)) + case ref: UnresolvedRef => openSelection(ref): (prefix, member, target) => + subTerm_nonTail(prefix): p => + conclude(Select(p, memberIdent(ref.id, member))(target, ref.toLoc)(false)) case sel @ Sel(prefix, nme) => subTerm(prefix): p => - conclude(Select(p, nme)(N, sel.toLoc)(false)) + conclude(Select(p, nme)(selSymbol(sel), trm.toLoc)(false).withLocOf(sel)) case Resolved(sel @ Sel(prefix, nme), sym) => subTerm(prefix): p => conclude(Select(p, definitionIdent(nme, sym))(S(sym), trm.toLoc)(false).withLocOf(sel)) @@ -1015,13 +1245,55 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): // * need the selected prefix/name/symbol in order to emit `AssignField`. // * Still, resolver expansions matter: `Resolved(lhs, sym)` carries the // * disambiguated member symbol needed for private fields and overloads. - val (target, resolvedSelectionSymbol) = lhs.instantiated match + val (target, resolvedSelectionSymbol) = lhs.withoutCaptures.instantiated match case Resolved(inner, sym) => inner -> S(sym) case target => target -> N target match + case SimpleRef(sym) => + subTerm(rhs): r => + assignSymbol(sym, sym, r, k(unit), trm.toLoc) case Ref(sym) => subTerm(rhs): r => assignSymbol(resolvedSelectionSymbol.getOrElse(sym), sym, r, k(unit), trm.toLoc) + case ref: MemberRef => + if ref.isErroneous then compError else ref.resolvedTargets.distinct match + case target :: Nil => + subTerm(rhs): r => + assignSymbol(target, ref.sym, r, k(unit), trm.toLoc) + case _ => fail: + ErrorReport(msg"Assignment requires one resolved member" -> ref.toLoc :: Nil, + source = Diagnostic.Source.Compilation) + case ref: UnresolvedRef => + openSelection(ref): (prefix, member, target) => + target match + case S(sym: TermSymbol) => + subTerm_nonTail(prefix): p => + subTerm_nonTail(rhs): r => + AssignField(p, memberIdent(ref.id, member), castTo(r, sym.erasedType, ref.toLoc), k(unit))(S(sym)) + case N if ref.resolvedTargets.forall(_.isInstanceOf[TermSymbol]) => + subTerm_nonTail(prefix): p => + subTerm_nonTail(rhs): r => + AssignField(p, memberIdent(ref.id, member), r, k(unit))(N) + case _ => fail: + ErrorReport(msg"Assignment requires a term member" -> ref.toLoc :: Nil, + source = Diagnostic.Source.Compilation) + case sel: NewSel => newSelection(sel): (prefix, name, target) => + target match + case N if sel.tupleIndex.nonEmpty => + subTerm_nonTail(prefix): p => + subTerm_nonTail(rhs): r => + AssignDynField(p, Value.Lit(Tree.IntLit(BigInt(sel.tupleIndex.get)))(trm.toLoc), true, r, k(unit)) + case S(sym: TermSymbol) => + subTerm_nonTail(prefix): p => + subTerm_nonTail(rhs): r => + AssignField(p, name, castTo(r, sym.erasedType, sel.toLoc), k(unit))(S(sym)) + case N if sel.resolvedTargets.forall(_.isInstanceOf[TermSymbol]) => + subTerm_nonTail(prefix): p => + subTerm_nonTail(rhs): r => + AssignField(p, name, r, k(unit))(N) + case _ => fail: + ErrorReport(msg"Assignment requires a term member" -> sel.toLoc :: Nil, + source = Diagnostic.Source.Compilation) case sel @ Sel(prefix, nme) => subTerm(prefix): p => subTerm_nonTail(rhs): r => @@ -1042,7 +1314,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): subTerm(prefix): p => subTerm_nonTail(rhs): r => AssignField(p, memberIdent(nme, sel.sym), castTo(r, fieldErasedType(sym), sel.toLoc), k(unit))(sym) - case sel @ DynSel(prefix, fld, ai) => + case sel @ DynSel(prefix, fld, ai, _) => subTerm(prefix): p => subTerm_nonTail(fld): f => subTerm_nonTail(rhs): r => @@ -1083,8 +1355,16 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): case whltrm: st.SynthWhile => ucs.Normalization(this)(whltrm)(k) + case sel: NewSel if sel.isClassValue => classOf(sel.prefix, sel)((p, _) => k(p)) + case sup: st.Super => superReference(sup)(k) + case sel: NewSel => newSelection(sel): (prefix, name, target) => + if sel.tupleIndex.nonEmpty then subTerm_nonTail(prefix)(p => k(selectionPath(sel, p, name, target))) + else setupNamedSelection(prefix, name, target, sel.toLoc)(k) + + case sel @ Sel(prefix, nme) => - setupSelection(prefix, nme, N, sel.toLoc)(k) + if sel.isErroneous then compError else + setupSelection(prefix, nme, selSymbol(sel), sel.toLoc)(k) case Resolved(sel @ Sel(prefix, nme), sym) => setupSelection(prefix, nme, S(sym), sel.toLoc)(k) @@ -1097,39 +1377,45 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): // * definition when one exists. Do not use this fallback for ordinary `Sel` // * lowering unless that convention is changed at the source. subTerm(prefix): p => - k(Select(p, memberIdent(nme, sel.sym))(sel.sym.collect: - case s: DefinitionSymbol[?] => s + k(Select(p, memberIdent(nme, sel.sym))(sel.sym.flatMap: + case s: DefinitionSymbol[?] => S(s) + case bms: BlockMemberSymbol => bms.asTrm // TODO: clean up logic + case err: ErrorSymbol => N , trm.toLoc)(false)) case Resolved(sel @ SynthSel(prefix, nme), sym) => // * Not using `setupSelection` as these selections are not meant to be sanity-checked subTerm(prefix): p => k(Select(p, definitionIdent(nme, sym))(S(sym), trm.toLoc)(false)) - case DynSel(prefix, fld, ai) => + case DynSel(prefix, fld, ai, _) => subTerm(prefix): p => subTerm_nonTail(fld): f => k(DynSelect(p, f, ai)(trm.toLoc)) - case nw @ (_: New | _: DynNew | Mut(_: New | _: DynNew)) => - val (mut, cls, as, rft) = nw match - case New(c, a, r) => (false, c, a, r) - case Mut(New(c, a, r)) => (true, c, a, r) - case DynNew(c, a) => (false, c, a, N) - case Mut(DynNew(c, a)) => (true, c, a, N) + case DynNew(cls, args) => + subTerm(cls)(sr => lowerMultiInstantiate(false, sr, getClassParamLists(sr), args, annots, trm.toLoc)(k)) + case Mut(DynNew(cls, args)) => + subTerm(cls)(sr => lowerMultiInstantiate(true, sr, getClassParamLists(sr), args, annots, trm.toLoc)(k)) + case nw @ (_: New | Mut(_: New)) => + val (mut, cls, as, rft, nwtrm) = nw match + case nwtrm @ New(c, a, r) => (false, c, a, r, nwtrm) + case Mut(nwtrm @ New(c, a, r)) => (true, c, a, r, nwtrm) case _ => spuriousWarning - subTerm(cls): sr => + classOf(cls, nwtrm): (sr, params) => rft match - case N => lowerMultiInstantiate(mut, sr, as, annots, trm.toLoc)(k) + case N => lowerMultiInstantiate(mut, sr, params, as, annots, trm.toLoc)(k) case S((isym, rft)) => - val sym = new BlockMemberSymbol(isym.name, Nil) + val sym = isym.defn.get.bsym loweringCtx.collectScopedSym(sym) - val (mtds, publicFlds, privateFlds, ctor) = gatherMembers(rft) - val pctor = parentConstructor(sr, cls, as) - val clsDef = ClsLikeDefn(N, isym, sym, N, syntax.Cls, N, Nil, S(sr), - mtds, privateFlds, publicFlds, pctor, ctor, N, N)(N, Nil) - val inner = new New(sym.ref().resolved(isym), Nil, N)(N) - Define(clsDef, term_nonTail(if mut then Mut(inner) else inner)(k)) + withClassParent(sr): parent => + val clsDef = LoweringCtx.withSuperBase(isym, parent).givenIn: + val (mtds, publicFlds, privateFlds, ctor) = gatherMembers(rft) + val pctor = parentConstructor(params, as, nw.toLoc) + ClsLikeDefn(N, isym, sym, N, syntax.Cls, N, Nil, S(parent), + mtds, privateFlds, publicFlds, pctor, ctor, N, N)(N, Nil) + val inner = new New(sym.ref().resolved(isym), Nil, N)(FlowSymbol.neww(), N) + Define(clsDef, term_nonTail(if mut then Mut(inner) else inner)(k)) case Try(sub, finallyDo) => val l = loweringCtx.registerTempSymbol(S(sub), erasedType = N) @@ -1176,7 +1462,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): msg"Cannot compile ${t.describe} term that was not elaborated (maybe elaboration was one in 'lightweight' mode?)" -> t.toLoc :: Nil, source = Diagnostic.Source.Compilation) - case _: CompType | _: Neg | _: Term.FunTy | _: Term.Forall | _: Term.WildcardTy | _: Term.Unquoted | _: LeadingDotSel | _: Term.Constrained | _: Term.Annotated + case _: Term.DynTy | _: CompType | _: Neg | _: Term.FunTy | _: Term.Forall | _: Term.WildcardTy | _: Term.Unquoted | _: LeadingDotSel | _: Term.Constrained | _: Term.Annotated => fail: ErrorReport( msg"Unexpected term form in expression position (${t.describe})" -> @@ -1479,6 +1765,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): def program(main: st.Blk, symbolsToPreserve: Set[BoundSymbol]): Program = + summon[Erasure].requireCompleted(main) val (imps, funs, rest) = splitBlock(main.stats, Nil, Nil, Nil) @@ -1493,8 +1780,12 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): def setupSelection(prefix: Term, nme: Tree.Ident, disamb: Opt[DefinitionSymbol[?]], loc: Opt[Loc])(k: Result => Block)(using LoweringCtx): Block = + setupNamedSelection(prefix, disamb.fold(memberIdent(nme, N))(definitionIdent(nme, _)), disamb, loc)(k) + + private def setupNamedSelection(prefix: Term, nme: Tree.Ident, disamb: Opt[DefinitionSymbol[?]], loc: Opt[Loc]) + (k: Result => Block)(using LoweringCtx): Block = subTerm(prefix): p => - k(Select(p, disamb.fold(memberIdent(nme, N))(definitionIdent(nme, _)))(disamb, loc)( + k(Select(p, nme)(disamb, loc)( !disamb.isDefined // * ^ We assume that resolved selections are well-behaved (will not yield undefined or debind a method) // || disamb.exists(_.defn.exists(_.hasDeclareModifier.isEmpty)) // * This checks `declare` members, which is normally unwanted @@ -1545,6 +1836,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): case Annot.Modifier(syntax.Keyword.`public` | syntax.Keyword.`private` | syntax.Keyword.`virtual`) => () case Annot.Modifier(syntax.Keyword("staged")) => () case Annot.Pure() => () + case Annot.Modifier(syntax.Keyword.`open` | syntax.Keyword.`override`) => () case a: Annot.Affine => target match case TermDefinition(k = syntax.Fun) => () case _ => warn(a) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala index 24383617d8..32773e3c7c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala @@ -209,6 +209,8 @@ class Printer(using Config, Ctx, Raise, ShowCfg, State, SymbolPrinter): case Value.Lit(lit) => doc"${lit.idStr}" def print(path: Path)(using Scope): Document = path match + case sel @ SuperSelect(qual, base, name) => + doc"super[${print(base)}](${print(qual)}).${showMemberSymbol(name.name, S(sel.symbol))}" case sel @ Select(qual, name) => val docQual = print(qual) doc"${docQual}.${showMemberSymbol(name.name, sel.symbol)}" diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/ReflectionInstrumenter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/ReflectionInstrumenter.scala index e266a55503..984dc75eee 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/ReflectionInstrumenter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/ReflectionInstrumenter.scala @@ -191,6 +191,9 @@ class ReflectionInstrumenter(using State, Raise, Ctx) extends BlockTransformer(n transformResult(value): v => blockCtor("Symbol", Ls(toValue(target.describe)), "target"): t => blockCtor("Cast", Ls(v, t, toValue(check)), "cast")(k) + case _: SuperSelect => + raise(ErrorReport(msg"Super selection is not supported in staged modules." -> p.toLoc :: Nil)) + End() case _: Value.This => raise(ErrorReport(msg"Value.This not supported in staged module." -> p.toLoc :: Nil)) End() diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala index 6941682f6d..f63c290a8f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala @@ -154,6 +154,7 @@ object MemberRefTo: locally: p match case Select(qual, _) => S(qual) + case SuperSelect(qual, _, _) => S(qual) case _ => N object CtorProducer: @@ -597,7 +598,7 @@ class FlowPreAnalyzer(val pgrm: Program)(using if ctxTracker.isEnclosingMatchScrutSym(disamb) => applyValueMemberRef(v, recordAffinity = false) case _ => applyPath(qual) - case p: Select => + case p: (Select | SuperSelect) => super.applyPath(p) case c: Cast => applyResult(c.value) case v: Value => applyValue(v) @@ -1060,6 +1061,10 @@ class FlowConstraintsCollector( case Some(fScheme) => fScheme.instantiate(refSite.uid, f) case None => generatedVars(f) + case s @ SuperSelect(qual, base, _) => + cc.constrain(processResult(qual), UnknownCons) + cc.constrain(processResult(base), UnknownCons) + generatedVars(s.symbol) case s@Select(qual, name) => cc.constrain(processResult(qual), UnknownCons) s.symbol.fold(UnknownProd): selSym => diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala index b15666febd..c205df5952 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala @@ -340,6 +340,14 @@ class JSBuilder(using Config, TL, State, Ctx) extends CodeBuilder: case Call(s @ Select(_, Elaborator.ctx.builtins.BuiltInOpIdent(jsOp)), (lhs :: rhs :: Nil) :: Nil) => val res = doc"${operand(lhs)} ${jsOp} ${operand(rhs)}" if needsParens(jsOp) then doc"(${res})" else res + case c @ Call(fun: SuperSelect, argss) => + val first = argss.head.map(argument) + val base = doc"${result(fun)}.call(${(result(fun.qual) :: first).mkDocument(", ")})" + val calls = argss.tail.foldLeft(base): (acc, args) => + doc"${acc}(${args.map(argument).mkDocument(", ")})" + if c.metadata.isMlsFun then + if checkMLsCalls then doc"$runtimeVar.checkCall($calls)" else calls + else doc"$runtimeVar.safeCall($calls)" case c @ Call(fun, argss) => val base = subexpression(fun) val calls = argss.foldLeft(base): (acc, args) => @@ -358,6 +366,8 @@ class JSBuilder(using Config, TL, State, Ctx) extends CodeBuilder: // lexical-`this` behavior of the Lambda IR. doc"(function* ($params) ${ braced(bodyDoc) }).bind(this)" else doc"($params) => ${ braced(bodyDoc) }" + case s @ SuperSelect(qual, base, id) => + doc"Reflect.get(${resultQual(base)}.prototype, ${makeStringLiteral(id.name)}, ${result(qual)})" case s @ Select(qual, id) => val checkCurrentSelection = checkSelections && s.sanitize val dotClass = s.symbol match @@ -503,7 +513,7 @@ class JSBuilder(using Config, TL, State, Ctx) extends CodeBuilder: case AssignDynField(p, f, ai, r, rst) => doc" # ${result(p)}[${result(f)}] = ${result(r)};${returningTerm(rst, endSemi)}" case Define(defn, rst) => - def mkThis(sym: InnerSymbol): Document = + inline def mkThis(sym: InnerSymbol): Document = result(sym.asThis) val resJS = defn match case ValDefn(tsym, sym, p) => @@ -574,12 +584,11 @@ class JSBuilder(using Config, TL, State, Ctx) extends CodeBuilder: val ctorParams = backendParamList.paramSyms.map(p => p -> scope.allocateName(p)) val sourceParamsOpt = isym.defn.flatMap(_.paramsOpt) - // * Whether the class should be "lifted" to a "class" property of the companion term - // * should currently be consistent with whether the class has source parameters. - // * This currently fails for faulty input programs (such as `object O(x)`); - // * we should make sure such programs fail compilation before they reach this point. - softTODO(sourceParamsOpt.isDefined === isym.shouldBeLifted, - s"$sourceParamsOpt.isDefined =/= ${isym.shouldBeLifted}") + // Parameterized classes have a generated constructor function; bare classes can + // instead have an explicit function companion. Both own their `.class` property. + // Invalid parameterized objects can still reach this point without a constructor. + softTODO(sourceParamsOpt.isEmpty || isym.shouldBeLifted, + s"Missing constructor function for parameterized class ${isym.nme}") def mkMethodName(td: FunDefn, owner: InnerSymbol): Document = if td.dSym.isPrivate @@ -1001,7 +1010,7 @@ class JSBuilder(using Config, TL, State, Ctx) extends CodeBuilder: :: locally: exprt match case S(sym) => - doc"\nlet ${sym.nme} = ${scope.lookup_!(sym, sym.toLoc)}; export default ${sym.nme};\n" + doc"\nexport default ${scope.lookup_!(sym, sym.toLoc)};\n" case N => doc"" def worksheet(p: Program)(using Raise, Scope): (Document, Document) = @@ -1182,14 +1191,17 @@ object JSBuilder: * This helper is used at reference sites (MemberRef, Select) to decide whether to append `.class` * when accessing a class value. It returns true only for class/module/object symbols, * not for term symbols β€” so constructor calls like `Foo(args)` which resolve to the term - * symbol are not affected. */ + * symbol are not affected. Elaboration rejects arbitrary values and parameterless + * functions as companions: the class property belongs to a compiler-created function, + * never to an existing value. Foreign declarations use their native binding instead. */ def shouldBeLifted: Bool = val bsym = dsym.asBlkMember ( (dsym.asTrm orElse bsym.flatMap(_.asTrm)).isDefined || (dsym.asCls orElse bsym.flatMap(_.asCls)).flatMap(_.defn).exists(_.paramsOpt.isDefined) ) && - (dsym.asModOrObj orElse dsym.asCls).isDefined + (dsym.asModOrObj orElse dsym.asCls).isDefined && + dsym.defn.forall(_.hasDeclareModifier.isEmpty) end JSBuilder diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala index a5a0d79b97..8fc57aa879 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala @@ -9,8 +9,8 @@ import hkmc2.utils.* import document.* import document.Document import semantics.{ - BlockMemberSymbol, Elaborator, - InnerSymbol, LabelSymbol, LocalVarSymbol, ModuleOrObjectSymbol, ParamList, TempSymbol, + BlockMemberSymbol, ClassSymbol, ClassLikeSymbol, ClassCtorSymbol, Elaborator, + InnerSymbol, LabelSymbol, LocalVarSymbol, ModuleOrObjectSymbol, ParamList, Symbol, TempSymbol, TermSymbol, }, Elaborator.State import text.Param as WasmParam @@ -35,6 +35,43 @@ sealed trait SessionBinding: object SessionBinding: val ReplModuleName: Str = "repl" + /** Dependencies to look up among previously exported session bindings. + * MIR `freeVars` describes lexical bindings, which cannot distinguish a class from + * its function companion. Follow resolved references and cast targets instead. + * Symbols defined in this block cannot match earlier exports: their identities + * are distinct, including after inlining refreshes local definitions. + */ + def referencedSymbols(block: Block)(using Elaborator.Ctx, State): Ls[Symbol] = + val symbols = LinkedHashSet.empty[Symbol] + val visited = scala.collection.mutable.Set.empty[Symbol] + new BlockTraverser: + override def applySymbol(sym: Symbol): Unit = if visited.add(sym) then + // An imported subclass layout refers to its ancestors even when no source expression + // names them. Parents must precede children in Wasm's type declaration order. + sym match + case cls: ClassLikeSymbol => cls.irClassHeader.flatMap(_.parent).foreach(applySymbol) + case ctor: ClassCtorSymbol => applySymbol(ctor.associatedCls) + case member: BlockMemberSymbol => member.asClsOrMod.foreach(applySymbol) + case member: TermSymbol => member.owner.foreach(applySymbol) + case _ => () + symbols += sym + override def applyValue(value: Value): Unit = value match + case Value.MemberRef(bms, disamb) => applySymbol(ExternSymbol.forReference(bms, disamb)) + case _ => super.applyValue(value) + override def applyResult(result: codegen.Result): Unit = + result match + case Cast(_, target, _) => target.canonicalize match + case ErasedType.AnyRef(_, tpeSym) => applySymbol(tpeSym) + case _ => () + case _ => () + super.applyResult(result) + override def applyPath(path: Path): Unit = path match + // ValDefn traverses its RHS as a Path, whereas Return and Assign use Result. + case cast: Cast => applyResult(cast) + case _ => super.applyPath(path) + .applyBlock(block) + symbols.toList + /** Metadata for an exported function that later Wasm REPL modules can import. * * @param sym @@ -47,7 +84,7 @@ object SessionBinding: * The Wasm function type expected by the import. */ final case class SessionFunc( - sym: BlockMemberSymbol, + sym: BlockMemberSymbol | ClassSymbol, wrapId: Opt[Str] -> Opt[Str], moduleName: Str, exportName: Str, @@ -971,9 +1008,7 @@ class Ctx(using Elaborator.Ctx, State) extends ToWat: def addFunc(funcInfo: FuncInfo)(using Ctx, Raise): FuncIdx = val id = funcInfo.id funcs = funcs + (id -> funcInfo) - funcInfo.sym match - case bms: BlockMemberSymbol => namedFuncs(bms) = funcInfo - case _ => + namedFuncs(funcInfo.sym) = funcInfo val idx = FuncIdx(funcInfo.id) val refType = RefType(funcInfo.typeUse.typeIdx, nullable = false) elemSegments = elemSegments + diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala index d04b1d317a..a83761f4c1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala @@ -6,13 +6,22 @@ package text import hkmc2.utils.*, shorthands.* import document.* -import semantics.{BlockMemberSymbol, DefinitionSymbol, Elaborator, InnerSymbol, LocalVarSymbol, TempSymbol}, Elaborator.State +import semantics.{BlockMemberSymbol, ClassCtorSymbol, ClassSymbol, DefinitionSymbol, Elaborator, InnerSymbol, LocalVarSymbol, TempSymbol}, Elaborator.State import utils.Scope import scala.collection.Map /** Symbols that can represent an importable or exportable Wasm construct. */ -private[text] type ExternSymbol = BlockMemberSymbol | TempSymbol +private[text] type ExternSymbol = BlockMemberSymbol | ClassSymbol | TempSymbol + +private[text] object ExternSymbol: + /** Class constructors and explicit function companions share a source name but + * have distinct Wasm functions. Imports and calls must use the same identity. */ + def forReference(bms: BlockMemberSymbol, disamb: DefinitionSymbol[?]): BlockMemberSymbol | ClassSymbol = + disamb match + case cls: ClassSymbol => cls + case ctor: ClassCtorSymbol => ctor.associatedCls + case _ => bms /** Symbols that can be lowered into a global or local slot in a Wasm function (i.e. parameters and locals). */ private[text] type SlotSymbol = InnerSymbol | ScopedSymbol @@ -28,7 +37,7 @@ extension (doc: Document) extension (scp: Scope) /** Convenience function for [[Scope.allocateOrGetName]] with an optional prefix and suffix. */ - private[text] def allocateOrGetNameWrapped(sym: ScopedSymbol, wrapId: Opt[Str] -> Opt[Str])(using Raise): Str = + private[text] def allocateOrGetNameWrapped(sym: ScopedSymbol | ClassSymbol, wrapId: Opt[Str] -> Opt[Str])(using Raise): Str = val prefix = wrapId._1.fold("")(prefix => s"${prefix}_") wrapId._2 match case S(suffix) => @@ -305,7 +314,7 @@ object ExternType: def toWat: Document = doc"""(global ${id.toWat} ${globalType.toWat})""" end ExternType -sealed abstract class ExternType(val sym: ScopedSymbol) extends ToWat: +sealed abstract class ExternType(val sym: ScopedSymbol | ClassSymbol) extends ToWat: /** Symbolic identifier for the extern declaration. */ val id: SymIdx diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala index 4ce5d1aa69..89a8d75746 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala @@ -668,24 +668,24 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui /** Returns the elaborated source methods for this class. */ private def semanticMethodDefs(defn: ClsLikeDefn)(using Raise): List[TermDefinition] = - semanticClassDef(defn).body.methods.filter(_.body.nonEmpty) + semanticClassDef(defn).body.methods /** Resolves the exact overridden parent method symbol for `methodDef`, if any. */ private def overriddenParentMethodSym( defn: ClsLikeDefn, methodDef: TermDefinition, )(using Raise): Opt[BlockMemberSymbol] = - resolveParentSym(defn).flatMap(_.asClsOrMod.flatMap(_.defn)) match - case S(parentDef: hkmc2.semantics.ClassLikeDef) => - parentDef.body.members.get(methodDef.sym.nme).flatMap(_.asTrm.flatMap(_.defn)) match - case S(parentMethodDef: TermDefinition) if parentMethodDef.k is syntax.Fun => S(parentMethodDef.sym) - case _ => N - case _ => N + def lookup(parent: ClassLikeSymbol, seen: Set[ClassLikeSymbol]): Opt[BlockMemberSymbol] = + if seen(parent) then N else + parent.defn.flatMap(_.body.members.get(methodDef.sym.nme)) match + case S(member) => member.asTrm.filter(_.k is syntax.Fun).flatMap(_.bms) + case N => parent.irClassHeader.flatMap(_.parent).flatMap(_.asClsOrMod).flatMap(lookup(_, seen + parent)) + resolveParentSym(defn).flatMap(_.asClsOrMod).flatMap(lookup(_, Set.empty)) /** True when a method introduces a new virtual slot at its declaring class if not already inherited. */ private def declaresVirtualSlot(methodDef: TermDefinition): Bool = - methodDef.annotations.exists: - case Annot.Modifier(syntax.Keyword.`virtual`) => true + methodDef.body.isEmpty || methodDef.annotations.exists: + case Annot.Modifier(syntax.Keyword.`virtual` | syntax.Keyword.`open`) => true case _ => false /** The declared Wasm parameter types of `methodDef`, excluding `this`. */ @@ -713,6 +713,9 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui }`, but the method it overrides declares `${expected.toWat.mkString()}`" -> methodDef.sym.toLoc), source = Diagnostic.Source.Compilation, )) + if declaredParamTypes(methodDef).size != slotInfo.paramTypes.size then + raise(ErrorReport(msg"Overriding method '${methodDef.sym.nme}' must preserve the number of parameters" -> + methodDef.sym.toLoc :: Nil, source = Diagnostic.Source.Compilation)) declaredParamTypes(methodDef).zip(slotInfo.paramTypes).zipWithIndex.foreach: case ((actual, expected), idx) => if !conformsToSlotType(actual, expected) then report(s"parameter ${idx + 1}", actual, expected) @@ -770,13 +773,23 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui case other => lastWords(s"Parent type must be a struct, found ${other.toWat.mkString()}") val classFields = ctx.elabCtx.givenIn: - (defn.publicFields.map(_._2) ++ defn.privateFields).map: f => + (defn.publicFields.map(_._2) ++ defn.privateFields).flatMap: f => // Reference fields need to be nullable so that `struct.new_default` has a valid value to write val fieldType: ValType = f.erasedType.flatMap(_.wasmType) match case S(ty: RefType) => ty.copy(nullable = true) case S(ty) => ty case N => RefType.anyref - f -> Field(fieldType, mutable = true, id = f.nme) + inheritedFields.collectFirst: + case (sym: TermSymbol, field) if !sym.isPrivate && !f.isPrivate && sym.nme == f.nme => field + match + case S(field) => + // Overrides denote the same public property, including abstract fields. Reuse its + // storage so a selection through the base symbol sees the overriding initializer. + if !conformsToSlotType(fieldType, field.ty) then raise(ErrorReport( + msg"Overriding field '${f.nme}' must preserve its inherited storage type" -> f.toLoc :: Nil, + source = Diagnostic.Source.Compilation)) + Nil + case N => (f -> Field(fieldType, mutable = true, id = f.nme)) :: Nil val allFields = inheritedFields ++ classFields val runtimeTag = ctx.getFreshObjectTag() @@ -879,13 +892,21 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui private def initFuncSym(sym: BlockMemberSymbol): BlockMemberSymbol = initFuncSyms.getOrElseUpdate(sym, BlockMemberSymbol("init", Nil, nameIsMeaningful = false)) + /** Constructors belong to the class definition, not its overloaded term binding. + * Using the block member for both would overwrite an explicit function companion. + * The class symbol already supplies a stable identity across definitions and imports. + */ + private def constructorSymbol(defn: ClsLikeDefn): BlockMemberSymbol | ClassSymbol = defn.isym match + case cls: ClassSymbol => cls + case _ => defn.sym + /** Registers a placeholder class-associated function so later lowering can overwrite it. */ private def predeclareClassFunc( defn: ClsLikeDefn, suffix: Str, params: Seq[WasmSlotSymbol -> SymIdx], results: Seq[Result], - sym: BlockMemberSymbol, + sym: BlockMemberSymbol | ClassSymbol, exportName: Opt[Str], thisType: Opt[ValType], )(using Raise): Unit = @@ -898,7 +919,7 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui suffix: Str, params: Seq[WasmSlotSymbol -> SymIdx], resultTypes: Seq[Result], - sym: BlockMemberSymbol, + sym: BlockMemberSymbol | ClassSymbol, exportName: Opt[Str], funcTy: TypeIdx, )(using Raise): Unit = @@ -953,13 +974,17 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui val ctorExportName = defn.sym .optionIf: sym => !(defn.k is syntax.Obj) && sym.nameIsMeaningful - .map(_.nme) + .map: sym => + // An explicit function companion exports the term under its source name. + // Give the constructor its own export so later worksheets can import both. + if defn.ctorSym.isEmpty && sym.hasTrmDef then s"mlscript.class:${sym.nme}" + else sym.nme predeclareClassFunc( defn, "ctor", ctorParams, Seq(Result(RefType(typeIdx, nullable = false))), - defn.sym, + constructorSymbol(defn), ctorExportName, thisType = N, ) @@ -1202,10 +1227,11 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui i32.const(tagValue), parentTypeInfo, ) ++ vtSlots.map: method => - ref.func( - ctx.getFunc_!(method.sym), - RefType(ctx.getFuncTypeUse_!(method.sym).typeIdx, nullable = false), - ) + // Abstract classes retain the slot and its calling convention without an implementation. + // Erasure rejects constructing them; each concrete subclass must fill every such slot. + ctx.getFunc(method.sym) match + case S(func) => ref.func(func, RefType(ctx.getFuncTypeUse_!(method.sym).typeIdx, nullable = false)) + case N => ref.`null`(virtualMethodFuncType(method.owner, method.paramTypes, method.resultType)) val globalInfo = GlobalInfo( globalType = GlobalType(RefType(typeInfoTypeIdx, nullable = false), mutable = false), init = struct.`new`(typeInfoTypeIdx, initFields), @@ -1618,31 +1644,40 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui case ts: TermSymbol => ts.owner.flatMap(_.asBlkMember) case ms: MemberSymbol => ms.asTrm.flatMap(_.owner.flatMap(_.asBlkMember)) - def fieldSelect(thisSym: BlockMemberSymbol, sym: DefinitionSymbol[?])(using Ctx, Raise): FieldIdx = + private def classField(thisSym: BlockMemberSymbol, sym: DefinitionSymbol[?])(using Ctx, Raise): Field = val structInfo = ctx.getTypeInfo_!(thisSym) - val symToField = structInfo.compType match - case ty: StructType => ty.fieldsBySym + val field = structInfo.compType match + case ty: StructType => ty.fieldsBySym.get(sym).orElse: + // A subclass symbol can name an inherited public slot. Resolve the alias from the + // immutable layout, which also works for class layouts imported from an earlier session. + sym.asTrm.filterNot(_.isPrivate).flatMap: target => + ty.fields.collectFirst: + case (base: TermSymbol, field) if !base.isPrivate && base.nme == target.nme => field case _ => lastWords(s"Cannot select field from non-struct type: ${structInfo.compType.toWat.mkString()}") - val fieldIdx = symToField.get(sym).fold(lastWords( + field.getOrElse(lastWords( s"Missing field `${sym.toString}` in struct `${thisSym.toString}` with type `${structInfo.toWat.mkString()}`", - )): field => - field.id - FieldIdx(SymIdx(fieldIdx)) + )) + end classField + + def fieldSelect(thisSym: BlockMemberSymbol, sym: DefinitionSymbol[?])(using Ctx, Raise): FieldIdx = + FieldIdx(SymIdx(classField(thisSym, sym).id)) end fieldSelect /** Resolves `sym` to a predeclared class method symbol, if any. */ private def predeclaredClassMethodSym(sym: DefinitionSymbol[?]): Opt[BlockMemberSymbol] = sym.asBlkMember.filter: methodSym => - methodSym.asTrm.exists(_.owner.exists(_.asCls.isDefined)) && ctx.getFunc(methodSym).nonEmpty + methodSym.asTrm.exists(_.owner.exists(_.asCls.isDefined)) && + (ctx.getFunc(methodSym).nonEmpty || fieldOwner(methodSym).flatMap(ctx.getVirtualTable).exists(_.slotOf.contains(methodSym))) /** Lowers a class method call, using virtual dispatch only when the selected owner class has a virtual slot. */ private def lowerClassMethodCall( qual: Path, methodSym: BlockMemberSymbol, args: Seq[Arg], + virtual: Bool, )(using FunctionCtx, Raise, SessionExportCtx): Ls[Expr] = val ownerCls = fieldOwner(methodSym).get - ctx.getVirtualTable(ownerCls).flatMap(vt => vt.slotOf.get(methodSym).map(slot => (vt, slot))) match + ctx.getVirtualTable(ownerCls).filter(_ => virtual).flatMap(vt => vt.slotOf.get(methodSym).map(slot => (vt, slot))) match case S((vt, slot)) => val baseSym = vt.slots(slot).owner val paramTypes = vt.slots(slot).paramTypes @@ -1721,8 +1756,8 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui errExpr: Ls(msg"Plain class references are not supported in Wasm; instantiate the class instead." -> r.toLoc) else - ctx.getFunc(bms) match - case S(funcIdx) => ref.func(funcIdx, RefType(ctx.getFuncTypeUse_!(bms).typeIdx, nullable = false)) + ctx.getFunc(ExternSymbol.forReference(bms, disamb)) match + case S(funcIdx) => ref.func(funcIdx, RefType(ctx.getFuncTypeUse_!(funcIdx).typeIdx, nullable = false)) case N => getVar(bms, r.toLoc) case Value.This(sym) => singletonInfoFor(sym) match @@ -1758,7 +1793,16 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui ) val args = argss.flatten val methodSym = sel.symbol.flatMap(predeclaredClassMethodSym).get - lowerClassMethodCall(qual, methodSym, args).mergeAsBlock_! + lowerClassMethodCall(qual, methodSym, args, virtual = true).mergeAsBlock_! + + case c @ Call(sel: SuperSelect, argss) => + if argss.length != 1 then errExpr(Ls(msg"Super calls with multiple parameter lists are not supported by WASM" -> c.toLoc)) + else lowerClassMethodCall(sel.qual, sel.symbol.bms.get, argss.head, virtual = false).mergeAsBlock_! + + case sel: SuperSelect => + if sel.symbol.defn.exists(_.params.isEmpty) then + lowerClassMethodCall(sel.qual, sel.symbol.bms.get, Nil, virtual = false).mergeAsBlock_! + else errExpr(Ls(msg"Super method values are not supported by WASM" -> sel.toLoc)) case c @ Call(fun, argss) => if argss.length > 1 then @@ -1815,8 +1859,8 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui ) case N => fun match - case Value.MemberRef(l, _) => - val base = ctx.getFunc(l) + case Value.MemberRef(l, disamb) => + val base = ctx.getFunc(ExternSymbol.forReference(l, disamb)) val baseFuncIdx = base match case S(idx) => idx case N => return errExpr( @@ -1878,7 +1922,7 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui val methodSym = predeclaredClassMethodSym(selSym).get methodSym.asTrm.flatMap(_.defn) match case S(defn: TermDefinition) if defn.params.isEmpty => - lowerClassMethodCall(qual, methodSym, Nil).mergeAsBlock_! + lowerClassMethodCall(qual, methodSym, Nil, virtual = true).mergeAsBlock_! case _ => errExpr( Ls( @@ -1900,17 +1944,19 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui val fieldidx = fieldSelect(selCls, selSym) val structInfo = ctx.getTypeInfo_!(selCls) val fieldTy = structInfo.compType match - case st: StructType => st.fieldsBySym(selSym).ty + case st: StructType => classField(selCls, selSym).ty case other => lastWords(s"Expected struct type for $selCls, found ${other.toWat.mkString()}") val getExpr = struct.get( fieldidx, ref = castConserve(qualRes, RefType(ctx.getType_!(selCls), nullable = false)), ty = fieldTy, ) - fieldTy match - case rt: RefType if rt.nullable && rt.heapType != HeapType.Any => - castConserve(getExpr, rt.copy(nullable = false)) - case _ => getExpr + selSym.asTrm.flatMap(_.erasedType).flatMap(_.wasmType) match + case S(declared) => castConserve(getExpr, declared) + case N => fieldTy match + case rt: RefType if rt.nullable && rt.heapType != HeapType.Any => + castConserve(getExpr, rt.copy(nullable = false)) + case _ => getExpr case N => errExpr( Ls( @@ -1987,7 +2033,7 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui s"Expected resolved class for an Instantiate(...) expression to be a BlockMemberSymbol, but got ${ ctorClsSym.getClass.getName }" - val ctorFuncIdx = ctx.getFunc(ctorClsBlkSym).getOrElse: + val ctorFuncIdx = ctx.getFunc(ctorClsSym.asCls.getOrElse(ctorClsBlkSym)).getOrElse: lastWords(s"Missing constructor definition for class ${ctorClsBlkSym.toString}") val ctorTypeIdx = ctx.getTypeInfo(ctx.getFuncTypeUse_!(ctorFuncIdx).typeIdx).getOrElse: lastWords(s"Missing type definition for class constructor ${ctorClsBlkSym.toString}") @@ -2128,7 +2174,7 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui val objRef = castConserve(lhsExpr, RefType(ctx.getType_!(selCls), nullable = false)) val structInfo = ctx.getTypeInfo_!(selCls) val fieldType = structInfo.compType match - case st: StructType => st.fieldsBySym(fieldSym).ty + case st: StructType => classField(selCls, fieldSym).ty case other => lastWords(s"Expected struct type for $selCls, found ${other.toWat.mkString()}") val rhsCasted = fieldType match case rt: RefType => castConserve(rhsExpr, rt) @@ -2196,7 +2242,7 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui val fieldIdx = fieldSelect(ownerBlkMem, tsym) val structInfo = ctx.getTypeInfo_!(ownerBlkMem) val fieldType = structInfo.compType match - case st: StructType => st.fieldsBySym(tsym).ty + case st: StructType => classField(ownerBlkMem, tsym).ty case other => lastWords(s"Expected struct type for $ownerBlkMem, found ${other.toWat.mkString()}") val pCasted = castToValType(p, fieldType) val assignInstr = struct.set( @@ -2338,9 +2384,10 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui exportName = predeclaredInit.exportName, )) - val predeclaredCtor = ctx.getFuncInfo_!(clsLikeDefn.sym) + val ctorSym = constructorSymbol(clsLikeDefn) + val predeclaredCtor = ctx.getFuncInfo_!(ctorSym) val ctorFuncInfo = FuncInfo( - sym = clsLikeDefn.sym, + sym = ctorSym, wrapId = S(clsLikeDefn.sym.nme) -> N, typeUse = predeclaredCtor.typeUse, params = ctorFnCtx.resolvedParams, @@ -2397,16 +2444,14 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui objectTag = typeinfo.objectTag, rttiTypeInfo = rttiTypeInfo, rttiGlobalExportName = rttiGlobalInfo.exportName.get, - aliasSyms = clsLikeDefn.isym match - case mos: ModuleOrObjectSymbol => mos :: Nil - case _ => Nil, + aliasSyms = clsLikeDefn.isym :: Nil, )) if !isSingletonObj && clsLikeDefn.sym.nameIsMeaningful then summon[SessionExportCtx].emit(SessionFunc( - sym = clsLikeDefn.sym, + sym = ctorSym, wrapId = ctorFuncInfo.wrapId, moduleName = SessionBinding.ReplModuleName, - exportName = clsLikeDefn.sym.nme, + exportName = ctorFuncInfo.exportName.get, funcType = FunctionType(ctorFuncInfo.getSignatureType), )) end if diff --git a/hkmc2/shared/src/main/scala/hkmc2/invalml/InvalML.scala b/hkmc2/shared/src/main/scala/hkmc2/invalml/InvalML.scala index e5a749cdbd..4614e2518f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/invalml/InvalML.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/invalml/InvalML.scala @@ -26,7 +26,7 @@ final case class InvalCtx( env: HashMap[Uid[Symbol], GeneralType], outRegAcc: Type, symbolCache: HashMap[Str, TypeSymbol], -): +)(using Config): def +=(p: Symbol -> GeneralType): Unit = env += p._1.uid -> p._2 def get(sym: Symbol): Option[GeneralType] = env.get(sym.uid) orElse parent.dlof(_.get(sym))(None) def getCls(name: Str): TypeSymbol = symbolCache.getOrElseUpdate(name, @@ -62,15 +62,19 @@ object InvalCtx: ClassLikeType(ctx.getCls("Region"), Wildcard.out(sk) :: Nil) def refTy(ct: Type, sk: Type)(using ctx: InvalCtx): Type = ClassLikeType(ctx.getCls("Ref"), Wildcard(ct, ct) :: Wildcard.out(sk) :: Nil) - def init(raise: Raise)(using Elaborator.State, Elaborator.Ctx): InvalCtx = + def init(raise: Raise)(using Elaborator.State, Elaborator.Ctx)(using Config): InvalCtx = new InvalCtx(raise, summon, None, 1, HashMap.empty, Bot, HashMap.empty) val builtinOps = Elaborator.binaryOps ++ Elaborator.unaryOps ++ Elaborator.aliasOps.keySet end InvalCtx -class InvalTyper(using elState: Elaborator.State, tl: TL)(using Ctx): +class InvalTyper(using codegen.Erasure)(using elState: Elaborator.State, tl: TL)(using Ctx, Config): import tl.{trace, log} + + // InvalML expands patterns while typing. Its early use of lowering is valid only + // for legacy resolution, whose terms do not carry deferred candidate sets. + require(!config.language.useNewResolution, "InvalML requires legacy resolution") private val infVarState = new InfVarUid.State() private val solver = new ConstraintSolver(infVarState, elState, tl) @@ -124,6 +128,7 @@ class InvalTyper(using elState: Elaborator.State, tl: TL)(using Ctx): def mono(ty: Term, pol: Bool): Type = monoOrErr(typeAndSubstType(ty, pol), ty) ty match + case Capture(base, _) => typeAndSubstType(base, pol) case Ref(sym: LocalSymbol) => log(s"Type lookup: ${sym.nme} ${sym.uid} ${map.keySet}") map.get(sym.uid) match @@ -262,7 +267,7 @@ class InvalTyper(using elState: Elaborator.State, tl: TL)(using Ctx): constrain(lhsTy, FunType(rhsTy.reverse, resTy, Bot)) // TODO: right (resTy, lhsCtx | rhsCtx, lhsEff | rhsEff) case sel @ Term.SynthSel(Term.Ref(_: TopLevelSymbol), _) if sel.symbol.isDefined => - val (opTy, eff) = typeCheck(Ref(sel.symbol.get)(sel.nme, N)) // FIXME 666 + val (opTy, eff) = typeCheck(Ref(sel.symbol.get)(sel.nme, N)) (tryMkMono(opTy, sel), Bot, eff) case unq @ Term.Unquoted(body) => val (ty, eff) = typeCheck(body) @@ -620,7 +625,7 @@ class InvalTyper(using elState: Elaborator.State, tl: TL)(using Ctx): case Term.Annotated(Annot.Untyped(), _) => (Bot, Bot) case sel @ Term.SynthSel(Ref(_: TopLevelSymbol), nme) if sel.symbol.isDefined => - typeCheck(Ref(sel.symbol.get)(sel.nme, N)) // FIXME 666 + typeCheck(Ref(sel.symbol.get)(sel.nme, N)) case Ref(sym) => ctx.get(sym) match case Some(ty) => (ty, Bot) diff --git a/hkmc2/shared/src/main/scala/hkmc2/package.scala b/hkmc2/shared/src/main/scala/hkmc2/package.scala index 21197bd4a6..311944c6a7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/package.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/package.scala @@ -20,6 +20,9 @@ extension [A](xs: Ls[A]) case Nil => N case xs: NELs[A] => S(xs) +extension [A](xs: NELs[A]) + def ne_map[B](f: A => B): NELs[B] = f(xs.head) ne_:: xs.tail.map(f) + // * Valid identifiers for the members of module and class-like definitions // * Importantly, these are the same as valid JavaScript identifiers, diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala index b5e6350344..2fe35681b0 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala @@ -17,7 +17,6 @@ import hkmc2.Message.MessageContext import Keyword.{`and`, `case`, `do`, `else`, `if`, `is`, `let`, `or`, `set`, `then`, `while`} import hkmc2.utils.Scope -import codegen.{ErasedType, ErasedValueType} import SimpleSplit.* import ucs.{error, unapply} @@ -47,18 +46,32 @@ object Elaborator: // TODO: rename to ScopeKind? enum OuterCtx: - case Function(returnHandlerSymbol: TempSymbol)(val isGenerator: Bool, val isAsync: Bool) + case Function(returnHandlerSymbol: TempSymbol, sym: Opt[DefinitionSymbol[?]])(val isGenerator: Bool, val isAsync: Bool) case InnerScope(innerSymbol: InnerSymbol) case LocalScope(nameHint: Str) case LambdaOrHandlerBlock - case NonReturnContext + case NonReturnContext(sym: Opt[DefinitionSymbol[?]]) def showDbg: Str = this match - case Function(sym) => s"fun:${sym.nme}" + case Function(retSym, sym) => s"fun:${retSym.nme}${sym.fold("")(s => s"β€Ή${s}β€Ί")}" case InnerScope(inner) => inner.toString case LocalScope(hint) => hint case LambdaOrHandlerBlock => "LambdaOrHandlerBlock" - case NonReturnContext => "NonReturnContext" + case NonReturnContext(sym) => s"NonReturnContext(${sym})" + + /** Functions and instantiated class bodies cross lexical resolution boundaries. + * Function's NonReturnContext already supplies its boundary, so Function must + * not introduce it again. Modules/objects have no per-instance boundary; + * type aliases substitute types without entering any value activation. + * In C's method, referring to the outer C constructor crosses the old instance + * boundary too. That capture cancels the old instance exit when the method's + * result is consumed; the fresh constructor exit belongs to the new instance. + */ + def resolutionBoundary: Opt[AnyDefinitionSymbol] = this match + case NonReturnContext(S(_: TypeAliasSymbol)) => N + case NonReturnContext(sym) => sym + case InnerScope(sym: ClassSymbol) => S(sym) + case _ => N def inner: Opt[InnerSymbol] = this match case InnerScope(inner) => S(inner) @@ -99,11 +112,23 @@ object Elaborator: env: Map[Str, Ctx.Elem], mode: Mode, labels: Map[LabelSymbol, LabelBinding], + wildcardOpens: Ls[Ctx.OpenSource], ): override def toString: Str = s"${parent.fold("")(_.toString+"/")}${outer.showDbg}" lazy val scope: SrcScope = SrcScope(outer, parent.map(_.scope)) + + /** A nominal member can refer to enclosing binders even when its own + * signature does not mention them, for example through a local type alias. + */ + lazy val typeBinders: Set[VarSymbol] = parent.fold(Set.empty[VarSymbol])(_.typeBinders) ++ + env.valuesIterator.flatMap(_.symbol).collect: + case symbol: VarSymbol if symbol.decl.exists(_.isInstanceOf[TyParam]) => symbol + + /** The resolution scopes that references from this context capture through. */ + lazy val resolutionBoundaries: Set[ResolutionBoundary] = + parent.fold(Set.empty[ResolutionBoundary])(_.resolutionBoundaries) ++ outer.resolutionBoundary.map(ResolutionBoundary(_)) def +(local: Str -> Symbol): Ctx = copy(env = env + local.mapSecond(Ctx.RefElem(_))) @@ -137,12 +162,34 @@ object Elaborator: labelSym, resultSym, nonLocalHandlerSym, nonLocalBreakMethodMarker, nonLocalContinueMethodMarker)) ) - def nest(outerCtx: OuterCtx): Ctx = Ctx(outerCtx, Some(this), Map.empty, mode, Map.empty) + def nest(outerCtx: OuterCtx): Ctx = Ctx(outerCtx, Some(this), Map.empty, mode, Map.empty, Nil) def nestLocal(nameHint: Str): Ctx = nest(OuterCtx.LocalScope(nameHint)) def nestInner(inner: InnerSymbol): Ctx = nest(OuterCtx.InnerScope(inner)) - def get(name: Str): Opt[Ctx.Elem] = - env.get(name).orElse(parent.flatMap(_.get(name))) + /** Explicit bindings in any enclosing scope take precedence over every wildcard open. */ + def get(name: Str)(using config: Config): Opt[Ctx.Elem] = + get(name, config.language.useNewResolution) + + def get(name: Str, captureScopes: Bool)(using config: Config): Opt[Ctx.Elem] = + getExplicit(name, captureScopes).orElse: + if config.language.useNewResolution then + val sources = visibleWildcardOpens.distinct + if sources.isEmpty then N else S(Ctx.WildcardElem(name, sources)) + else N + + private def getExplicit(name: Str, captureScopes: Bool)(using config: Config): Opt[Ctx.Elem] = + env.get(name).orElse: + val inherited = parent.flatMap(_.getExplicit(name, captureScopes)) + if captureScopes then inherited.map(capture) + else inherited + + private def capture(elem: Ctx.Elem): Ctx.Elem = + outer.resolutionBoundary.fold(elem)(Ctx.CaptElem(elem, _)) + + private lazy val visibleWildcardOpens: Ls[Ctx.OpenSource] = + val inherited = parent.toList.flatMap(_.visibleWildcardOpens) + wildcardOpens ::: inherited.map(source => Ctx.OpenSource(capture(source.elem))(source.id)) + def lookupLabel(name: Str): LabelLookup = @tailrec def go( @@ -177,27 +224,33 @@ object Elaborator: case _ => false) go(ctx.parent, nextCrossedFunction, nextCrossedLambdaOrHandler) go(S(this), false, false) + + // Explicit `this` must carry the same intervening captures as a lexical + // member reference; inherited member results can retain the receiver's scope. + def getReceiver: Opt[Ctx.Elem] = + outer.inner.map(Ctx.RefElem(_)).orElse(parent.flatMap(_.getReceiver).map(capture)) + def getOuter: Opt[InnerSymbol] = outer.inner.orElse(parent.flatMap(_.getOuter)) def getNonLocalRetHandler: Opt[TempSymbol] = outer match - case OuterCtx.Function(sym) => S(sym) + case OuterCtx.Function(rsym, _) => S(rsym) case _ => parent.flatMap(_.getNonLocalRetHandler) def getRetHandler: ReturnHandler = outer match - case OuterCtx.Function(sym) => ReturnHandler.Direct + case OuterCtx.Function(rsym, _) => ReturnHandler.Direct case _: (OuterCtx.LambdaOrHandlerBlock.type | OuterCtx.InnerScope) => getNonLocalRetHandler.fold(ReturnHandler.NotInFunction)(ReturnHandler.Required(_)) - case OuterCtx.NonReturnContext => ReturnHandler.Forbidden + case OuterCtx.NonReturnContext(_) => ReturnHandler.Forbidden case _: OuterCtx.LocalScope => parent.fold(ReturnHandler.NotInFunction)(_.getRetHandler) def inGenerator: Bool = outer match case f: OuterCtx.Function => f.isGenerator case _: OuterCtx.LocalScope => parent.fold(false)(_.inGenerator) - case _: (OuterCtx.LambdaOrHandlerBlock.type | OuterCtx.InnerScope | OuterCtx.NonReturnContext.type) => false + case _: (OuterCtx.LambdaOrHandlerBlock.type | OuterCtx.InnerScope | OuterCtx.NonReturnContext) => false def inAsync: Bool = outer match case f: OuterCtx.Function => f.isAsync case _: OuterCtx.LocalScope => parent.fold(false)(_.inAsync) - case _: (OuterCtx.LambdaOrHandlerBlock.type | OuterCtx.InnerScope | OuterCtx.NonReturnContext.type) => false + case _: (OuterCtx.LambdaOrHandlerBlock.type | OuterCtx.InnerScope | OuterCtx.NonReturnContext) => false def potentiallyInstrumented(using Config): Bool = config.effectHandlers.isDefined || inAsync // * Invariant: We expect that the top-level context only contain hard-coded symbols like `globalThis` @@ -245,6 +298,9 @@ object Elaborator: val Str = assumeBuiltinCls("Str") val BigInt = assumeBuiltinCls("BigInt") val Function = assumeBuiltinCls("Function") + lazy val Promise = assumeBuiltinCls("Promise") + lazy val Awaited = assumeBuiltinTpe("Awaited") + lazy val Nothing = assumeBuiltinTpe("Nothing") val Error = assumeBuiltinCls("Error") val Bool = assumeBuiltinCls("Bool") val Object = assumeBuiltinCls("Object") @@ -280,7 +336,7 @@ object Elaborator: case Tree.Modified(_, body) => underlyingTypeDef(body) case _ => N - val stmts = parent.tree.withPart match + val stmts = parent.tree.bodies.headOption match case S(Tree.Block(stmts)) => stmts case _ => Nil stmts.flatMap(underlyingTypeDef).collectFirst: @@ -379,28 +435,74 @@ object Elaborator: val primitivelyRepresentedRoots: Set[TypeSymbol] = Set(Num, Str, Bool) object Ctx: + /** The opening site is diagnostic metadata, excluded from equality so + * reopening the same receiver still contributes only one candidate. + * Captures must preserve this site independently of the receiver's definition. + */ + final case class OpenSource(elem: Elem)(val id: Ident) + abstract class Elem: def nme: Str - def ref(id: Ident)(using Elaborator.State, Ctx): Resolvable + def ref(id: Ident)(using Elaborator.State, Ctx, Config, NewResolver, NewResolverState): Term def symbol: Opt[Symbol] def isImport: Bool final case class RefElem(sym: Symbol) extends Elem: val nme = sym.nme - def ref(id: Ident)(using Elaborator.State, Ctx): Resolvable = - // * Note: due to symbolic ops, we may have `id.name =/= nme`; - // * e.g., we can have `id.name = "|>"` and `nme = "pipe"`. - Term.Ref(sym)(id, N) // FIXME: 666 is a temporary placeholder + def ref(id: Ident)(using Elaborator.State, Ctx, Config, NewResolver, NewResolverState): Term = + if config.language.useNewResolution then + sym match + case sym: codegen.SimpleSymbol => + Term.SimpleRef(sym)(id) + case sym: InnerSymbol => + Term.SelfRef(sym)(id) + case sym: MemberSymbol => + Term.MemberRef(sym)(id, FlowSymbol.memSym(sym)) + // case sym: TermSymbol => // FIXME: should never happen... (currently happens for ref to ctor let) + // Term.MemberRef(sym)(id) + else + // * Note: due to symbolic ops, we may have `id.name =/= nme`; + // * e.g., we can have `id.name = "|>"` and `nme = "pipe"`. + sym.ref(id) def symbol = S(sym) def isImport: Bool = false final case class SelElem(base: Elem, nme: Str, symOpt: Opt[MemberSymbol], isImport: Bool) extends Elem: - def ref(id: Ident)(using Elaborator.State, Ctx): Resolvable = + def ref(id: Ident)(using Elaborator.State, Ctx, Config, NewResolver, NewResolverState): Term = // * Same remark as in RefElem#ref - Term.SynthSel(base.ref(Ident(base.nme)), - new Ident(nme).withLocOf(id))(symOpt, FlowSymbol.synthSel(nme), N, S(summon)) + val prefix = base.ref(Ident(base.nme)) + val name = new Ident(nme).withLocOf(id) + if config.language.useNewResolution then + val res = new Term.NewSel(prefix, name, N)(FlowSymbol.synthSel(nme)).withLocOf(id) + summon[NewResolver].newSel(res) + res + else + Term.SynthSel(prefix, name)(symOpt, FlowSymbol.synthSel(nme), N, S(summon)) def symbol = symOpt + final case class WildcardElem(nme: Str, sources: Ls[OpenSource]) extends Elem: + def ref(id: Ident)(using Elaborator.State, Ctx, Config, NewResolver, NewResolverState): Term = + // Each ref is fresh. Locate its outer capture at the opening site too; + // a chained wildcard reference may already have attached this location. + val prefixes = sources.map(source => source.elem.ref(source.id).withoutLoc.withLocOf(source.id)) + val res = new Term.UnresolvedRef(prefixes, id)(FlowSymbol.synthSel(nme)).withLocOf(id) + summon[NewResolver].unresolvedRef(res) + res + def symbol: Opt[Symbol] = N + def isImport: Bool = true + final case class CaptElem(base: Elem, thru: DefinitionSymbol[?]) extends Elem: + def ref(id: Ident)(using Elaborator.State, Ctx, Config, NewResolver, NewResolverState): Term = + Term.Capture(base.ref(id), thru) + def symbol = base.symbol + def isImport: Bool = false + def nme: Str = base.nme given Conversion[Symbol, Elem] = RefElem(_) - val empty: Ctx = Ctx(OuterCtx.LocalScope("top-level"), N, Map.empty, Mode.Full, Map.empty) + val empty: Ctx = Ctx(OuterCtx.LocalScope("top-level"), N, Map.empty, Mode.Full, Map.empty, Nil) + /** The interpretation required by the enclosing syntax. Class and pattern + * targets are resolved by their enclosing construct; types must not request + * a runtime value merely because they use the same reference syntax. + */ + enum Interpretation: + case Trm, Receiver, Specialization, Clss, Ptrn, Tpe + enum Mode: case Full case Light @@ -415,6 +517,9 @@ object Elaborator: tuple: ModuleOrObjectSymbol, str: ModuleOrObjectSymbol, unreachable: TermSymbol, + assertFail: TermSymbol, + continue: ModuleOrObjectSymbol, + toJsAsync: TermSymbol, tupleGet: TermSymbol, tupleSlice: TermSymbol, tupleLazySlice: TermSymbol, @@ -456,13 +561,16 @@ object Elaborator: .getOrElse(lastWords(s"Runtime.mls module '${module.nme}' does not define term '$memberName'.")) val tuple = modOrObj("Tuple") - val str = modOrObj("Str") + val str = modOrObj("StrOps") RuntimeSymbols( unit = modOrObj("Unit"), loopEnd = modOrObj("LoopEnd"), tuple = tuple, str = str, unreachable = term("unreachable"), + assertFail = term("assertFail"), + continue = modOrObj("Continue"), + toJsAsync = term("toJsAsync"), tupleGet = moduleMember(tuple, "get"), tupleSlice = moduleMember(tuple, "slice"), tupleLazySlice = moduleMember(tuple, "lazySlice"), @@ -505,6 +613,7 @@ object Elaborator: case Private(sym: BlockMemberSymbol, modulePath: Str) class State: + lazy val newResolverState: NewResolverState = new NewResolverState(this) val suid = new Uid.Symbol.State given State = this private var _compilationUnit: Opt[CompilationUnit] = N @@ -567,6 +676,9 @@ object Elaborator: val id = new Ident("ret") BlockMemberSymbol(id.name, Nil, true) def unreachableSymbol: TermSymbol = runtimeSymbols.unreachable + def assertFailSymbol: TermSymbol = runtimeSymbols.assertFail + def continueSymbol: ModuleOrObjectSymbol = runtimeSymbols.continue + def toJsAsyncSymbol: TermSymbol = runtimeSymbols.toJsAsync def tupleGetSymbol: TermSymbol = runtimeSymbols.tupleGet def tupleSliceSymbol: TermSymbol = runtimeSymbols.tupleSlice def tupleLazySliceSymbol: TermSymbol = runtimeSymbols.tupleLazySlice @@ -626,6 +738,7 @@ end Elaborator import Elaborator.* +import Elaborator.Interpretation.{Trm, Receiver, Specialization, Clss, Ptrn, Tpe} class Elaborator(val tl: TraceLogger, val wd: io.Path, val prelude: Ctx) @@ -633,14 +746,19 @@ class Elaborator(val tl: TraceLogger, val wd: io.Path, val prelude: Ctx) extends Importer: import tl.* given TraceLogger = tl + private given NewResolver = this + private given NewResolverState = state.newResolverState.withReporter(raise) + + val newResolution: Bool = config.language.useNewResolution lazy val illegalMemberNameTail = msg"Member names must start with a letter or underscore, followed by letters, digits, or underscores." -> N :: Nil def mkLetBinding(sym: LocalVarSymbol | TermSymbol, rhs: Term, annotations: Ls[Annot])(loc: Opt[Loc]): Ls[Statement] = - LetDecl(sym, annotations)(loc) :: DefineVar(sym, rhs)(loc) :: Nil + LetDecl(sym, annotations)(loc) :: defineVar(sym, rhs)(loc) :: Nil + // TODO: remove in favor of new resolution logic def resolveField(srcTree: Tree, base: Opt[Symbol], nme: Ident): Opt[MemberSymbol] = base match case S(psym: BlockMemberSymbol) => @@ -663,6 +781,8 @@ extends Importer: | Keyword.`data` | Keyword.`staged` | Keyword.`virtual` + | Keyword.`open` + | Keyword.`override` | Keyword.`public` | Keyword.`private` )) => S(Annot.Modifier(kw)(tree.toLoc)) @@ -671,10 +791,12 @@ extends Importer: S(Annot.Config(modify)(tree.toLoc)) case App(Ident("affine"), Tup(IntLit(whichParamList) :: Nil)) => S(Annot.Affine(whichParamList.toInt)(tree.toLoc)) - case _ => term(tree) match + case _ => term(tree, Trm) match case Term.Error() => N case trm => - trm.symbol match + val symbol = if newResolution then annotationSymbol(trm) else trm.symbol + if newResolution && symbol.isEmpty then return N + symbol match case S(sym) => sym match case ctx.builtins.annotations.untyped => @@ -691,6 +813,10 @@ extends Importer: return S(Annot.Generator()(tree.toLoc)) case ctx.builtins.annotations.async => return S(Annot.Async()(tree.toLoc)) + case ctx.builtins.annotations.buffered => + return S(Annot.Bufferable(false)(tree.toLoc)) + case ctx.builtins.annotations.bufferable => + return S(Annot.Bufferable(true)(tree.toLoc)) case ctx.builtins.annotations.pure => return S(Annot.Pure()(tree.toLoc)) case _ => () @@ -715,7 +841,7 @@ extends Importer: /** Use a synthesized selection so the sentinel object can be referenced without field-access sanity checks. */ private def nonLocalContinueSentinel(using Ctx): Term = - State.runtimeSymbol.ref().selNoSym("Continue", synth = true) + State.runtimeSymbol.ref().synthSel(State.continueSymbol) /** Build an effect handler around `body` for non-local control flow. */ private def mkEffectHandleAbortive( @@ -729,7 +855,7 @@ extends Importer: val valueSym = spec.valueParamName.map(nme => VarSymbol(Ident(nme), erasedType = N)) val resumeSym = VarSymbol(Ident("resume"), erasedType = N) val mtdSym = BlockMemberSymbol(spec.methodName, Nil, true) - val tsym = TermSymbol(Fun, N, Ident(spec.methodName), erasedType = N) + val tsym = TermSymbol(Fun, N, Ident(spec.methodName)) val td = TermDefinition( Fun, mtdSym, @@ -743,8 +869,8 @@ extends Importer: Nil, N, )(N) - tsym.defn = S(td) mtdSym.tsym = S(tsym) + tsym.defn = S(td) HandlerTermDefinition(resumeSym, td) Term.Handle(handlerSymbol, state.nonLocalRetHandlerTrm, Nil, clsSym, htds, body) @@ -759,7 +885,7 @@ extends Importer: val rs = FlowSymbol.app() val mtdTree = new Ident(methodName) val argTree = new Tup(argTrees) - Term.App( + app( Term.Sel(handlerSymbol.ref(callSiteId), mtdTree)( S(state.nonLocalRet), FlowSymbol.sel(callSiteId.name), N, S(summon)), Term.Tup(argTerms.map(term => PlainFld(term)))(argTree), @@ -786,8 +912,8 @@ extends Importer: if methods.isEmpty then body else mkEffectHandleAbortive(nonLocalHandlerSym, "NonLocalLabelEffect", methods, body) - /** A scrutinee is a function that returns a reference to the symbol. */ - private type Reference = () => Term.Ref + /** A scrutinee reference retains captures until its pattern has been resolved. */ + private type Reference = () => Term private type Connective = `do`.type | `then`.type @@ -840,13 +966,32 @@ extends Importer: t.split match case block: Block => termSplit(block.desugStmts, identity) case other: Tree => termSplit(Ls(other), identity) - new Term.IfLike(t.kw.kw, form, split).withLocOf(t) + ifLike(t.kw.kw, form, split, t.toLoc) /** Elaborate `case` expressions */ protected def caseSplit(scrut: VarSymbol, tree: Case): Ctxl[Term.IfLike] = val (form, split) = withScopedConnectives(tree.kw): patternBranch(() => scrut.ref(), tree.branches, identity) - new Term.IfLike(tree.kw.kw, form, split).withLocOf(tree) + ifLike(tree.kw.kw, form, split, tree.toLoc) + + protected def mkSplitLet(binding: LocalVarSymbol, term: Term): Head.Let = + if newResolution then + binding match + case binding: ShapeHost => + if newResolution then pipeTerm(term, binding) + // case _ => ??? + Head.Let(binding, term) + + protected def mkMatch(scrutinee: Term, pattern: Pattern, consequent: SimpleSplit) = + log(s"mkMatch: scrutinee = ${scrutinee.showDbg}, pattern = ${pattern.showDbg}, consequent = ${consequent.showDbg}") + matchScrutPat(scrutinee, pattern) + // Captures carry the scope crossings needed to resolve extracted bindings, + // but matching the value at runtime only needs the underlying reference. + val ref = scrutinee.withoutCaptures match + case ref: Term.Ref => ref + case ref: (Term.SimpleRef | Term.SelfRef) => ref.sym.ref().withLocOf(scrutinee) + case _ => lastWords("A pattern scrutinee must be a reference") + Head.Match(ref, pattern, consequent) /** Elaborate shorthand expressions. */ protected def shorthandSplit(tree: Tree)(using UnderCtx): Ctxl[SimpleSplit] = @@ -858,15 +1003,15 @@ extends Importer: pattern match case Block(Nil) => val recordPattern = Pattern.Record(Nil).withLocOf(pattern) - Head.Match(scrutinee(), recordPattern, innerSplit) ~: negative + mkMatch(scrutinee(), recordPattern, innerSplit) ~: negative case Block(trees) => trees.foldRight(negative): case (pattern, alternative) => - Head.Match(scrutinee(), this.pattern(pattern), innerSplit) ~: alternative + mkMatch(scrutinee(), this.pattern(pattern), innerSplit) ~: alternative case _ => val firstPattern = this.pattern(pattern) firstPattern.variables.report (ctx ++ firstPattern.variables.allocate).givenIn: - Head.Match(scrutinee(), firstPattern, innerSplit) ~: negative + mkMatch(scrutinee(), firstPattern, innerSplit) ~: negative /** Desugar a list of trees as a term split. The returned function takes a * function, which takes a `Ctx` and returns a `SimpleSplit` representing @@ -916,22 +1061,22 @@ extends Importer: // Interleaved-`let` bindings like `{ x is A then 0; let x = 1; ... }`. case LetLike(Keywrd(`let`), ident: Ident, S(rhsTree), N) => val symbol = VarSymbol(ident, erasedType = N) - val head = Head.Let(symbol, term(rhsTree)) + val head = mkSplitLet(symbol, term(rhsTree, Trm)) ((ctx + (ident.name -> symbol)), head ~: End) // Interleaved-`do` statements like `{ x is A then 0; do log(1); ... }`. case PrefixApp(Keywrd(`do`), rhsTree) => - (ctx, Head.Let(TempSymbol(N, erasedType = N, "unused"), term(rhsTree)) ~: End) + (ctx, mkSplitLet(TempSymbol(N, erasedType = N, "unused"), term(rhsTree, Trm)) ~: End) // Although the `else`-clause marks the end of the split, we cannot // stop and still have to elaborate the remaining trees. case PrefixApp(kwTree @ Keywrd(`else`), elseTree) => - (ctx, Else(term(elseTree))(S(new Keywrd(`else`).withLocOf(kwTree)))) + (ctx, Else(term(elseTree, Trm))(S(new Keywrd(`else`).withLocOf(kwTree)))) private def expandMatches(matchesTree: Ls[TT])(consequent: Ctxl[SimpleSplit]): Ctxl[SimpleSplit] = val z = (ctx, Ls[(Term, Pattern)]()) // Elaborate the term and the pattern in each match. val (innerCtx, matches) = matchesTree.foldLeft(z): case ((curCtx, matches), (scrutineeTree, patternTree)) => - val scrutinee = term(scrutineeTree)(using curCtx) + val scrutinee = term(scrutineeTree, Trm)(using curCtx) val pattern = this.pattern(patternTree)(using curCtx) pattern.variables.report val resCtx = curCtx ++ pattern.variables.allocate @@ -940,37 +1085,37 @@ extends Importer: val split = matches.foldLeft(consequent(using innerCtx)): case (innerSplit, (scrutinee, pattern)) => scrutinee.reference: scrutineeRef => - Head.Match(scrutineeRef(), pattern, innerSplit) ~: End + mkMatch(scrutineeRef(), pattern, innerSplit) ~: End split private def termBranch(t: Tree, mk: Term => Term): Ctxl[(Ctx, SimpleSplit)] = branch.appOrElse(t): case block: Block => (ctx, termSplit(block.desugStmts, mk)) - case lhs is rhs => (ctx, mk(term(lhs)).reference(patternBranch(_, rhs, identity))) + case lhs is rhs => (ctx, mk(term(lhs, Trm)).reference(patternBranch(_, rhs, identity))) // Several matches followed by `and`, `do`, or `then`. case matchesTree ~> consequent => val (coda, patternTree) :: matches = disaggregate(matchesTree) def innerSplit(using ctx: Ctx) = expandMatches(matches): consequent match case L(tree) => termSplit(Ls(tree), identity) - case R((kw, tree)) => Else(term(tree))(S(kw)) + case R((kw, tree)) => Else(term(tree, Trm))(S(kw)) val split = coda match case Under() => innerSplit - case coda => mk(term(coda)).reference: scrutinee => + case coda => mk(term(coda, Trm)).reference: scrutinee => val pattern = this.pattern(patternTree) val innerCtx = ctx ++ pattern.variables.allocate - Head.Match(scrutinee(), pattern, innerSplit(using innerCtx)) ~: End + mkMatch(scrutinee(), pattern, innerSplit(using innerCtx)) ~: End (ctx, split) // Handle splits on binary operators. case OpApp(lhs, ident: Ident, rhss) => - val op = term(ident) - val split = term(lhs).reference: lhs => + val op = term(ident, Trm) + val split = term(lhs, Trm).reference: lhs => val mk2 = (rhs: Term) => val args = Term.Tup(PlainFld(lhs()) :: PlainFld(rhs) :: Nil)(DummyTup) - Term.App(op, args)(Tree.DummyApp, N, FlowSymbol("β€Ήoperator-splitβ€Ί")) + app(op, args)(Tree.DummyApp, N, FlowSymbol("β€Ήoperator-splitβ€Ί")) termSplit(rhss, mk2 andThen mk) (ctx, split) case OpSplit(lhs, rhss) => - val split = mk(term(lhs)).reference: lhs => + val split = mk(term(lhs, Trm)).reference: lhs => val (_, splits) = rhss.foldLeft((ctx, Ls[SimpleSplit]())): case ((curCtx, splits), t) => operatorBranch(lhs, t)(using curCtx).mapSecond(_ :: splits) @@ -983,7 +1128,7 @@ extends Importer: private def operatorBranch(scrutinee: Reference, rhs: Tree): Ctxl[(Ctx, SimpleSplit)] = branch.appOrElse(rhs): rhsTree => - termBranch(rhsTree.splitOn(Trm(scrutinee())), identity) + termBranch(rhsTree.splitOn(Tree.Trm(scrutinee())), identity) private def patternBranch(scrutinee: Reference, t: Tree, mk: Tree => Tree): Ctxl[SimpleSplit] = t match case block: Block => @@ -1007,8 +1152,8 @@ extends Importer: consequentTree match case L(tree) => termSplit(Ls(tree), identity) - case R((kw, tree)) => Else(term(tree))(S(kw)) - Head.Match(scrutinee(), firstPattern, split) ~: End + case R((kw, tree)) => Else(term(tree, Trm))(S(kw)) + mkMatch(scrutinee(), firstPattern, split) ~: End case _ => error(msg"Unrecognized pattern split (${t.describe})." -> t.toLoc) Else(Term.Error().withLocOf(t))(N).withLocOf(t) // To inspect the source of errors. @@ -1018,10 +1163,13 @@ extends Importer: term match // If the term is already a reference, we can re-reference its symbol. case Term.Ref(symbol) => continuation(() => symbol.ref().withLocOf(term)) - // Otherwise, we need to create a temporary symbol holding the term. - case term: Term => - val symbol = TempSymbol(N, erasedType = N, "scrut") - Head.Let(symbol, term) ~: continuation(() => symbol.ref()) + // New references may carry lexical captures with no runtime evaluation. + case _ => term.withoutCaptures match + case _: Term.SimpleRef | _: Term.SelfRef => continuation(() => term) + // Member references can invoke getters; evaluate those only once. + case _ => + val symbol = TempSymbol(N, erasedType = N, "scrut") + mkSplitLet(symbol, term) ~: continuation(() => symbol.ref()) private type TT = (Tree, Tree) @@ -1058,32 +1206,109 @@ extends Importer: case test => () => acc((test, Tree.BoolLit(true))) go(tree, ::(_, Nil))() - def term(tree: Tree): Ctxl[Term] = - trace[Term](s"Elab term ${tree.showDbg}", r => s"~> $r"): + // Callers specify the interpretation at each elaboration entry point. Keeping + // it explicit (rather than implicit) prevents a target's interpretation from + // leaking into its value operands. + def term(tree: Tree, interp: Interpretation): Ctxl[Term] = + trace[Term](s"Elab term ${tree.showDbg}", r => s"~> ${r.showDbg}"): val unders = mutable.ArrayBuffer.empty[VarSymbol] given UnderCtx = new UnderCtx(S(unders)) - val st = subterm(tree) + val st = subterm(tree, interp) val params = unders.iterator.map: sym => Param.simple(sym) .toList if params.isEmpty then st else Term.Lam(PlainParamList(params)(N), st) - def subterm(tree: Tree): Ctxl[UnderCtx ?=> Term] = - trace[Term](s"Elab subterm ${tree.showDbg}", r => s"~> $r"): + private def app(lt: Term, rt: Term)(tree: Tree.App, typ: Opt[typing.Type], resSym: FlowSymbol): Term = + val res = new Term.App(lt, rt)(tree, typ, resSym) + if newResolution then listenTerm(lt)(shape => appShape(shape, rt, res)) + res + + private def assignment(lhs: Term, rhs: Term): Term.Assgn = + // Resolve the term interpretation of the l-value before lowering. The + // selected definition is recorded even when its value publishes no shape. + // Use this for synthesized assignments as well as source-level `set`. + if newResolution then + listenTerm(lhs)(_ => ()) + assignArrayElement(lhs, rhs) + assignVariable(lhs, rhs) + Term.Assgn(lhs, rhs) + + def ifLike(kw: Keyword.SplitLike, form: IfLikeForm, split: SimpleSplit, loc: Opt[Loc]): Term.IfLike = + val res = new Term.IfLike(kw, form, split).withLoc(loc) + if newResolution then + // log(s"Listening to if-like split ${res.showDbg}") + // Imperative branches are evaluated for effects; lowering discards their + // values and returns unit, including when a loop executes zero iterations. + if form.isImperative then pipeTerm(unit, res) + else split.results.foreach: trm => + pipeTerm(trm, res) + res + + + /** Request the reference's term interpretation in expression positions. + * Listeners can wait for forward definitions to complete. + */ + private def interpretRef(ref: Term, interp: Interpretation): Term = + if newResolution then interp match + case Trm => requireTerm(ref) + case Receiver => listenReceiver(ref)(_ => ()) + case Tpe => ref.typeInterpretation = S(typeResolution(ref)) + case _ => () + ref + + // Most recursive operands are terms. Transparent wrappers must explicitly + // forward their interpretation; class, pattern, and type operands specify theirs. + def subterm(tree: Tree, interp: Interpretation = Trm): Ctxl[UnderCtx ?=> Term] = + trace[Term](s"Elab subterm ${tree.showDbg}", r => s"~> ${r.showDbg}"): def error = Term.Error().withLocOf(tree) + def mkNew(cls: Term, args: Ls[Term], rft: Opt[ClassSymbol -> ObjBody])(typ: Opt[typing.Type]): Term.New = + val res = new Term.New(cls, args, rft)(FlowSymbol.neww(), typ).withLocOf(tree) + rft.foreach: (symbol, body) => + // An anonymous refinement is a concrete subclass too. Publish its declaration + // so override lookup, super references and implementation checks use the same graph. + // The written class and arguments occur outside the anonymous class's scope. + // Its synthetic extends clause crosses that boundary just like a named class. + def captured(term: Term): Term = if newResolution then Term.Capture(term, symbol) else term + val parent = new Term.New(captured(cls), args.map(captured), N)(FlowSymbol.neww(), typ).withLocOf(tree) + if newResolution then resolveNew(parent) + val definition = ClassDef.Plain(ctx.getOuter, syntax.Cls, symbol, + BlockMemberSymbol(symbol.nme, Nil), Nil, S(parent), body, N, Nil, Nil, N)(tree.toLoc) + symbol.defn = S(definition) + registerInheritance(definition) + if newResolution then resolveNew(res) + res + + def elaborateProjection(prefix: Term, cls: Term, name: Ident): Term = + // The qualifier denotes a class even when its source is a stored constructor. + // Resolve that interpretation on the qualifier itself, as for explicit `.class`. + val qualifier = new Term.NewSel(cls, new Ident("class").withLocOf(cls), N)(FlowSymbol.sel("class")).withLocOf(cls) + val res = new Term.NewSel(prefix, name, S(qualifier))(FlowSymbol.selProj(name.name)).withLocOf(tree) + newSel(res) + interpretRef(res, interp) + /** Fallback to a normal selection + application when label-specific handling does not apply. */ def mkNonLabelSelectionApp(tree: App, sel: Sel, args: Ls[Tree]): Term = val sym = FlowSymbol.app() val lt = subterm(sel) val rt = subterm(Tup(args)) - Term.App(lt, rt)(tree, N, sym) + app(lt, rt)(tree, N, sym) def elaborateSelection(tree: Sel): Term = - val preTrm = subterm(tree.prefix) - val sym = resolveField(tree.name, preTrm.symbol, tree.name) + // `.class` chooses the class overload before requesting a receiver value. + // Interpreting C as a receiver first would also select its companion. + val preTrm = subterm(tree.prefix, if newResolution && tree.name.name == "class" then Clss else Receiver) + // `source` has no runtime object. Recognize its statically named fields + // before constructing a runtime selection, in either resolution mode. + // Compare symbols so a user-defined binding named `source` stays ordinary. + val sym = if newResolution then preTrm.withoutCaptures match + case Term.MemberRef(bms) if bms === ctx.builtins.source.bms => + ctx.builtins.source.module.tree.definedSymbols.get(tree.name.name) + case _ => N + else resolveField(tree.name, preTrm.symbol, tree.name) if sym.contains(ctx.builtins.source.line) then val loc = tree.toLoc.getOrElse(???) val (line, _, _) = loc.origin.fph.getLineColAt(loc.spanStart) @@ -1093,11 +1318,15 @@ extends Importer: else if sym.contains(ctx.builtins.source.file) then val loc = tree.toLoc.getOrElse(???) Term.Lit(StrLit(loc.origin.fileName.toString)) + else if newResolution then + val res = new Term.NewSel(preTrm, tree.name, N)(FlowSymbol.sel(tree.name.name)).withLocOf(tree) + newSel(res) + interpretRef(res, interp) else Term.Sel(preTrm, tree.name)(sym, FlowSymbol.sel(tree.name.name), N, S(summon)) tree.desugared match - case Trm(term) => term + case Tree.Trm(term) => interpretRef(term, interp) case unt @ Unt() => unit.withLocOf(unt) case Bra(k, e) => k match @@ -1105,25 +1334,25 @@ extends Importer: case Curly => case _ => raise(ErrorReport(msg"Unsupported ${k.name} in this position" -> tree.toLoc :: Nil)) - term(e) // * not `subterm` as `e` could be a lambda shorthand + term(e, interp) // * not `subterm` as `e` could be a lambda shorthand case b: Block => ctx.nestLocal("β€Ήblockβ€Ί").givenIn: - block(b, hasResult = true)._1 match + block(b, hasResult = true, resultInterp = interp)._1 match case Term.Blk(Nil, res) => res case res => res case lit: Literal => Term.Lit(lit) case d: Def => - subterm(Block(d :: Unt() :: Nil)) + subterm(Block(d :: Unt() :: Nil), interp) case LetLike(kw @ Keywrd(`let`), lhs, rhso, S(bod)) => - subterm(Block(LetLike(kw, lhs, rhso, N) :: bod :: Nil)) + subterm(Block(LetLike(kw, lhs, rhso, N) :: bod :: Nil), interp) case LetLike(kw @ Keywrd(`let`), lhs, rhso, N) => raise(ErrorReport( msg"Expected a body for let bindings in expression position" -> tree.toLoc :: Nil)) block(LetLike(kw, lhs, rhso, N) :: Nil, hasResult = true)._1 case LetLike(Keywrd(`set`), lhs, S(rhs), N) => - Term.Assgn(subterm(lhs), subterm(rhs)) + assignment(subterm(lhs), subterm(rhs)) case LetLike(Keywrd(`set`), lhs, N, N) => raise(ErrorReport( msg"Expected a right-hand side for this assignment" -> @@ -1140,16 +1369,16 @@ extends Importer: val lt = subterm(lhs) val sym = TempSymbol(S(lt), erasedType = N, "old") Blk( - LetDecl(sym, Nil)(tree.toLoc) :: DefineVar(sym, lt)(tree.toLoc) :: Nil, Term.Try(Blk( - Term.Assgn(lt, subterm(rhs)) :: Nil, - subterm(bod), - ), Term.Assgn(lt, sym.ref()))) + LetDecl(sym, Nil)(tree.toLoc) :: defineVar(sym, lt)(tree.toLoc) :: Nil, Term.Try(Blk( + assignment(lt, subterm(rhs)) :: Nil, + subterm(bod, interp), + ), assignment(lt, sym.ref()))) case LetLike(Keywrd(Keyword.`set`), _, N, S(_)) => raise: ErrorReport(msg"Expected a right-hand side for this assignment" -> tree.toLoc :: Nil) error case TryFinally(tryBody, finallyBody) => - Term.Try(subterm(tryBody), subterm(finallyBody)) + Term.Try(subterm(tryBody, interp), subterm(finallyBody)) case (hd @ Hndl(id: Ident, c, Block(sts_), S(bod))) => ctx.nest(OuterCtx.LambdaOrHandlerBlock).givenIn: val sym = VarSymbol(id, erasedType = N) @@ -1187,52 +1416,64 @@ extends Importer: val (cp, p) = c match case App(c, Tup(params)) => - (subterm(c), params.map(subterm(_))) + (subterm(c, Clss), params.map(subterm(_))) case c => - (subterm(c), Nil) + (subterm(c, Clss), Nil) (ctx + (id.name -> sym)).givenIn: - Term.Handle(sym, cp, p, derivedClsSym, tds, subterm(bod)) + Term.Handle(sym, cp, p, derivedClsSym, tds, subterm(bod, interp)) case h: Hndl => raise(ErrorReport( msg"Unsupported handle binding shape" -> h.toLoc :: Nil)) error case id @ Ident("this") => - ctx.getOuter match - case S(sym) => sym.ref(id) + (if newResolution then ctx.getReceiver else ctx.getOuter.map(Ctx.RefElem(_))) match + case S(elem) => elem.ref(id) case N => raise: ErrorReport(msg"Cannot use 'this' outside of an object scope" -> tree.toLoc :: Nil) error - case id @ Ident(name) => ident(id).getOrElse: + case Ident("super") => + raise(ErrorReport(msg"'super' must be followed by a member selection" -> tree.toLoc :: Nil)) + error + case id @ Ident(name) => ident(id, interp).map(interpretRef(_, interp)).getOrElse: raise(ErrorReport(msg"Name not found: $name" -> id.toLoc :: Nil)) error case TyApp(lhs, targs) => - Term.TyApp(subterm(lhs), targs.map { - case Modified(Keywrd(Keyword.`in`), arg) => Term.WildcardTy(S(subterm(arg)), N) - case Modified(Keywrd(Keyword.`out`), arg) => Term.WildcardTy(N, S(subterm(arg))) - case Tup(Modified(Keywrd(Keyword.`in`), arg1) :: Modified(Keywrd(Keyword.`out`), arg2) :: Nil) => - Term.WildcardTy(S(subterm(arg1)), S(subterm(arg2))) - case arg => subterm(arg) + // The whole type application owns a by-name invocation's binder group. + // Do not observe its underlying reference before its arguments are available. + val baseInterp = interp match + case Trm | Receiver | Specialization => Specialization + case _ => interp + val result = Term.TyApp(subterm(lhs, baseInterp), targs.map { + case Modified(kw @ Keywrd(Keyword.`in`), arg) => Term.WildcardTy(S(subterm(arg, Tpe)), N).mkLocWith(kw) + case Modified(kw @ Keywrd(Keyword.`out`), arg) => Term.WildcardTy(N, S(subterm(arg, Tpe))).mkLocWith(kw) + case Tup(Modified(in @ Keywrd(Keyword.`in`), arg1) :: Modified(out @ Keywrd(Keyword.`out`), arg2) :: Nil) => + Term.WildcardTy(S(subterm(arg1, Tpe)), S(subterm(arg2, Tpe))).mkLocWith(in, out) + case arg => subterm(arg, Tpe) })(N).withLocOf(tree) + // A term specialization must validate its arguments even when unused. + // Type and constructor interpretations have their own argument checks. + if newResolution && interp == Trm then listenTerm(result)(_ => ()) + result case InfixApp(TyTup(tvs), Keywrd(Keyword.`->`), body) => val boundVars = mutable.HashMap.empty[Str, VarSymbol] - def genSym(id: Tree.Ident, erasedType: Opt[ErasedValueType]) = - val sym = VarSymbol(id, erasedType) + def genSym(id: Tree.Ident) = + val sym = VarSymbol(id, erasedType = N) sym.decl = S(TyParam(FldFlags.empty, N, sym)(id.toLoc)) // TODO vce boundVars += id.name -> sym sym val syms = (tvs.collect: - case id: Tree.Ident => (genSym(id, erasedType = N), N, N) - case InfixApp(id: Tree.Ident, Keywrd(Keyword.`extends`), ub) => (genSym(id, erasedType = N), S(ub), N) - case InfixApp(id: Tree.Ident, Keywrd(Keyword.`restricts`), lb) => (genSym(id, erasedType = N), N, S(lb)) + case id: Tree.Ident => (genSym(id), N, N) + case InfixApp(id: Tree.Ident, Keywrd(Keyword.`extends`), ub) => (genSym(id), S(ub), N) + case InfixApp(id: Tree.Ident, Keywrd(Keyword.`restricts`), lb) => (genSym(id), N, S(lb)) case InfixApp(InfixApp(id: Tree.Ident, Keywrd(Keyword.`extends`), ub), Keywrd(Keyword.`restricts`), lb) => - (genSym(id, erasedType = N), S(ub), S(lb)) + (genSym(id), S(ub), S(lb)) ) val outer = (tvs.collect: - case Outer(S(name: Tree.Ident)) => genSym(name, erasedType = N) - case Outer(N) => genSym(Tree.Ident("outer"), erasedType = N) + case Outer(S(name: Tree.Ident)) => genSym(name) + case Outer(N) => genSym(Tree.Ident("outer")) ) match case ot :: Nil => S(ot) case _ :: rest => @@ -1247,49 +1488,56 @@ extends Importer: given Ctx = ctx ++ boundVars val bds = syms.map: case (sym, ub, lb) => - QuantVar(sym, ub.map(ub => subterm(ub)), lb.map(lb => subterm(lb))) - Term.Forall(bds, outer, subterm(body)) + QuantVar(sym, ub.map(ub => subterm(ub, Tpe)), lb.map(lb => subterm(lb, Tpe))) + Term.Forall(bds, outer, subterm(body, Tpe)) case InfixApp(lhs, Keywrd(Keyword.`->`), Effectful(eff, rhs)) => - Term.FunTy(subterm(lhs), subterm(rhs), S(subterm(eff))) + Term.FunTy(subterm(lhs, Tpe), subterm(rhs, Tpe), S(subterm(eff, Tpe))) case InfixApp(lhs, Keywrd(Keyword.`->`), rhs) => - Term.FunTy(subterm(lhs), subterm(rhs), N) + Term.FunTy(subterm(lhs, Tpe), subterm(rhs, Tpe), N) case InfixApp(lhs, Keywrd(Keyword.`=>`), rhs) => lhs match case Tup(_) => ctx.nest(OuterCtx.LambdaOrHandlerBlock).givenIn: val (syms, nestCtx) = funParams(lhs) - Term.Lam(syms, term(rhs)(using nestCtx)) + Term.Lam(syms, term(rhs, Trm)(using nestCtx)) case TyTup(tys) => val constraints = tys.flatMap(maybeConstraint) - val body = term(rhs) + val body = term(rhs, interp) Term.Constrained(constraints, body) case _ => lastWords(s"Unexpected lambda parameter shape: $lhs") + case Keywrd(Keyword.`dyn`) => Term.DynTy().withLocOf(tree) case InfixApp(lhs, Keywrd(Keyword.`as`), rhs) => - Term.Asc(subterm(lhs), subterm(rhs)) + val body = subterm(lhs, interp) + val sign = subterm(rhs, Tpe) + if newResolution then checkAscription(body, sign) + Term.Asc(body, sign) case InfixApp(lhs, Keywrd(Keyword.`:`), rhs) => - block(tree :: Nil, hasResult = false)._1 + block(Block(tree :: Nil), hasResult = false, resultInterp = interp)._1 case PrefixApp(kw @ Keywrd(Keyword.`not`), rhs) => - Term.App(State.builtinOpsMap("!").ref(new Ident("not").withLocOf(kw)), Term.Tup( + app(State.builtinOpsMap("!").ref(new Ident("not").withLocOf(kw)), Term.Tup( PlainFld(subterm(rhs)) :: Nil)(DummyTup))(DummyApp, N, FlowSymbol("not-app")) case tree @ InfixApp(lhs, Keywrd(Keyword.`is` | Keyword.`and` | Keyword.`or`), rhs) => - Term.IfLike(Keyword.`if`, IfLikeForm.ReturningIf, shorthandSplit(tree)) + ifLike(Keyword.`if`, IfLikeForm.ReturningIf, shorthandSplit(tree), tree.toLoc) case InfixApp(Sel(pre, idn: Ident), Keywrd(Keyword.`#`), idp: Ident) => - val c = subterm(idn) - val f = c.symbol.flatMap(_.asCls) match - case S(cls: ClassSymbol) => - cls.tree.allSymbols.get(idp.name) match - case S(fld: MemberSymbol) => S(fld) + val c = subterm(idn, Clss) + if newResolution then + elaborateProjection(subterm(pre), c, idp) + else + val f = c.symbol.flatMap(_.asCls) match + case S(cls: ClassSymbol) => + cls.tree.allSymbols.get(idp.name) match + case S(fld: MemberSymbol) => S(fld) + case _ => + raise(ErrorReport(msg"Class '${cls.nme}' does not contain member '${idp.name}'." -> idp.toLoc :: Nil)) + N case _ => - raise(ErrorReport(msg"Class '${cls.nme}' does not contain member '${idp.name}'." -> idp.toLoc :: Nil)) + raise(ErrorReport(msg"Identifier `${idn.name}` does not name a known class symbol." -> idn.toLoc :: Nil)) N - case _ => - raise(ErrorReport(msg"Identifier `${idn.name}` does not name a known class symbol." -> idn.toLoc :: Nil)) - N - Term.SelProj(subterm(pre), c, idp)(f, FlowSymbol.selProj(idp.name), N, S(summon)) + Term.SelProj(subterm(pre), c, idp)(f, FlowSymbol.selProj(idp.name), N, S(summon)) case InfixApp(lhs, op @ Keywrd(Keyword.`|`), rhs) => - Term.CompType(subterm(lhs), subterm(rhs), true)//.withLocOf(tree) + Term.CompType(subterm(lhs, Tpe), subterm(rhs, Tpe), true)//.withLocOf(tree) case InfixApp(lhs, op @ Keywrd(Keyword.`&`), rhs) => - Term.CompType(subterm(lhs), subterm(rhs), false)//.withLocOf(tree) + Term.CompType(subterm(lhs, Tpe), subterm(rhs, Tpe), false)//.withLocOf(tree) case InfixApp(lhs, kw, rhs) => raise: ErrorReport(msg"Unexpected infix use of keyword '${kw.name}' here" -> tree.toLoc :: Nil) @@ -1299,14 +1547,14 @@ extends Importer: case App(Ident("!"), Tup(rhs :: Nil)) => Term.Deref(subterm(rhs)) case App(Ident("~"), Tup(rhs :: Nil)) => - Term.Neg(subterm(rhs)) + Term.Neg(subterm(rhs, Tpe)).withLocOf(tree) case PrefixApp(Keywrd(Keyword.`|` | Keyword.`&`), rhs) => - subterm(rhs) + subterm(rhs, interp) case tree @ OpSplit(lhs, rhss) => val tree = rhss.foldLeft(lhs): case (acc, rhs) => rhs.splitOn(acc) - subterm(tree) + subterm(tree, interp) case tree @ App(sel @ Sel(labelId @ Ident(labelName), nme @ Ident("break")), Tup(args)) => val value = args match case Nil => N @@ -1353,13 +1601,13 @@ extends Importer: val sym = FlowSymbol.app() val lt = subterm(lhs) val rt = subterm(rhs) - Term.App(lt, rt)(tree, N, sym) + app(lt, rt)(tree, N, sym) case tree @ OpApp(lhs, op, rhss) => val sym = FlowSymbol.app() val lt = subterm(lhs) val ot = subterm(op) val rts = rhss.map(r => PlainFld(subterm(r))) - Term.App(ot, Term.Tup(PlainFld(lt) :: rts)(DummyTup))( + app(ot, Term.Tup(PlainFld(lt) :: rts)(DummyTup))( DummyApp, N, sym) case SynthSel(pre, nme) => val preTrm = subterm(pre) @@ -1396,11 +1644,20 @@ extends Importer: mkNonLocalContinueInvocation(binding, nme) case LabelLookup.NotFound => elaborateSelection(sel) + case sel @ Sel(id @ Ident("super"), nme) => + (ctx.getOuter, ctx.getReceiver) match + case (S(owner: (ClassSymbol | ModuleOrObjectSymbol)), S(receiver)) => + val res = new Term.Super(receiver.ref(id), owner, nme)(FlowSymbol.sel(nme.name)).withLocOf(sel) + resolveSuper(res) + interpretRef(res, interp) + case _ => + raise(ErrorReport(msg"Cannot use 'super' outside of an object scope" -> sel.toLoc :: Nil)) + error case sel @ Sel(pre, nme) => elaborateSelection(sel) case MemberProj(ct, nme) => - val c = subterm(ct) - val f = c.symbol.flatMap(_.asCls) match + val c = subterm(ct, Clss) + val f = if newResolution then N else c.symbol.flatMap(_.asCls) match case S(cls: ClassSymbol) => cls.tree.allSymbols.get(nme.name) match case S(fld: MemberSymbol) => S(fld) @@ -1424,16 +1681,19 @@ extends Importer: )(N) val rs = FlowSymbol.app() Term.Lam(ps, - Term.App(Term.SelProj(self.ref(), c, nme)(f, FlowSymbol.selProj(nme.name), N, S(summon)), args.ref())( + app( + if newResolution then elaborateProjection(self.ref(), c, nme) + else Term.SelProj(self.ref(), c, nme)(f, FlowSymbol.selProj(nme.name), N, S(summon)), + args.ref())( App(nme, Tup(Nil)) // FIXME , N, rs) ) case tree @ Tup(TermDef(Ins, f, N) :: fs) => - Term.CtxTup((f :: fs).map(fld(_)))(tree) + Term.CtxTup((f :: fs).map(fld(_, interp)))(tree) case Modified(kw @ Keywrd(Keyword.`mut`), tree @ Tup(fields)) => - Term.Mut(Term.Tup(fields.map(fld(_)))(tree)).mkLocWith(kw) + Term.Mut(Term.Tup(fields.map(fld(_, interp)))(tree)).mkLocWith(kw) case tree @ Tup(fields) => - Term.Tup(fields.map(fld(_)))(tree) + Term.Tup(fields.map(fld(_, interp)))(tree) case DynamicNew(Apps(c, args)) => val (mut, c2) = c match @@ -1451,21 +1711,21 @@ extends Importer: clsSym -> // TODO integrate context inherited from cls // TODO make context with var symbols for class parameters - ObjBody(block(rft, hasResult = false)._1) + ObjBody(block(rft, hasResult = false, resultInterp = Trm)._1) body match case S(Apps(c, args)) => val (mut, c2) = c match case Modified(Keywrd(Keyword.`mut`), c) => (true, c) case c => (false, c) - val inner = new Term.New( - subterm(c2), // * Note: we'll catch bad `new` targets during type checking + val inner = mkNew( + subterm(c2, Clss), // * Note: we'll catch bad `new` targets during type checking args.map(subterm(_)), bodo - )(N).withLocOf(tree) + )(N) if mut then Term.Mut(inner) else inner case N => val objectRef = ctx.builtins.Object.bms.get.ref(Ident("Object")) - Term.New(objectRef, Nil, bodo)(N).withLocOf(tree) + mkNew(objectRef, Nil, bodo)(N) // case _ => // raise(ErrorReport(msg"Illegal new expression." -> tree.toLoc :: Nil)) @@ -1479,7 +1739,7 @@ extends Importer: (org.startLineNum + org.fph.getLineColAt(loc.spanStart)._1).toString) case N => ("β€Ήunknownβ€Ί", "β€Ήunknownβ€Ί") val elsPart = els.fold(PrefixApp(Keywrd(Keyword.`else`), Tree.Trm( - State.runtimeSymbol.ref().selNoSym("assertFail") + State.runtimeSymbol.ref().synthSel(State.assertFailSymbol) .app(Term.Lit(StrLit(fl)), Term.Lit(StrLit(ln))) )))(PrefixApp.apply.tupled) subterm: @@ -1519,7 +1779,7 @@ extends Importer: case PrefixApp(kw @ Keywrd(Keyword.`yield` | Keyword.`yield*`), body) => if ctx.inGenerator then val synthIdent = new Tree.Ident(kw.kw.name).withLocOf(kw) - Term.App(ident(synthIdent).get, Term.Tup(PlainFld(subterm(body)) :: Nil)(DummyTup))(DummyApp, N, FlowSymbol("yield")) + app(ident(synthIdent, Trm).get, Term.Tup(PlainFld(subterm(body)) :: Nil)(DummyTup))(DummyApp, N, FlowSymbol("yield")) else raise: ErrorReport(msg"Yield expressions are not allowed in this context." -> tree.toLoc :: Nil) @@ -1549,7 +1809,7 @@ extends Importer: raise(ErrorReport(msg"Illegal outer binding." -> tree.toLoc :: Nil)) error case Outer(N) => ctx.get("outer") match - case S(sym) => sym.ref(Ident("outer")) + case S(sym) => interpretRef(sym.ref(Ident("outer")), interp) case N => raise(ErrorReport(msg"Illegal outer reference." -> tree.toLoc :: Nil)) error @@ -1565,7 +1825,7 @@ extends Importer: raise(ErrorReport(msg"Illegal type declaration in term position." -> tree.toLoc :: Nil)) error case Modified(Keywrd(Keyword.`mut`), body: Block) => - blockOrRcd(body, hasResult = true) match + blockOrRcd(body, hasResult = true, resultInterp = interp) match case (Blk(Nil, Term.UnitVal()), ctx) => Rcd(mut = true, Nil).withLocOf(body) case (blk: Blk, ctx) => @@ -1589,13 +1849,13 @@ extends Importer: val res = go(acc, lhs :: Nil) val sym = FlowSymbol.app() val fl = Fld(FldFlags.empty, res, N) - val app = Term.App(subterm(f), Term.Tup( - fl :: args.map(fld))(tup))(ap, N, sym) - go(app, trees) + val ap_ = app(subterm(f), Term.Tup( + fl :: args.map(fld(_, Trm)))(tup))(ap, N, sym) + go(ap_, trees) case (ap @ App(f, tup @ Tup(args))) :: trees => val sym = FlowSymbol.app() - go(Term.App(subterm(f), - Term.Tup(Fld(FldFlags.empty, acc, N) :: args.map(fld))(tup) + go(app(subterm(f), + Term.Tup(Fld(FldFlags.empty, acc, N) :: args.map(fld(_, Trm)))(tup) )(ap, N, sym), trees) case Block(sts) :: trees => go(acc, sts ::: trees) @@ -1608,13 +1868,13 @@ extends Importer: raise(ErrorReport(msg"Illegal position for 'open' statement." -> tree.toLoc :: Nil)) error case OpenIn(op, body) => - subterm(Block(Open(op) :: body :: Nil)) - case DynAccess(obj, rhs) => + subterm(Block(Open(op) :: body :: Nil), interp) + case DynAccess(obj, rhs, dynamic) => rhs match - case Bra(bk @ (Round | Square), fld) => Term.DynSel(subterm(obj), subterm(fld), bk is Square) - case fld: Literal => Term.DynSel(subterm(obj), subterm(fld), false) + case Bra(bk @ (Round | Square), fld) => Term.DynSel(subterm(obj), subterm(fld), bk is Square, !dynamic) + case fld: Literal => Term.DynSel(subterm(obj), subterm(fld), false, false) case id: Ident => - Term.DynSel(subterm(obj), Term.Lit(StrLit(id.name)).withLocOf(id), false) + Term.DynSel(subterm(obj), Term.Lit(StrLit(id.name)).withLocOf(id), false, false) case _ => raise(ErrorReport(msg"Illegal dynamic field access selector (${rhs.describe})." -> tree.toLoc :: Nil)) error @@ -1631,8 +1891,8 @@ extends Importer: unds += sym sym.ref() case Annotated(lhs, rhs) => - annot(lhs).fold(subterm(rhs))(ann => - Term.Annotated(ann, subterm(rhs))) + annot(lhs).fold(subterm(rhs, interp))(ann => + Term.Annotated(ann, subterm(rhs, interp))) case Keywrd(kw) => raise(ErrorReport(msg"Unexpected keyword '${kw.name}' in this position." -> tree.toLoc :: Nil)) error @@ -1648,20 +1908,20 @@ extends Importer: raise(ErrorReport(msg"Unsupported term in this position (${tree.describe})." -> tree.toLoc :: Nil)) error - def arg(tree: Tree)(using UnderCtx): Ctxl[Term] = tree match - case u: Under => subterm(tree) // Note: currently `f(a, _, c)` is treated the same as `f of a, _, c` - case _ => term(tree) - def fld(tree: Tree)(using UnderCtx): Ctxl[Elem] = tree match + def arg(tree: Tree, interp: Interpretation)(using UnderCtx): Ctxl[Term] = tree match + case u: Under => subterm(tree, interp) // Note: currently `f(a, _, c)` is treated the same as `f of a, _, c` + case _ => term(tree, interp) + def fld(tree: Tree, interp: Interpretation)(using UnderCtx): Ctxl[Elem] = tree match case InfixApp(id: Ident, Keywrd(Keyword.`:`), rhs) => - Fld(FldFlags.empty, Term.Lit(StrLit(id.name).withLocOf(id)), S(arg(rhs))) + Fld(FldFlags.empty, Term.Lit(StrLit(id.name).withLocOf(id)), S(arg(rhs, interp))) case InfixApp(lhs, Keywrd(Keyword.`:`), rhs) => - Fld(FldFlags.empty, term(lhs), S(arg(rhs))) + Fld(FldFlags.empty, term(lhs, interp), S(arg(rhs, interp))) case Spread(Keywrd(Keyword.`..`), S(trm)) => - Spd(SpreadKind.Lazy, arg(trm)) + Spd(SpreadKind.Lazy, arg(trm, interp)) case Spread(Keywrd(Keyword.`...`), S(trm)) => - Spd(SpreadKind.Eager, arg(trm)) + Spd(SpreadKind.Eager, arg(trm, interp)) case _ => - val t = arg(tree) + val t = arg(tree, interp) var flags = FldFlags.empty Fld(flags, t, N) @@ -1670,10 +1930,10 @@ extends Importer: def block(sts: Ls[Tree], hasResult: Bool)(using UnderCtx): Ctxl[(Blk, Ctx)] = - block(new Block(sts), hasResult) + block(new Block(sts), hasResult, Trm) - def block(blk: Block, hasResult: Bool)(using UnderCtx): Ctxl[(Blk, Ctx)] = - blockOrRcd(blk, hasResult) match + def block(blk: Block, hasResult: Bool, resultInterp: Interpretation)(using UnderCtx): Ctxl[(Blk, Ctx)] = + blockOrRcd(blk, hasResult, resultInterp) match case (blk: Blk, ctx) => (blk, ctx) case (rcd: Rcd, ctx) => (Blk(Nil, rcd), ctx) @@ -1695,13 +1955,16 @@ extends Importer: // * for these, elaborate with `hasResult = false`, which uses `undefined` as the result // * when there is no other result available. This is fine since the value is never used. // * These useless trailing `undefined`s are then removed by `Lowering`. - def blockOrRcd(blk: Block, hasResult: Bool)(using UnderCtx) + def blockOrRcd(blk: Block, hasResult: Bool, resultInterp: Interpretation)(using UnderCtx) : Ctxl[(Blk | Rcd, Ctx)] = trace[(Blk | Rcd, Ctx)]( - pre = s"Elab block ${blk.desugStmts.toString.truncate(100, "[...]")} ${ctx.outer}", r => s"~> ${r._1}" + pre = s"Elab block ${blk.desugStmts.toString.truncate(100, "[...]")} ${ctx.outer}", r => s"~> ${r._1.showDbg}" ): val members = blk.definedSymbols.toMap + val fieldInterpretation = resultInterp match + case Tpe => Tpe + case _ => Trm val newSignatureTrees = mutable.Map.empty[Str, Tree] // * Store trees of signatures // * Check for double/incompatible definitions and declarations @@ -1735,9 +1998,26 @@ extends Importer: log(s"Processing overloadings for '$name'") defns.iterator.foreach: defn => if defn.k > k then - if !supportedOverloadings(k -> defn.k) then raise: + // Only function definitions with parameters create a fresh companion object. + // A val initializer or parameterless function can yield an existing value; + // attaching a class to that value would mutate it (and may fail if frozen). + val functionCompanion = (k is Fun) && mainDefn.paramLists.nonEmpty + val instanceMethodCompanion = functionCompanion && ctx.outer.inner.exists(_.isInstanceOf[ClassSymbol]) + val bareClassOverload = newResolution && functionCompanion && !instanceMethodCompanion && (defn match + case td: TypeDef => (td.k is Cls) && td.paramLists.isEmpty + case _ => false) + val functionModuleOverload = newResolution && functionCompanion && !instanceMethodCompanion && (defn.k is Mod) + if !supportedOverloadings(k -> defn.k) && !bareClassOverload && !functionModuleOverload then raise: ErrorReport: - if notYetSupportedOverloadings(k -> defn.k) + if ((defn.k is Cls) || (defn.k is Mod)) && instanceMethodCompanion then + msg"Not yet supported: ${defn.k.desc} companion '$name' defined as an instance method" -> mainDefn.toLoc + :: msg"An instance method is shared by all instances, but its companion is created per instance" -> defn.toLoc + :: Nil + else if ((defn.k is Cls) || (defn.k is Mod)) && ((k is Fun) || k.isInstanceOf[Val]) && !functionCompanion then + msg"A ${defn.k.desc} companion must be a function with a parameter list" -> mainDefn.toLoc + :: msg"Arbitrary values cannot be overloaded with ${defn.k.desc} '$name'" -> defn.toLoc + :: Nil + else if notYetSupportedOverloadings(k -> defn.k) then msg"Not yet supported: overloading of ${k.desc} '$name'" -> mainDefn.toLoc :: msg"with ${defn.k.desc} of the same name" -> defn.toLoc :: Nil @@ -1814,6 +2094,9 @@ extends Importer: base match case baseId: Ident => ctx.get(baseId.name) match + case S(baseElem) if newResolution && importedTrees.isEmpty => + ctx.copy(wildcardOpens = Ctx.OpenSource(baseElem)(baseId) :: ctx.wildcardOpens).givenIn: + go(sts, Nil, acc)(N) case S(baseElem) => val importedNames = importedTrees match case N => // "wilcard" open @@ -1828,8 +2111,10 @@ extends Importer: case id: Ident => if ctx.env.contains(id.name) then raise(WarningReport(msg"Imported name '${id.name}' is shadowed by a name already defined in the same scope" -> id.toLoc :: Nil)) - val sym = resolveField(id, baseElem.symbol, id) + val sym = if newResolution then N else resolveField(id, baseElem.symbol, id) val e = Ctx.SelElem(baseElem, id.name, sym, isImport = true) + // Validate the named selection even when its binding is unused. + if newResolution then e.ref(id) id.name -> e :: Nil case t => raise(ErrorReport(msg"Illegal 'open' statement element." -> t.toLoc :: Nil)) @@ -1870,31 +2155,37 @@ extends Importer: case Spread(Keywrd(Keyword.`...`), S(body)) :: sts => reportUnusedAnnotations - go(sts, Nil, RcdSpread(term(body)) :: acc)(N) + go(sts, Nil, RcdSpread(term(body, fieldInterpretation)) :: acc)(N) case InfixApp(lhs, Keywrd(Keyword.`:`), rhs) :: sts => var newCtx = ctx val (rlhs, rhs_t) = rhs match - case _: Under => (lhs, subterm(rhs)) + case _: Under => (lhs, subterm(rhs, fieldInterpretation)) case _ => lhs match case Apps(base, tups) => val rrhs = tups.foldRight(rhs): InfixApp(_, Keywrd(Keyword.`=>`), _) - (base, term(rrhs)) + (base, term(rrhs, fieldInterpretation)) val newAcc = rlhs match + case id: Ident if fieldInterpretation == Tpe => + // Record type fields declare interfaces, not local value bindings. + // Keep their types directly so lookup never follows initializer flow. + RcdField.signature(Term.Lit(StrLit(id.name)).withLocOf(id), rhs_t) :: acc case id: Ident => val sym = new VarSymbol(id, erasedType = N) newCtx += id.name -> sym RcdField(Term.Lit(StrLit(id.name)).withLocOf(id), sym.ref(id)) - :: DefineVar(sym, rhs_t)(Loc(rlhs, rhs)) + :: defineVar(sym, rhs_t)(Loc(rlhs, rhs)) :: LetDecl(sym, annotations)(id.toLoc) :: acc case lit: Literal => reportUnusedAnnotations - RcdField(Term.Lit(lit).withLocOf(lit), rhs_t) :: acc + val field = Term.Lit(lit).withLocOf(lit) + (if fieldInterpretation == Tpe then RcdField.signature(field, rhs_t) + else RcdField(field, rhs_t)) :: acc case Bra(Round, inner) => reportUnusedAnnotations - RcdField(term(inner), rhs_t) :: acc + RcdField(term(inner, Trm), rhs_t) :: acc case _ => raise(ErrorReport(msg"Unexpected record key shape." -> rlhs.toLoc :: Nil)) RcdField(Term.Error().withLocOf(rlhs), rhs_t) :: acc @@ -1912,7 +2203,7 @@ extends Importer: case S(rhs) => val rrhs = tups.foldRight(rhs): InfixApp(_, Keywrd(Keyword.`=>`), _) - mkLetBinding(sym, term(rrhs), annotations)(statementLoc) reverse_::: acc + mkLetBinding(sym, term(rrhs, Trm), annotations)(statementLoc) reverse_::: acc case N => if tups.nonEmpty then raise(ErrorReport(msg"Expected a right-hand side for let bindings with parameters" -> hd.toLoc :: Nil)) @@ -1926,11 +2217,11 @@ extends Importer: reportUnusedAnnotations lhs match case id: Ident => - val r = term(rhs) + val r = term(rhs, Trm) ctx.get(id.name) match case S(elem) => elem.symbol match - case S(sym: (LocalSymbol | TermSymbol)) => go(sts, Nil, DefineVar(sym, r)(statementLoc) :: acc)(N) + case S(sym: (LocalSymbol | TermSymbol)) => go(sts, Nil, defineVar(sym, r)(statementLoc) :: acc)(N) case S(sym) => raise(ErrorReport(msg"Symbol '${id.name}' is not a variable and cannot be reassigned" -> id.toLoc :: Nil)) go(sts, Nil, Term.Error().withLocOf(id) :: acc)(N) @@ -1972,27 +2263,36 @@ extends Importer: :: illegalMemberNameTail return go(sts, Nil, acc)(N) val isMethod = owner.exists(_.isInstanceOf[ClassSymbol]) - val tdf = ctx.nest(OuterCtx.NonReturnContext).givenIn: newCtx ?=> + + val tsym = TermSymbol(k, owner, id) // TODO? + + val tdf = ctx.nest(OuterCtx.NonReturnContext(S(tsym))).givenIn: newCtx ?=> // * Add type parameters to context val (tps, newCtx1) = td.typeParams match case S(t) => val (tps, ctx) = typeParams(t) (S(tps), ctx) case N => (N, ctx) + if newResolution then tps.foreach(ps => registerTypeParameters(tsym, ps.map(_.sym))) // * Add parameters to context var newCtx = newCtx1 val pss = td.paramLists.map: ps => val (res, newCtx2) = funParams(ps)(using newCtx) newCtx = newCtx2 res + // * Elaborate signature val st = td.annotatedResultType.orElse(newSignatureTrees.get(id.name)) // FIXME: may elaborate external sig twice!! val s = st.map: // unwrap possible module modifier // e.g, `fun f: module M` // ^^^^^^ - case TypeDef(Mod, st, N) => term(st)(using newCtx) - case st => term(st)(using newCtx) + case TypeDef(Mod, st, N) => term(st, Tpe)(using newCtx) + case st => term(st, Tpe)(using newCtx) + + if newResolution && td.annotatedResultType.isEmpty then + s.foreach(registerParameterSignature(pss, _)) + val body: Opt[Term] = rhs match case N => N case _ if ctx.mode is Mode.Light => S(Term.Missing) @@ -2002,8 +2302,8 @@ extends Importer: val hasAsyncAnnotation = annotations.exists(_.isInstanceOf[Annot.Async]) if pss.isEmpty && hasGeneratorAnnotation then raise(ErrorReport(msg"Generators are not supported on functions without a parameter list" -> statementLoc :: Nil)) - newCtx.nest(OuterCtx.Function(nonLocalRetHandler)(pss.nonEmpty && hasGeneratorAnnotation, hasAsyncAnnotation)).givenIn: newCtx ?=> - val b = term(rhs)(using newCtx) + newCtx.nest(OuterCtx.Function(nonLocalRetHandler, S(tsym))(pss.nonEmpty && hasGeneratorAnnotation, hasAsyncAnnotation)).givenIn: newCtx ?=> + val b = term(rhs, Trm)(using newCtx) if nonLocalRetHandler.directRefs.isEmpty then b else mkEffectHandleAbortive( nonLocalRetHandler, @@ -2022,81 +2322,13 @@ extends Importer: case _ => Modulefulness.none - /** Splits a signature's arrow chain into the parameter lists it describes and the type it returns. - * Yields `N` if the signature is not an arrow or if some parameter list's arity cannot be read. - */ - def splitSignature(sign: Term): Opt[(Ls[Ls[Opt[ErasedValueType]]], Term)] = - def paramsOf(lhs: Term): Opt[Ls[Opt[ErasedValueType]]] = lhs match - // * A spread parameter leaves the list's arity unknown, so the signature is left unsplit. - case Term.Tup(fields) => - val noParams: Opt[Ls[Opt[ErasedValueType]]] = S(Nil) - fields.foldRight(noParams): (fld, acc) => - (fld, acc) match - case (Fld(_, t, _), S(rest)) => S(ErasedType.eraseSign(t) :: rest) - case _ => N - // * An unparenthesized type is a single parameter. - case single => S(ErasedType.eraseSign(single) :: Nil) - sign match - case Term.Forall(_, _, body) => splitSignature(body) - case Term.FunTy(lhs, rhs, _) => paramsOf(lhs).map: ps => - splitSignature(rhs) match - case S((rest, ret)) => (ps :: rest, ret) - case N => (ps :: Nil, rhs) - case _ => N - - // * A signature's arrows are the definition's own parameter lists when a reference to it is not - // * auto-invoked. - // * - // * - A `fun` writing no parameter lists is a getter, so `fun bar: A -> Int` yields - // * the arrow itself; - // * - A `declare`d `fun` becomes a `globalThis` selection, so its arrows are its parameters. - val sigShape: Opt[(Ls[Ls[Opt[ErasedValueType]]], Term)] = - if (k is syntax.Fun) && pss.isEmpty && Annot.declareModifierOf(annotations).isDefined - then s.flatMap(splitSignature) - else N - - // * A moduleful signature (`fun f: module M`) denotes the module itself. - val retTpe = mfn.msym match - case S(msym) => S(ErasedType.ValueLike(rsc = S(false), msym)) - case N => s.flatMap: s => - // * A function that inherits a signature with leading arrows consumes those arrows as its own - // * parameter lists. The exception is a `declare`d function, whose arrows are always its own - // * parameters (see `sigShape`). - def stripSignatureParams(s: Term, n: Int): Term = (s, n) match - case (Term.Forall(_, _, body), _) => stripSignatureParams(body, n) - case (Term.FunTy(_, rhs, _), n) if n > 0 => stripSignatureParams(rhs, n - 1) - case _ => s - val resultSign: Term = - if (k is syntax.Fun) && td.annotatedResultType.isEmpty - then stripSignatureParams(s, pss.length) - else sigShape.map(_._2).getOrElse(s) - ErasedType.eraseSign(resultSign) - val erasedTpe = k match - case syntax.Fun => - // * A `declare`d function's parameter lists are derived from its signature when it writes none. - val paramLists = sigShape match - case S((ps, _)) => ps - case N => pss.map(_.params.map(_.sym.erasedType)) - // * An erased type must describe what a definition is *compiled to*, and a paramless `fun` is - // * compiled in two different ways: - // * - // * - As a class-like member, it becomes either a getter method or a `globalThis` selection depending - // * on if it is `declare`d or not - neither denotes a function value, so the erased type is its - // * result; - // * - At block level, it is lowered to a function with an implicit empty parameter list which every - // * reference auto-invokes, so the erased type will carry that parameter list. - val isCompiledAsGetter = owner.isDefined || Annot.declareModifierOf(annotations).isDefined - val physicalParamLists = - if paramLists.isEmpty && !isCompiledAsGetter then Nil :: Nil else paramLists - if physicalParamLists.isEmpty then retTpe - else S(ErasedType.FuncRef(rsc = S(false), physicalParamLists, retTpe)) - case _: syntax.Val => retTpe - case _ => N - val tsym = TermSymbol(k, owner, id, erasedType = erasedTpe) // TODO? + // Retain the source signature's meaning; Erasure decides which arrows are physical parameters. + if newResolution then s.foreach(registerSignature) val tdf = TermDefinition(k, sym, tsym, pss, tps, s, body, - TermDefFlags.empty.copy(isMethod = isMethod), mfn, annotations, N)(statementLoc) - tsym.defn = S(tdf) + TermDefFlags.empty.copy(isMethod = isMethod, hasResultAnnotation = td.annotatedResultType.isDefined), mfn, annotations, N)(statementLoc) sym.tsym = S(tsym) + tsym.defn = S(tdf) + if newResolution then checkDeclaredResult(tdf) tdf go(sts, Nil, tdf :: acc)(N) @@ -2108,7 +2340,7 @@ extends Importer: val owner = ctx.outer.inner softTODO((k is Als) || (k is Cls) || (k is Mod) || (k is Obj) || (k is Pat), k.desc + " not yet supported") - val body = td.reft ++ td.withPart match + val body = td.bodies match case Nil => N case hd :: Nil => S(hd) case hd :: hd2 :: tl => @@ -2134,10 +2366,12 @@ extends Importer: val sym = members.getOrElse(nme.name, lastWords(s"Symbol not found: ${nme.name}")) val outerCtx = ctx + rstate.lexicalTypeBinders(td.symbol) = ctx.typeBinders + rstate.lexicalBoundaries(td.symbol) = ctx.resolutionBoundaries var newCtx = S(td.symbol).collectFirst: case s: InnerSymbol => s - .fold(ctx.nest(OuterCtx.NonReturnContext))(ctx.nestInner(_)) + .fold(ctx.nest(OuterCtx.NonReturnContext(S(td.symbol))))(ctx.nestInner(_)) val tps = td.typeParams match case S(ts) => @@ -2160,6 +2394,7 @@ extends Importer: case N => Nil newCtx ++= tps.map(tp => tp.sym.name -> tp.sym) // TODO: correct ++? + if newResolution && (k isnt Als) then registerTypeParameters(td.symbol, tps.map(_.sym)) val isDataClass = annotations.exists: case Annot.Modifier(Keyword.`data`) => true @@ -2192,7 +2427,10 @@ extends Importer: case s: InnerSymbol => S(s) case _: TypeAliasSymbol => die - if p.flags.isVal || isDataClass + val isPublicField = p.flags.isVal || isDataClass + if isPublicField + // TODO: + || newResolution // TODO: rm `|| newResolution` then val k = if p.flags.mut then MutVal else ImmutVal val fsym = BlockMemberSymbol(p.sym.nme, Nil) @@ -2205,23 +2443,26 @@ extends Importer: fsym, tsym, Nil, N, N, - S(p.sym.ref()), - TermDefFlags.empty.copy(isMethod = (k is Cls)), + if newResolution then + S(Term.Capture(Term.SimpleRef(p.sym)(Ident(p.sym.name)), tsym)) + else S(p.sym.ref()) + , + TermDefFlags.empty.copy(isMethod = (k is Cls)), // FIXME?! p.modulefulness, - Nil, + if isPublicField then Nil else Annot.Private :: Nil, N, )(p.toLoc) assert(p.fldSym.isEmpty) p.fldSym = S(fsym) fsym.tsym = S(tsym) tsym.defn = S(fdef) - p.sym.erasedType.foreach(tsym.populateErasedType) + fsym.complete() fdef :: Nil else - val psym = TermSymbol(LetBind, owner, p.sym.id, erasedType = p.sym.erasedType) + val psym = TermSymbol(LetBind, owner, p.sym.id) psym.sourceAliases = p.sym.sourceAliases val decl = LetDecl(psym, Nil)(p.toLoc) // TODO: never use term symbols on LetDecl LHS - val defn = DefineVar(psym, p.sym.ref())(p.toLoc) + val defn = defineVar(psym, p.sym.ref())(p.toLoc) p.fldSym = S(psym) decl :: defn :: Nil @@ -2231,10 +2472,10 @@ extends Importer: val owner = td.symbol match case s: InnerSymbol => S(s) case _: TypeAliasSymbol => die - val psym = TermSymbol(LetBind, owner, p.sym.id, erasedType = p.sym.erasedType) + val psym = TermSymbol(LetBind, owner, p.sym.id) psym.sourceAliases = p.sym.sourceAliases val decl = LetDecl(psym, Nil)(p.toLoc) - val defn = DefineVar(psym, p.sym.ref())(p.toLoc) + val defn = defineVar(psym, p.sym.ref())(p.toLoc) p.fldSym = S(psym) decl :: defn :: Nil @@ -2268,7 +2509,7 @@ extends Importer: def mkBody(extraParams: Ls[ParamList])(using Ctx) = withFields(extraParams): body match case N | S(Error()) => (new Blk(Nil, Term.Lit(UnitLit(false))), ctx) - case S(b: Block) => block(b, hasResult = false) + case S(b: Block) => block(b, hasResult = false, resultInterp = Trm) case S(t) => raise(ErrorReport( msg"Illegal body of ${k.desc} definition (should be a block; found ${t.describe})." -> t.toLoc :: Nil)) @@ -2283,7 +2524,7 @@ extends Importer: assert(body.isEmpty) val d = given Ctx = newCtx - semantics.TypeDef(alsSym, sym, tps, rhs.map(term(_)), N, annotations)(statementLoc) + semantics.TypeDef(alsSym, sym, tps, rhs.map(term(_, Tpe)), N, annotations)(statementLoc) alsSym.defn = S(d) d case Pat => @@ -2318,12 +2559,13 @@ extends Importer: raise(ErrorReport(msg"Pattern definitions must have a body." -> statementLoc :: Nil)) Tree.Under() // Elaborate the pattern body with the pattern parameters. - val pat = pattern(rhs)(using ctx ++ patternParams.iterator.map(p => p.sym.name -> p.sym)) + val pat = pattern(rhs, extractionParams.map(p => p.sym.name -> p.sym).toMap)( + using ctx ++ patternParams.iterator.map(p => p.sym.name -> p.sym)) // Report all invalid variables we found in the top-level pattern. pat.variables.report - // Note that the remaining variables have not been bound to any - // `VarSymbol` yet. Thus, we need to pair them with the extraction - // parameters. We only report warnings for unbound variables + // Pair the remaining variables with the extraction parameters. + // Guarded aliases already use these symbols so their elaborated + // references remain valid. We only report warnings for unbound variables // because they are harmless. Variables used in guard conditions // (from `where` clauses) are not considered useless. val guardedNames = pat.varNamesUsedInGuards @@ -2340,6 +2582,7 @@ extends Importer: val pd = PatternDef(owner, patSym, sym, tps, allParams, patternParams, extractionParams, pat, annotations)(statementLoc) patSym.defn = S(pd) + if newResolution then checkPatternDefinition(pd) pd case k: (Mod.type | Obj.type) => val modSym = td.symbol.asInstanceOf[ModuleOrObjectSymbol] // TODO: improve `asInstanceOf` @@ -2438,15 +2681,48 @@ extends Importer: go(sts, annotations, acc)(N) case (st: Tree) :: sts => // TODO reject plain term statements? Currently, `(1, 2)` is allowed to elaborate (tho it should be rejected in type checking later) - val res = annotations.foldLeft(term(st)): + val interpretation = if sts.isEmpty then resultInterp else Trm + val res = annotations.foldLeft(term(st, interpretation)): case (acc, ann) => Term.Annotated(ann, acc) sts match case Nil => (mkBlk(acc, S(res), hasResult), ctx) case _ => go(sts, Nil, res :: acc)(N) end go - ctx.withMembers(members).givenIn: + val res = ctx.withMembers(members).givenIn: go(blk.desugStmts, Nil, Nil)(N) + + // if newResolution then + members.valuesIterator.foreach(_.complete()) + members.valuesIterator.foreach: bms => + bms.symbols.foreach: + case sym: (ClassLikeSymbol & InnerSymbol) => + sym.defn.foreach: d => // erroneous code may not have a defn even after the BMS is completed + registerInheritance(d) + // A declaration supplies no fresh function object to hold a companion. + // Generated constructor functions also have no source body, but lowering creates them. + // Foreign class-like declarations produce no companion initialization at all. + if d.hasDeclareModifier.isEmpty then + bms.asTrm.filterNot(_.isInstanceOf[ClassCtorSymbol]).flatMap(_.defn).foreach: term => + if term.body.isEmpty || term.hasDeclareModifier.isDefined then raise: + ErrorReport: + msg"A term declaration cannot have an implemented ${sym.describeKind} companion" -> term.toLoc + :: msg"Companion '${sym.nme}' is defined here" -> d.toLoc + :: Nil + d.ext match + case S(ext) => + listenTerm(ext): esh => + val sh = BaseShape(d, S(esh)) // TODO actually use applied shape providing generics? + sym.notifyShapeListeners(sh) + case N => + val sh = BaseShape(d, N) // TODO actually use applied shape providing generics? + sym.notifyShapeListeners(sh) + case sym: InnerSymbol => + // println(">>> "+sym) + // TODO: patterns + case _ => () + + res def mkBlk(acc: Ls[Statement], res: Opt[Term], hasResult: Bool): Blk | Rcd = @@ -2464,7 +2740,7 @@ extends Importer: def newOf(td: TypeDef): Ctxl[Opt[Term.New]] = td.extension match - case S(ext) => S(term(ProperNew(S(ext), N))) + case S(ext) => S(term(ProperNew(S(ext), N), Trm)) case N => N match case S(n: Term.New) => S(n) @@ -2489,17 +2765,16 @@ extends Importer: new Ident(base).withLocOf(id) -> (id.name :: Nil) case N => id -> Nil - val sig = sign.map(term(_)) + val sig = sign.map(term(_, Tpe)) val mfn = Modulefulness.ofSign(sig)(Mod in modifiers) - // * As for return signatures, a moduleful parameter (`module m: M`) denotes the module itself, which - // * `eraseSign` would miss by resolving the name through `asTpe`. - val erasedTpe = mfn.msym match - case S(msym) => S(ErasedType.ValueLike(rsc = S(false), msym)) - case N => sig.flatMap(ErasedType.eraseSign) - val sym = VarSymbol(canonicalId, erasedType = erasedTpe) + if newResolution then sig.foreach(registerSignature) + val sym = VarSymbol(canonicalId) sym.sourceAliases = aliases val p = Param(flg, sym, sig, mfn)(t.toLoc) sym.decl = S(p) + if newResolution then sig.foreach: sign => + listenTypeInstances(sign): shape => + if sym.currentShapes.add(shape) then sym.notifyShapeListeners(shape) (p, spd, aliases) def funParams(t: Tree): Ctxl[(ParamList, Ctx)] = @@ -2514,8 +2789,8 @@ extends Importer: /** Elaborate a subtyping constraint. */ def constraint(lhs: Tree, op: "<:<" | ">:>", rhs: Tree): Ctxl[SubConstraint] = - val l = term(lhs) - val r = term(rhs) + val l = term(lhs, Tpe) + val r = term(rhs, Tpe) val dir = op match case "<:<" => SubDir.Sub case ">:>" => SubDir.Sup @@ -2569,14 +2844,21 @@ extends Importer: msg"Expected a parameter list (a tuple of parameters), but found ${t.describe}" -> t.toLoc :: Nil (ParamList(ParamListFlags.empty, Nil, N)(t.toLoc), ctx) - def ident(id: Ident)(using Ctx): Ctxl[Opt[Term]] = ctx.get(id.name) match - case S(elem) => S(elem.ref(id)) - case N => - state.builtinOpsMap.get(id.name) match - case S(bi) => S(bi.ref(id)) - case N => N + // Imported legacy signatures are also consumed by the new resolver. Keep + // their lexical type captures even though legacy term lookup ignores scopes. + def ident(id: Ident, interp: Interpretation)(using Ctx): Ctxl[Opt[Term]] = + ctx.get(id.name, newResolution || interp == Tpe) match + case candidate @ (S(_: Ctx.WildcardElem) | N) => + // Primitive operators are implicit bindings outside the Ctx environments; + // like explicit bindings, they take precedence over wildcard sources. + state.builtinOpsMap.get(id.name) match + case S(bi) => S(bi.ref(id)) + case N => candidate.map(_.ref(id)) + case S(elem) => S(elem.ref(id)) - def pattern(t: Tree): Ctxl[Pattern] = + def pattern(t: Tree): Ctxl[Pattern] = pattern(t, Map.empty) + + private def pattern(t: Tree, extractionBindings: Map[Str, VarSymbol]): Ctxl[Pattern] = import ucs.{Ctor, unapply, error}, ucs.extractors.*, Keyword.*, Pattern.*, InvalidReason.* /** String range bounds must be single characters. */ def isInvalidStringBounds(lo: StrLit, hi: StrLit)(using Raise): Bool = @@ -2587,7 +2869,7 @@ extends Importer: ds += msg"The upper bound of character ranges must be a single character." -> hi.toLoc if ds.nonEmpty then error(ds.toSeq*) ds.nonEmpty - /** Resolve an identifier. We need to perform a very preliminary check to + /** Legacy pattern lookup. We need to perform a very preliminary check to * determine whether this identifier refers to a pattern, a class, an * object, or creates a new binding. * @@ -2609,10 +2891,17 @@ extends Importer: state.builtinOpsMap.get(id.name) match case S(bi) => S(bi.ref(id)) case N => N + def constructor(target: Term, arguments: Opt[Ls[Pattern]], source: Tree): Pattern.Constructor = + val res = new Constructor(target, arguments) + if newResolution then res.withLocOf(source) + constructorPattern(res) + res /** Elaborate arrow patterns like `p => t`. Meanwhile, report all invalid * variables we found in `p`. */ def arrow(lhs: Tree, rhs: Tree): Ctxl[Pattern] = - val pattern = go(lhs) + // A transform consumes its inner bindings; they are not extraction + // parameters of the surrounding definition, even if their names coincide. + val pattern = this.pattern(lhs) // The symbol allocated here will be bound in `split` to the values // destructed from the scrutinee. val variables = pattern.variables.allocate @@ -2632,7 +2921,7 @@ extends Importer: (name -> parameterSymbol, symbol -> parameterSymbol) .toList.unzip pattern.variables.report // Report all invalid variables we found in `pattern`. - Transform(pattern, correspondence, term(rhs)(using ctx ++ contextEntries)) + Transform(pattern, correspondence, term(rhs, Trm)(using ctx ++ contextEntries)) /** Elaborate tuple patterns like `[p1, p2, ...ps, pn]`. */ def tuple(ts: Ls[Tree]): Ctxl[Pattern.Tuple] = // We are accumulating two components: the leading patterns, the spread @@ -2678,7 +2967,7 @@ extends Importer: t match // Annotated patterns like `@compile P`. case Tree.Annotated(annotation, target) => - go(target).annotate(term(annotation), t.toLoc) + go(target).annotate(term(annotation, Trm), t.toLoc) // Brackets. case Bra(BracketKind.Round | BracketKind.Curly, t) => go(t) // Tuple patterns like `[p1, p2, ...ps, pn]`. @@ -2698,8 +2987,8 @@ extends Importer: case InfixApp(lhs, Keywrd(op @ (Keyword.`|` | Keyword.`&`)), rhs) => Composition(op is Keyword.`|`, go(lhs), go(rhs)) // Constructor patterns with pattern arguments and arguments. - case App(ctor: Ctor, Tup(argTrees)) => - Constructor(term(ctor), S(argTrees.map(go(_)))) + case App(ctor: Ctor, Tup(argTrees)) => // TODO: rm this weird `: Ctor` guard + constructor(term(ctor, Ptrn), S(argTrees.map(go(_))), t) // `[p1, p2, ...ps, pn] => term`: All patterns are in the `TyTup`. case (lhs: TyTup) `=>` rhs => arrow(lhs, rhs) // `pattern => term`: Note that `pattern` is wrapped in a `Tup`. @@ -2709,19 +2998,23 @@ extends Importer: case lhs :: Nil => arrow(lhs, rhs) case _ :: _ | Nil => ??? // TODO: this case reached by, eg, `pattern p = () => Unit` case p as q => q match - // `p as id` is elaborated into alias if `id` is not a constructor. + // New resolution distinguishes bindings from constructors by capitalization. + case id: Ident if newResolution => + if id.name.isUncapitalized then go(p) binds id + else Chain(go(p), constructor(term(id, Ptrn), N, id)) + // Legacy resolution classifies the name by eager lookup. case id: Ident => ident(id) match case S(target) if target.symbol.exists(_.isInstanceOf[VarSymbol]) => // If the target is a variable, we should shadow it. This check is // probably insufficient as there are more cases. go(p) binds id - case S(target) => Chain(go(p), Constructor(target, N)) + case S(target) => Chain(go(p), constructor(target, N, id)) case N => go(p) binds id // Fallback to alias. // `p as q` where `q` is not an identifier is elaborated into chain. case _: Tree => Chain(go(p), go(q)) case p where t => val q = go(p) - Guarded(q, term(t)(using ctx ++ q.variables.allocate)) + Guarded(q, term(t, Trm)(using ctx ++ q.variables.allocate(extractionBindings))) case Under() => Pattern.Wildcard().withLocOf(t) // Singleton blocks like `{1}`. case Block(p :: Nil) => go(p) @@ -2746,16 +3039,17 @@ extends Importer: // pattern translation. case OpApp(lhs, Ident("~"), rhs :: Nil) => Pattern.Concatenation(go(lhs), go(rhs)) // Constructor patterns can be written in the infix form. - case OpApp(lhs, op, rhs :: Nil) => Pattern.Constructor(term(op), S(Ls(go(lhs), go(rhs)))) + case OpApp(lhs, op, rhs :: Nil) => constructor(term(op, Ptrn), S(Ls(go(lhs), go(rhs))), t) // Constructor patterns without arguments case id @ Ident(name) if name.isUncapitalized => Variable(id) + case id: Ident if newResolution => constructor(term(id, Ptrn), N, id) case id @ Ident(name) => ident(id) match - case S(target) => Constructor(target, N) + case S(target) => constructor(target, N, id) case N => raise: ErrorReport(msg"Pattern name not found: ${id.name}." -> id.toLoc :: Nil) Pattern.Wildcard() - case sel: (SynthSel | Sel) => Constructor(term(sel), N) + case sel: (SynthSel | Sel) => constructor(term(sel, Ptrn), N, sel) case _: Tree => raise(ErrorReport(msg"Unrecognized pattern (${t.describe})." -> t.toLoc :: Nil)) Pattern.Wildcard() @@ -2778,16 +3072,30 @@ extends Importer: msg"Expected a type parameter list (a tuple of identifiers), but found ${t.describe}" -> t.toLoc :: Nil (Nil, ctx) - def importFrom(sts: Block): Ctxl[(Blk, Ctx)] = + def importFrom(sts: Block, exports: Ls[BlockMemberSymbol]): Ctxl[(Blk, Ctx)] = given UnderCtx = new UnderCtx(N) - val (res, newCtx) = block(sts, hasResult = false) + val (res, newCtx) = block(sts, hasResult = false, resultInterp = Trm) // TODO handle name clashes + if newResolution then + InterfaceExposure(this).check(exports, Nil) + rstate.completeBlock(res) (res, newCtx) def topLevel(sts: Block): Ctxl[(Blk, Ctx)] = given UnderCtx = new UnderCtx(N) - val (res, ctx) = block(sts, hasResult = false) + val (res, ctx) = block(sts, hasResult = false, resultInterp = Trm) computeVariances(res) + if newResolution then + if config.language.strictResolution then + val exports = res.stats.collect: + case d: Definition if InterfaceExposure.isPublic(d) => d.bsym + val values = res.stats.collect: + case DefineVar(sym: LocalVarSymbol, rhs) => Term.SimpleRef(sym)(new Ident(sym.nme).withLocOf(rhs)) + InterfaceExposure(this).check(exports, res.res :: values) + else + // Explicit annotations still check callbacks whose signatures they hide. + InterfaceExposure(this).check(Nil, Nil) + rstate.completeBlock(res) (res, ctx) def computeVariances(s: Statement): Unit = diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Importer.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Importer.scala index 8aa5213b93..bacb192593 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Importer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Importer.scala @@ -14,12 +14,12 @@ import Elaborator.* import hkmc2.syntax.LetBind -class Importer: +class Importer extends NewResolver: self: Elaborator => import tl.* - def importPath(path: Str, alias: Opt[syntax.Tree.Ident])(loc: Opt[Loc])(using cfg: Config): Import = + def importPath(path: Str, alias: Opt[syntax.Tree.Ident])(loc: Opt[Loc])(using cfg: Config, rs: NewResolverState): Import = // log(s"pwd: ${os.pwd}") // log(s"wd: ${wd}") @@ -42,6 +42,7 @@ class Importer: file.ext match case "mjs" | "js" => + if newResolution then sym.currentShapes.add(DynShape()) Import(sym, file.toString, file)(loc) case "mls" => @@ -64,6 +65,13 @@ class Importer: val sym: VarSymbol | BlockMemberSymbol = alias.fold(importedSym): alias => VarSymbol(alias, erasedType = N) + // An import alias denotes the imported value, just like a local + // binding. Publish its shapes so selection and opens can follow it. + if newResolution then sym match + case local: VarSymbol => + pipeTerm(Term.MemberRef(importedSym)(local.id, FlowSymbol.memSym(importedSym)), local) + case _: BlockMemberSymbol => () + val jsFile = file.up / io.RelPath(file.baseName + ".mjs") Import(sym, jsFile.toString, jsFile)(loc) @@ -73,5 +81,6 @@ class Importer: Import(sym, path, file)(loc) else + if newResolution then sym.currentShapes.add(DynShape()) Import(sym, path, file)(loc) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Inheritance.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Inheritance.scala new file mode 100644 index 0000000000..daa8bfa601 --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Inheritance.scala @@ -0,0 +1,84 @@ +package hkmc2 +package semantics + +import scala.collection.mutable +import hkmc2.utils.*, shorthands.* +import syntax.Keyword +import hkmc2.Message.MessageContext +import codegen.Erasure + +/** Declaration edges are separate from callable shapes: selecting a base declaration must + * not replace the implementation flow inferred for an ordinary virtual selection. + */ +private[semantics] final class OverrideLinks extends Host[TermSymbol]: + def showDbg(using DebugPrinter): Str = "overridden members" + def members(using Erasure): Ls[TermSymbol] = shapes.toList + +object Inheritance: + def hasModifier(d: Definition, keyword: Keyword.Modifier): Bool = d.annotations.exists: + case Annot.Modifier(kw) => kw == keyword + case _ => false + + def isAbstract(d: TermDefinition): Bool = + d.body.isEmpty && !isExternal(d) && !d.tsym.isInstanceOf[ClassCtorSymbol] + + /** A foreign declaration also supplies the implementation of its nested declarations. */ + def isExternal(d: Definition): Bool = d.hasDeclareModifier.nonEmpty || (d match + case td: TermDefinition => td.owner.exists(_.asDefnSym.defn.exists(isExternal)) + case cd: ClassLikeDef => cd.owner.exists(_.asDefnSym.defn.exists(isExternal)) + case _ => false) + + def isOpen(d: TermDefinition): Bool = d.body.isEmpty || hasModifier(d, Keyword.`open`) + + /** Symbol states identify compilation units, including all blocks of a worksheet. Source + * locations cannot establish this boundary: generated classes may have no source location, + * and imported aliases must retain the defining class's unit rather than the alias's unit. + */ + def validateParent(child: ClassLikeDef, parent: ClassLikeDef, site: Term.New)(using Erasure, Raise): Unit = + if parent.isInstanceOf[ClassDef] && (parent.sym.getState isnt child.sym.getState) && + !hasModifier(parent, Keyword.`open`) && !hasModifier(parent, Keyword.`abstract`) then + raise(ErrorReport( + msg"Cannot extend sealed class '${parent.sym.nme}' outside its compilation unit" -> site.toLoc :: + (msg"Class '${parent.sym.nme}' is defined here; declare it 'open' or 'abstract' to allow external subclasses" -> parent.toLoc) :: Nil, + source = Diagnostic.Source.Compilation)) + + /** An ancestor may itself be a target (one receiver overrides a member, another inherits it). + * Discard less-specific common ancestors, so a chain of overrides is not an ambiguity. + */ + def commonMember(targets: Ls[DefinitionSymbol[?]])(using Erasure): Opt[DefinitionSymbol[?]] = + def ancestors(sym: DefinitionSymbol[?]): Set[DefinitionSymbol[?]] = + val seen = mutable.Set.empty[DefinitionSymbol[?]] + def visit(current: DefinitionSymbol[?]): Unit = if seen.add(current) then + current.overriddenMembers.foreach(visit) + visit(sym) + seen.toSet + targets.distinct match + case Nil => N + case target :: Nil => S(target) + case first :: rest => + val common = rest.foldLeft(ancestors(first))((acc, sym) => acc intersect ancestors(sym)) + common.filterNot(base => common.exists(other => (other isnt base) && ancestors(other)(base))).toList match + case target :: Nil => S(target) + case _ => N + + /** Check only completed edges; an empty list during resolution can still acquire a parent. */ + def validateMember(td: TermDefinition)(using Erasure, Raise): Unit = + val bases = td.tsym.overriddenMembers + def fail(message: Message, base: Opt[TermSymbol]): Unit = raise(ErrorReport( + (message -> td.toLoc) :: base.toList.map(sym => msg"Inherited member '${sym.nme}' is defined here" -> sym.toLoc), + source = Diagnostic.Source.Compilation)) + if hasModifier(td, Keyword.`override`) && bases.isEmpty then + fail(msg"Member '${td.tsym.nme}' is marked 'override' but overrides no inherited member", N) + bases.foreach: base => + base.defn.foreach: inherited => + if !isOpen(inherited) then + fail(msg"Cannot override non-open member '${base.nme}'", S(base)) + else if inherited.body.nonEmpty && !hasModifier(td, Keyword.`override`) then + fail(msg"Overriding member '${base.nme}' requires the 'override' modifier", S(base)) + if td.tsym.isPrivate then + fail(msg"Overriding member '${base.nme}' cannot be private", S(base)) + // Getter evaluation and method selection have different effects and calling conventions. + // A shared target symbol must never cause a getter read to be treated as a pure method value. + if !isExternal(td) && (td.k != inherited.k || + td.params.nonEmpty != inherited.params.nonEmpty) then + fail(msg"Overriding member '${base.nme}' must preserve whether the member is a field, getter, or method", S(base)) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/InterfaceExposure.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/InterfaceExposure.scala new file mode 100644 index 0000000000..dcd47838f6 --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/InterfaceExposure.scala @@ -0,0 +1,211 @@ +package hkmc2 +package semantics + +import scala.collection.mutable +import hkmc2.utils.*, shorthands.* +import Term.* +import hkmc2.Message.MessageContext +import NewResolverState.Listener + +object InterfaceExposure: + final case class Input(shape: TermShape, state: NewResolverState)(val provenance: ShapeProvenance) + + def isPublic(definition: Definition): Bool = + !definition.annotations.contains(Annot.Private) && (definition match + case td: TermDefinition => (td.k isnt syntax.LetBind) || td.annotations.contains(Annot.Modifier(syntax.Keyword.`public`)(N)) + case _: TypeDef => false + case _ => true) + +/** Check the inputs admitted by the exposed interface before resolution is sealed. + * Existing flow listeners propagate external inputs; temporary observers discover + * escaping values incrementally. Calling a private helper does not expose it, but + * returning that helper (possibly inside a record or tuple) does. + */ +final class InterfaceExposure(resolver: NewResolver)(using NewResolverState, TL): + private given Elaborator.State = rstate.owner + private val work = mutable.Queue.empty[() => Unit] + private val detach = mutable.ArrayBuffer.empty[() => Unit] + private val watched = mutable.Set.empty[Any] + private val exposed = mutable.Set.empty[TermShape] + private val flows = mutable.Map.empty[BlockMemberSymbol, FlowSymbol] + + /** Forwarding edges belong to the inference graph. Only the final observer + * captures this pass's queue and visited sets, and it is detached on exit. + */ + private def watch(key: Any)(connect: Listener => Unit)(receive: Listener)(using state: NewResolverState): Unit = + if watched.add((key, state)) then + val host = new TermShapeHost + connect(host.publish) + detach += host.inferenceHost.observe: shape => + val current = rstate + work.enqueue(() => receive(shape)(using current)) + + private type Path = ShapeProvenance + + private def term(value: Term, marks: Ls[Marks], path: Path)(using NewResolverState): Unit = + watch((new Identity(value), marks))(resolver.listenTerm(value)): shape => + emit(shape.exit(marks), path) + + private def emit(shape: TermShape | NoShape, path: Path)(using NewResolverState): Unit = shape match + case shape: TermShape if exposed.add(shape) => + val current = rstate + work.enqueue(() => value(shape, path)(using current)) + case _ => () + + private def source(symbol: BlockMemberSymbol): MemberRef = + val flow = flows.getOrElseUpdate(symbol, FlowSymbol("exposed")) + MemberRef(symbol)(new syntax.Tree.Ident(symbol.nme).withLocOf(symbol), flow) + + private def member(symbol: BlockMemberSymbol, marks: Ls[Marks], path: Path)(using NewResolverState): Unit = + if (symbol.getState is rstate.owner) && (symbol.asModOrObj.isDefined || symbol.asTrm.isDefined || symbol.asCls.isDefined) then + val ref = source(symbol) + watch((symbol, marks))(resolver.fromBMS(symbol, ref.resSym, marks, _, ref, _ => (), false))(emit(_, path)) + // Both overloads are public: selecting a module member must not hide the + // function's inputs, and exposing a function must not hide module members. + if symbol.asModOrObj.isDefined && symbol.asTrm.isDefined then + watch((symbol, marks, true))(resolver.fromBMS(symbol, ref.resSym, marks, _, ref, _ => (), true))(emit(_, path)) + // A term companion can hide the constructor in term position, but clients + // still have access to the class through `new` and class projections. + if symbol.asModOrObj.isDefined || symbol.asTrm.exists(!_.isInstanceOf[ClassCtorSymbol]) then + symbol.asCls.flatMap(_.defn).foreach: cls => + val resolver = this.resolver + watch((cls.sym, marks))(listener => resolver.listenExt(cls, ext => + DefnShape(cls, ext).exit(ExitMark(ResolutionBoundary(cls.sym), S(ref.resSym), NoMarks)).exit(marks) match + case shape: TermShape => listener(shape) + case NoShape => ()))(emit(_, path)) + + private def parameter(param: Param, marks: Ls[Marks], path: Path)(using NewResolverState): Unit = + val ref = SimpleRef(param.sym)(param.sym.id) + val unknown = UnknownValueShape(ref, fromPublicInterface = true)(path.via( + msg"Parameter '${param.sym.nme}' admits values of unknown shape." -> param.toLoc)) + resolver.parameterSignature(param) match + case N => resolver.constrainParameter(param, unknown.enter(marks), marks) + case S(tpe) => unknown.enter(marks) match + case value: TermShape => resolver.exposeTypeHoles(tpe.instantiate(rstate.instances), value, marks) + case NoShape => () + + private def parameters(lists: Ls[(ParamList, Ls[Marks])], path: Path)(using NewResolverState): Unit = + lists.foreach: (params, marks) => + params.params.foreach(parameter(_, marks, path)) + params.restParam.foreach: rest => + val ref = SimpleRef(rest.sym)(rest.sym.id) + val unknown = UnknownValueShape(ref, fromPublicInterface = true)(path.via( + msg"Rest parameter '${rest.sym.nme}' admits elements of unknown shape." -> rest.toLoc)) + val tuple = TupleShape(ref, TupleShape.Unknown(ref, TupleShape.ValueField(unknown, Nil)) :: Nil)(resolver) + resolver.constrainParameter(rest, tuple.enter(marks), marks) + + private def members(cls: ClassLikeDef, marks: Ls[Marks], path: Path)(using NewResolverState): Unit = + cls.body.blk.stats.foreach: + case d: Definition if InterfaceExposure.isPublic(d) => member(d.bsym, marks, path.via(msg"Member '${d.bsym.nme}' is accessible here." -> d.toLoc)) + case _ => () + cls.ext.foreach: parent => + term(parent, marks, path.via(msg"This parent contributes inherited members." -> parent.toLoc)) + + private def result(body: Term, sign: Opt[Term], marks: Ls[Marks], path: Path)(using NewResolverState): Unit = sign match + case N => + watch((new Identity(body), marks, "result"))(resolver.listenResult(body)): shape => + emit(shape.exit(marks), path.via(msg"This value is returned here." -> body.toLoc)) + case S(sign) => + // Check returned implementations against the declared calling interface. + // Its parameter types constrain returned closures instead of unrestricted + // unknowns, and consumers see only the annotated result. + val declared = resolver.declaredType(resolver.typeResolution(sign), Map.empty).instantiate(rstate.instances) + watch((new Identity(body), declared, marks))(resolver.listenResult(body)): shape => + resolver.constrainFunction(declared, shape, marks) + watch((declared, marks))(resolver.listenTypeInstances(declared)): shape => + emit(shape.exit(marks), path) + + private def value(shape: TermShape, path: Path)(using NewResolverState): Unit = valueIn(shape, path, rstate.instances) + + private def valueIn(shape: TermShape, path: Path, instances: TypeSubstitution)(using NewResolverState): Unit = + val (head, marks) = shape.applicationHead + def contextualTerm(value: Term, context: Ls[Marks], path: Path, + substitution: TypeSubstitution)(using NewResolverState): Unit = + watch((new Identity(value), context, substitution))( + listener => resolver.listenTerm(value)(listener)(using rstate.withInstances(substitution))): shape => + emit(resolver.instantiateShape(shape, substitution).exit(context), path) + head match + case contextual: ContextualShape => contextual.source.exit(marks) match + case value: TermShape => + val substitution = instances.withOverrides(contextual.instances) + valueIn(value, path, substitution)(using rstate.withInstances(substitution)) + case NoShape => () + case specialized: SpecializedShape => + resolver.instantiateShape(specialized.declaration, instances.withOverrides(specialized.instances)).exit(marks) match + case value: TermShape => valueIn(value, path, instances) + case NoShape => () + case _: InstanceShape => + watch((shape, "instance view"))(resolver.listenInstanceViews(shape))(emit(_, path)) + case ds: DefnShape if ds.defn.sym.getState is rstate.owner => + parameters(shape.unappliedParams, path) + ds.clsDef match + case S(cls) => + // Exposing a constructor also exposes instances clients can create. + shape match + case Marked(_: DefnShape, _) if cls.paramsOpt.isEmpty => + parameters(cls.auxParams.map(_ -> marks), path) + case _ => () + members(cls, marks, path) + case N => ds.defn match + case td: TermDefinition => td.body.foreach(result(_, resolver.resultSignature(td), marks, path)) + case _ => () + case _: DefnShape => () // Imported definitions were checked before publication. + case intro: IntroShape => intro.trm match + case Lam(_, body) => + parameters(shape.unappliedParams, path) + watch((new Identity(body), marks, instances, "result"))( + listener => resolver.listenResult(body)(listener)(using rstate.withInstances(instances))): shape => + emit(resolver.instantiateShape(shape, instances).exit(marks), + path.via(msg"This value is returned here." -> body.toLoc)) + case _ => () + case tuple: TupleShape => tuple.segments.foreach: + case field: TupleShape.Fixed => + watch((tuple, field, marks))(resolver.listenTupleField(field)): shape => + emit(shape.exit(marks), path.via(msg"This value is stored in this tuple." -> tuple.source.toLoc)) + case _ => () + case record: RecordShape => record.elements.foreach: + case RecordShape.Field(field) => contextualTerm(field.rhs, marks, + path.via(msg"This value is stored in this record field." -> field.toLoc), instances.withOverrides(record.instances)) + case RecordShape.Spread(inner, context) => + emit(resolver.instantiateShape(inner, instances.withOverrides(record.instances)).exit(context).exit(marks), path) + case _ => () + case base: BaseShape => members(base.defn, marks, path) + case callable: CallableTypeShape => + // Written structure constrains external values; missing output parts + // still admit unknown shapes even when local callers supplied evidence. + val unknown = UnknownValueShape(callable.source, fromPublicInterface = true)(path) + callable.paramLists.foreach: params => + (params.params.flatten ::: params.rest.toList).foreach: input => + unknown.enter(marks) match + case value: TermShape => resolver.exposeTypeHoles(input, value, marks) + case NoShape => () + watch((callable, marks, "call result"))(resolver.listenCallableResult(callable)): shape => + emit(shape.exit(marks), path) + case nominal: NominalInstanceView => + watch((shape, "promise payload"))(listener => { resolver.listenPromisePayload(shape)(listener); () }): payload => + emit(payload, path.via(msg"This value is fulfilled by this promise." -> nominal.toLoc)) + // Array element bindings may contain escaping closures. Follow only the + // declared binding; concrete annotations still hide implementation shapes. + resolver.arrayElementType(nominal).foreach: binding => + watch((shape, "array elements"))(listener => { resolver.listenArrayElements(shape)(listener); () }): element => + emit(element, path.via(msg"This value is stored in this array." -> binding.resolution.source.toLoc)) + // Structural annotations hide initializer implementations. Unknown and + // dynamic values have no static graph. + case _: (RecordTypeShape | OpaqueTypeShape | UnknownValueShape | RigidTypeShape | DynShape | ErrShape) => () + + def check(exports: Ls[BlockMemberSymbol], values: Ls[Term]): Unit = + try + exports.foreach: symbol => + member(symbol, Nil, ShapeProvenance.empty.via(msg"'${symbol.nme}' is exposed by this compilation unit." -> symbol.toLoc)) + values.foreach: value => + term(value, Nil, ShapeProvenance.empty.via(msg"This value is exposed by this compilation unit." -> value.toLoc)) + // Annotated callbacks can escape even from private code. Delay their + // exposure until the block is elaborated, so forward definitions and + // returned closures are available. Processing one root may discover more. + def pending(): Unit = rstate.takeExposureInputs().foreach: input => + emit(input.shape, input.provenance)(using input.state) + pending() + while work.nonEmpty do + work.dequeue()() + pending() + finally detach.reverseIterator.foreach(_()) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/NewResolver.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/NewResolver.scala new file mode 100644 index 0000000000..894ca9f90f --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/NewResolver.scala @@ -0,0 +1,3779 @@ +package hkmc2 +package semantics + +import scala.collection.mutable +import scala.annotation.tailrec + +import hkmc2.utils.*, shorthands.* +import hkmc2.utils.* +import hkmc2.Message.MessageContext +import hkmc2.io +import utils.TraceLogger +import syntax.* +// import Tree.* +import Term.* + +import Elaborator.* +import hkmc2.syntax.LetBind + + +class NewResolver: + self: Elaborator => + import tl.* + import NewResolverState.Listener + + /* + class Constraint(val lhs: Term, val rhs: Term): + override def equals(obj: Any): Bool = obj match + case that: Constraint => (this.lhs is that.lhs) && (this.rhs is that.rhs) + case _ => false + private var _hash: Int = 0 + override def hashCode(): Int = + if _hash =/= 0 then _hash + else + var h = lhs.hashCode() * 31 + rhs.hashCode() + if h === 0 then h += 1 + _hash = h + h + def showDbg(using DebugPrinter): Str = + s"${lhs.showDbg} <: ${rhs.showDbg}" + + val processedConstraints: mutable.Set[Constraint] = mutable.Set.empty + val collectedConstraints: mutable.Buffer[Constraint] = mutable.Buffer.empty + */ + + // * The `FlowSymbol`s are currently used to uniquely identify terms + private def appShapes(using rs: NewResolverState) = rs.appShapes + private def newShapes(using rs: NewResolverState) = rs.newShapes + private def introShapes(using rs: NewResolverState) = rs.introShapes + private def symShapes(using rs: NewResolverState) = rs.symShapes + private def declaredSymShapes(using rs: NewResolverState) = rs.declaredSymShapes + private def selfShapes(using rs: NewResolverState) = rs.selfShapes + // Tuple and record AST nodes for which we have subscribed to spread operands. + // Each node owns its shapes and listeners. Comparing nodes by structural equality + // would skip subscriptions for a second equal expression, leaving its listeners + // without results; compare their identities instead. + private def aggregateProducers(using rs: NewResolverState) = rs.aggregateProducers + private def defnShapes(using rs: NewResolverState) = rs.defnShapes + // These results carry no value flow or lexical context. Reuse one immutable + // shape so repeated subscriptions do not manufacture distinct unit candidates. + private lazy val unitResultShape = IntroShape(unit, N) + + /** Interpret types through completed symbolic candidates, independently of term overloads. */ + def typeResolution(term: Term)(using rs: NewResolverState): TypeResolution = + rs.typeInterpretations.get(new Identity(term)).orElse(term.typeInterpretation) match + case S(result) => result + case N => + val result = new TypeResolution(term, messages => resolError(term, messages)) + // Cache before following a recursive alias. Imported legacy annotations + // have no stored interpretation: their new inference graph must remain + // private to this consumer, including listeners on synthesized hosts. + rs.typeInterpretations(new Identity(term)) = result + def selectInto(symbol: TypeSymbol, publish: ShapeListener[TypeShape])(using NewResolverState): Unit = + term.withoutCaptures match + case ref: NewResolvable => + rstate.recordResolution(ref, ref.resolvedTargets.contains(symbol))(ref.resolvedTargets ::= symbol) + case _ => () + def definition(defn: Definition)(using NewResolverState): Unit = defn match + case cls: ClassLikeDef => publish(TypeShape.Nominal(cls)) + // Nothing admits no values. In particular, a rejected promise must + // not contribute an unknown fulfillment alternative to catch or any. + case alias: TypeDef if alias.sym is prelude.builtins.Nothing => publish(TypeShape.Bottom) + case alias: TypeDef => + publish(TypeShape.Alias(alias.sym, alias.rhs.map(typeResolution))) + // Validate unused annotations too, once their forward source graph + // is complete. Interface observation uses the same gate below. + withCanonicalType(DeclaredType(result, Map.empty, TypeSubstitution.empty, true)(N))(_ => ()) + case _ => publish(TypeShape.Abstract) + symbol.defn match + case S(defn) => definition(defn) + case N => symbol.defnListeners += definition + def select(symbol: TypeSymbol)(using NewResolverState): Unit = selectInto(symbol, result.publish) + def selectReference(symbol: TypeSymbol, shape: SymShape)(using NewResolverState): Unit = + val (source, instances) = shape match + case contextual: ContextualSymShape => (contextual.source, contextual.instances) + case source: CoreSymShape => (source, TypeSubstitution.empty) + val bindings = source match + case declared: DeclaredSymShape => declared.bindings + case _ => Map.empty[VarSymbol, DeclaredType] + if shape.markss.isEmpty && bindings.isEmpty && instances.isEmpty then select(symbol) + else + // A symbol identity determines erasure, but not the environment of + // its free binders. Keep the selected declaration at its own endpoint + // and transport it just as a syntactic Capture transports a type. + val key = (new Identity(term), symbol) + val base = rstate.selectedTypes.get(key).getOrElse: + val base = new TypeResolution(term, result.fail) + rstate.selectedTypes(key) = base + selectInto(symbol, base.publish) + base + result.publish(TypeShape.Reference(base, shape.markss, bindings, instances)) + def reject(shape: Shape)(using NewResolverState): Unit = + result.fail(msg"${shape.describe.capitalize} cannot be used as a type" -> shape.toLoc :: Nil) + result.publish(TypeShape.Abstract) + def selectMember(member: BlockMemberSymbol)(using NewResolverState): Unit = member.onComplete: () => + member.asTpe.orElse(member.asModOrObj) match + case S(symbol) => select(symbol) + case N => + result.fail(msg"${member.describe.capitalize} cannot be used as a type" -> member.toLoc :: Nil) + result.publish(TypeShape.Abstract) + term match + // Imported declarations can still contain legacy references. + case Ref(symbol: TypeSymbol) => select(symbol) + case Ref(member: BlockMemberSymbol) => selectMember(member) + // Imported parent declarations can contain legacy synthesized selections. + // Their identities were fixed by the old resolver; do not reinterpret + // their term syntax or consult unfinished new-resolution candidates. + case ref: (SynthSel | Sel | Resolved) => ref.legacyResolvedSym match + case S(symbol: TypeSymbol) => select(symbol) + case S(ctor: ClassCtorSymbol) => select(ctor.associatedCls) + case S(member: BlockMemberSymbol) => selectMember(member) + case _ => + result.fail(msg"This reference does not denote a type." -> ref.toLoc :: Nil) + result.publish(TypeShape.Abstract) + // An alias's lexical qualification substitutes type references; it + // does not cross an invocation boundary. Captured function and class + // parameters still retain their actual value-scope crossings. + case Capture(base, _: TypeAliasSymbol) => typeResolution(base).listen(result.publish) + case Capture(base, thru) => result.publish(TypeShape.Reference(typeResolution(base), + EntryMark(ResolutionBoundary(thru), N, NoMarks) :: Nil, Map.empty, TypeSubstitution.empty)) + case TyApp(base, args) => result.publish(TypeShape.Applied(typeResolution(base), args.map(typeResolution))) + case Forall(params, outer, body) => + result.publish(TypeShape.Polymorphic(params.map: param => + TypeQuantifier(TypeShape.Parameter(param.sym, param.sym.inferenceHost), + param.lb.map(typeResolution), param.ub.map(typeResolution)) + , outer, typeResolution(body))) + case CompType(left, right, union) => + val l = typeResolution(left) + val r = typeResolution(right) + result.publish(if union then TypeShape.Union(l, r) else TypeShape.Intersection(l, r)) + case Neg(base) => result.publish(TypeShape.Negation(typeResolution(base))) + case DynTy() => result.publish(TypeShape.Dynamic) + case FunTy(params, ret, _) => result.publish(TypeShape.Function(params, typeResolution(ret))) + case UnitVal() => result.publish(TypeShape.Unit) + case WildcardTy(input, output) => result.publish(TypeShape.Wildcard(input.map(typeResolution), output.map(typeResolution))) + case SimpleRef(sym: VarSymbol) if sym.decl.exists(_.isInstanceOf[TyParam]) => + result.publish(TypeShape.Parameter(sym, sym.inferenceHost)) + case Ref(sym: VarSymbol) if sym.decl.exists(_.isInstanceOf[TyParam]) => + result.publish(TypeShape.Parameter(sym, sym.inferenceHost)) + case Tup(fields) if fields.forall(_.isInstanceOf[Fld]) => + result.publish(TypeShape.Tuple(fields.collect { case Fld(_, sign, _) => typeResolution(sign) })) + case record: Rcd => + val fields = record.stats.collect { case field: RcdField => field } + if fields.length != record.stats.length || fields.exists(field => field.field match + case Lit(_: Tree.StrLit) => false + case _ => true) + then + result.fail(msg"A record type requires statically named fields." -> record.toLoc :: Nil) + result.publish(TypeShape.Abstract) + else result.publish(TypeShape.Record(record, + fields.reverse.distinctBy(_.sym.nme).reverse.map(field => field -> typeResolution(field.rhs)))) + // Signed numeric literals use operator applications in the syntax tree. + // Their primitive interface is not recovered from these applications yet. + case App(Ref(op: BuiltinSymbol), Tup(Fld(_, Lit(_: Tree.IntLit | _: Tree.DecLit), _) :: Nil)) + if op.nme === "-" || op.nme === "+" => + result.publish(TypeShape.Abstract) + case literal: Lit => + listenTerm(literal)(shape => result.publish(TypeShape.Inferred(shape))) + case _: Tup | Missing | Error() => + result.publish(TypeShape.Abstract) + case _ => listen(term, discardMarks = true): + case shape: SymShape => shape.sym.onComplete: () => + shape.sym.asTpe.orElse(shape.sym.asModOrObj) match + case S(symbol) => selectReference(symbol, shape) + case N => reject(shape) + case shape: TermShape => reject(shape) + result + + /** Register complete signatures while resolution can still publish candidates. Arrow operands + * need their own interpretations: erasure may consume the arrows as physical parameter lists. + */ + def registerSignature(sign: Term)(using NewResolverState): Unit = + sign.typeInterpretation = S(typeResolution(sign)) + sign match + case Forall(params, _, body) => + params.foreach: param => + param.lb.foreach(registerSignature) + param.ub.foreach(registerSignature) + registerSignature(body) + case FunTy(lhs, rhs, _) => + lhs match + case Tup(fields) => fields.foreach: + case Fld(_, term, _) => registerSignature(term) + case _ => () + case single => registerSignature(single) + registerSignature(rhs) + case _ => () + + // Guard and dependency analysis inspect the declaration behind a capture; + // its value-scope marks do not change whether it supplies a type constructor. + private def typeApplicationTarget(base: TypeResolution)(using NewResolverState): Opt[TypeShape] = + @tailrec + def loop(base: TypeResolution, seen: Set[TypeResolution]): Opt[TypeShape] = + if seen(base) then N else base.currentShapes.toList match + case TypeShape.Reference(inner, _, _, _) :: Nil => loop(inner, seen + base) + case shape :: Nil => S(shape) + case _ => N + loop(base, Set.empty) + + /** Compute free binders on the finite source graph, not on substituted types. + * Each edge can hide binders introduced by an alias or quantifier. Iterating + * finite binder sets reaches a fixed point even for mutually recursive types. + * An unresolved target is a dependency still to discover, never a closed type. + * Valid written types have one target (validation rejects ambiguity). The + * synthetic nodes that can receive more candidates use no ambient bindings. + * The unguarded variant uses the same equations but stops dependencies at + * structural constructors, to distinguish forwarding and guarding aliases. + */ + private def typeDependencies(resolution: TypeResolution)(using NewResolverState) + : Either[Set[TypeResolution], Set[VarSymbol]] = typeDependencies(resolution, false) + + private def typeDependencies(resolution: TypeResolution, unguardedOnly: Bool)(using NewResolverState) + : Either[Set[TypeResolution], Set[VarSymbol]] = + (if unguardedOnly then rstate.unguardedTypeDependencies else rstate.typeDependencies).get(resolution) match + case S(binders) => R(binders) + case N => + val cache = if unguardedOnly then rstate.unguardedTypeDependencies else rstate.typeDependencies + val graph = mutable.LinkedHashMap.empty[TypeResolution, (Set[VarSymbol], Ls[(TypeResolution, Set[VarSymbol])])] + val argumentUses = mutable.Map.empty[TypeResolution, Ls[(TypeResolution, VarSymbol, TypeResolution)]] + val pending = mutable.Set.empty[TypeResolution] + def visit(res: TypeResolution): Unit = if !graph.contains(res) then + graph(res) = (Set.empty, Nil) + cache.get(res) match + case S(binders) => graph(res) = (binders, Nil) + case N => + val shapes = res.currentShapes.toList + if shapes.isEmpty then pending += res + var direct = Set.empty[VarSymbol] + var edges = List.empty[(TypeResolution, Set[VarSymbol])] + def edge(child: TypeResolution, hidden: Set[VarSymbol]): Unit = + edges ::= child -> hidden + visit(child) + def child(res: TypeResolution): Unit = edge(res, Set.empty) + // Both analyses discover the whole source graph. Only free-binder + // projection propagates dependencies across a structural guard. + def guardedChild(res: TypeResolution): Unit = if unguardedOnly then visit(res) else child(res) + def reference(tpe: DeclaredType): Unit = + visit(tpe.resolution) + tpe.bindings.values.foreach(reference) + shapes.foreach: + case TypeShape.Parameter(symbol, _) => direct += symbol + case TypeShape.Nominal(defn) => + // This immutable declaration metadata belongs to the original + // graph, even when the type was reached through an imported AST. + if !unguardedOnly then + direct ++= nominalLexicalBinders(defn) + case TypeShape.Alias(symbol, rhs) => + val bound = symbol.defn.get.tparams.map(_.sym).toSet + rhs.foreach(edge(_, bound)) + case TypeShape.Reference(base, _, bindings, _) => + edge(base, bindings.keySet) + bindings.values.foreach(reference) + case TypeShape.Applied(base, args) => + child(base) + // An alias argument contributes dependencies only if the body + // uses its formal. Solve this with the other equations: a cycle + // such as Loop[A] = {next: Loop[Array[A]]} has no use of A. + typeApplicationTarget(base) match + case S(TypeShape.Alias(symbol, S(rhs))) => + visit(rhs) + val formals = symbol.defn.get.tparams + args.zipWithIndex.foreach: (arg, index) => + formals.lift(index) match + case S(formal) => + visit(arg) + argumentUses(res) = (rhs, formal.sym, arg) :: argumentUses.getOrElse(res, Nil) + case N => child(arg) + case S(TypeShape.Nominal(_)) => args.foreach(guardedChild) + // An abstract constructor could forward an argument unchanged. + case _ => args.foreach(child) + case TypeShape.Tuple(fields) => fields.foreach(guardedChild) + case TypeShape.Record(_, fields) => fields.foreach((_, field) => guardedChild(field)) + case TypeShape.Function(params, result) => guardedChild(typeResolution(params)); guardedChild(result) + case TypeShape.Polymorphic(params, _, body) => + val bound = params.map(_.parameter.symbol).toSet + params.foreach: param => + param.lower.foreach(edge(_, bound)) + param.upper.foreach(edge(_, bound)) + edge(body, bound) + case TypeShape.Union(left, right) => child(left); child(right) + case TypeShape.Intersection(left, right) => child(left); child(right) + case TypeShape.Negation(base) => child(base) + case TypeShape.Wildcard(input, output) => input.foreach(child); output.foreach(child) + // These synthesized nodes retain their own interpreted references + // or inference hosts; none reads the surrounding binding map. + case TypeShape.Combined(formula) => formula.orderedAtoms.foreach(reference) + case TypeShape.Argument(parts) => reference(parts.input); reference(parts.output) + case TypeShape.SelectedArgument(argument, _) => reference(argument) + case TypeShape.Contextual(context) => reference(context.tpe) + case TypeShape.Inferred(_) | TypeShape.Hole(_) | TypeShape.Unit | TypeShape.Dynamic | TypeShape.Abstract | + TypeShape.Top | TypeShape.Bottom => () + graph(res) = (direct, edges) + visit(resolution) + if pending.nonEmpty then L(pending.toSet) else + val dependencies = mutable.Map.from(graph.iterator.map((res, node) => res -> node._1)) + var changed = true + while changed do + changed = false + graph.foreach: (res, node) => + val direct = argumentUses.getOrElse(res, Nil).foldLeft(node._1): (acc, use) => + if dependencies(use._1)(use._2) then acc ++ dependencies(use._3) else acc + val next = node._2.foldLeft(direct): (acc, edge) => + acc ++ (dependencies(edge._1) -- edge._2) + if next != dependencies(res) then + dependencies(res) = next + changed = true + dependencies.foreach((res, binders) => cache(res) = binders) + R(dependencies(resolution)) + + /** Check guarded recursion and substitutions on the finite source graph, before observing + * structural children can generate an unbounded sequence of environments. + * Edges connect original formal parameters, and record whether their argument + * puts the source formal underneath a non-Boolean constructor. A growing edge + * is forbidden only when its destination can reach its source: constants and + * resets break dependency cycles, while permutations do not add constructors. + * Alias and Boolean reduction is shared with ordinary argument interpretation. + * This is a conservative sufficient condition, not an exact regularity test: + * argument-part selection and absorption across recursive steps can make a + * rejected cycle finite. Do not describe rejection as proof of non-regularity. + */ + private def regularType(root: TypeResolution)(using NewResolverState): Bool = rstate.regularTypes.get(root) match + case S(result) => result + case N => + val visited = mutable.LinkedHashSet.empty[TypeResolution] + val applications = mutable.ArrayBuffer.empty[(TypeResolution, TypeAliasSymbol, TypeResolution, Ls[TypeResolution])] + def alias(base: TypeResolution): Opt[(TypeAliasSymbol, TypeResolution)] = + typeApplicationTarget(base) match + case S(TypeShape.Alias(symbol, S(rhs))) => S(symbol -> rhs) + case _ => N + def visit(res: TypeResolution): Unit = if visited.add(res) then + res.currentShapes.foreach: + case TypeShape.Alias(_, rhs) => rhs.foreach(visit) + case TypeShape.Reference(base, _, _, _) => visit(base) + case TypeShape.Applied(base, args) => + alias(base).foreach: (symbol, rhs) => + applications += ((res, symbol, rhs, args)) + visit(base) + args.foreach(visit) + case TypeShape.Tuple(fields) => fields.foreach(visit) + case TypeShape.Record(_, fields) => fields.foreach((_, field) => visit(field)) + case TypeShape.Function(params, result) => visit(typeResolution(params)); visit(result) + case TypeShape.Polymorphic(params, _, body) => + params.foreach: param => + param.lower.foreach(visit) + param.upper.foreach(visit) + visit(body) + case TypeShape.Union(left, right) => visit(left); visit(right) + case TypeShape.Intersection(left, right) => visit(left); visit(right) + case TypeShape.Negation(base) => visit(base) + case TypeShape.Wildcard(input, output) => input.foreach(visit); output.foreach(visit) + case TypeShape.Combined(formula) => formula.orderedAtoms.foreach(atom => visit(atom.resolution)) + case TypeShape.Argument(parts) => visit(parts.input.resolution); visit(parts.output.resolution) + case TypeShape.SelectedArgument(argument, _) => visit(argument.resolution) + case TypeShape.Contextual(reference) => visit(reference.tpe.resolution) + case _ => () + visit(root) + // Check source cycles before substitution or Boolean normalization can + // hide an unguarded occurrence. Alias applications are transparent, but + // their arguments are followed only through unguarded formal uses: + // Id[Loop] is unguarded, whereas RecordWrapper[Loop] can be guarded. + val checked = mutable.Set.empty[TypeResolution] + var guarded = true + def checkGuarded(res: TypeResolution, path: Ls[TypeResolution]): Unit = + if path.contains(res) then + val cycle = res :: path.takeWhile(_ != res) + cycle.iterator.flatMap(node => node.currentShapes.collect: + case TypeShape.Alias(symbol, S(rhs)) => (node, symbol, rhs) + ).nextOption().foreach: (reference, symbol, rhs) => + guarded = false + rhs.fail(msg"Unguarded recursive type alias '${symbol.nme}'." -> reference.source.toLoc :: + (msg"Recursive references must pass through a record, tuple, function, or nominal type." -> symbol.toLoc) :: Nil) + else if checked.add(res) then + val next = res :: path + def follow(child: TypeResolution): Unit = checkGuarded(child, next) + res.currentShapes.foreach: + case TypeShape.Alias(_, rhs) => rhs.foreach(follow) + case TypeShape.Reference(base, _, _, _) => follow(base) + case TypeShape.Applied(base, args) => + follow(base) + typeApplicationTarget(base) match + case S(TypeShape.Alias(symbol, S(rhs))) => + val uses = typeDependencies(rhs, true) match + case R(binders) => binders + case L(_) => lastWords("Guardedness requires a completed source dependency graph") + symbol.defn.get.tparams.zip(args).foreach: (formal, argument) => + if uses(formal.sym) then follow(argument) + case S(TypeShape.Nominal(_)) => () + case _ => args.foreach(follow) + case TypeShape.Union(left, right) => follow(left); follow(right) + case TypeShape.Intersection(left, right) => follow(left); follow(right) + case TypeShape.Negation(base) => follow(base) + case TypeShape.Wildcard(input, output) => input.foreach(follow); output.foreach(follow) + case TypeShape.Polymorphic(params, _, body) => + params.foreach: param => + param.lower.foreach(follow) + param.upper.foreach(follow) + follow(body) + case TypeShape.Combined(formula) => formula.orderedAtoms.foreach(atom => follow(atom.resolution)) + case TypeShape.Argument(parts) => follow(parts.input.resolution); follow(parts.output.resolution) + case TypeShape.SelectedArgument(argument, _) => follow(argument.resolution) + case TypeShape.Contextual(reference) => follow(reference.tpe.resolution) + case _ => () + // Structural children start fresh paths, so every cycle is checked even + // when an enclosing interface guards the use of an invalid alias. + visited.foreach(checkGuarded(_, Nil)) + if !guarded then + rstate.regularTypes(root) = false + return false + def dependencies(res: TypeResolution): Set[VarSymbol] = typeDependencies(res) match + case R(binders) => binders + case L(_) => lastWords("Regularity requires a completed source dependency graph") + def uses(tpe: DeclaredType, wrapped: Bool, seen: Set[DeclaredType], aliases: Set[TypeAliasSymbol]): Set[(VarSymbol, Bool)] = + if seen(tpe) then Set.empty else + val next = seen + tpe + def bound(symbol: VarSymbol, inside: Bool): Set[(VarSymbol, Bool)] = tpe.bindings.get(symbol) match + case S(argument) => uses(selectArgument(argument, tpe.positive), inside, next, aliases) + case N => Set(symbol -> inside) + tpe.resolution.currentShapes.toList match + case TypeShape.Parameter(symbol, _) :: Nil => bound(symbol, wrapped) + case TypeShape.Combined(formula) :: Nil => formula.atoms.flatMap(uses(_, wrapped, next, aliases)) + case TypeShape.Contextual(reference) :: Nil => uses(reference.tpe, wrapped, next, aliases) + case TypeShape.Wildcard(input, output) :: Nil => + input.toSet.flatMap(part => uses(declaredType(part, tpe.copy(positive = !tpe.positive)(tpe.origin)), wrapped, next, aliases)) ++ + output.toSet.flatMap(part => uses(declaredType(part, tpe), wrapped, next, aliases)) + case TypeShape.Argument(parts) :: Nil => uses(parts.input, wrapped, next, aliases) ++ uses(parts.output, wrapped, next, aliases) + case TypeShape.SelectedArgument(argument, _) :: Nil => uses(argument, wrapped, next, aliases) + case TypeShape.Applied(base, args) :: Nil => alias(base) match + case S((symbol, rhs)) if !aliases(symbol) => + // Constructor analysis ignores scope crossings, but still follows + // the alias's substitution. The guard bounds body expansion; + // substitution values are finite immutable reference DAGs. + val substitutions = symbol.defn.get.tparams.zip(args).map: (formal, argument) => + formal.sym -> argumentType(declaredArgument(argument, tpe, true), formal) + uses(declaredType(rhs, tpe.bindings ++ substitutions, tpe.positive), wrapped, next, aliases + symbol) + case S(_) => dependencies(tpe.resolution).flatMap(bound(_, wrapped)) + case N => dependencies(tpe.resolution).flatMap(bound(_, true)) + case _ => dependencies(tpe.resolution).flatMap(bound(_, true)) + val edges = applications.toList.flatMap: (source, symbol, rhs, args) => + symbol.defn.get.tparams.zip(args).flatMap: (formal, argument) => + if !dependencies(rhs)(formal.sym) then Nil + else uses(declaredType(argument, Map.empty), false, Set.empty, Set.empty).toList.map: + case (origin, growing) => (origin, formal.sym, growing, source, symbol) + val successors = edges.groupMap(_._1)(_._2) + def reaches(current: VarSymbol, target: VarSymbol, seen: Set[VarSymbol]): Bool = + current == target || (!seen(current) && successors.getOrElse(current, Nil).exists(reaches(_, target, seen + current))) + val invalid = edges.filter: (origin, target, growing, _, _) => + growing && reaches(target, origin, Set.empty) + invalid.foreach: (_, formal, _, source, symbol) => + source.fail(msg"Recursive type '${symbol.nme}' is not supported by the current regularity check." -> source.source.toLoc :: + (msg"Type parameter '${formal.nme}' occurs under a type constructor on a recursive dependency cycle." -> formal.toLoc) :: Nil) + val result = invalid.isEmpty + // Failure belongs to this reachable graph, not to unrelated children in + // it. A later root may observe a regular subgraph of a rejected alias. + rstate.regularTypes(root) = result + result + + /** Wait for the source graph before observing a substituted interface. The + * retained environment is projected only after every forward dependency has + * a target. Subscriptions and completed summaries are shared by source node; + * callbacks and their hosts remain private to each consuming inference graph. + */ + private def withCanonicalType(tpe: DeclaredType)(listener: ShapeListener[DeclaredType])(using NewResolverState): Unit = + withTypeDependencies(tpe.resolution): binders => + if regularType(tpe.resolution) then + listener(declaredType(tpe.resolution, tpe.bindings.filter((symbol, _) => binders(symbol)), tpe.positive) + .instantiate(tpe.instances).withOrigin(tpe.origin)) + + /** Discover binders without normalizing the type. Parent annotations must + * receive their bindings at the original lexical endpoint: normalization + * can move captures into references with their own closed environments. + */ + private def withTypeDependencies(resolution: TypeResolution)(listener: ShapeListener[Set[VarSymbol]]) + (using NewResolverState): Unit = + typeDependencies(resolution) match + case R(binders) => listener(binders) + case L(_) => + rstate.pendingTypeDependencies.get(resolution) match + case S(host) => host.listen(listener) + case N => + val host = new TypeDependencyHost + rstate.pendingTypeDependencies(resolution) = host + host.listen(listener) + def retry()(using NewResolverState): Unit = typeDependencies(resolution) match + case R(binders) => host.publish(binders) + case L(pending) => pending.foreach: unresolved => + if rstate.dependencySubscriptions.add((resolution, unresolved)) then + unresolved.listen(_ => retry()) + retry() + + /** Substitute a bound parameter before storing an argument. Otherwise recursive + * interfaces such as Tree[A] = Node[A] | Empty would accumulate equivalent + * A-to-A environments every time a recursive field is selected. + */ + private[semantics] def declaredType(resolution: TypeResolution, bindings: Map[VarSymbol, DeclaredType])(using NewResolverState): DeclaredType = + declaredType(resolution, bindings, true) + + private def declaredType(resolution: TypeResolution, bindings: Map[VarSymbol, DeclaredType], positive: Bool) + (using NewResolverState): DeclaredType = + declaredType(resolution, bindings, positive, asArgument = false) + + private def declaredType(resolution: TypeResolution, bindings: Map[VarSymbol, DeclaredType], positive: Bool, asArgument: Bool) + (using NewResolverState): DeclaredType = + // Splitting each argument visits both polarities. Share work within this + // traversal so nested alias applications do not repeatedly normalize the + // same source and environment along an exponential number of paths. + val normalized = mutable.Map.empty[(TypeResolution, Map[VarSymbol, DeclaredType], Set[TypeAliasSymbol], Bool), DeclaredType] + def argument(resolution: TypeResolution, bindings: Map[VarSymbol, DeclaredType], aliases: Set[TypeAliasSymbol], positive: Bool): DeclaredType = + resolution.currentShapes.toList match + case TypeShape.Wildcard(_, _) :: Nil | TypeShape.Argument(_) :: Nil => loop(resolution, bindings, aliases, positive) + case _ => normalizedArgument(resolution, TypeArgument( + loop(resolution, bindings, aliases, !positive), loop(resolution, bindings, aliases, positive))) + def loop(resolution: TypeResolution, bindings: Map[VarSymbol, DeclaredType], aliases: Set[TypeAliasSymbol], positive: Bool): DeclaredType = + normalized.getOrElseUpdate((resolution, bindings, aliases, positive), interpret(resolution, bindings, aliases, positive)) + def interpret(resolution: TypeResolution, bindings: Map[VarSymbol, DeclaredType], aliases: Set[TypeAliasSymbol], positive: Bool): DeclaredType = + def retain: DeclaredType = + val relevant = if bindings.isEmpty then bindings else typeDependencies(resolution) match + case R(binders) => bindings.filter((symbol, _) => binders(symbol)) + case L(_) => bindings + DeclaredType(resolution, relevant, TypeSubstitution.empty, positive)(N) + resolution.currentShapes.toList match + case TypeShape.Parameter(symbol, _) :: Nil if bindings.contains(symbol) => selectArgument(bindings(symbol), positive) + case (reference: TypeShape.Reference) :: Nil => + transportType(loop(reference.base, bindings ++ reference.bindings, aliases, positive) + .instantiate(reference.instances), reference.marks) + case TypeShape.Union(left, right) :: Nil => + combinedType(resolution, loop(left, bindings, aliases, positive), loop(right, bindings, aliases, positive), true) + case TypeShape.Intersection(left, right) :: Nil => + combinedType(resolution, loop(left, bindings, aliases, positive), loop(right, bindings, aliases, positive), false) + case TypeShape.Wildcard(input, output) :: Nil => + // Normalize both parts before retaining the argument. Otherwise even + // out A keeps another A-to-wildcard environment at each recursive + // step. Preserve the written source: it overrides declaration variance. + val parts = TypeArgument(input.fold(extremeType(false, S(resolution)))(loop(_, bindings, aliases, !positive)), + output.fold(missingOutput(resolution))(loop(_, bindings, aliases, positive))) + normalizedArgument(resolution, parts) + case TypeShape.Applied(base, arguments) :: Nil => + def applyAlias(base: DeclaredType, arguments: => TypeApplication, seen: Set[TypeResolution]): Opt[DeclaredType] = + lazy val supplied = arguments + if seen(base.resolution) then N else base.resolution.currentShapes.toList match + case TypeShape.Alias(symbol, S(rhs)) :: Nil if !aliases(symbol) => + val parameters = symbol.defn.get.tparams + if supplied.arguments.length > parameters.length then N else + // Omitted formals use the same source holes as deferred + // interpretation. Retaining a partial forwarding application + // would nest its environment at each recursive projection. + S(loop(rhs, typeBindings(base, supplied, parameters, true), aliases + symbol, positive)) + case TypeShape.Contextual(reference) :: Nil => + // Use exactly the interpreter's application rule: move caller + // arguments to the template endpoint, substitute there, then + // transport the result back. A wildcard exit followed by entry + // is not cancelled; transportType retains its ordinary marks. + applyAlias(reference.tpe.instantiate(base.instances).withOrigin(base.origin), + supplied.move(inverseMarks(reference.marks)), seen + base.resolution) + .map(transportType(_, reference.marks)) + case _ => N + // Reduce arguments in their caller environment before binding formals. + // The alias guard grows only on body expansion, so nested Identity + // applications reduce while unproductive recursive aliases stop. + lazy val supplied = TypeApplication(arguments.map(res => AppliedTypeArgument( + argument(res, bindings, aliases, false), argument(res, bindings, aliases, true)))) + applyAlias(loop(base, bindings, aliases, positive), supplied, Set.empty).getOrElse(retain) + case _ => retain + if asArgument then argument(resolution, bindings, Set.empty, positive) + else loop(resolution, bindings, Set.empty, positive) + + /** Preserve both occurrences of an unqualified argument before substitution + * can select either bound. A nominal application composes these with its own + * polarity; an alias binds them before interpreting its body at that polarity. + */ + private def declaredArgument(resolution: TypeResolution, context: DeclaredType, positive: Bool) + (using NewResolverState): DeclaredType = + declaredType(resolution, context.bindings, positive, asArgument = true).instantiate(context.instances).withOrigin(context.origin) + + private def normalizedArgument(source: TypeResolution, parts: TypeArgument)(using NewResolverState): DeclaredType = + rstate.argumentTypes.getOrElseUpdate((source, parts), { + val normalized = new TypeResolution(source.source, source.fail) + normalized.publish(TypeShape.Argument(parts)) + DeclaredType(normalized, Map.empty, TypeSubstitution.empty, true)(N) + }) + + /** Normalize Boolean combinations without observing an atom's candidates. + * Transport distributes over components using the ordinary transportType + * operation; no exit/entry cancellation is introduced by normalization. + */ + private def typeFormula(tpe: DeclaredType)(using NewResolverState): TypeFormula[DeclaredType] = + tpe.resolution.currentShapes.toList match + case (parameter: TypeShape.Parameter) :: Nil if !tpe.bindings.contains(parameter.symbol) => + // Different written occurrences of A are the same lattice atom. Limit + // this canonicalization to combinations, so ordinary annotation and + // storage references retain their precise diagnostic source location. + TypeFormula.atom(parameterType(parameter).copy(positive = tpe.positive)(tpe.origin).instantiate(tpe.instances)) + case TypeShape.Combined(formula) :: Nil => formula.map(_.instantiate(tpe.instances)) + case TypeShape.Contextual(reference) :: Nil => + typeFormula(reference.tpe.instantiate(tpe.instances).withOrigin(tpe.origin)).map(transportType(_, reference.marks)) + case TypeShape.Top :: Nil => TypeFormula.top + case TypeShape.Bottom :: Nil => TypeFormula.bottom + case _ => TypeFormula.atom(tpe) + + private def combinedType(source: TypeResolution, left: DeclaredType, right: DeclaredType, union: Bool) + (using NewResolverState): DeclaredType = + val lhs = typeFormula(left) + val rhs = typeFormula(right) + val formula = if union then lhs.union(rhs) else lhs.intersection(rhs) + if formula.clauses.isEmpty then extremeType(false) + else if formula.clauses.contains(Set.empty) then + // A top operand can explain a top result after Boolean normalization has + // erased its atom. Do not blame an opaque operand when a union/intersection + // instead simplifies to a more precise type that still has an interface. + val witness = (if lhs.clauses.contains(Set.empty) then left.origin else N) + .orElse(if rhs.clauses.contains(Set.empty) then right.origin else N) + extremeType(true).withOrigin(witness.orElse(S(TypeInterfaceReason.Unrestricted(source.source)))) + else if formula.clauses.size == 1 && formula.atoms.size == 1 then formula.atoms.head + else rstate.combinedTypes.getOrElseUpdate(formula, { + val resolution = new TypeResolution(source.source, source.fail) + resolution.publish(TypeShape.Combined(formula)) + DeclaredType(resolution, Map.empty, TypeSubstitution.empty, true)(N) + }) + + private def declaredType(resolution: TypeResolution, context: DeclaredType)(using NewResolverState): DeclaredType = + declaredType(resolution, context.bindings, context.positive).instantiate(context.instances).withOrigin(context.origin) + + /** A scalar occurrence selects one endpoint of a substituted argument. Keep + * that endpoint fixed when subsequent constraints inspect the resulting type. + * Nominal arguments select both endpoints separately in declaredArgument. + * Only delayed/captured pairs need a node, and selecting a selection is inert. + */ + private[semantics] def selectArgument(argument: DeclaredType, positive: Bool)(using NewResolverState): DeclaredType = + def deferred(using NewResolverState): DeclaredType = + rstate.selectedArguments.getOrElseUpdate((argument, positive), { + val resolution = new TypeResolution(argument.resolution.source, argument.resolution.fail) + resolution.publish(TypeShape.SelectedArgument(argument, positive)) + declaredType(resolution, Map.empty) + }) + rstate.selectedArguments.get((argument, positive)).getOrElse: + argument.resolution.currentShapes.toList match + case TypeShape.SelectedArgument(_, _) :: Nil => argument + case TypeShape.Argument(parts) :: Nil => + val selected = (if positive then parts.output else parts.input).instantiate(argument.instances) + if positive then selected.withOrigin(argument.origin) else selected + case TypeShape.Wildcard(input, output) :: Nil => + // Keep the wildcard's source for a missing part's diagnostics; the + // shared extreme node itself has no source location. + (if positive then output else input).fold(deferred): part => + declaredType(part, argument.copy(positive = if positive then argument.positive else !argument.positive)(argument.origin)) + case TypeShape.Contextual(reference) :: Nil => + transportType(selectArgument(reference.tpe.instantiate(argument.instances).withOrigin(argument.origin), positive), reference.marks) + case Nil | TypeShape.Reference(_, _, _, _) :: Nil => deferred + case _ => argument + + private def instanceType(instance: TypeParameterInstance)(using NewResolverState): DeclaredType = + declaredType(typeResolution(instance.reference), Map.empty) + + private def parameterType(parameter: TypeShape.Parameter)(using NewResolverState): DeclaredType = + rstate.parameterTypes.getOrElseUpdate(parameter, { + val source = SimpleRef(parameter.symbol)(parameter.symbol.id) + val resolution = new TypeResolution(source, messages => resolError(source, messages)) + resolution.publish(parameter) + declaredType(resolution, Map.empty) + }) + + private def extremeType(top: Bool)(using NewResolverState): DeclaredType = + extremeType(top, N) + + private def extremeType(top: Bool, source: Opt[TypeResolution])(using NewResolverState): DeclaredType = + rstate.extremeTypes.getOrElseUpdate((top, source), { + val resolution = new TypeResolution(source.fold[Term](Term.Missing)(_.source), + _ => lastWords("A synthesized extreme type cannot be invalid")) + resolution.publish(if top then TypeShape.Top else TypeShape.Bottom) + declaredType(resolution, Map.empty) + }) + + private def missingOutput(source: TypeResolution)(using NewResolverState): DeclaredType = + extremeType(true, S(source)).withOrigin(S(TypeInterfaceReason.MissingOutput(source.source))) + + /** Written wildcards override declaration variance. Unqualified arguments have + * already been interpreted in both polarities; declaration variance keeps the + * appropriate endpoint and supplies an extreme for the unused one. + */ + private def argumentType(tpe: DeclaredType, parameter: TyParam)(using NewResolverState): DeclaredType = + val variance = parameter.vce + // SelectedArgument fixes one endpoint of a type argument. If the argument + // was a wildcard, its source survives for diagnostics even though it no + // longer denotes an input/output pair. Treating that retained syntax as a + // fresh use-site override would wrongly skip this parameter's variance. + // Only an unselected written wildcard overrides declaration-site variance. + val selected = tpe.resolution.currentShapes.exists: + case TypeShape.SelectedArgument(_, _) => true + case _ => false + if variance.isEmpty || (!selected && tpe.resolution.source.withoutCaptures.isInstanceOf[WildcardTy]) then tpe else + val varianceValue = variance.get + val variant = rstate.variantTypes.getOrElseUpdate((tpe, varianceValue), { + val selected = selectArgument(tpe, varianceValue) + val parts = if varianceValue then TypeArgument(extremeType(false), selected) + else TypeArgument(selected, extremeType(true)) + normalizedArgument(tpe.resolution, parts) + }) + // Different parameters can share the same semantic argument pair. + // Keep the declaration witness on this use, outside the interned pair. + if varianceValue then variant else variant.withOrigin( + S(TypeInterfaceReason.ContravariantParameter(parameter, tpe.resolution.source))) + + private def listenArgumentParts(tpe: DeclaredType)(listener: ShapeListener[TypeArgument])(using NewResolverState): Unit = + tpe.resolution.listen: + case TypeShape.Wildcard(input, output) => + listener(TypeArgument(input.fold(extremeType(false))(declaredType(_, tpe.copy(positive = !tpe.positive)(tpe.origin))), + output.fold(missingOutput(tpe.resolution))(declaredType(_, tpe)))) + case TypeShape.Argument(parts) => + listener(TypeArgument(parts.input.instantiate(tpe.instances), parts.output.instantiate(tpe.instances).withOrigin(tpe.origin))) + case reference: TypeShape.Reference => + listenArgumentParts(referenceType(reference, tpe))(listener) + case TypeShape.Contextual(reference) => + listenArgumentParts(reference.tpe.instantiate(tpe.instances).withOrigin(tpe.origin)): parts => + listener(TypeArgument(transportType(parts.input, reference.marks), transportType(parts.output, reference.marks))) + case TypeShape.Parameter(symbol, _) if tpe.bindings.contains(symbol) => + val selected = selectArgument(tpe.bindings(symbol).instantiate(tpe.instances), tpe.positive) + listener(TypeArgument(selected, selected)) + case _ => listener(TypeArgument(tpe, tpe)) + + private def effectiveBindings(tpe: DeclaredType)(using NewResolverState): Map[VarSymbol, DeclaredType] = + tpe.instances.map((parameter, instance) => parameter -> instanceType(instance)) ++ + tpe.bindings.map((parameter, bound) => parameter -> bound.instantiate(tpe.instances)) + + /** A parameterless generic declaration returns a polymorphic value. Keep its + * binders around the annotation until the value's interface is observed. + */ + private def quantifiedType(tpe: DeclaredType, parameters: Ls[VarSymbol])(using NewResolverState): DeclaredType = + if parameters.isEmpty then tpe else + val resolution = rstate.quantifiedTypes.getOrElseUpdate((tpe.resolution, parameters), { + val result = new TypeResolution(tpe.resolution.source, tpe.resolution.fail) + result.publish(TypeShape.Polymorphic(parameters.map: parameter => + TypeQuantifier(TypeShape.Parameter(parameter, parameter.inferenceHost), N, N) + , N, tpe.resolution)) + result + }) + declaredType(resolution, tpe) + + private def typeViews(using rs: NewResolverState) = rs.typeViews + /** Each missing formal position belongs to its source type use. Keeping one + * live host preserves late bounds without allocating a quantified parameter. + */ + private[semantics] def omittedType(source: TypeResolution, parameter: VarSymbol)(using NewResolverState): DeclaredType = + rstate.omittedTypes.getOrElseUpdate((source, parameter), { + val resolution = new TypeResolution(source.source, source.fail) + val bounds = new TermShapeHost + resolution.publish(TypeShape.Hole(bounds.inferenceHost)) + declaredType(resolution, Map.empty) + }) + + /** Save both interpretations before traversing the template reference. A + * nominal template uses the application's polarity, whereas an alias binds + * the positive pair and composes polarity while interpreting its body. This + * keeps Identity[A] transparent and preserves captured enclosing binders. + * Each interpretation is itself an input/output argument pair. + * + * Supplied arguments belong to the application scope and move back to the + * template's endpoint before substitution. Omitted arguments instead belong + * to the source type reference: all views must read and constrain their shared + * host in that same scope. Rebasing an omitted host with each observation's + * path would reinterpret its bounds in unrelated callers' lexical scopes. + */ + private case class AppliedTypeArgument(negative: DeclaredType, positive: DeclaredType): + def at(polarity: Bool): DeclaredType = if polarity then positive else negative + def move(marks: Ls[Marks])(using NewResolverState): AppliedTypeArgument = + AppliedTypeArgument(transportType(negative, marks), transportType(positive, marks)) + + private case class TypeApplication(arguments: Ls[AppliedTypeArgument]): + def move(marks: Ls[Marks])(using NewResolverState): TypeApplication = + TypeApplication(arguments.map(_.move(marks))) + private val noTypeArguments = TypeApplication(Nil) + + private def typeApplication(arguments: Ls[TypeResolution], context: DeclaredType)(using NewResolverState): TypeApplication = + TypeApplication(arguments.map(res => AppliedTypeArgument( + declaredArgument(res, context, false), declaredArgument(res, context, true)))) + + private def typeArguments(source: DeclaredType, application: TypeApplication, parameters: Ls[TyParam], positive: Bool) + (using NewResolverState): Ls[DeclaredType] = + parameters.zipWithIndex.map: (parameter, index) => + application.arguments.lift(index).map(_.at(positive)).getOrElse( + omittedType(source.resolution, parameter.sym).instantiate(source.instances)) + + private def typeBindings(source: DeclaredType, arguments: TypeApplication, parameters: Ls[TyParam], positive: Bool) + (using NewResolverState): Map[VarSymbol, DeclaredType] = + effectiveBindings(source) ++ parameters.zip(typeArguments(source, arguments, parameters, positive)).map: (parameter, argument) => + parameter.sym -> argumentType(argument, parameter) + + /** External inputs contribute unknowns only to missing annotation parts. + * A written binder still denotes its declared type, including after a partial + * application fixes it; exposure must not add arbitrary values to that binder. + */ + private[semantics] def exposeTypeHoles(tpe: DeclaredType, unknown: TermShape, marks: Ls[Marks]) + (using NewResolverState): Unit = + if !rstate.exposedTypeHoles.add((tpe, unknown, marks)) then return + def follow(current: DeclaredType, args: TypeApplication, captures: Ls[Marks], seen: Set[TypeResolution]) + (using NewResolverState): Unit = if !seen(current.resolution) then + val next = seen + current.resolution + def descend(child: DeclaredType)(using NewResolverState): Unit = unknown.enter(captures) match + case value: TermShape => exposeTypeHoles(child, value, marks) + case NoShape => () + current.resolution.listen: + case TypeShape.Hole(host) => unknown.enter(captures) match + case value: TermShape => host.publish(value) + case NoShape => () + case TypeShape.Parameter(symbol, _) => + current.bindings.get(symbol).foreach(bound => follow(selectArgument(bound.instantiate(current.instances), current.positive), noTypeArguments, captures, next)) + case reference: TypeShape.Reference => + follow(referenceType(reference, current), args, captures, next) + case TypeShape.Contextual(reference) => + follow(reference.tpe.instantiate(current.instances).withOrigin(current.origin), args.move(inverseMarks(reference.marks)), + reference.marks ::: captures, next) + case TypeShape.Applied(base, arguments) => + follow(declaredType(base, current), typeApplication(arguments, current), captures, next) + case TypeShape.Alias(symbol, S(rhs)) => + follow(declaredType(rhs, typeBindings(current, args, symbol.defn.get.tparams, true), current.positive), noTypeArguments, captures, next) + case TypeShape.Nominal(cls) => + cls.tparams.zip(typeArguments(current, args, cls.tparams, current.positive)).foreach: (param, argument) => + listenArgumentParts(argumentType(argument, param)): parts => + exposeTypeHoles(transportType(parts.output, captures), unknown, marks) + case TypeShape.Tuple(fields) => fields.foreach(field => descend(declaredType(field, current))) + case TypeShape.Record(_, fields) => fields.foreach((_, field) => descend(declaredType(field, current))) + case TypeShape.Function(_, result) => descend(declaredType(result, current)) + case TypeShape.Polymorphic(parameters, _, body) => + val symbols = parameters.map(_.parameter.symbol) + val lexical = current.copy(bindings = current.bindings -- symbols, instances = current.instances.without(symbols))(current.origin) + follow(declaredType(body, lexical), args, captures, next) + case TypeShape.Wildcard(_, output) => output.foreach(res => follow(declaredType(res, current), args, captures, next)) + case TypeShape.Argument(parts) => follow(parts.output.instantiate(current.instances).withOrigin(current.origin), args, captures, next) + case TypeShape.SelectedArgument(argument, positive) => + listenArgumentParts(argument.instantiate(current.instances)): parts => + follow(if positive then parts.output else parts.input, args, captures, next) + case TypeShape.Union(left, right) => + follow(declaredType(left, current), noTypeArguments, captures, next) + follow(declaredType(right, current), noTypeArguments, captures, next) + case TypeShape.Intersection(left, right) => + follow(declaredType(left, current), noTypeArguments, captures, next) + follow(declaredType(right, current), noTypeArguments, captures, next) + case TypeShape.Combined(formula) => formula.orderedAtoms.foreach: atom => + follow(atom.instantiate(current.instances), noTypeArguments, captures, next) + case _ => () + withCanonicalType(tpe)(follow(_, noTypeArguments, Nil, Set.empty)) + + private def abstractTypes(using rs: NewResolverState) = rs.abstractTypes + // Narrowing a nominal receiver by projection does not infer the narrower + // class's arguments. This abstract interface is distinct from an inference hole. + private def abstractType(source: TypeResolution)(using NewResolverState): DeclaredType = abstractTypes.getOrElseUpdate(source, { + val resolution = new TypeResolution(source.source, source.fail) + resolution.publish(TypeShape.Abstract) + declaredType(resolution, Map.empty) + }) + + /** An annotation is an abstraction boundary, even when its implementation is + * available. Subscribe to the type graph, never to values flowing into it. + * Shared hosts retain subscriptions for forward and recursive type references. + */ + def listenTypeInstances(sign: Term)(listener: Listener)(using NewResolverState): Unit = + listenTypeInstances(declaredType(typeResolution(sign), Map.empty))(listener) + + private[semantics] def listenTypeInstances(tpe: DeclaredType)(listener: Listener)(using NewResolverState): Unit = + listener(InstanceShape(tpe.instantiate(rstate.instances))) + + /** Expanding a type is an observation, not a type-argument constraint. Cache + * observations before following parameters so recursive interfaces reach the + * same graph nodes instead of allocating fresh inference variables. + */ + private[semantics] def listenInstanceViews(value: TermShape)(listener: Listener)(using NewResolverState): Unit = + listenInstanceViews(value, mergeIntersections = true)(listener) + + private def listenConstraintViews(value: TermShape)(listener: Listener)(using NewResolverState): Unit = + listenInstanceViews(value, mergeIntersections = false)(listener) + + private def listenInstanceViews(value: TermShape, mergeIntersections: Bool)(listener: Listener)(using NewResolverState): Unit = value match + case Marked(instance: InstanceShape, marks) => + listenTypeViews(transportType(instance.tpe, marks :: Nil), mergeIntersections)(listener) + case _ => listener(value) + + private def listenTermViews(term: Term)(listener: Listener)(using NewResolverState): Unit = + listenTerm(term)(shape => listenInstanceViews(shape)(listener)) + + /** Apply a value's binder substitution without copying the source inference + * graph, allocating parameter instances, or consuming a callable's scheme. + * Deferred children inherit the flat map; existing captures take precedence + * over incoming entries. Each representation stores the view in its own form + * (DeclaredType, aggregate fields, or ContextualShape), preserving outer marks. + * Receiver activation is separate and is attached by publishActivated. + */ + private[semantics] def instantiateShape(value: TermShape, instances: TypeSubstitution)(using NewResolverState): TermShape = + if instances.isEmpty then value else rstate.shapeViews.getOrElseUpdate((value, instances), { + val Marked(source, marks) = value + val instantiated: NonMarkedShape = source match + case instance: InstanceShape => InstanceShape(instance.tpe.instantiate(instances)) + case rigid: RigidTypeShape => instances.get(rigid.parameter).fold[NonMarkedShape](rigid)(p => InstanceShape(instanceType(p))) + case tuple: TupleShape => tuple.copy(instances = instances.withOverrides(tuple.instances))(this) + case record: RecordShape => record.copy(instances = instances.withOverrides(record.instances)) + case nominal: NominalInstanceView => + // Own arguments are already bound; only enclosing binders can acquire + // new entries. Retaining unrelated callers' binders makes closed types + // such as Int distinct on each path through a cyclic constraint graph. + val lexical = nominalLexicalBinders(nominal.defn) + val bindings = instances.filter((parameter, _) => lexical(parameter)) + .map((parameter, instance) => parameter -> instanceType(instance)) ++ + nominal.bindings.map((parameter, bound) => parameter -> bound.instantiate(instances)) + nominal.copy(bindings = bindings, parent = nominal.parent.map(instantiateShape(_, instances)))(nominal.annotation, nominal.provenance)(this) + case record: RecordTypeShape => + RecordTypeShape(record.fields.map((name, tpe) => name -> tpe.instantiate(instances)))(record.source, record.declarations) + case contextual: ContextualShape => contextual.copy(instances = instances.withOverrides(contextual.instances)) + case specialized: SpecializedShape => specialized.copy( + arguments = specialized.arguments.map(_.instantiate(instances)), instances = instances.withOverrides(specialized.instances)) + case callable: CallableTypeShape => + val captured = instances.without(callable.tparams.map(_.parameter.symbol)) + def instantiate(tpe: DeclaredType): DeclaredType = tpe.instantiate(captured) + val scheme = callable.scheme.map: scheme => + scheme.copy(parameters = scheme.parameters.map: parameter => + parameter.copy(lower = parameter.lower.map(instantiate), upper = parameter.upper.map(instantiate))) + callable.copy(paramLists = callable.paramLists.ne_map: params => + DeclaredParams(params.params.map(_.map(instantiate)), params.hasRest, params.rest.map(instantiate)) + , result = callable.result.map(instantiate), scheme = scheme) + case literal: IntroShape if !literal.trm.isInstanceOf[Lam] => literal + case _: (UnknownValueShape | DynShape | OpaqueTypeShape | ErrShape) => source + case source: ContextualSource => ContextualShape(source, instances) + Marked(instantiated, marks) + }) + + /** Decode outer views only. ContextualSource excludes views and marked shapes; + * SpecializedShape contains a DefnShape; AppShape receivers are CoreTermShapes. + * After peeling outer marks, at most one view remains; application receivers + * cannot hide another view. MarkedShape preserves the decoded core's type. + * Keep applications intact: their consumed term lists are not view wrappers. + */ + private def shapeParts(value: TermShape): ShapeParts = + val Marked(source, marks) = value + source match + case ContextualShape(source, instances) => ShapeParts(Marked(source, marks), instances, N) + case SpecializedShape(declaration, arguments, instances) => + ShapeParts(Marked(declaration, marks), instances, S(arguments)) + case source: CoreShape => ShapeParts(Marked(source, marks), TypeSubstitution.empty, N) + + /** Merge only the outer fields, retaining symbolic references at every depth. + * Shared names use the same interned Boolean combinations as written types; + * recursively selecting a merged field therefore returns to existing typeViews. + * No merged shape is embedded in another or used as a new type atom. A finite + * set of regular field references yields finitely many normalized field types. + * The enclosing typeViews host caches the observation, and combinedTypes caches + * field combinations; a separate cache keyed by whole record sets is unnecessary. + */ + private def intersectViews(values: Ls[TermShape])(publish: Listener)(using NewResolverState): Unit = + val records = values.collect: + case Marked(record: RecordTypeShape, marks) => + RecordTypeShape(record.fields.map((name, tpe) => name -> transportType(tpe, marks :: Nil)))(record.source, record.declarations) + records.headOption.foreach: first => + val merged = records.tail.foldLeft(first): (left, right) => + val fields = right.fields.foldLeft(left.fields): + case (fields, (name, tpe)) => fields.updated(name, fields.get(name).fold(tpe)(previous => + combinedType(previous.resolution, previous, tpe, false))) + RecordTypeShape(fields)(left.source, right.declarations ++ left.declarations) + publish(merged) + values.distinct.foreach: + case Marked(_: RecordTypeShape, _) => () + case other => publish(other) + + private def listenTypeViews(tpe: DeclaredType)(listener: Listener)(using NewResolverState): Unit = + listenTypeViews(tpe, mergeIntersections = true)(listener) + + /** Constraint inspection propagates every intersection component separately. + * L1 & L2 <: R installs both L1 <: R and L2 <: R; it does not choose one. + * Do not wait for every component or form products of their candidates. + * Member lookup still merges record intersections, so cache the two kinds of + * observation separately and retain the choice through deferred references. + */ + private def listenTypeViews(tpe: DeclaredType, mergeIntersections: Bool)(listener: Listener)(using NewResolverState): Unit = + // Equal types can be observed through different input-only arguments. Reuse + // their semantic host, but report this observation's witness rather than the + // parameter declaration retained by the host's first subscriber. + def observe(shape: TermShape)(using NewResolverState): Unit = + val origin = tpe.origin.orElse: + if tpe.resolution.currentShapes.contains(TypeShape.Top) then S(TypeInterfaceReason.Unrestricted(tpe.resolution.source)) else N + (shape, origin) match + case (Marked(opaque: OpaqueTypeShape, marks), S(reason)) => listener(Marked(opaque.copy()(reason), marks)) + case _ => listener(shape) + val key = (tpe, mergeIntersections) + typeViews.get(key) match + case S(host) => host.listen(observe) + case N => + val host = new TermShapeHost + typeViews(key) = host + host.listen(observe) + def follow(current: DeclaredType, args: TypeApplication, aliases: Set[TypeResolution], source: Term, + publish: Listener)(using NewResolverState): Unit = + current.resolution.listen: shape => + def bind(params: Ls[TyParam], positive: Bool)(using NewResolverState): Map[VarSymbol, DeclaredType] = + typeBindings(current, args, params, positive) + def next(res: TypeResolution)(using NewResolverState): Unit = + follow(declaredType(res, current), noTypeArguments, aliases, res.source, publish) + // Each type can expose several union alternatives. Form products + // within a conjunction, never across separate union clauses. + // Each component is also its own diagnostic witness. Aliases and + // applications preserve that witness while following their definition. + def conjunction(remaining: Ls[DeclaredType], values: Ls[TermShape])(using NewResolverState): Unit = remaining match + case head :: tail => follow(head, noTypeArguments, aliases, head.resolution.source, + value => conjunction(tail, value :: values)) + case Nil => intersectViews(values.reverse)(publish) + shape match + case TypeShape.Dynamic => publish(DynShape()) + case TypeShape.Top => publish(OpaqueTypeShape(source)( + current.origin.getOrElse(TypeInterfaceReason.Unrestricted(source)))) + case TypeShape.Bottom => () + case TypeShape.Wildcard(_, output) => + output.fold(publish(OpaqueTypeShape(source)(TypeInterfaceReason.MissingOutput(current.resolution.source))))(next) + case TypeShape.Argument(parts) => follow(parts.output.instantiate(current.instances).withOrigin(current.origin), noTypeArguments, aliases, source, publish) + case TypeShape.SelectedArgument(argument, positive) => + // An argument cycle can forward this selection without exposing + // an interface. Keep the subscription for later parts, but stop + // traversing the same unproductive reference in this observation. + if !aliases(current.resolution) then + listenArgumentParts(argument.instantiate(current.instances)): parts => + follow(if positive then parts.output else parts.input, args, aliases + current.resolution, source, publish) + case TypeShape.Inferred(value) => listenInstanceViews(instantiateShape(value, current.instances), mergeIntersections)(publish) + case TypeShape.Hole(host) => host.subscribe: value => + listenInstanceViews(instantiateShape(value, current.instances), mergeIntersections)(publish) + case TypeShape.Nominal(defn) => + val parameters = nominalLexicalBinders(defn) ++ defn.tparams.map(_.sym) + val bindings = bind(defn.tparams, current.positive).filter((parameter, _) => parameters(parameter)) + defn.ext match + case N => publish(NominalInstanceView(defn, bindings, implicitParent(defn))(S(source), ShapeProvenance.empty)(this)) + case S(parent) => + // Only the parent's declared type is relevant here. Evaluating + // its constructor arguments would reintroduce implementation flow. + // Restrict substitution before capturing: a recursive field + // can supply an argument already inside this class. A parent + // that does not use it must not attempt that scope crossing. + val parentType = typeResolution(parent.cls) + withTypeDependencies(parentType): dependencies => + val local = captureNominalBindings(defn, bindings.filter((symbol, _) => dependencies(symbol))) + listenTypeViews(declaredType(parentType, local), mergeIntersections): ext => + publish(NominalInstanceView(defn, bindings, S(ext))(S(source), ShapeProvenance.empty)(this)) + case TypeShape.Alias(symbol, _) if symbol is prelude.builtins.Awaited => + val arguments = typeArguments(current, args, symbol.defn.get.tparams, true) + softAssert(arguments.length == 1) + listenTypeViews(arguments.head)(value => listenAwaitedValue(value, source)(publish)) + case TypeShape.Alias(symbol, rhs) => + // Revisiting the same alias reference without reaching a structural + // shape supplies no additional interface. Track + // references, not symbols: Id[Id[T]] has two distinct references. + if aliases(current.resolution) then publish(OpaqueTypeShape(source)(TypeInterfaceReason.RecursiveAlias(symbol))) + else rhs match + case S(rhs) => follow(declaredType(rhs, bind(symbol.defn.get.tparams, true), current.positive), noTypeArguments, + aliases + current.resolution, source, publish) + case N => publish(OpaqueTypeShape(source)(TypeInterfaceReason.AbstractDeclaration(symbol, source))) + case TypeShape.Applied(base, params) => + follow(declaredType(base, current), typeApplication(params, current), aliases, source, publish) + case TypeShape.Parameter(symbol, host) => current.bindings.get(symbol) match + case S(bound) => follow(selectArgument(bound.instantiate(current.instances), current.positive), noTypeArguments, aliases, source, publish) + case N => + val target = current.instances.get(symbol).fold(host)(_.inferenceHost) + listenTypeArgument(target): value => + // Report the use of an abstract parameter in a signature, not + // just its declaration. This survives substitution through + // generic members and nested tuple or function annotations. + val annotated = value match + case Marked(rigid: RigidTypeShape, marks) => + UnknownValueShape(rigid.source, fromPublicInterface = false)(rigid.provenance.withTypeOrigin(source)).exit(marks) + case Marked(unknown: UnknownValueShape, marks) => + unknown.copy()(unknown.provenance.withTypeOrigin(source)).exit(marks) + case _ => value + annotated match + case value: TermShape => listenInstanceViews(instantiateShape(value, current.instances), mergeIntersections)(publish) + case NoShape => () + case reference: TypeShape.Reference => + follow(referenceType(reference, current), args, aliases, source, publish) + case TypeShape.Contextual(reference) => + // Arguments belong to the application scope, whereas the + // template belongs to the reference's source scope. Move the + // arguments back before substitution, then transport the result. + follow(reference.tpe.instantiate(current.instances).withOrigin(current.origin), args.move(inverseMarks(reference.marks)), aliases, source, + shape => shape.exit(reference.marks) match + case value: TermShape => publish(value) + case NoShape => ()) + case TypeShape.Function(params, ret) => + val ps = params match + case Tup(fields) => DeclaredParams(fields.collect: + case Fld(_, sign, _) => S(declaredType(typeResolution(sign), current.copy(positive = !current.positive)(current.origin))) + , fields.exists(!_.isInstanceOf[Fld]), N) + case single => DeclaredParams(S(declaredType(typeResolution(single), current.copy(positive = !current.positive)(current.origin))) :: Nil, false, N) + publish(CallableTypeShape(source, ps ne_:: Nil, + S(declaredType(ret, current)), N, N, N, N)) + case TypeShape.Polymorphic(params, _, body) => + val symbols = params.map(_.parameter.symbol) + val lexical = current.copy(bindings = current.bindings -- symbols, instances = current.instances.without(symbols))(current.origin) + val binders = params.map: param => + DeclaredTypeParameter(param.parameter, + param.lower.map(declaredType(_, lexical)), + param.upper.map(declaredType(_, lexical))) + follow(declaredType(body, lexical), args, aliases, source, shape => shape match + case Marked(callable: CallableTypeShape, marks) => + callable.copy(scheme = S(TypeScheme(current.resolution, binders ::: callable.tparams))).exit(marks) match + case value: TermShape => publish(value) + case NoShape => () + case _ => publish(shape)) + case TypeShape.Tuple(fields) => + publish(TupleShape(current.resolution.source, + fields.map(field => TupleShape.TypedField(declaredType(field, current), Nil)))(this)) + case TypeShape.Record(source, fields) => + publish(RecordTypeShape(fields.map((field, sign) => field.sym.nme -> declaredType(sign, current)).toMap)( + source, fields.map((field, _) => field.sym.nme -> field).toMap)) + case TypeShape.Union(left, right) => next(left); next(right) + case TypeShape.Intersection(left, right) if !mergeIntersections => next(left); next(right) + case TypeShape.Intersection(left, right) => + conjunction(declaredType(left, current) :: declaredType(right, current) :: Nil, Nil) + case TypeShape.Combined(formula) if !mergeIntersections => + formula.orderedAtoms.foreach: atom => + val component = atom.instantiate(current.instances) + follow(component, noTypeArguments, aliases, component.resolution.source, publish) + case TypeShape.Combined(formula) => + // Choose witnesses in source order, independently of set hashing. + val clauses = formula.clauses.toList.sortWith((left, right) => + formula.orderedAtoms.find(atom => left(atom) != right(atom)).exists(left)) + clauses.foreach: clause => + conjunction(formula.orderedAtoms.filter(clause).map(_.instantiate(current.instances)).toList, Nil) + case TypeShape.Negation(_) => publish(OpaqueTypeShape(source)(TypeInterfaceReason.Negated(current.resolution.source))) + case TypeShape.Unit | TypeShape.Abstract => + publish(OpaqueTypeShape(source)(TypeInterfaceReason.Unavailable(current.resolution.source))) + withCanonicalType(tpe)(follow(_, noTypeArguments, Set.empty, tpe.resolution.source, host.publish)) + + // A separate full signature also annotates the implementation's parameters. + // Register these before its body is elaborated, so observed calls can never + // publish inferred alternatives for parameters behind this boundary. + private def signatureParameters(using rs: NewResolverState) = rs.signatureParameters + def registerParameterSignature(paramLists: Ls[ParamList], sign: Term)(using NewResolverState): Unit = + def bind(paramLists: Ls[ParamList], tpe: DeclaredType)(using NewResolverState): Unit = paramLists match + case Nil => () + case ps :: tail => listenTypeViews(tpe): + case Marked(callable: CallableTypeShape, marks) => + val declared = callable.paramLists.head + if !declared.hasRest then ps.params.zip(declared.params).foreach: (param, sign) => + if param.sign.isEmpty then sign.foreach: tpe => + val contextual = transportType(tpe, marks :: Nil) + signatureParameters(param.sym) = contextual + listenTypeInstances(contextual): shape => + if param.sym.currentShapes.add(shape) then param.sym.notifyShapeListeners(shape) + callable.result.foreach(tpe => bind(tail, transportType(tpe, marks :: Nil))) + case _ => () + bind(paramLists, declaredType(typeResolution(sign), Map.empty)) + + /** Synthesized constructor fields retain their source declaration on the symbol. */ + private[semantics] def resultSignature(td: TermDefinition)(using NewResolverState): Opt[Term] = td.tsym.decl match + case S(p: Param) => p.sign + case _ => td.resultSignature + + /** Materialize a deferred value result as a type edge, preserving the value's + * lexical marks until its consumer leaves the definition scope. In particular, + * observing an async result must not mix payloads from separate generic calls. + */ + private def resultType(source: Term, instances: TypeSubstitution)(connect: Listener => Unit) + (using NewResolverState): DeclaredType = + val key = (new Identity(source), instances) + rstate.resultTypes.get(key) match + case S(tpe) => tpe + case N => + val resolution = new TypeResolution(source, messages => resolError(source, messages)) + val tpe = declaredType(resolution, Map.empty) + rstate.resultTypes(key) = tpe + connect(value => resolution.publish(TypeShape.Inferred(value))) + tpe + + private def promiseShape(annotation: Annot.Async, payload: DeclaredType)(using NewResolverState): NominalInstanceView = + val cls = prelude.builtins.Promise.defn.get + softAssert(cls.tparams.length == 1 && cls.ext.isEmpty, + "The builtin Promise must have one fulfillment parameter and the implicit Object parent") + val provenance = ShapeProvenance( + msg"This async annotation wraps the body's result in a promise." -> N :: Nil).withTypeOrigin(annotation) + NominalInstanceView(cls, Map(cls.tparams.head.sym -> awaitedType(payload)), implicitParent(cls))(N, provenance)(this) + + /** Reify the projection before observing its operand. Expanding an inferred + * body's type parameter here would freeze the declaration's abstract view + * before a caller can substitute its own type argument. + */ + private def awaitedType(tpe: DeclaredType)(using NewResolverState): DeclaredType = + rstate.awaitedTypes.getOrElseUpdate(tpe, { + val symbol = prelude.builtins.Awaited match + case symbol: TypeAliasSymbol => symbol + case _ => lastWords("The builtin Awaited must be an abstract type alias") + val constructor = new TypeResolution(tpe.resolution.source, tpe.resolution.fail) + constructor.publish(TypeShape.Alias(symbol, N)) + val resolution = new TypeResolution(tpe.resolution.source, tpe.resolution.fail) + resolution.publish(TypeShape.Applied(constructor, tpe.resolution :: Nil)) + declaredType(resolution, tpe) + }) + + private def listenAwaitedValue(value: TermShape, source: Term)(listener: Listener)(using NewResolverState): Unit = + listenInstanceViews(value)(view => listenAwaitedView(view, source)(listener)) + + private def listenAwaitedView(value: TermShape, source: Term)(listener: Listener)(using NewResolverState): Unit = + // Intern before following nested promises, so recursive fulfillment edges + // share a host and late candidates still reach the existing consumers. + val key = (value, rstate.instances) + val tpe = rstate.awaitedValues.get(key) match + case S(result) => result + case N => + val resolution = new TypeResolution(source, messages => resolError(source, messages)) + val result = declaredType(resolution, Map.empty) + rstate.awaitedValues(key) = result + def publish(value: TermShape)(using NewResolverState): Unit = resolution.publish(TypeShape.Inferred(value)) + if !listenPromisePayload(value)(payload => listenAwaitedValue(payload, source)(publish)) then + value.applicationHead._1 match + case _: (DynShape | UnknownValueShape | OpaqueTypeShape | RigidTypeShape) => publish(value) + // JavaScript may adopt a structural thenable instead of returning + // the object itself. Until its callback interface can be inferred, + // preserve uncertainty rather than exposing the object's fields. + case _ => value.getMember("then") match + case MemberLookup.Missing => publish(value) + case _ => publish(UnknownValueShape(source, fromPublicInterface = false)(ShapeProvenance( + msg"This value may be a thenable; its fulfillment interface is not known." -> value.toLoc :: Nil))) + result + listenTypeViews(tpe)(listener) + + /** Result annotations constrain the body before async lowering. Consumers of + * the function observe Promise[Awaited[result]], including explicit returns. + * Inferred values retain their lexical endpoint until callers leave the scope. + */ + private def listenDefinitionResult(td: TermDefinition, instances: TypeSubstitution)(listener: Listener)(using NewResolverState): Unit = + def bodyResult(listener: Listener): Unit = td.sign match + case S(sign) => listenSignatureResult(declaredType(typeResolution(sign), Map.empty).instantiate(instances), + if td.flags.hasResultAnnotation then 0 else td.params.length)(listener) + case N => td.body.foreach(body => listenResult(body)(listener)) + td.asyncAnnotation match + case S(annotation) => + val source = td.sign.orElse(td.body).get + listener(promiseShape(annotation, resultType(source, instances)(bodyResult))) + case N => bodyResult(listener) + + /** Observe one application of a declared callable. Partial application keeps + * the async wrapper pending, even across the nested arrows of a full signature. + */ + private[semantics] def listenCallableResult(callable: CallableTypeShape)(listener: Listener)(using NewResolverState): Unit = + callable.asyncResult match + case S(AsyncResult(annotation, 1)) => + softAssert(callable.paramLists.tail.isEmpty) + // A nominal member without a result annotation still returns Promise, + // but its interface must not recover a payload type from the hidden body. + listener(promiseShape(annotation, callable.result.getOrElse(extremeType(true)))) + case pending => + val remaining = pending.map(info => info.copy(remainingLists = info.remainingLists - 1)) + callable.paramLists.tail match + case tail: NELs[DeclaredParams] => listener(callable.copy(paramLists = tail, asyncResult = remaining)) + case Nil => callable.result match + case S(tpe) if remaining.nonEmpty => listenTypeViews(tpe): + case Marked(next: CallableTypeShape, marks) => listener(Marked(next.copy(asyncResult = remaining), marks)) + case other => listener(other) + case S(tpe) => listenTypeInstances(tpe)(listener) + case N => listener(UnknownValueShape.at(callable.source)) + + /** Retain a scope transfer on the reference, including deferred fields and + * both argument parts. Flatten existing transfers before interning, so a + * recursive projection cannot build a tower of transport wrappers. + */ + private[semantics] def transportType(bound: DeclaredType, marks: Ls[Marks])(using NewResolverState): DeclaredType = + val (base, previous) = bound.resolution.currentShapes.toList match + case TypeShape.Contextual(reference) :: Nil => (reference.tpe.instantiate(bound.instances).withOrigin(bound.origin), reference.marks) + case _ => (bound, Nil) + softAssert(!base.resolution.currentShapes.exists: + case TypeShape.Contextual(_) => true + case _ => false + , "Contextual references must be flattened before they are interned") + // A wildcard exit followed by an entry is not an identity: the exit + // consumes a candidate's entry site and the new entry supplies its own. + // Deferred references use exactly the same transport as ordinary values. + InstanceShape(base).exit(previous).exit(marks) match + case Marked(_, NoMarks) => base + case Marked(_, path: SomeMarks) => + val reference = ContextualType(base, path :: Nil) + rstate.contextualTypes.getOrElseUpdate(reference, { + val resolution = new TypeResolution(base.resolution.source, base.resolution.fail) + resolution.publish(TypeShape.Contextual(reference)) + declaredType(resolution, Map.empty) + }).withOrigin(base.origin) + case NoShape => extremeType(false) + + private def inverseMarks(marks: Ls[Marks])(using NewResolverState): Ls[Marks] = + // Reverse directions using ordinary mark operations, not a reordered list + // of scopes. This is not an inverse on candidates: a wildcard exit can lose + // a call-site ID, which the reversed path cannot reconstruct. + InstanceShape(extremeType(false)).enter(marks) match + case Marked(_, NoMarks) => Nil + case Marked(_, path: SomeMarks) => path :: Nil + case NoShape => lastWords("Inverting a scope path cannot reject a candidate") + + private def referenceType(reference: TypeShape.Reference, context: DeclaredType)(using NewResolverState): DeclaredType = + transportType(declaredType(reference.base, context.bindings ++ reference.bindings, context.positive) + .instantiate(context.instances.withOverrides(reference.instances)).withOrigin(context.origin), reference.marks) + + private[semantics] def captureType(bound: DeclaredType, scope: AnyDefinitionSymbol)(using NewResolverState): DeclaredType = + transportType(bound, EntryMark(ResolutionBoundary(scope), N, NoMarks) :: Nil) + + /** Finish a projection on the deferred reference before delivering it to a + * caller. Leaving a wildcard entry outside InstanceShape would let a later + * call exit cancel it before the reference's own path had been composed. + */ + private def transportShape(value: TermShape, marks: Ls[Marks])(using NewResolverState): TermShape | NoShape = value match + case Marked(instance: InstanceShape, inner) => + InstanceShape(transportType(instance.tpe, inner :: marks)) + case _ => value.exit(marks) + + private def listenDeclaredMember(member: BlockMemberSymbol, bindings: Map[VarSymbol, DeclaredType], flow: FlowSymbol, + source: Term, annotation: Opt[Term], selected: ShapeListener[DefinitionSymbol[?]], receiver: Bool, positive: Bool, + receiverMarks: Ls[Marks], specialization: Opt[(FlowSymbol, Ls[DeclaredType])])(listener: Listener)(using NewResolverState): Unit = + member.onComplete: () => + valueTarget(member, receiver) match + case S(symbol: TermSymbol) if !symbol.isInstanceOf[ClassCtorSymbol] => + selected(symbol) + val td = symbol.defn.get + val crossesValueScope = !(td.k is syntax.RecordField) && !td.tsym.decl.exists(_.isInstanceOf[Param]) + val callerInstances = rstate.instances + // A selected generic method binds its own parameters anew. Receiver + // bindings can mention an earlier call of that same source method in + // their values, but cannot bind the new method's local names. + val capturedBindings = bindings -- definitionBinders(td) + val instances = if isByName(td) then instantiateByName(td, flow, + ExitMark(ResolutionBoundary(td.tsym), S(flow), NoMarks) :: receiverMarks, specialization, capturedBindings) else callerInstances + def publish(shape: TermShape)(using NewResolverState): Unit = + val Marked(interface, context) = shape + val generic = interface match + case callable: CallableTypeShape if !isByName(td) => + val parameters = td.tparams.toList.flatten.map(p => declaredParameter(p.sym)) ::: callable.tparams + callable.copy(scheme = if parameters.isEmpty then N else S(TypeScheme(td.tsym, parameters)), declaration = S(td), + asyncResult = td.asyncAnnotation.map(AsyncResult(_, td.params.length))) + case instance: InstanceShape if !isByName(td) => + InstanceShape(quantifiedType(instance.tpe, td.tparams.toList.flatten.map(_.sym))) + case _ => interface + // Constructor fields keep their parameter scope; structural fields + // follow their written type directly. Other members leave their own + // definition scope when their declared interface is selected. + val path = context :: (if crossesValueScope then + ExitMark(ResolutionBoundary(td.tsym), S(flow), NoMarks) :: Nil else Nil) + val exited = transportShape(generic, path) + exited match + case value: TermShape => listener(instantiateShape(value, instances))(using rstate.withInstances(callerInstances)) + case NoShape => () + def signature(sign: Term, polarity: Bool)(using NewResolverState): DeclaredType = + val tpe = declaredType(typeResolution(sign), capturedBindings, polarity) + if isByName(td) then tpe.instantiate(instances) else tpe + val result = resultSignature(td).map(signature(_, positive)) + if td.sign.nonEmpty && (td.k is syntax.Fun) && !td.flags.hasResultAnnotation && !isByName(td) then + listenTypeViews(signature(td.sign.get, positive))(publish) + else td.params match + case params: NELs[ParamList] => publish(CallableTypeShape(source, + params.ne_map(ps => DeclaredParams(ps.params.map(_.sign.map(signature(_, !positive))), + ps.restParam.nonEmpty, ps.restParam.flatMap(_.sign).map(signature(_, !positive)))), + result, N, N, S(td), N)) + case Nil if td.asyncAnnotation.nonEmpty => + publish(promiseShape(td.asyncAnnotation.get, result.getOrElse(extremeType(true)))) + case Nil => result match + case S(tpe) => listenTypeInstances(tpe)(publish) + case N => + // Keep the missing constructor annotation on the unknown value so + // later selections and calls can explain why argument flow is hidden. + val unknown = td.tsym.decl match + case S(param: Param) => UnknownValueShape(source, fromPublicInterface = false)(ShapeProvenance( + (msg"Constructor parameter '${param.sym.nme}' has no type annotation." -> param.toLoc) :: + annotation.toList.map(sign => + msg"This type annotation does not provide a type for field '${member.nme}'." -> sign.toLoc))) + case _ => UnknownValueShape.at(source) + publish(unknown) + case _ => + // A nested nominal declaration denotes its statically selected symbol; + // selecting it does not inspect an instance field or method body. + fromBMSAt(member, flow, Nil, listener, source, selected, receiver, specialization) + + def resolError(src: Term | Pattern, msgs: Ls[(Message, Opt[Loc])])(using rs: NewResolverState): Unit = + rs.reportResolutionError(src, msgs) + + def registerTypeParameters(owner: AnyDefinitionSymbol, params: Ls[VarSymbol])(using State, NewResolverState): Unit = + if params.nonEmpty then + // Check every generic body under an abstract activation, including private + // definitions and non-strict worksheets. Its fresh entry site cannot match + // a real caller's exit, so identity-like results still substitute the actual + // type argument without leaking this abstract candidate into the caller. + val site = FlowSymbol("generic interface") + params.foreach: param => + val ref = SimpleRef(param)(param.id) + val unknown = RigidTypeShape(param, ref) + publishParameter(param, MarkedShape.enter(unknown, ResolutionBoundary(owner), S(site))) + + /** Explicit and inferred type arguments use the same entry/exit paths as term + * arguments. An explicit argument fixes that instantiation; its value argument + * must not add a more precise implementation shape to the declared interface. + */ + private def listenTypeArgument(symbol: VarSymbol)(listener: Listener)(using NewResolverState): Unit = + listenTypeArgument(symbol.inferenceHost)(listener) + + private def listenTypeArgument(host: Publisher.Data[ShapeEvent])(listener: Listener)(using NewResolverState): Unit = + host.subscribe: + case value: TermShape => listener(value) + case _ => softAssert(false, "A type parameter's bounds must contain values without activation envelopes") + + private[semantics] def publishParameter(symbol: VarSymbol, shape: TermShape | NoShape)(using NewResolverState): Unit = + shape match + case value: TermShape if !symbol.decl.exists(_.isInstanceOf[TyParam]) => publishActivated(symbol, value) + case _ => publishParameter(symbol.inferenceHost, shape) + + private[semantics] def publishParameter(host: Publisher.Data[ShapeEvent], shape: TermShape | NoShape)(using NewResolverState): Unit = shape match + case value: TermShape => host.publish(value) + case _ => () + + private def declaredParameter(symbol: VarSymbol)(using NewResolverState): DeclaredTypeParameter = + DeclaredTypeParameter(TypeShape.Parameter(symbol, symbol.inferenceHost), N, N) + + private def bindTypeArguments(parameters: Ls[TypeShape.Parameter], marks: Ls[Marks], arguments: Ls[DeclaredType]) + (using NewResolverState): Unit = + // Establish every supplied position before publication can replay body + // constraints or trigger observations of a mutually dependent parameter. + parameters.zip(arguments).foreach((p, _) => rstate.markExplicitTypeArgument(p.symbol)) + parameters.zip(arguments).foreach: (parameter, argument) => + publishParameter(parameter.host, InstanceShape(argument).enter(marks)) + + /** Bounds constrain the instantiated references, never the shared checking + * binders. Install them after suppliedness is recorded, so a lower bound is + * checked against an explicit argument rather than widening its interface. + * Recursive and mutually dependent bounds reuse the ordinary relation graph. + */ + private def constrainQuantifiedBounds(parameters: Ls[DeclaredTypeParameter], substitution: TypeSubstitution) + (using NewResolverState): Unit = + parameters.foreach: parameter => + val instance = ContextualType(parameterType(parameter.parameter).instantiate(substitution), Nil) + parameter.lower.foreach(bound => constrainTypes(ContextualType(bound.instantiate(substitution), Nil), instance)) + parameter.upper.foreach(bound => constrainTypes(instance, ContextualType(bound.instantiate(substitution), Nil))) + + /** Consume the scheme at its authoritative site. Specialized values and curried + * tails carry instantiated references and no scheme, so later calls reuse it. + */ + private def instantiateCallable(callable: CallableTypeShape, site: FlowSymbol, + marks: Ls[Marks])(using NewResolverState): CallableTypeShape = callable.scheme match + case N => callable + case S(scheme) => + val key = (callable, site, marks) + rstate.instantiatedCallables.get(key) match + case S(instantiated) => instantiated + case N => + val substitution = rstate.instantiateTypeParameters(scheme.owner, site, scheme.parameters.map(_.parameter.symbol)) + def instantiate(tpe: DeclaredType): DeclaredType = tpe.instantiate(substitution) + val instantiated = callable.copy( + paramLists = callable.paramLists.ne_map: params => + DeclaredParams(params.params.map(_.map(instantiate)), params.hasRest, params.rest.map(instantiate)) + , result = callable.result.map(instantiate), scheme = N, supplied = N) + // Reentrant observations must find the view before supplied arguments + // can publish candidates or trigger another observation of this call. + rstate.instantiatedCallables(key) = instantiated + callable.supplied.foreach: arguments => + val parameters: Ls[TypeShape.Parameter] = scheme.parameters.map: parameter => + val instance = substitution(parameter.parameter.symbol) + TypeShape.Parameter(instance, instance.inferenceHost) + bindTypeArguments(parameters, marks, arguments) + constrainQuantifiedBounds(scheme.parameters, substitution) + instantiated + + /** The body must satisfy its annotated result even if no value escapes or no + * call inspects the implementation. In particular a returned closure receives + * its declared domain before its member targets are completed. + */ + def checkDeclaredResult(definition: TermDefinition)(using NewResolverState): Unit = + definition.body.foreach: body => + resultSignature(definition).foreach: sign => + val expected = declaredType(typeResolution(sign), Map.empty) + listenResult(body): shape => + inferTypeArguments(expected.instantiate(rstate.instances), shape, Nil) + + def checkAscription(body: Term, sign: Term)(using NewResolverState): Unit = + val expected = declaredType(typeResolution(sign), Map.empty) + listenTerm(body): shape => + inferTypeArguments(expected.instantiate(rstate.instances), shape, Nil) + + // Install each constraint edge before subscribing: callback parameter/result + // flow can revisit it immediately. Distinct instantiations retain their marks. + private def typeConstraints(using rs: NewResolverState) = rs.typeConstraints + /** Keep both endpoints symbolic. Register before following either endpoint, + * so recursive references reuse the relation even before any bound exists. + */ + private[semantics] def constrainTypes(lower: ContextualType, upper: ContextualType)(using NewResolverState): Unit = + if rstate.typeRelations.add((lower, upper)) then + InstanceShape(lower.tpe).exit(lower.marks).enter(upper.marks) match + case value: TermShape => inferTypeArguments(upper.tpe, value, upper.marks) + case NoShape => () + + private def inferTypeArguments(tpe: DeclaredType, value: TermShape, marks: Ls[Marks])(using NewResolverState): Unit = + if !typeConstraints.add((tpe, value, marks)) then return + val annotation = tpe.resolution.source + def follow(tpe: DeclaredType, args: TypeApplication, captures: Ls[Marks], + seen: Set[TypeResolution])(using NewResolverState): Unit = if !seen(tpe.resolution) then + val next = seen + tpe.resolution + def observe(listener: Listener)(using NewResolverState): Unit = + listenConstraintViews(value): actual => + actual.enter(captures) match + case actual: TermShape => listener(actual) + case NoShape => () + tpe.resolution.listen: + case TypeShape.Parameter(symbol, host) => tpe.bindings.get(symbol) match + case S(bound) => follow(selectArgument(bound.instantiate(tpe.instances), tpe.positive), noTypeArguments, captures, next) + case N => + val target = tpe.instances.get(symbol) + val destination = target.fold(host)(_.inferenceHost) + val lower = value.enter(captures) + if rstate.hasExplicitTypeArgument(target.getOrElse(symbol)) then + listenTypeArgument(destination): supplied => + // Compare in the caller's scope, retaining the supplied + // reference's endpoint for any further parameter constraint. + supplied.exit(captures).exit(marks) match + case Marked(instance: InstanceShape, context) => + val endpoint = if context == NoMarks then Nil else context :: Nil + value.exit(marks).enter(context) match + case actual: TermShape => inferTypeArguments(instance.tpe, actual, endpoint) + case NoShape => () + case _ => () + else publishParameter(destination, lower) + case reference: TypeShape.Reference => + follow(referenceType(reference, tpe), args, captures, next) + case TypeShape.Contextual(reference) => + follow(reference.tpe.instantiate(tpe.instances).withOrigin(tpe.origin), args.move(inverseMarks(reference.marks)), + reference.marks ::: captures, next) + case TypeShape.Applied(base, params) => + follow(declaredType(base, tpe), typeApplication(params, tpe), captures, next) + case TypeShape.Alias(symbol, S(rhs)) => + follow(declaredType(rhs, typeBindings(tpe, args, symbol.defn.get.tparams, true), tpe.positive), noTypeArguments, captures, next) + case TypeShape.Hole(host) => value.enter(captures) match + case value: TermShape => host.publish(value) + case NoShape => () + case TypeShape.Tuple(fields) => observe: + case unknown @ Marked(_: UnknownValueShape, _) => + fields.foreach(field => inferTypeArguments(declaredType(field, tpe), unknown, marks)) + case Marked(tuple: TupleShape, context) => + fields.zipWithIndex.foreach: (field, index) => + tuple.getMember(index.toString) match + case MemberLookup.Indexed(actual, inner) => actual.foreach: element => + listenTupleField(element): shape => + shape.exit(inner).exit(context) match + case value: TermShape => + inferTypeArguments(declaredType(field, tpe), value, marks) + case NoShape => () + case _ => () + case _ => () + case TypeShape.Union(left, right) => + follow(declaredType(left, tpe), noTypeArguments, captures, next) + follow(declaredType(right, tpe), noTypeArguments, captures, next) + case TypeShape.Intersection(left, right) => + follow(declaredType(left, tpe), noTypeArguments, captures, next) + follow(declaredType(right, tpe), noTypeArguments, captures, next) + // Resolution approximates an upper union by constraining every component. + // This supplies bounds without choosing branches or retaining alternative + // constraint sets. Lower intersections likewise contribute independent + // candidates through listenConstraintViews, avoiding candidate products. + case TypeShape.Combined(formula) => + formula.orderedAtoms.foreach(atom => follow(atom.instantiate(tpe.instances), noTypeArguments, captures, next)) + case TypeShape.Record(_, fields) => observe(constrainRecord(fields.map((field, sign) => field -> declaredType(sign, tpe)), _, marks)) + case TypeShape.Function(_, _) => observe(constrainFunction(tpe, _, marks)) + case TypeShape.Polymorphic(_, _, body) => + follow(declaredType(body, tpe), args, captures, next) + case TypeShape.Wildcard(input, _) => input.foreach(res => follow(declaredType(res, tpe.copy(positive = !tpe.positive)(tpe.origin)), args, captures, next)) + case TypeShape.Argument(parts) => follow(parts.input.instantiate(tpe.instances), args, captures, next) + case TypeShape.SelectedArgument(argument, positive) => + listenArgumentParts(argument.instantiate(tpe.instances)): parts => + follow(if positive then parts.output else parts.input, args, captures, next) + case TypeShape.Nominal(cls) => listenConstraintViews(value): value => + // Erasing a callback's signature must not erase the obligation to + // check its implementation. Its unannotated inputs can receive any + // value, independently of the calls observed through Function. + listenTypeViews(tpe): + case Marked(nominal: NominalInstanceView, _) if nominal.isInstanceOfClass(prelude.builtins.Function.defn.get) => + value.enter(captures) match + case actual: TermShape => + val provenance = ShapeProvenance(msg"The 'Function' type does not specify parameter types." -> N :: Nil) + .withTypeOrigin(annotation) + rstate.exposeValue(instantiateShape(actual, rstate.instances), provenance) + case NoShape => () + case _ => () + val arguments = typeArguments(tpe, args, cls.tparams, tpe.positive).map(transportType(_, captures)) + def constrainNominal(value: TermShape)(using NewResolverState): Unit = + val ShapeParts(actual, instances, _) = shapeParts(value) + val (head, context) = actual.applicationHead + def constrain(param: TyParam, bound: DeclaredType, pattern: DeclaredType)(using NewResolverState): Unit = + listenArgumentParts(bound.instantiate(instances)): actual => + listenArgumentParts(argumentType(pattern, param)): expected => + constrainTypes(ContextualType(actual.output, context), ContextualType(expected.output, Nil)) + constrainTypes(ContextualType(expected.input, Nil), ContextualType(actual.input, context)) + def originalParameters()(using NewResolverState): Unit = cls.tparams.zip(arguments).foreach: (param, pattern) => + val bound = parameterType(TypeShape.Parameter(param.sym, param.sym.inferenceHost)) + constrain(param, argumentType(bound, param), pattern) + def parent(ext: TermShape)(using NewResolverState): Unit = + // Parent arguments live at the parent's endpoint, which can cross + // both class and enclosing scopes. Preserve those marks together + // with the child's substitution on every step of the ancestry. + transportShape(instantiateShape(ext, instances), context) match + case value: TermShape => constrainNominal(value) + case NoShape => () + head match + case _: UnknownValueShape => + cls.tparams.zip(arguments).foreach: (param, argument) => + listenArgumentParts(argumentType(argument, param)): parts => + inferTypeArguments(parts.output, value, marks) + case ds: DefnShape => + if ds.clsDef.contains(cls) then originalParameters() else ds.ext.foreach(parent) + case base: BaseShape => + if base.defn is cls then originalParameters() else base.ext.foreach(parent) + case nominal: NominalInstanceView => + if nominal.defn is cls then cls.tparams.zip(arguments).foreach: (param, pattern) => + nominal.bindings.get(param.sym).foreach(constrain(param, _, pattern)) + else nominalParent(nominal).foreach(parent) + case tuple: TupleShape => parent(tuple.arrayParent) + case _ => () + constrainNominal(value) + case TypeShape.Top => () + case _ => value match + // A concrete target stays fixed, but must receive bounds that arrive + // through a symbolic source later. Concrete mismatch reporting is + // separate from retaining this obligation in the graph. + case Marked(_: InstanceShape, _) => listenConstraintViews(value)(inferTypeArguments(tpe, _, marks)) + case _ => () + // Alias expansion guards only unproductive cycles. Descending into a tuple, + // nominal argument, or arrow installs another edge with its own cycle guard. + withCanonicalType(tpe)(follow(_, noTypeArguments, Nil, Set.empty)) + + /** Structural constraints follow declared fields only. This supplies generic + * argument inference and callback checking without refining the annotation + * with additional properties from an actual record. + */ + private def constrainRecord(fields: Ls[(RcdField, DeclaredType)], + actual: TermShape, marks: Ls[Marks])(using NewResolverState): Unit = + fields.foreach: (field, expected) => + val flow = rstate.fieldProjectionSite(field.sym) + def receive(value: TermShape)(using NewResolverState): Unit = inferTypeArguments(expected, value, marks) + def member(info: MemberLookup, instances: TypeSubstitution)(using NewResolverState): Unit = info match + case MemberLookup.Contextual(source, substitution) => member(source, instances.withOverrides(substitution)) + case MemberLookup.Found(RecordMember(member, true), _) => receive(UnknownValueShape.at(member.rhs)) + case MemberLookup.Found(member, inner) => + fromBMS(member.memberSymbol, flow, inner, value => receive(instantiateShape(value, instances)), field.rhs, _ => (), false)(using rstate.withInstances(instances)) + case MemberLookup.Declared(member, bound, inner, annotation, positive) => + listenDeclaredMember(member, bound, flow, field.rhs, annotation, _ => (), false, positive, inner, N): value => + transportShape(instantiateShape(value, instances), inner) match + case value: TermShape => receive(value) + case NoShape => () + case MemberLookup.Typed(_, tpe, inner) => + receive(InstanceShape(transportType(tpe.instantiate(instances), inner))) + case MemberLookup.Dynamic(inner) => DynShape().exit(inner) match + case value: TermShape => receive(value) + case NoShape => () + case _ => () + actual match + case Marked(_: UnknownValueShape, _) => receive(actual) + case _ => member(actual.getMember(field.sym.nme), TypeSubstitution.empty) + + /** Function constraints reverse the parameter flow and preserve result flow. + * Read an implementation only while checking it against its declared interface; + * calls through that interface continue to expose only the declared result. + */ + private[semantics] def constrainFunction(expected: DeclaredType, source: TermShape, marks: Ls[Marks])(using NewResolverState): Unit = + val ShapeParts(actual, instances, _) = shapeParts(source) + actual match + case Marked(_: InstanceShape, _) => + listenConstraintViews(actual)(constrainFunction(expected, _, marks)) + return + case _ => () + val context = actual.applicationHead._2 + def results(listener: Listener)(using NewResolverState): Unit = actual.applicationHead._1 match + case intro: IntroShape => intro.trm match + case Lam(_, body) => listenResult(body)(shape => listener(instantiateShape(shape, instances)))(using rstate.withInstances(instances)) + case _ => () + case ds: DefnShape => ds.defn match + case td: TermDefinition => listenDefinitionResult(td, instances)(shape => + listener(instantiateShape(shape, instances)))(using rstate.withInstances(instances)) + case _ => () + case _ => () + def matchLists(expected: DeclaredType, remaining: Ls[(ParamList, Ls[Marks])])(using NewResolverState): Unit = + listenTypeViews(expected): + case Marked(callable: CallableTypeShape, expectedContext) => remaining match + case (params, _) :: tail => + val declared = callable.paramLists.head + if declared.params.length < params.params.length + || (declared.params.length > params.params.length && params.restParam.isEmpty) + || (declared.hasRest && params.restParam.isEmpty) then + resolError(expected.resolution.source, msg"Callback parameter list does not match its declared function type." -> actual.toLoc :: Nil) + else + params.params.zip(declared.params).foreach: (param, sign) => + sign.foreach: tpe => + listenTypeInstances(tpe): shape => + constrainParameterIn(param, shape.exit(expectedContext).enter(context), context, instances) + params.restParam.foreach: rest => + val fields = declared.params.drop(params.params.length).map: + case S(tpe) => TupleShape.TypedField(tpe, Nil) + case N => TupleShape.UnknownField(callable.source, Nil) + val suffix = if declared.hasRest + then TupleShape.Unknown(callable.source, TupleShape.UnknownField(callable.source, Nil)) :: Nil + else Nil + val tuple = TupleShape(callable.source, fields ::: suffix)(this) + constrainParameterIn(rest, tuple.exit(expectedContext).enter(context), context, instances) + callable.result.foreach: ret => + tail match + case Nil => results: shape => + shape.exit(context).enter(expectedContext) match + case value: TermShape => inferTypeArguments(ret, value, marks) + case NoShape => () + case _ => matchLists(ret, tail) + case Nil => () + case Marked(record: RecordTypeShape, _) => + constrainRecord(record.fields.iterator.map((name, tpe) => record.declarations(name) -> tpe).toList, + actual, marks) + case _ => () + actual match + case Marked(_: UnknownValueShape, _) => + listenTypeViews(expected): + case Marked(callable: CallableTypeShape, _) => + callable.result.foreach(ret => inferTypeArguments(ret, actual, marks)) + case _ => () + case Marked(givenType: CallableTypeShape, actualContext) => + listenTypeViews(expected): + case Marked(wanted: CallableTypeShape, expectedContext) => + val domain = givenType.paramLists.head + val expectedDomain = wanted.paramLists.head + if domain.params.length != expectedDomain.params.length || domain.hasRest != expectedDomain.hasRest then + resolError(expected.resolution.source, msg"Callback parameter list does not match its declared function type." -> actual.toLoc :: Nil) + else + domain.params.zip(expectedDomain.params).foreach: + case (S(givenParam), S(wantedParam)) => + listenTypeInstances(wantedParam): shape => + shape.exit(expectedContext).enter(actualContext) match + case value: TermShape => inferTypeArguments(givenParam, value, context) + case NoShape => () + case _ => () + wanted.result.foreach: ret => + def receive(shape: TermShape)(using NewResolverState): Unit = shape.exit(actualContext).enter(expectedContext) match + case value: TermShape => inferTypeArguments(ret, value, marks) + case NoShape => () + listenCallableResult(givenType)(receive) + case _ => () + case _ => matchLists(expected, actual.unappliedParams) + + private def listenSignatureResult(tpe: DeclaredType, count: Int)(listener: Listener)(using NewResolverState): Unit = + if count == 0 then listenTypeInstances(tpe)(listener) + else listenTypeViews(tpe): + case Marked(callable: CallableTypeShape, marks) => + callable.result.foreach(tpe => listenSignatureResult(transportType(tpe, marks :: Nil), count - 1)(listener)) + case _ => () + + private def tupleArrayParents(using rs: NewResolverState) = rs.tupleArrayParents + /** A tuple is an Array whose element argument is the union of its field shapes. + * Cache the parent before following fields: a recursive tuple can require its + * own Array interface while one of those fields is still being resolved. + */ + private[semantics] def tupleArrayParent(tuple: TupleShape)(using NewResolverState): NominalInstanceView = + tupleArrayParents.get(new Identity(tuple)) match + case S(parent) => parent + case N => + val cls = prelude.builtins.Array.defn.get + softAssert(cls.tparams.length == 1, "The builtin Array must have one element type parameter") + val elements = new TypeResolution(tuple.source, messages => resolError(tuple.source, messages)) + val parent = NominalInstanceView(cls, Map(cls.tparams.head.sym -> declaredType(elements, Map.empty)), implicitParent(cls))(N, ShapeProvenance.empty)(this) + tupleArrayParents(new Identity(tuple)) = parent + tuple.segments.foreach: segment => + listenTupleField(segment.element): value => + elements.publish(TypeShape.Inferred(value)) + parent + + /** Mutable literals share Array's existing element parameter, with an allocation + * mark to keep distinct arrays and enclosing call activations independent. + * Seed the interface before following elements so empty and recursive arrays + * can receive writes. No positions or lengths survive this conversion. + */ + private def mutableArray(source: Term.Mut, underlying: Tup)(using NewResolverState): TermShape = + rstate.mutableArrays.get(new Identity(source)) match + case S(array) => array + case N => + val cls = prelude.builtins.Array.defn.get + softAssert(cls.tparams.length == 1, "The builtin Array must have one element type parameter") + val param = cls.tparams.head.sym + val site = FlowSymbol("mutable array")(using rstate.owner) + val context = ExitMark(ResolutionBoundary(cls.sym), S(site), NoMarks) + val elements = new TypeResolution(source, messages => resolError(source, messages)) + elements.publish(TypeShape.Parameter(param, param.inferenceHost)) + // A nominal interface lives at the literal's use site. Its element + // reference points back into the allocation; putting the allocation exit + // on the whole interface would also exit a selected method's class scope + // twice, once here and once in NominalInstanceView.getMemberImpl. + val element = transportType(declaredType(elements, Map.empty), context :: Nil) + val shape = NominalInstanceView(cls, Map(param -> element), implicitParent(cls))(N, ShapeProvenance.empty)(this) + rstate.mutableArrays(new Identity(source)) = shape + listenTerm(underlying): tuple => + listenArrayElements(tuple): element => + publishParameter(param, element.enter(context :: Nil)) + shape + + /** Member lookup on a declared nominal interface reached through `receiver`. + * + * A member's signature is interpreted at its class's definition: its Capture + * nodes cross from the member into the class's enclosing scopes. The receiver + * path, however, starts where the value was created or annotated, which can be + * nested more deeply. For example, in + * `fun mk(xs) = xs.map((x, ...) => mk([x]))`, the tuple `[x]` is created in + * one activation of `mk` and passed to another, so its path is `β†˜mk β†—mk`, + * whereas `mk([1])` passes a tuple with path `β†˜mk`. The innermost exits of + * scopes that do not enclose the class's definition (here, the first `β†—mk`, + * since `Array` is global) are the value's provenance. They transport the + * class's type-argument bindings, but do not move the member's scope. + * Appending the whole path to the member instead places the scope of `map` + * inside `mk` for one receiver and outside it for the other. Both receivers + * share the `U` instance of the same `map` selection. Candidates published + * through one path then cross the same scope twice when they are read + * through the other path. + */ + private[semantics] def nominalMember(view: NominalInstanceView, name: Str, receiver: Marks)(using NewResolverState): MemberLookup = + val defn = view.defn + // Module/object references introduce no value invocation boundary. + val scope = defn.sym match + case _: ModuleOrObjectSymbol => N + case symbol => S(ResolutionBoundary(symbol)) + defn.body.members.get(name) match + case S(member) => + val (location, provenance) = splitReceiverPath(defn, receiver) + // Supplied arguments are outside the class; its parameter annotations + // are inside. Enclosing binders keep their own lexical contexts and + // enter the class through the annotation's explicit Capture nodes. + val transported = view.bindings.map: (symbol, bound) => + val argument = provenance match + case NoMarks => bound + case provenance: ExitMark => transportType(bound, provenance :: Nil) + symbol -> argument + val local = captureNominalBindings(defn, transported) + MemberLookup.Declared(member, local, scope.toList.map(ExitMark(_, N, NoMarks)), view.annotation, true) + .withMarks(location match + case NoMarks => Nil + case location: SomeMarks => location :: Nil) + case N => + (name.toIntOption, arrayElementType(view)) match + // An element's value keeps its whole path, including its provenance. + case (S(index), S(element)) if index >= 0 => + MemberLookup.Indexed(TupleShape.TypedField(element, Nil) :: Nil, Nil).withMarks(receiver :: Nil) + case _ => + // The parent is referenced inside the class, like its own members' + // annotations: `class Int extends Num` captures `Num` into Int's + // scope. An inherited member exits that scope before the receiver + // path applies, just like an own member. + val inherited = nominalParent(view).fold[MemberLookup](MemberLookup.Missing): parent => + val Marked(parentView, inner) = parent + InstanceShape(extremeType(false)).exit(inner).exit(receiver) match + case Marked(_, path) => parentView.getMemberThrough(name, path) + // The parent interface is not reachable through this receiver; + // applying these marks to its members yields no candidates. + case NoShape => parentView.getMember(name).withMarks(inner :: receiver :: Nil) + inherited.withAnnotation(view.annotation) + + private def nominalLexicalBinders(defn: ClassLikeDef): Set[VarSymbol] = + defn.sym.getState.newResolverState.lexicalTypeBinders.get(defn.sym).getOrElse( + lastWords(s"Nominal declaration ${defn.sym.nme} must record its enclosing type binders")) + + /** Supplied class arguments are outside the instance scope. Both member and + * parent annotations refer to those parameters from inside that scope. + * Enclosing binders instead enter through their own explicit Capture nodes. + */ + private def captureNominalBindings(defn: ClassLikeDef, bindings: Map[VarSymbol, DeclaredType]) + (using NewResolverState): Map[VarSymbol, DeclaredType] = + val parameters = defn.tparams.map(_.sym).toSet + bindings.map: (symbol, bound) => + symbol -> (if parameters(symbol) && !defn.sym.isInstanceOf[ModuleOrObjectSymbol] + then captureType(bound, defn.sym) else bound) + + /** A nominal value lives outside its class's instance scope, but its parent + * annotation is resolved inside that scope. Leave it before applying the + * value's caller path, both for inherited members and ancestor constraints. + * Constructed shapes already carry this exit in their constructor context; + * adding it to those shapes would cross the same boundary twice. + */ + private def nominalParent(view: NominalInstanceView)(using NewResolverState): Opt[TermShape] = + view.parent.flatMap: parent => + val exited = view.defn.sym match + case _: ModuleOrObjectSymbol => parent + case symbol => transportShape(parent, ExitMark(ResolutionBoundary(symbol), N, NoMarks) :: Nil) + exited match + case parent: TermShape => S(parent) + case NoShape => N + + /** Split a receiver path into the path from `defn`'s definition to the consumer, + * and the innermost exits of scopes not enclosing that definition. Exits are + * applied innermost first, so once a path leaves an enclosing scope of `defn`, + * it cannot leave a scope that does not enclose `defn`. + */ + private def splitReceiverPath(defn: ClassLikeDef, receiver: Marks)(using NewResolverState): (Marks, ExitMarks) = + val enclosing = defn.sym.getState.newResolverState.lexicalBoundaries.get(defn.sym).getOrElse( + lastWords(s"Nominal declaration ${defn.sym.nme} must record its enclosing resolution scopes")) + def exits(path: ExitMarks): (ExitMarks, ExitMarks) = path match + case ExitMark(boundary, id, rest) if enclosing(boundary) => + val (location, provenance) = exits(rest) + (ExitMark(boundary, id, location), provenance) + case provenance => + softAssert(!enclosing.exists(provenance.hasExit), + "A receiver path cannot leave an enclosing scope of its class before a nested scope") + (NoMarks, provenance) + def go(path: Marks): (Marks, ExitMarks) = path match + case EntryMark(boundary, id, rest) => + val (location, provenance) = go(rest) + (EntryMark(boundary, id, location), provenance) + case path: ExitMarks => exits(path) + go(receiver) + + /** A checked index `prefix.[idx]` reads an element of an array. The index is + * evaluated at runtime, so every element is a candidate, as for a spread. + * Unlike a dynamic access `prefix![idx]`, it does not authorize dynamic + * operations on the result: only a dynamic array has dynamic elements. + */ + private def listenArrayIndex(sel: DynSel, prefix: Term)(listener: Listener)(using NewResolverState): Unit = + listenTermViews(prefix): receiver => + if !listenArrayElements(receiver)(listener) then receiver match + case Marked(_: DynShape, _) => listener(receiver) + case Marked(unknown: UnknownValueShape, marks) => + unknown.copy(source = sel)(unknown.provenance.via(msg"This array index has no known element shape." -> sel.toLoc)) + .exit(marks) match + case value: TermShape => listener(value) + case NoShape => () + case _ => + if rstate.arrayIndexErrors.add(new Identity(sel)) then + resolError(sel, receiver.diagnostic(msg"${receiver.describe.capitalize} does not support checked array indexing.") ::: + (msg"Use '![...]' for a dynamic access." -> N) :: Nil) + + private[semantics] def arrayElementType(array: NominalInstanceView): Opt[DeclaredType] = + val cls = prelude.builtins.Array.defn.get + array.ancestor(cls).flatMap(_.bindings.get(cls.tparams.head.sym)) + + /** Element writes constrain the same declared parameter as Array's methods. + * An annotated element type receives input constraints without exposing + * additional members from the assigned value. + */ + private[semantics] def assignArrayElement(lhs: Term, rhs: Term)(using NewResolverState): Unit = + val receiver = lhs match + case NewSel(prefix, id, N) if id.name.toIntOption.exists(_ >= 0) => S(prefix) + // Dynamic writes also reach the elements that checked indexing reads. + case DynSel(prefix, _, true, _) => S(prefix) + case _ => N + receiver.foreach: prefix => + listenTermViews(prefix): array => + val (head, context) = array.applicationHead + head match + case nominal: NominalInstanceView => arrayElementType(nominal).foreach: element => + listenTerm(rhs): value => + value.enter(context) match + case value: TermShape => inferTypeArguments(element, value, context) + case NoShape => () + case _ => () + + private[semantics] def listenTupleField(field: TupleShape.Fixed)(listener: Listener)(using NewResolverState): Unit = + def receive(shape: TermShape)(using NewResolverState): Unit = transportShape(shape, field.marks) match + case value: TermShape => listener(value) + case NoShape => () + field match + case TupleShape.Field(field, _) => listenTerm(field.term)(receive) + case TupleShape.TypedField(tpe, _) => listenTypeInstances(tpe)(receive) + case TupleShape.UnknownField(source, _) => receive(UnknownValueShape.at(source)) + case TupleShape.ValueField(value, _) => receive(value) + case TupleShape.ViewedField(source, instances, _) => + listenTupleField(source)(value => receive(instantiateShape(value, instances)))(using rstate.withInstances(instances)) + + /** Array spreads have unknown length, but their declared element type still + * constrains every element. Never recover elements from constructor values: + * Arrays are mutable, and the one-number constructor creates empty slots. + */ + private[semantics] def listenArrayElements(shape: TermShape)(listener: Listener)(using NewResolverState): Bool = + shape.applicationHead._1 match + case tuple: TupleShape => + listenArrayElements(tuple.arrayParent)(element => + transportShape(element, shape.applicationHead._2) match + case value: TermShape => listener(value) + case NoShape => ()) + case _ => listenClassArgument(shape, prelude.builtins.Array.defn.get)(listener) + + private[semantics] def listenPromisePayload(shape: TermShape)(listener: Listener)(using NewResolverState): Bool = + listenClassArgument(shape, prelude.builtins.Promise.defn.get)(listener) + + /** Read the sole type argument of a nominal instance or an inferred construction. + * Marks leave the allocation and enclosing scopes before the payload is observed. + */ + private def listenClassArgument(shape: TermShape, cls: ClassLikeDef)(listener: Listener)(using NewResolverState): Bool = + softAssert(cls.tparams.length == 1, "The observed class must have one type parameter") + val param = cls.tparams.head.sym + val ShapeParts(source, instances, _) = shapeParts(shape) + val head = source.applicationHead._1 + val context = source.applicationHead._2 + def receive(element: TermShape)(using NewResolverState): Unit = transportShape(element, context) match + case value: TermShape => listener(value) + case NoShape => () + head match + case nominal: NominalInstanceView => nominal.ancestor(cls) match + case S(instance) => + instance.bindings.get(param) match + case S(bound) => + listenTypeInstances(bound.instantiate(instances))(receive) + true + case N => false + case N => false + case defn: DefnShape if defn.clsDef.contains(cls) && source.isInstanceOfClass(cls) => + val parameter = instances.getOrElse(param, param) + listenTypeArgument(parameter)(receive) + true + case _ => false + + /** Subscribe once to complete argument-tuple candidates. A pending spread is not + * an argument, nor evidence of an arity mismatch; each resolved combination is. + */ + def zipArgs(mss: Ls[Marks], ps: Ls[Param], r: Opt[Param], args: Term, src: Term, funSh: TermShape)(using NewResolverState): Unit = + zipArgsIn(mss, ps, r, args, src, funSh, rstate.instances) + + private def zipArgsIn(mss: Ls[Marks], ps: Ls[Param], r: Opt[Param], args: Term, src: Term, funSh: TermShape, + instances: TypeSubstitution)(using NewResolverState): Unit = + val params = ps ::: r.toList + zipArgumentShapes(mss, ps.length, r.nonEmpty, args, src, funSh): (index, shape) => + constrainParameterIn(params(index), shape, mss, instances) + + private[semantics] def constrainParameter(p: Param, shape: TermShape | NoShape, marks: Ls[Marks])(using NewResolverState): Unit = + constrainParameterIn(p, shape, marks, rstate.instances) + + private def constrainParameterIn(p: Param, shape: TermShape | NoShape, marks: Ls[Marks], + instances: TypeSubstitution)(using NewResolverState): Unit = + shape match + case NoShape => () + case sh: TermShape => + parameterSignature(p) match + case S(tpe) => inferTypeArguments(tpe.instantiate(instances), sh, marks) + case N => publishParameter(p.sym, sh)(using rstate.withInstances(instances)) + + private[semantics] def parameterSignature(p: Param)(using NewResolverState): Opt[DeclaredType] = + p.sign.map(sign => declaredType(typeResolution(sign), Map.empty)).orElse(signatureParameters.get(p.sym)) + .orElse(p.sym.getState.newResolverState.signatureParameters.get(p.sym)) + + private def zipArgumentShapes(mss: Ls[Marks], expectedCount: Int, hasRest: Bool, + args: Term, src: Term, funSh: TermShape)(publish: (Int, TermShape | NoShape) => NewResolverState ?=> Unit)(using NewResolverState): Unit = + def matchSegments(tuple: TupleShape, marks: Marks)(using NewResolverState): Unit = + val segments = tuple.segments + val knownCount = segments.count(_.isInstanceOf[TupleShape.Fixed]) + // Distribute only successful argument counts. Unknown spreads retain all + // possible fixed positions and the residual tail for a rest parameter. + val extra = (expectedCount - knownCount).max(0) + def assign(segment: TupleShape.Segment, positions: Range)(using NewResolverState): Unit = + positions.foreach: index => + listenTupleField(segment.element): sh => + publish(index, sh.exit(marks).enter(mss)) + def loop(rest: Ls[TupleShape.Segment], before: Int, unknownBefore: Bool)(using NewResolverState): Unit = rest match + case Nil => () + case (field: TupleShape.Fixed) :: tail => + val unknownAfter = tail.exists(_.isInstanceOf[TupleShape.Unknown]) + // With no later unknown segment, preceding spreads must supply all + // missing fixed arguments, even when the function has a rest parameter. + val first = if unknownBefore && !unknownAfter then before + extra else before + val last = if !unknownBefore then before + else if hasRest then expectedCount - 1 else before + extra + assign(field, first.max(0) until (last + 1).min(expectedCount)) + loop(tail, before + 1, unknownBefore) + case (unknown: TupleShape.Unknown) :: tail => + val end = if hasRest then expectedCount else before + extra + assign(unknown, before until end) + loop(tail, before, true) + loop(segments, 0, false) + if hasRest then + // If consumption reaches an unknown segment, some subsequent fields + // may have been consumed too. Approximate that optional prefix with an + // unknown segment, retaining the suffix that must remain. Publishing + // separate concrete tails here would turn uncertainty into false arity + // failures when the rest tuple is spread into another call. + def drop(xs: Ls[TupleShape.Segment], count: Int, approximate: Bool)(using NewResolverState): Ls[TupleShape.Segment] = + if count == 0 then xs + else xs match + case Nil => Nil + case (_: TupleShape.Fixed) :: rest => drop(rest, count - 1, approximate) + case (unknown: TupleShape.Unknown) :: Nil => + // Consuming a prefix changes the length, but not the element + // shape, when there are no following fields to merge into it. + if approximate then unknown :: Nil else Nil + case (_: TupleShape.Unknown) :: rest => + val suffix = drop(rest, count, false) + if approximate then TupleShape.Unknown(tuple.source, TupleShape.UnknownField(tuple.source, Nil)) :: suffix else suffix + val remaining = drop(segments, expectedCount, true) + val rest = if expectedCount == 0 then tuple else TupleShape.restView(tuple, remaining)(this) + publish(expectedCount, rest.exit(marks).enter(mss)) + checkArgumentArity(args, expectedCount, hasRest, src, funSh): + case (tuple, marks) => matchSegments(tuple, marks) + + private def checkArgumentArity(args: Term, expected: Int, rest: Bool, src: Term, + callable: TermShape)(matched: ShapeListener[(TupleShape, Marks)])(using NewResolverState): Unit = + val reportKey = new Object + listenTermViews(args): + case Marked(tuple: TupleShape, marks) => + val known = tuple.segments.count(_.isInstanceOf[TupleShape.Fixed]) + val unknown = known != tuple.segments.length + val mismatch = (!unknown && known < expected) || (!rest && known > expected) + if mismatch then + if rstate.reportedArities.getOrElseUpdate(reportKey, mutable.Set.empty).add(known) then + val count = if unknown then msg"at least ${known}" else msg"${known}" + resolError(src, callable.diagnostic(msg"${callable.describe.capitalize} expected ${expected} ${ + "argument".pluralized(expected)}, but got ${count}")) + else matched((tuple, marks)) + case _ => resolError(src, msg"Expected an argument tuple." -> args.toLoc :: Nil) + + /** Resolve every constructor pattern through the same symbolic interpretation. + * A class overload takes precedence over its term companion in this context. + * Keep every candidate so lowering can diagnose ambiguity independently of + * the order in which definitions and receiver shapes become available. */ + def constructorPattern(res: Pattern.Constructor)(using NewResolverState): Unit = if newResolution then + val lhs = res.target + def select(sym: DefinitionSymbol[?])(using NewResolverState): Bool = + lhs.withoutCaptures match + case trm: NewResolvable => + rstate.recordResolution(trm, trm.resolvedTargets.contains(sym))(trm.resolvedTargets ::= sym) + case _ => () + if res.resolvedTargets.contains(sym) then false + else + rstate.recordResolution(res, false)(res.resolvedTargets ::= sym) + true + def reject(sh: TermShape)(using NewResolverState): Unit = + rstate.markError(res) + resolError(res, sh.diagnostic(msg"${sh.describe.capitalize} cannot be used as a constructor pattern.")) + def classPattern(cls: ClassDef | ModuleOrObjectDef)(using NewResolverState): Unit = if select(cls.sym) then + val assoc = res.arguments match + case N => Nil + case S(args) => cls.paramsOpt match + case N => + if args.nonEmpty || cls.isInstanceOf[ClassDef] then + rstate.markError(res) + resolError(res, msg"${cls.describe.capitalize} does not take pattern arguments." -> cls.toLoc :: Nil) + Nil + case S(ps) => + if ps.restParam.nonEmpty then TODO(ps.restParam) + if args.sizeCompare(ps.params) =/= 0 then + rstate.markError(res) + resolError(res, + msg"${cls.describe.capitalize} expected ${ps.params.length} ${ + "pattern argument".pluralized(ps.params.length)}, but got ${args.length}" -> cls.toLoc :: Nil) + ps.params.lazyZip(args).flatMap: (p, a) => + p.fldSym match + case S(fldSym: BlockMemberSymbol) => + // withFields reports member/alias conflicts before dropping all + // generated fields for recovery. Parameters retain their fldSym, + // so verify its identity in the recovered body: a body member may + // occupy the same name. Do not publish dangling pattern fields or + // report another error for this already-diagnosed class. + if cls.body.members.get(fldSym.nme).contains(fldSym) then (fldSym -> a) :: Nil + else + rstate.markError(res) + Nil + case _ => + rstate.markError(res) + resolError(res, msg"Pattern argument requires an accessible constructor field." -> p.toLoc :: Nil) + Nil + if !rstate.hasError(res) then + val site = FlowSymbol.pat()(using rstate.owner) + def publish(marks: Ls[Marks])(using NewResolverState): Unit = + val psh = CtorPatternShape(cls, assoc, res, site, marks) + if res.currentShapes.add(psh) then res.notifyShapeListeners(psh) + cls match + // An ambiguous lookup can publish several constructors. Pair each + // declaration only with its own references; the lookup diagnoses + // competing candidates independently of this shape subscription. + // The pattern already selected its class target above; this subscription + // recovers capture marks without adding the operand's term target. + case _: ClassDef => listenClass(lhs, recordTargets = false)(ref => + if ref.definition eq cls then publish(ref.marks) + , _ => ()) + case _: ModuleOrObjectDef => publish(Nil) + def valuePattern(sh: TermShape)(using NewResolverState): Unit = shapeParts(sh).value match + case Marked(ds: DefnShape, _) => ds.defn match + case cls: ClassDef => classPattern(cls) + case obj: ModuleOrObjectDef if obj.sym.asObj.isDefined => classPattern(obj) + case td: TermDefinition => td.tsym match + case ctor: ClassCtorSymbol => classPattern(ctor.associatedCls.defn.get) + case _ => reject(sh) + case _ => reject(sh) + case _ => reject(sh) + lhs.withoutCaptures match + case Term.Error() => rstate.markError(res) + case Term.Ref(sym: VarSymbol) if sym.decl.exists(_.isPatternConstructor) => + res.resolvedTargets ::= sym + case Term.SimpleRef(sym: VarSymbol) if sym.decl.exists(_.isPatternConstructor) => + res.resolvedTargets ::= sym + case _ => listen(lhs, discardMarks = true): sh => + sh match + case sh: SymShape => + val bms = sh.sym + bms.onComplete: () => + bms.asPat.orElse(bms.asCls).orElse(bms.asObj) match + case S(sym: PatternSymbol) => select(sym) + case S(sym: (ClassSymbol | ModuleOrObjectSymbol)) => + sym.defn match + case S(cls: (ClassDef | ModuleOrObjectDef)) => classPattern(cls) + case _ => softAssert(false, "Completed pattern member has no definition") + case N => + fromBMS(bms, FlowSymbol.pat()(using rstate.owner), sh.markss, valuePattern, lhs, _ => (), false) + case sh: TermShape => valuePattern(sh) + + /** Pattern bodies are resolved before their runtime matching code is generated. + * Check their bindings against an arbitrary input, so a local caller cannot + * supply undeclared assumptions to guards or transformations. + */ + def checkPatternDefinition(definition: PatternDef)(using NewResolverState): Unit = + val source = Term.MemberRef(definition.bsym)(new syntax.Tree.Ident(definition.bsym.nme).withLocOf(definition), + FlowSymbol.pat()(using rstate.owner)) + val unknown = UnknownValueShape(source, fromPublicInterface = false)(ShapeProvenance( + msg"Pattern '${definition.sym.nme}' accepts values of unknown shape." -> definition.toLoc :: Nil)) + matchShapePat(unknown, definition.pattern)(_ => ()) + + /** Propagate possible values to pattern bindings. Constructor tests filter by + * nominal class; guards and literal tests may conservatively retain shapes. + * Both scrutinee and constructor shapes can arrive after this registration. */ + def matchShapePat(shape: Shape, pattern: Pattern)(matched: ShapeListener[Shape])(using NewResolverState): Unit = + shape match + case value @ Marked(_: InstanceShape, _) if pattern.isInstanceOf[Pattern.Constructor] || pattern.isInstanceOf[Pattern.Tuple] => + listenInstanceViews(value)(matchShapePat(_, pattern)(matched)) + return + case _ => () + // Some pattern outputs (notably concatenations and rebuilt constructors) + // need a separate output interface. Do not reuse the input's fields for them. + def unknownOutput: UnknownValueShape = UnknownValueShape(Term.Error().withLocOf(pattern), fromPublicInterface = false)(ShapeProvenance( + msg"The output shape of this pattern is not yet known." -> pattern.toLoc :: Nil)) + def isNominal(ctor: Pattern.Constructor): Bool = + ctor.resolvedTargets.nonEmpty && ctor.resolvedTargets.forall: + case _: ClassSymbol | _: ModuleOrObjectSymbol => true + case _ => false + def preservesInput(pat: Pattern): Bool = pat match + case Pattern.Wildcard() | Pattern.Literal(_) | Pattern.Range(_, _, _) | Pattern.Negation(_) => true + case Pattern.Alias(inner, _) => preservesInput(inner) + case Pattern.Guarded(inner, _) => preservesInput(inner) + case Pattern.Annotated(inner, _) => preservesInput(inner) + case ctor: Pattern.Constructor => + isNominal(ctor) && ctor.arguments.forall(_.forall(preservesInput)) + case _ => false + // Negation filters the input, never a transformed output. Only discard a + // candidate when the whole shape must match the excluded pattern. In + // particular, failing to prove membership in a class is not proof of its + // complement: abstract and structural inputs may still contain instances. + def exclude(input: Shape, pat: Pattern)(receive: ShapeListener[Shape])(using NewResolverState): Unit = + input match + case value @ Marked(_: InstanceShape, _) => + listenInstanceViews(value)(exclude(_, pat)(receive)) + case symbol: SymShape => fromSymbol(symbol, exclude(_, pat)(receive), + Term.Error().withLocOf(pat), _ => (), false) + case value: TermShape => pat match + case Pattern.Wildcard() => () + case Pattern.Literal(literal) => shapeParts(value).value match + case Marked(intro: IntroShape, _) => intro.trm match + case Lit(actual) if actual === literal => () + case _ => receive(value) + case _ => receive(value) + case Pattern.Alias(inner, _) => exclude(value, inner)(receive) + case Pattern.Annotated(inner, _) => exclude(value, inner)(receive) + case Pattern.Composition(true, left, right) => + // Not (left or right): exclude both alternatives from the input. + exclude(value, left)(exclude(_, right)(receive)) + case Pattern.Composition(false, left, right) => + // Not (left and right): either failed test admits the input. + exclude(value, left)(receive) + exclude(value, right)(receive) + case Pattern.Negation(inner) if preservesInput(inner) => + matchShapePat(value, inner)(receive) + case ctor: Pattern.Constructor if isNominal(ctor) => + def unrestricted(pat: Pattern): Bool = pat match + case Pattern.Wildcard() => true + case Pattern.Alias(inner, _) => unrestricted(inner) + case Pattern.Annotated(inner, _) => unrestricted(inner) + case _ => false + ctor.subscribeToShapes: + case CtorPatternShape(cls, fields, _, _, _) => + // A nominal match alone cannot exclude C(p) when p tests a + // field's value. Retain it unless every field accepts anything. + if !value.isInstanceOfClass(cls) || !fields.forall((_, p) => unrestricted(p)) then receive(value) + // Guards, extractors, and patterns with transformed intermediate + // results may fail even on a matching nominal input. Retain their + // input interfaces until those tests have a definite shape semantics. + case _ => receive(value) + pattern match + case al @ Pattern.Alias(pat, _) => + matchShapePat(shape, pat): sh => + // Rejected duplicate/negated bindings have no allocated symbol. Pattern + // validation already reports them; only valid bindings receive flow. + al.symbolOption.foreach: symbol => + publishActivated(symbol, sh) + matched(sh) + case Pattern.Wildcard() | Pattern.Literal(_) | Pattern.Range(_, _, _) => matched(shape) + case Pattern.Chain(left, right) => + matchShapePat(shape, left)(sh => matchShapePat(sh, right)(matched)) + case Pattern.Composition(false, left, right) => + // Conjunction tests both sides against the input and produces a pair; + // only a chain feeds one pattern's output into the next pattern. + matchShapePat(shape, left)(_ => ()) + matchShapePat(shape, right)(_ => ()) + matched(unknownOutput) + case Pattern.Composition(true, left, right) => + matchShapePat(shape, left)(matched) + matchShapePat(shape, right)(matched) + case Pattern.Negation(pat) => + matchShapePat(shape, pat)(_ => ()) + exclude(shape, pat)(matched) + case Pattern.Concatenation(left, right) => + // Prefix matching and concatenating transformed results need their own + // shape rules. Still check nested bindings; no output interface is assumed. + val unknown = unknownOutput + matchShapePat(unknown, left)(_ => ()) + matchShapePat(unknown, right)(_ => ()) + matched(unknown) + case Pattern.Record(fields) => + // A field-presence test supplies no field interface by itself. Nested + // class tests can refine these unknowns before guards use their aliases. + val unknown = unknownOutput + fields.foreach((_, pat) => matchShapePat(unknown, pat)(_ => ())) + matched(unknown) + case Pattern.Transform(pat, parameters, transform) => + // The transform is lowered to a separate lambda. Its source references + // already name these parameter symbols, not the aliases used by guards. + parameters.foreach: (binding, parameter) => + pipeTerm(Term.SimpleRef(binding)(binding.id), parameter) + matchShapePat(shape, pat)(_ => ()) + listenTerm(transform)(matched) + case Pattern.Guarded(pat, _) => matchShapePat(shape, pat)(matched) + // Compilation-strategy annotations do not change the pattern's bindings. + case Pattern.Annotated(pat, _) => matchShapePat(shape, pat)(matched) + case ctor: Pattern.Constructor => + def listenConstructor(psh: PatternShape)(using NewResolverState): Unit = psh match + case CtorPatternShape(cls, fs, _, resSym, constructorMarks) => + // Nominal views already live outside the instance. Cancel the + // constructor's own exit, retaining its enclosing lexical path. + val context = cls match + case _: ClassDef => MarkedShape.enter(unitResultShape, ResolutionBoundary(cls.sym), N).exit(constructorMarks) match + case Marked(_, marks) => marks :: Nil + case NoShape => lastWords("A wildcard constructor entry cannot reject its own exit") + case _: ModuleOrObjectDef => Nil + def atPattern(tpe: DeclaredType)(receive: Listener)(using NewResolverState): Unit = + listenTypeViews(tpe): candidate => + transportShape(candidate, context) match + case value: TermShape => receive(value) + case NoShape => () + def check(sh: TermShape, unknown: Opt[TermShape])(using NewResolverState): Unit = + if sh.isInstanceOfClass(cls) then + fs.foreach: (bms, pat) => + def member(info: MemberLookup, instances: TypeSubstitution)(using NewResolverState): Unit = info match + case MemberLookup.Contextual(source, substitution) => member(source, instances.withOverrides(substitution)) + case MemberLookup.Found(sym: BlockMemberSymbol, marks) => + fromBMS(sym, resSym, marks, + field => matchShapePat(instantiateShape(field, instances), pat)(_ => ()), ctor.target, _ => (), false) + case MemberLookup.Declared(member, bindings, marks, annotation, positive) => + listenDeclaredMember(member, bindings, resSym, ctor.target, annotation, _ => (), false, positive, marks, N): field => + listenInstanceViews(instantiateShape(field, instances)): field => + transportShape(field, marks) match + case value: TermShape => + val field = (value.applicationHead._1, unknown) match + case (_: UnknownValueShape, S(Marked(origin: UnknownValueShape, marks))) => + origin.copy()(origin.provenance.via( + msg"This constructor field may contain a value of unknown shape." -> member.toLoc)).exit(marks) + case _ => value + field match + case field: TermShape => matchShapePat(field, pat)(_ => ()) + case NoShape => () + case NoShape => () + case _ => + // classPattern only publishes fields present in cls.body. + // The nominal test above admits only that class's instances, + // this-values, and subclasses. Their member lookup follows + // the same class bodies and inheritance chain; overrides are + // also BlockMemberSymbols. Record members cannot replace a + // class's own field. Thus this branch is an internal mismatch + // between nominal matching and member lookup, not recovery + // from a malformed class (filtered by classPattern above). + softAssert(false, "Matched constructor is missing its field") + member(sh.getMember(bms.nme), TypeSubstitution.empty) + matched(if fs.forall((_, pat) => preservesInput(pat)) then sh else unknownOutput) + // A structural annotation describes possible class instances too. + // A successful class test supplies the tested class's declared + // interface even when the input did not have a nominal interface. + // Interpret the constructor reference with its lexical captures: + // inside C, testing against C must not place the refined value + // outside C's scope before a subsequent field read leaves it. + def testedClass(using NewResolverState): DeclaredType = + rstate.patternTypes.getOrElseUpdate((new Identity(ctor), cls.sym), { + val resolution = new TypeResolution(ctor.target, messages => resolError(ctor.target, messages)) + resolution.publish(TypeShape.Nominal(cls)) + declaredType(resolution, Map.empty) + }) + def subclass(base: ClassLikeDef)(using NewResolverState): Unit = + // A base-class annotation or self reference can denote a subclass + // instance. The test exposes that subclass's declared fields even + // when no constructor call supplies a concrete receiver shape. + // Omitted subclass arguments remain abstract and share the cached + // type, rather than borrowing types from unrelated allocations. + atPattern(testedClass): candidate => + if candidate.isInstanceOfClass(base) then check(candidate, N) + def narrow(sh: TermShape)(using NewResolverState): Unit = shapeParts(sh).value match + case Marked(_: RecordTypeShape, _) => + atPattern(testedClass)(check(_, N)) + case Marked(unknown: UnknownValueShape, _) => + // A class test can succeed for an external input. Its fields then + // expose their declarations, not shapes observed in local calls. + // Unknown type arguments retain the original diagnostic witness. + // Refinement is a function of a static test and its input, not + // an allocation on every delivery. A recursive callback can + // feed the same unknown back to this test through Array[T]. + // Fresh argument hosts would make each otherwise identical + // refined Array a new candidate and prevent a fixed point. + val refined = rstate.patternRefinements.getOrElseUpdate((new Identity(ctor), cls.sym, context, sh), { + val base = testedClass.resolution + val arg = new TypeResolution(unknown.source, messages => resolError(unknown.source, messages)) + val contextual = transportShape(sh, inverseMarks(context)) + contextual match + case value: TermShape => arg.publish(TypeShape.Inferred(value)) + case NoShape => () + val applied = new TypeResolution(ctor.target, messages => resolError(ctor.target, messages)) + applied.publish(TypeShape.Applied(base, cls.tparams.map(_ => arg))) + declaredType(applied, Map.empty) + }) + atPattern(refined)(check(_, S(sh))) + case Marked(opaque: OpaqueTypeShape, marks) => + UnknownValueShape(opaque.source, fromPublicInterface = false)(opaque.provenance).exit(marks) match + case value: TermShape => narrow(value) + case NoShape => () + case Marked(nominal: NominalInstanceView, _) if !nominal.isInstanceOfClass(cls) => + subclass(nominal.defn) + case Marked(base: BaseShape, _) if !base.isInstanceOfClass(cls) => + subclass(base.defn) + case _ => check(sh, N) + shape match + case sh: TermShape => narrow(sh) + case sh: SymShape => fromSymbol(sh, narrow, ctor.target, _ => (), false) + ctor.subscribeToShapes(listenConstructor) + case Pattern.Tuple(leading, spread) => + val trailing = spread.fold(Nil)(_._3) + val required = leading.length + trailing.length + def tupleBindings(tuple: TupleShape, marks: Marks)(using NewResolverState): Unit = + val segments = tuple.segments + val known = segments.count(_.isInstanceOf[TupleShape.Fixed]) + val uncertain = known != segments.length + if (uncertain || known >= required) && (spread.nonEmpty || known <= required) then + def bind(segment: TupleShape.Segment, p: Pattern)(using NewResolverState): Unit = + def receive(value: TermShape)(using NewResolverState): Unit = value.exit(marks) match + case value: TermShape => matchShapePat(value, p)(_ => ()) + case NoShape => () + listenTupleField(segment.element)(receive) + // A spread can move subsequent fields. Retain every possible position + // rather than treating the first unknown segment as the only candidate. + def positions(xs: Ls[TupleShape.Segment], ps: Ls[Pattern])(using NewResolverState): Unit = + var before = 0 + var unknownBefore = false + xs.foreach: + case field: TupleShape.Fixed => + ps.iterator.zipWithIndex.foreach: (p, index) => + if index >= before && (if !unknownBefore then index == before + else spread.nonEmpty || index <= before + required - known) then bind(field, p) + before += 1 + case unknown: TupleShape.Unknown => + ps.drop(before).foreach(bind(unknown, _)) + unknownBefore = true + positions(segments, leading) + positions(segments.reverse, trailing.reverse) + spread.foreach: (_, p, _) => + // Stop trimming at an unknown segment: its possible lengths include + // consuming the remaining prefix/suffix, so all later values may remain. + def trim(xs: Ls[TupleShape.Segment], count: Int): Ls[TupleShape.Segment] = + if count == 0 then xs else xs match + case (_: TupleShape.Fixed) :: tail => trim(tail, count - 1) + case _ => xs + val middle = trim(trim(segments, leading.length).reverse, trailing.length).reverse + // Reusing an unchanged view also bounds recursive tail matching on + // arrays with unknown length; wrapping it again would grow forever. + val rest = if middle == segments then tuple + else TupleShape.restView(tuple, middle)(this) + rest.exit(marks) match + case value: TermShape => matchShapePat(value, p)(_ => ()) + case NoShape => () + tuple.exit(marks) match + case value: TermShape => matched(value) + case NoShape => () + def narrow(value: TermShape)(using NewResolverState): Unit = value match + case Marked(tuple: TupleShape, marks) => tupleBindings(tuple, marks) + case Marked(unknown: UnknownValueShape, marks) => + tupleBindings(TupleShape(unknown.source, + TupleShape.Unknown(unknown.source, TupleShape.ValueField(unknown, Nil)) :: Nil)(this), marks) + case Marked(nominal: NominalInstanceView, marks) => + // Binding an element does not request its interface. Keep the declared + // type so later uses can apply the enclosing tuple's substitution. + arrayElementType(nominal).foreach: element => + tupleBindings(TupleShape(element.resolution.source, + TupleShape.Unknown(element.resolution.source, TupleShape.TypedField(element, Nil)) :: Nil)(this), marks) + case _ => () + shape match + case value: TermShape => narrow(value) + case sym: SymShape => fromSymbol(sym, narrow, Term.Missing, _ => (), false) + case _ => + raise(ErrorReport(msg"This pattern is not supported during shape resolution." -> pattern.toLoc :: Nil)) + + def matchScrutPat(scrutinee: Term, pattern: Pattern)(using NewResolverState): Unit = if newResolution then + listenTerm(scrutinee)(sh => matchShapePat(sh, pattern)(_ => ())) + + /** Both class interpretations use the original class as their scheme owner. + * Instantiated views must never become new owners of the same source binders. + */ + private def definitionParameters(definition: DefnShape): (AnyDefinitionSymbol, Ls[VarSymbol]) = + definition.clsDef match + case S(cls) => (cls.sym, cls.tparams.map(_.sym)) + case N => definition.defn match + case td: TermDefinition => (td.tsym, definitionBinders(td)) + case _ => lastWords("A callable definition must have a class or term owner") + + private def instantiateDefinition(definition: DefnShape, site: FlowSymbol, marks: Ls[Marks], + captured: TypeSubstitution, supplied: Opt[Ls[DeclaredType]]) + (using NewResolverState): TypeSubstitution = + val (owner, parameters) = definitionParameters(definition) + instantiateParameters(owner, parameters, site, marks, captured, supplied) + + private def instantiateParameters(owner: AnyDefinitionSymbol, parameters: Ls[VarSymbol], site: FlowSymbol, + marks: Ls[Marks], captured: TypeSubstitution, supplied: Opt[Ls[DeclaredType]]) + (using NewResolverState): TypeSubstitution = + val substitution = captured.withOverrides(rstate.instantiateTypeParameters(owner, site, parameters)) + supplied.foreach: arguments => + bindTypeArguments(parameters.map: parameter => + val instance = substitution(parameter) + TypeShape.Parameter(instance, instance.inferenceHost) + , marks, arguments) + if parameters.nonEmpty && rstate.inferredInstantiations.add((owner, site, substitution, marks)) then + parameters.foreach: parameter => + val instance = substitution(parameter) + listenTypeArgument(parameter): bound => + bound.applicationHead._1 match + case _: RigidTypeShape => () + case _ => inferTypeArguments(instanceType(instance), instantiateShape(bound, substitution), marks) + substitution + + def appShape(source: TermShape, args: Term, res: App)(using NewResolverState): Unit = + val callerInstances = rstate.instances + val ShapeParts(lhs, lexical, supplied) = shapeParts(source) + val captured = callerInstances.withOverrides(lexical) + val instances = lhs match + case Marked(definition: DefnShape, _) if supplied.isEmpty => + instantiateDefinition(definition, res.resSym, lhs.applicationHead._2, captured, N) + case Marked(construction: NewShape, _) if construction.argss.isEmpty && !construction.isSaturated && construction.supplied.isEmpty => + instantiateDefinition(construction.receiver, res.resSym, lhs.applicationHead._2, captured, N) + case _ => captured + def publish(value: TermShape)(using NewResolverState): Unit = + publishViewed(res, value, instances)(using rstate.withInstances(callerInstances)) + lhs match + case Marked(_: InstanceShape, _) => + listenInstanceViews(instantiateShape(lhs, instances))(appShape(_, args, res)) + return + case _ => () + // Only explicit dynamic values authorize calls without a known interface. + // An unknown alternative must invalidate the call even if other candidates + // happen to be callable in this compilation unit. + lhs match + case Marked(_: DynShape, _) => + publish(lhs) + return + case _ => () + lhs match + case Marked(nominal: NominalInstanceView, _) if nominal.isInstanceOfClass(prelude.builtins.Function.defn.get) => + // The builtin Function class guarantees callability, including through + // aliases and subclasses, but supplies neither an arity nor a result + // interface. Check the argument tuple and leave arity checks to runtime; + // the result must remain unknown rather than acquiring dynamic access. + checkArgumentArity(args, 0, true, res, lhs)(_ => ()) + // The call identifies the result in the flow graph, while the annotation + // explains its missing interface. Preserve that distinction so subsequent + // selections and applications both diagnose the restricting type once. + val provenance = ShapeProvenance(msg"The 'Function' type does not specify a return type." -> N :: Nil) + publish(UnknownValueShape(res, fromPublicInterface = false)(nominal.annotation.fold(provenance)(provenance.withTypeOrigin))) + return + case Marked(original: CallableTypeShape, context) => + val viewed = instantiateShape(original, instances) match + case callable: CallableTypeShape => callable + case _ => lastWords("A callable view must remain callable") + val callable = instantiateCallable(viewed, res.resSym, context :: Nil) + val ps = callable.paramLists.head + zipArgumentShapes(context :: Nil, ps.params.length, ps.hasRest, args, res, lhs): (index, value) => + (if index < ps.params.length then ps.params(index) else ps.rest).foreach: tpe => + value match + case value: TermShape => inferTypeArguments(tpe, value, context :: Nil) + case NoShape => () + def publish(shape: TermShape)(using NewResolverState): Unit = transportShape(shape, context :: Nil) match + case value: TermShape => + publishViewed(res, value, TypeSubstitution.empty)(using rstate.withInstances(callerInstances)) + case NoShape => () + listenCallableResult(callable)(publish) + return + case _ => () + // log(s"appShape? lhs = $lhs, args = $args, res = $res") + val sh = appShapes.getOrElseUpdate((lhs, res.resSym), { + log(s"appShape: lhs = ${lhs.shwDbg}, args = ${args.showDbg}, res = ${res.showDbg}") + new AppShape(lhs, args, res) + }) + lhs.unappliedParams match + case Nil => () + case (ps, mss) :: pss => + zipArgsIn(mss, ps.params, ps.restParam, args, res, lhs, instances) + log(s"appShape isSaturated? ${sh.isSaturated}; head? ${sh.applicationHead}") + def register(using NewResolverState) = publish(sh) + log(s"lhs ${lhs.isSaturated} ${lhs.unappliedParams.map(_.mapFirst(_.showDbg).mapSecond(_.map(_.showDbg)))}") + if lhs.isSaturated && !rstate.hasError(res) then + rstate.markError(res) + // Marks transport a value across scopes; they do not apply arguments. + // Inspect the value beneath them, retaining actual calls/instantiations + // so an already constructed instance never gets the suggestion to use new. + val message = lhs match + case Marked(ds: DefnShape, _) if ds.defn.isInstanceOf[ClassDef] => + msg"Class '${ds.defn.bsym.nme}' must be instantiated with 'new'." + case Marked(_: (AppShape | NewShape), _) => + msg"${lhs.describe.capitalize} cannot receive more argument lists." + case _ => + msg"${lhs.describe.capitalize} cannot be called like a function." + resolError(res, lhs.diagnostic(message)) + if sh.isSaturated then + def go(body: Term, mss: Ls[Marks])(using NewResolverState) = + listenResult(body): sh => + transportShape(sh, mss) match + case NoShape => + case sh: TermShape => + publish(sh) + sh.applicationHead match + case (ds: DefnShape, mss) => + ds.defn match + case cd: ClassDef => + // TODO: resolve ctor? + // TODO: handle `mss` + register + case td: TermDefinition => + // listenTerm(td.body, sh => newShape(sh, args, res)) + td.tsym match + case ccs: ClassCtorSymbol => // TOOD: to avoid the special case, give this the actual body? + softAssert(td.body.isEmpty) + // ccs.associatedCls + // TODO: handle `mss` + register + case _ => + log(s"appShape: td.body = ${td.body}") + listenDefinitionResult(td, instances): result => + transportShape(result, mss) match + case value: TermShape => publish(value) + case NoShape => () + case _ => + softAssert(rstate.hasError(res)) + case (sh: IntroShape, mss) => + sh.trm match + case Lam(params, body) => + // Exit the same context that zipArgs enters, filtering results from other uses. + go(body, mss) + case _ => + softAssert(rstate.hasError(res)) + case _ => + softAssert(rstate.hasError(res)) + else register + + private def publishActivated(host: ShapeHost, source: Shape)(using NewResolverState): Unit = + val shape = if rstate.instances.isEmpty then source else ActivatedShapeEvent(source, rstate.instances) + if host.currentShapes.add(shape) then host.notifyShapeListeners(shape) + + /** `instances` interprets references in the value; rstate.instances identifies + * the receiving body activation. Preserve both: a recursive argument may still + * refer to the caller's binder instance when delivered into a callee activation. + */ + private def publishViewed(host: ShapeHost, source: Shape, + instances: TypeSubstitution)(using NewResolverState): Unit = + val shape = source match + case value: TermShape => instantiateShape(value, instances) + case symbol: SymShape if instances.nonEmpty => + val (original, substitution) = symbol match + case contextual: ContextualSymShape => (contextual.source, instances.withOverrides(contextual.instances)) + case core: CoreSymShape => (core, instances) + rstate.contextualSymbols.getOrElseUpdate((original, substitution), ContextualSymShape(original, substitution)) + case _ => source + publishActivated(host, shape) + + private def publishMember(host: NewResolvable & ShapeHost, member: BlockMemberSymbol | RecordMember, + flow: FlowSymbol, marks: Ls[Marks], instances: TypeSubstitution)(using NewResolverState): Unit = + def publish(shape: Shape)(using NewResolverState): Unit = publishViewed(host, shape, instances) + def definition(sym: BlockMemberSymbol)(using NewResolverState): Unit = + publish(symShapes.getOrElseUpdate((sym, flow, marks), CoreSymShape(sym, flow, marks))) + member match + case member: BlockMemberSymbol => definition(member) + case RecordMember(field, false) => definition(field.sym) + case RecordMember(field, true) => + // Assignments can replace the stored value, so its initializer no longer + // determines what a read returns. Publish the known property symbol for + // selection/assignment, but an unknown shape for the value being read. + rstate.recordResolution(host, host.resolvedTargets.contains(field.tsym))(host.resolvedTargets ::= field.tsym) + publish(UnknownValueShape.at(field.rhs)) + + private def publishDeclared(host: NewResolvable & ShapeHost, member: BlockMemberSymbol, flow: FlowSymbol, + bindings: Map[VarSymbol, DeclaredType], marks: Ls[Marks], annotation: Opt[Term], positive: Bool, + instances: TypeSubstitution)(using NewResolverState): Unit = + // Keep one diagnostic witness per semantic candidate, as for unknown values. + // A different annotation location must not create additional inference flow. + val shape = declaredSymShapes.getOrElseUpdate((member, flow, marks, bindings, positive), + DeclaredSymShape(member, flow, marks, bindings, annotation, positive)) + publishViewed(host, shape, instances) + + private def publishTyped(host: NewResolvable & ShapeHost, field: RcdField, tpe: DeclaredType, + marks: Ls[Marks], instances: TypeSubstitution)(using NewResolverState): Unit = + rstate.recordResolution(host, host.resolvedTargets.contains(field.tsym))(host.resolvedTargets ::= field.tsym) + publishActivated(host, InstanceShape(transportType(tpe.instantiate(instances), marks))) + + private def publishDynamic(host: NewResolvable & ShapeHost, marks: Ls[Marks])(using NewResolverState): Unit = + DynShape().exit(marks) match + case shape: TermShape => + publishActivated(host, shape) + case NoShape => () + + private def unknownMember(host: NewResolvable, name: Str, reason: MemberLookup.Uncertainty, + fromPublicInterface: Bool, provenance: ShapeProvenance)(using NewResolverState): Unit = + val message = reason match + case MemberLookup.Uncertainty.ValueShape => + msg"Cannot resolve member '$name' of a value with unknown shape." + case MemberLookup.Uncertainty.RecordOverwrite => + msg"Cannot resolve member '$name' across a computed key or unknown record spread." + host match + case sel: NewSel => + rstate.missingMember(sel, config.language.eagerResolution || fromPublicInterface): + provenance.diagnostic(message, N) + case _ if !rstate.hasError(host) => + rstate.markError(host) + resolError(host, provenance.diagnostic(message, N)) + case _ => () + + def unresolvedRef(ref: UnresolvedRef)(using NewResolverState): Unit = + ref.prefixes.foreach: prefix => + listenReceiver(prefix): shape => + def member(info: MemberLookup, instances: TypeSubstitution)(using NewResolverState): Unit = info match + case MemberLookup.Contextual(source, substitution) => member(source, instances.withOverrides(substitution)) + case MemberLookup.Found(member, marks) if rstate.canResolve(ref) || ref.resolvedMembers.contains(prefix -> member.memberSymbol) => + val candidate = prefix -> member.memberSymbol + rstate.recordResolution(ref, ref.resolvedMembers.contains(candidate))(ref.resolvedMembers ::= candidate) + publishMember(ref, member, ref.resSym, marks, instances) + case MemberLookup.Declared(member, bindings, marks, annotation, positive) if rstate.canResolve(ref) || ref.resolvedMembers.contains(prefix -> member) => + val candidate = prefix -> member + rstate.recordResolution(ref, ref.resolvedMembers.contains(candidate))(ref.resolvedMembers ::= candidate) + publishDeclared(ref, member, ref.resSym, bindings, marks, annotation, positive, instances) + case MemberLookup.Typed(field, tpe, marks) if rstate.canResolve(ref) || ref.resolvedMembers.contains(prefix -> field.sym) => + val candidate = prefix -> field.sym + rstate.recordResolution(ref, ref.resolvedMembers.contains(candidate))(ref.resolvedMembers ::= candidate) + publishTyped(ref, field, tpe, marks, instances) + case MemberLookup.Indexed(_, _) if rstate.canResolve(ref) => + resolError(ref, msg"Tuple elements must be selected by index." -> ref.toLoc :: Nil) + case MemberLookup.Dynamic(marks) if rstate.canResolve(ref) || ref.dynamicPrefixes.contains(prefix) => + rstate.recordResolution(ref, ref.dynamicPrefixes.contains(prefix))(ref.dynamicPrefixes ::= prefix) + publishDynamic(ref, marks) + case MemberLookup.Missing => + // A known miss in one wildcard source is not an error: another may + // provide the name. Lowering diagnoses references with no candidates. + () + case MemberLookup.Unknown(reason, fromPublicInterface, provenance) => + unknownMember(ref, ref.id.name, reason, fromPublicInterface, provenance) + + case _ => () // Completed references only route their selected members. + member(shape.getMember(ref.id.name), TypeSubstitution.empty) + + /** Inspect an overload set only once its definitions have all been published. */ + private def completedClass(shape: SymShape)(selected: ShapeListener[ClassDef], absent: () => NewResolverState ?=> Unit)(using NewResolverState): Unit = + shape.sym.onComplete: () => + shape.sym.asCls match + case S(cls) => selected(cls.defn.get) + case N => absent() + + /** Class interpretations wait for completed overload sets, independently of term + * companions. Aliases can supply constructor shapes; applied instances cannot. + * Capture marks are retained for subsequent instance-member lookup. */ + private case class ClassReference(target: ClassValueTarget, marks: Ls[Marks], + instances: TypeSubstitution, supplied: Opt[Ls[DeclaredType]]): + def definition: ClassDef = ClassValueShape.definitionOf(target) + + // Declaration references choose their class overload. Stored values instead + // retain their runtime representation, whether a class or its constructor. + private def listenClass(trm: Term, recordTargets: Bool)(selected: ShapeListener[ClassReference], reject: ShapeListener[TermShape])(using NewResolverState): Unit = + def select(ref: ClassReference)(using NewResolverState): Unit = + if recordTargets then recordValueTarget(trm.classHead, ref.definition.sym) + selected(ref) + def value(sh: TermShape)(using NewResolverState): Unit = + val ShapeParts(source, instances, supplied) = shapeParts(sh) + val marks = source.applicationHead._2 + source match + case Marked(ds: DefnShape, _) => ds.clsDef match + case S(cls: ClassDef) => + val target: ClassValueTarget = ds.defn match + case td: TermDefinition => td.tsym match + case ctor: ClassCtorSymbol => ctor + case _ => lastWords("Class shape without a class or constructor definition") + case _: ClassDef => cls.sym + case _ => lastWords("Class shape without a class or constructor definition") + selected(ClassReference(target, marks, instances, supplied)) + case _ => reject(sh) + case _ => reject(sh) + trm match + // Local variables store the term interpretation of their initializer, even + // when their flow still contains its unconverted overload-set shape. + case _: SimpleRef => listenTerm(trm)(value) + case application @ TyApp(base, args) => listenClass(base, recordTargets)(ref => + val count = if ref.supplied.nonEmpty then 0 else ref.definition.tparams.length + if count != args.length && rstate.typeArgumentArityErrors.add((new Identity(application), count)) then + resolError(trm, msg"Class '${ref.definition.sym.nme}' expected ${count} type ${ + "argument".pluralized(count)}, but got ${args.length}" -> ref.definition.toLoc :: Nil) + val supplied = args.map(arg => declaredType(typeResolution(arg), Map.empty).instantiate(rstate.instances)) + if ref.supplied.nonEmpty then selected(ref) + else + val instances = instantiateParameters(ref.definition.sym, ref.definition.tparams.map(_.sym), + rstate.typeApplicationSite(application), ref.marks, rstate.instances.withOverrides(ref.instances), S(supplied)) + selected(ref.copy(instances = instances, supplied = S(supplied))) + , reject) + case Capture(base, thru) => + listenClass(base, recordTargets)(ref => selected(ref.copy(marks = + ref.marks ::: EntryMark(ResolutionBoundary(thru), N, NoMarks) :: Nil)), reject) + case _ => listen(trm): + case sh: SymShape => + completedClass(sh)(cls => select(ClassReference(cls.sym, + ExitMark(ResolutionBoundary(cls.sym), S(sh.resSym), NoMarks) :: sh.markss, + sh match + case contextual: ContextualSymShape => contextual.instances + case _ => TypeSubstitution.empty + , N)), + () => fromSymbol(sh, value, trm, sym => if recordTargets then recordValueTarget(trm, sym), false)) + case sh: TermShape => value(sh) + + /** Locate a projected declaration in the actual receiver's class ancestry. + * Each parent shape carries the crossing from its class into the child, so + * keep those crossings before adding the receiver's outer captures. + */ + private def projectedMember(receiver: TermShape, cls: ClassDef, name: Str)(using NewResolverState): Opt[MemberLookup] = + val ShapeParts(source, instances, _) = shapeParts(receiver) + val (head, marks) = source.applicationHead + val member = head match + case definition: DefnShape => + if definition.clsDef.contains(cls) then S(definition.getInstanceMember(name)) + else definition.ext.flatMap(projectedMember(_, cls, name)) + case base: BaseShape => + if base.defn is cls then S(base.getMember(name)) + else base.ext.flatMap(projectedMember(_, cls, name)) + case _ => N + member.map(_.withMarks(marks).instantiate(instances)) + + /** Class selections own their resolved class targets, including for aliases. + * Projection qualifiers use this same representation without evaluating it. + * The caller decides whether non-class values permit ordinary field selection. + */ + private def classSelection(sel: NewSel)(selected: ShapeListener[ClassReference], reject: ShapeListener[TermShape])(using NewResolverState): Unit = + listenClass(sel.prefix, recordTargets = true)(ref => + val cls = ref.definition.sym + rstate.recordResolution(sel, sel.resolvedTargets.contains(cls))(sel.resolvedTargets ::= cls) + selected(ref) + , reject) + + /** Super references expose every inherited candidate to inference, but never dispatch + * through the overriding member. The parent shape retains generic arguments and scope marks. + */ + def resolveSuper(sup: Super)(using NewResolverState): Unit = + def receive(info: MemberLookup, instances: TypeSubstitution)(using NewResolverState): Unit = info match + case MemberLookup.Contextual(source, substitution) => receive(source, instances.withOverrides(substitution)) + case MemberLookup.Found(member, marks) => publishMember(sup, member, sup.resSym, marks, instances) + case MemberLookup.Declared(member, bindings, marks, annotation, positive) => + publishDeclared(sup, member, sup.resSym, bindings, marks, annotation, positive, instances) + case _ => + rstate.markError(sup) + resolError(sup, msg"No inherited member '${sup.id.name}' is available through 'super'" -> sup.toLoc :: Nil) + def completed(defn: ClassLikeDef)(using NewResolverState): Unit = + listenInheritedMember(defn, sup.id.name): info => + def captures(term: Term): Ls[Marks] = term match + case Capture(base, thru) => EntryMark(ResolutionBoundary(thru), N, NoMarks) :: captures(base) + case _ => Nil + receive(info.withMarks(captures(sup.receiver)), TypeSubstitution.empty) + sup.owner.asDefnSym.defn match + case S(defn) => completed(defn) + case N => sup.owner.asDefnSym.defnListeners += completed + + def newSel(sel: NewSel)(using NewResolverState): Unit = + log(s"newSel? sel = ${sel.showDbg}") + def member(info: MemberLookup, description: Message, diagnostic: Message => Ls[(Message, Opt[Loc])], + instances: TypeSubstitution)(using NewResolverState): Unit = info match + case MemberLookup.Contextual(source, substitution) => member(source, description, diagnostic, instances.withOverrides(substitution)) + case MemberLookup.Found(bms, marks) if rstate.canResolve(sel) || sel.resolvedMembers.contains(bms.memberSymbol) => + log(s"newSel member: bms = ${bms.memberSymbol.showDbg}, mss = ${marks.map(_.showDbg)}") + rstate.recordResolution(sel, sel.resolvedMembers.contains(bms.memberSymbol))(sel.resolvedMembers ::= bms.memberSymbol) + publishMember(sel, bms, sel.resSym, marks, instances) + case MemberLookup.Declared(member, bindings, marks, annotation, positive) if rstate.canResolve(sel) || sel.resolvedMembers.contains(member) => + rstate.recordResolution(sel, sel.resolvedMembers.contains(member))(sel.resolvedMembers ::= member) + publishDeclared(sel, member, sel.resSym, bindings, marks, annotation, positive, instances) + case MemberLookup.Typed(field, tpe, marks) if rstate.canResolve(sel) || sel.resolvedMembers.contains(field.sym) => + rstate.recordResolution(sel, sel.resolvedMembers.contains(field.sym))(sel.resolvedMembers ::= field.sym) + publishTyped(sel, field, tpe, marks, instances) + case MemberLookup.Indexed(fields, marks) if rstate.canResolve(sel) || sel.tupleIndex == sel.id.name.toIntOption => + val index = sel.id.name.toIntOption + softAssert(index.exists(_ >= 0), "Tuple lookup must identify a nonnegative index") + rstate.recordResolution(sel, sel.tupleIndex == index)(sel.tupleIndex = index) + fields.foreach: field => + listenTupleField(field): shape => + transportShape(shape, marks) match + case value: TermShape => + publishViewed(sel, value, instances) + case NoShape => () + case MemberLookup.Dynamic(marks) if rstate.canResolve(sel) || sel.hasDynamicTarget => + rstate.recordResolution(sel, sel.hasDynamicTarget)(sel.hasDynamicTarget = true) + publishDynamic(sel, marks) + case MemberLookup.Missing if rstate.canResolve(sel) => + rstate.missingMember(sel, config.language.eagerResolution): + diagnostic(msg"$description does not contain member '${sel.id.name}'") + case MemberLookup.Unknown(reason, fromPublicInterface, provenance) => + unknownMember(sel, sel.id.name, reason, fromPublicInterface, provenance) + // Later activations still transport field values through the compiled + // selection, but cannot choose a different member for that old syntax. + case _ => () + sel.cls match + case N if sel.id.name == "class" => + classSelection(sel)(ref => + listenExt(ref.definition, ext => + val shape = ClassValueShape(ref.target, ext) + assert(rstate.canResolve(sel) || ClassValueShape.targetsOf(sel).contains(ref.target), + "Inference changed a completed class-value interpretation") + val specialized = ref.supplied.fold[TermShape](shape)(args => SpecializedShape(shape, args, ref.instances)) + transportShape(specialized, ref.marks) match + case value: TermShape => publishViewed(sel, value, ref.instances) + case NoShape => ()) + , shape => + member(shape.getMember(sel.id.name), msg"${shape.describe.capitalize}", shape.diagnostic, TypeSubstitution.empty)) + case N => listenReceiver(sel.prefix): shape => + log(s"newSel: sel = ${sel.showDbg}, shape = ${shape.shwDbg}") + member(shape.getMember(sel.id.name), msg"${shape.describe.capitalize}", shape.diagnostic, TypeSubstitution.empty) + case S(cls) => + classSelection(cls)(ref => + val cd = ref.definition + listenExt(cd, ext => + val info = DefnShape(cd, ext).getInstanceMember(sel.id.name) + info match + case MemberLookup.Found(bms: BlockMemberSymbol, declarationMarks) => + // Resolve the projection target even in an unused function. Wait + // for the receiver before deciding whether its result may expose + // implementation flow or only the declared member signature. + rstate.recordResolution(sel, sel.resolvedMembers.contains(bms))(sel.resolvedMembers ::= bms) + bms.onComplete: () => + bms.asModOrObj.orElse(bms.asTrm).orElse(bms.asCls).foreach: sym => + rstate.recordResolution(sel, sel.resolvedTargets.contains(sym))(sel.resolvedTargets ::= sym) + listenTermViews(sel.prefix): receiver => + val selected = receiver match + case Marked(nominal: NominalInstanceView, context) => + nominal.ancestor(cd) match + case S(view) => view.getMember(sel.id.name).withAnnotation(nominal.annotation).withMarks(context :: Nil) + case N => + // An explicit projection can name a narrower class, but + // it cannot recover that class's implementation arguments. + val opaque = abstractType(new TypeResolution(sel, msgs => resolError(sel, msgs))) + MemberLookup.Declared(bms, cd.tparams.map(_.sym -> opaque).toMap, context :: Nil, nominal.annotation, true) + case _ => + // C# selects a declaration, not an instance of C. Transport + // the member through the actual receiver, never the qualifier. + projectedMember(receiver, cd, sel.id.name).getOrElse: + // Without a path into this class, only its declared + // interface is available; qualifier values add no evidence. + val opaque = abstractType(new TypeResolution(sel, msgs => resolError(sel, msgs))) + MemberLookup.Declared(bms, cd.tparams.map(_.sym -> opaque).toMap, + declarationMarks ::: ExitMark(ResolutionBoundary(cd.sym), N, NoMarks) :: Nil, N, true) + member(selected, msg"Class '${cd.sym.nme}'", message => (message -> cd.toLoc) :: Nil, TypeSubstitution.empty) + case _ => member(info, msg"Class '${cd.sym.nme}'", message => (message -> cd.toLoc) :: Nil, TypeSubstitution.empty)) + , sh => + rstate.markError(sel) + resolError(sel, sh.diagnostic(msg"${sh.describe.capitalize} cannot be used as a projection class.")) + ) + + /** Both constructor references and explicit `new` use the same definition and + * parameter lists, including auxiliary constructor(...) lists. A partial `new` + * therefore retains the same callable head and contexts as a partial C(...). + */ + private def constructorShape(cls: ClassDef, ext: Opt[TermShape])(using NewResolverState): DefnShape = + // The shared cache distinguishes the constructor's TermDefinition from its + // ClassDef, even though both definitions cross the same resolution boundary. + val symbol: ClassCtorSymbol | ClassSymbol = cls.ctorSym.getOrElse(cls.sym) + val definition = symbol.defn.get + val shape = defnShapes.getOrElseUpdate(symbol, { + val base = if cls.ctorSym.isDefined then S(BaseShape(cls, ext)) else ext + DefnShape(definition, base) + }) + softAssert(shape.defn is definition) + if cls.ctorSym.isDefined then shape.ext match + case S(base: BaseShape) => softAssert((base.defn is cls) && base.ext == ext) + case _ => softAssert(false, "Constructor shape is missing its class base") + else softAssert(shape.ext == ext) + shape + + def resolveNew(nw: Term.New)(using NewResolverState): Unit = nw.cls.classHead match + case Term.Error() => rstate.markError(nw) + // Class references resolve symbolically; variables must carry a class value. + // Other expression forms require dynamic construction, even if they publish + // no shapes (for example an unsupported reference in an extends clause). + case _: NewResolvable | _: SimpleRef => resolveNewClass(nw) + case _ => + rstate.markError(nw) + resolError(nw, msg"Invalid class expression: ${nw.cls.describe}" -> nw.cls.toLoc :: Nil) + + private def resolveNewClass(nw: Term.New)(using NewResolverState): Unit = + // listenClass preserves captures and supplies the same class-body exit as a + // constructor value. In particular, a captured constructor already carries + // that exit; adding a second one here would duplicate its instance boundary. + listenClass(nw.cls, recordTargets = true)(ref => + val cd = nw.rft.flatMap(_._1.defn).getOrElse(ref.definition) + // Constructor arity is compiled from these shapes even when the runtime + // operand is a variable with no resolved class symbol of its own. + assert(rstate.canResolve(nw) || ClassValueShape.targetsOf(nw).contains(ref.target), + "Inference changed a completed constructor target") + val marks = nw.rft.fold(ref.marks): (symbol, _) => + ExitMark(ResolutionBoundary(symbol), S(nw.resSym), NoMarks) :: Nil + val callerInstances = rstate.instances + listenExt(cd, extsh => + val dsh = constructorShape(cd, extsh) + val captured = callerInstances.withOverrides(ref.instances) + // Explicit type arguments already consumed this scheme. Otherwise an + // unapplied constructor waits for its first term application. + val instances = if ref.supplied.nonEmpty || (nw.args.isEmpty && dsh.unappliedParams.nonEmpty) then captured + else instantiateDefinition(dsh, nw.resSym, marks, captured, N) + val sh = newShapes.getOrElseUpdate((ref.target, marks, nw.resSym, ref.supplied), + NewShape(dsh, ref.target, marks, nw.args, nw, ref.supplied)) + if rstate.constructorApplications.add((sh, instances)) then + dsh.unappliedParams.lazyZip(nw.args).foreach: + case ((ps, _), args) => zipArgsIn(marks, ps.params, ps.restParam, args, nw, dsh, instances) + publishViewed(nw, sh, instances)(using rstate.withInstances(callerInstances)) + ) + , shape => + if !rstate.hasError(nw) then + rstate.markError(nw) + resolError(nw, shape.diagnostic(msg"${shape.describe.capitalize} cannot be instantiated with keyword 'new'.")) + ) + + /** Assignments return to the binding's lexical scope before contributing + * candidates. Reads must see every assigned shape, including assignments + * inside nested functions, without recording the RHS's symbol as their target. + */ + private[semantics] def assignVariable(lhs: Term, rhs: Term)(using NewResolverState): Unit = + def assign(target: Term, shape: TermShape)(using NewResolverState): Unit = target match + case Capture(base, thru) => + MarkedShape.exit(shape, ResolutionBoundary(thru), N) match + case value: TermShape => assign(base, value) + case NoShape => () + case MemberRef(sym: TermSymbol) if sym.k is LetBind => + publishActivated(rstate.memberVariable(sym), shape) + case SimpleRef(sym: LocalSymbol) => publishActivated(sym, shape) + case Ref(sym: LocalSymbol) => publishActivated(sym, shape) + case _ => () + listenTerm(rhs)(assign(lhs, _)) + + def defineVar(sym: LocalSymbol | TermSymbol, rhs: Term)(loc: Opt[Loc])(using NewResolverState): DefineVar = + if newResolution then sym match + case sym: TermSymbol => + listenTerm(rhs): sh => + publishActivated(rstate.memberVariable(sym), sh) + case sym: LocalSymbol => + listen(rhs): sh => + publishActivated(sym, sh) + DefineVar(sym, rhs)(loc) + + def listenDefn(sym: TermSymbol, listener: Listener)(using NewResolverState): Unit = + sym.defn match + case S(td: TermDefinition) if td.params.isEmpty => + td.body match + case S(body) => + listenByNameBody(td, body)(listener) + case N => + ??? // TODO error + case S(d) => + listener(defnShapes.getOrElseUpdate(sym, DefnShape(d, N))) + case N => + sym.defnListeners += (d => listener(defnShapes.getOrElseUpdate(sym, DefnShape(d, N)))) + + /** Close recursive getter dependencies before following the body. A cycle with + * no normal result publishes nothing; a productive branch can still publish + * later and propagate around the cycle until duplicate candidates stop it. + * Results stay at the body's lexical endpoint: each reference applies its own + * exit/capture marks after subscribing. Activation events retain substitutions + * when a deferred body candidate arrives through an enclosing generic call. + */ + private def listenByNameBody(td: TermDefinition, body: Term)(listener: Listener)(using NewResolverState): Unit = + assert(td.params.isEmpty) + val key = (td.tsym, rstate.instances) + rstate.byNameResults.get(key) match + case S(host) => listenValueHost(host)(listener) + case N => + val host = new Host[ShapeEvent]: + def showDbg(using DebugPrinter): Str = s"result of ${td.tsym.nme}" + rstate.byNameResults(key) = host + listenValueHost(host)(listener) + td.asyncAnnotation match + case S(_) => listenDefinitionResult(td, rstate.instances)(publishActivated(host, _)) + case N => listenResult(body)(publishActivated(host, _)) + + def pipeTerm(from: Term, to: ShapeHost)(using NewResolverState): Unit = + log(s"pipeTerm: from = ${from.showDbg}, to = ${to.showDbg}; ${to.currentShapes}") + listenTerm(from): sh => + publishActivated(to, sh) + + // Classes without an explicit parent expose Object's declarations. Share this + // root interface between inferred instances, nominal annotations, and this-values; + // Object itself must terminate the chain. This does not add a runtime constructor call. + private lazy val objectShape = NominalInstanceView(prelude.builtins.Object.defn.get, Map.empty, N)(N, ShapeProvenance.empty)(this) + private def implicitParent(defn: ClassLikeDef): Opt[TermShape] = + if defn.sym is prelude.builtins.Object then N else S(objectShape) + + /** Register declaration edges even for unused classes. Parent lookup waits for forward + * definitions and follows inherited members using the same shapes as ordinary selection. + */ + def registerInheritance(defn: ClassLikeDef)(using NewResolverState): Unit = + defn.body.members.values.toList.distinct.foreach: member => + member.asTrm.filterNot(sym => sym.isInstanceOf[ClassCtorSymbol] || sym.isPrivate).foreach: own => + // Bind this host to the defining unit before an importer can observe its edges. + own.overrideLinks.inferenceHost + listenInheritedMember(defn, member.nme): lookup => + def record(info: MemberLookup): Unit = info match + case MemberLookup.Contextual(source, _) => record(source) + case MemberLookup.Found(base: BlockMemberSymbol, _) => link(base) + case MemberLookup.Declared(base, _, _, _, _) => link(base) + case _ => () + def link(base: BlockMemberSymbol): Unit = base.onComplete: () => + base.asTrm.filterNot(_.isPrivate).foreach: target => + if target isnt own then own.overrideLinks.inferenceHost.publish(target) + record(lookup) + + private def listenInheritedMember(defn: ClassLikeDef, name: Str) + (listener: ShapeListener[MemberLookup])(using NewResolverState): Unit = + // Legacy preludes may have no Object declaration or even an enclosing prelude context. + // Their root classes have no implicit nominal parent in the legacy resolver. + if !newResolution && defn.ext.isEmpty then listener(MemberLookup.Missing) + else listenExt(defn, parent => listener(parent.fold[MemberLookup](MemberLookup.Missing)(_.getMember(name)))) + + def listenExt(defn: ClassLikeDef, listener: ShapeListener[Opt[TermShape]])(using NewResolverState): Unit = + defn.ext match + case S(trm) => + listenTerm(trm): sh => + listener(S(sh)) + case N => + listener(implicitParent(defn)) + + /** Constructed receivers and nominal annotations share the same symbolic + * parameter endpoints. Explicit arguments still receive input obligations; + * observing only their output candidates would discard callback constraints. + */ + private def instanceBindings(td: TermDefinition)(using NewResolverState): Map[VarSymbol, DeclaredType] = + td.tsym.owner.toList.flatMap(_.asDefnSym.defn.toList).flatMap(_.tparams).map: param => + val symbol = rstate.instances.getOrElse(param.sym, param.sym) + param.sym -> parameterType(TypeShape.Parameter(symbol, symbol.inferenceHost)) + .toMap + + private def fromSymbol(shape: SymShape, listener: Listener, source: Term, + selected: ShapeListener[DefinitionSymbol[?]], receiver: Bool)(using NewResolverState): Unit = + fromSymbolAt(shape, listener, source, selected, receiver, N) + + private def fromSymbolAt(shape: SymShape, listener: Listener, source: Term, + selected: ShapeListener[DefinitionSymbol[?]], receiver: Bool, + specialization: Opt[(FlowSymbol, Ls[DeclaredType])])(using NewResolverState): Unit = shape match + case contextual: ContextualSymShape => + fromSymbolAt(contextual.source, value => listener(instantiateShape(value, contextual.instances)), + source, selected, receiver, specialization)(using rstate.withInstances(contextual.instances)) + case declared: DeclaredSymShape => + listenDeclaredMember(shape.sym, declared.bindings, shape.resSym, source, declared.annotation, selected, receiver, declared.positive, shape.markss, specialization): value => + transportShape(value, shape.markss) match + case value: TermShape => listener(value) + case NoShape => () + case _ => fromBMSAt(shape.sym, shape.resSym, shape.markss, listener, source, selected, receiver, specialization) + + def fromBMS(bms: BlockMemberSymbol, resSym: FlowSymbol, markss: Ls[Marks], listener: Listener, + trm: Term, selected: ShapeListener[DefinitionSymbol[?]], receiver: Bool)(using NewResolverState): Unit = + fromBMSAt(bms, resSym, markss, listener, trm, selected, receiver, N) + + // External declarations with no written parameter lists derive their calling + // convention from the signature. An ordinary parameterless function executes + // at its reference, before any returned callable receives term arguments. + private def isByName(td: TermDefinition): Bool = + (td.k is syntax.Fun) && td.params.isEmpty && Annot.declareModifierOf(td.annotations).isEmpty + + /** A separate signature quantifies the definition. A quantified result + * annotation instead belongs to the returned value and remains unconsumed. + */ + private def definitionBinders(td: TermDefinition): Ls[VarSymbol] = + td.tparams.toList.flatten.map(_.sym) ::: definitionQuantifiers(td).map(_.sym) + + private def definitionQuantifiers(td: TermDefinition): Ls[QuantVar] = + def signatureQuantifiers(sign: Term): Ls[QuantVar] = sign match + case Forall(parameters, _, body) => parameters ::: signatureQuantifiers(body) + case _ => Nil + if td.flags.hasResultAnnotation then Nil else td.sign.toList.flatMap(signatureQuantifiers) + + private def instantiateByName(td: TermDefinition, site: FlowSymbol, marks: Ls[Marks], + specialization: Opt[(FlowSymbol, Ls[DeclaredType])], bindings: Map[VarSymbol, DeclaredType]) + (using NewResolverState): TypeSubstitution = + val instances = instantiateParameters(td.tsym, definitionBinders(td), specialization.fold(site)(_._1), + marks, rstate.instances, specialization.map(_._2)) + val parameters = definitionQuantifiers(td).map: parameter => + DeclaredTypeParameter(TypeShape.Parameter(parameter.sym, parameter.sym.inferenceHost), + parameter.lb.map(bound => declaredType(typeResolution(bound), bindings)), + parameter.ub.map(bound => declaredType(typeResolution(bound), bindings))) + constrainQuantifiedBounds(parameters, instances) + instances + + private def fromBMSAt(bms: BlockMemberSymbol, resSym: FlowSymbol, markss: Ls[Marks], listener: Listener, + trm: Term, selected: ShapeListener[DefinitionSymbol[?]], receiver: Bool, + specialization: Opt[(FlowSymbol, Ls[DeclaredType])])(using NewResolverState): Unit = + log(s"listenBMS: bms = ${bms.describe}") + bms.onComplete: () => + log(s"listenedBMS: bms = ${bms.describe}") + valueTarget(bms, receiver) match + case S(sym: (ModuleOrObjectSymbol | TermSymbol | ClassSymbol | PatternSymbol)) => + // Selection is independent of the selected value's shape. In particular, + // an assignment needs its target even if the value has no inferred shape. + // Pattern resolution supplies its own interpretation of the selected head. + selected(sym) + val wrappedListener: Listener = sh => + log(s"fromBMS: bms = ${bms.showDbg}, sh = ${sh.shwDbg}, flow = ${resSym.showDbg}, markss = ${markss.map(_.showDbg)}") + val sh0 = sh + // Modules, objects, and runtime pattern objects introduce no value + // boundary. Pattern bodies have separate matching scopes; passing the + // generated matcher object does not enter or leave those scopes. + val exited = sym match + case _: ModuleOrObjectSymbol | _: PatternSymbol => sh + case sym: TermSymbol if sym.k is LetBind => sh + case _ => MarkedShape.exit(sh, ResolutionBoundary(sym), S(resSym)) + (exited match + case value: TermShape => transportShape(value, markss) + case NoShape => NoShape) match + case NoShape => + log(s"FILTER OUT ${sh.shwDbg} for ${sym.showDbg} % ${resSym.showDbg}") + case sh: TermShape => + // if sh is sh0 + if sh0.isInstanceOf[MarkedShape[?]] + then log(s"MATCH ${sh.shwDbg} for ${sym.showDbg} % ${resSym.showDbg}") + else log(s"PUSH ${sh.shwDbg}") + listener(sh) + sym.defn match + case S(td: TermDefinition) if td.params.isEmpty => + log(s"listenTerm: td.body = ${td.body.fold("N")(_.showDbg)}") + val callerInstances = rstate.instances + val instances = if isByName(td) then instantiateByName(td, resSym, + ExitMark(ResolutionBoundary(td.tsym), S(resSym), NoMarks) :: markss, specialization, Map.empty) + else callerInstances.without(td.tparams.toList.flatten.map(_.sym)) + def receive(shape: TermShape)(using NewResolverState): Unit = + wrappedListener(instantiateShape(shape, instances))(using rstate.withInstances(callerInstances)) + if td.asyncAnnotation.nonEmpty then listenDefinitionResult(td, instances)(receive) + else resultSignature(td) match + case S(sign) => + val result = declaredType(typeResolution(sign), Map.empty).instantiate(instances) + val tpe = if isByName(td) then result else quantifiedType(result, td.tparams.toList.flatten.map(_.sym)) + listenTypeInstances(tpe): shape => + td.tsym.decl match + case S(_: Param) => receive(MarkedShape.enter(shape, ResolutionBoundary(td.tsym), N)) + case _ => receive(shape) + case N => td.body.foreach: body => + listenByNameBody(td, body)(shape => + // Legacy synthesized fields use a plain reference to their + // constructor parameter. Supply the field capture explicitly; + // new-resolution fields already carry it in their body syntax. + val captured = (td.tsym.decl, body) match + case (S(_: Param), Ref(_: VarSymbol)) => + MarkedShape.enter(shape, ResolutionBoundary(td.tsym), N) + case _ => shape + receive(captured) + )(using rstate.withInstances(instances)) + case S(d: TermDefinition) if !d.tsym.isInstanceOf[ClassCtorSymbol] && + ((d.sign.nonEmpty && (!d.flags.hasResultAnnotation || d.params.forall(ps => + (ps.params ::: ps.restParam.toList).forall(_.sign.nonEmpty)))) || + (d.body.isEmpty && d.tsym.owner.exists(_.asDefnSym.defn.exists(_.tparams.nonEmpty)))) => + listenDeclaredMember(bms, instanceBindings(d), resSym, trm, N, selected, receiver, true, markss, specialization): value => + transportShape(value, markss) match + case value: TermShape => listener(value) + case NoShape => () + case S(d: TermDefinition) => + d.tsym match + case ccs: ClassCtorSymbol => + val cls = ccs.associatedCls.defn.get + listenExt(cls, extsh => wrappedListener(constructorShape(cls, extsh))) + case _ => + wrappedListener(defnShapes.getOrElseUpdate(sym, DefnShape(d, N))) + case S(d: ClassLikeDef) => + listenExt(d, extsh => + // defnShapes.get(sym).foreach: existing => + // ??? // TODO error? + wrappedListener(defnShapes.getOrElseUpdate(sym, DefnShape(d, extsh))) + ) + case N if sym.asTrm.exists(_.k is LetBind) => + listenMemberVariable(sym.asTrm.get)(wrappedListener) + case N => + // sym.defnListeners += (d => listener(defnShapes.getOrElseUpdate(sym, DefnShape(d)))) + softAssert(false, s"Symbol definition of ${sym} is not set upon completion of ${bms}") + case _ => + def reportError(using NewResolverState) = resolError(trm, + msg"Expected a term; got ${bms.describe} '${bms.nme}'" -> N :: Nil) + trm.withoutCaptures match + case ref: NewResolvable => + if !rstate.hasError(ref) then + rstate.markError(ref) + reportError + case _ => reportError + + /** Request the term interpretation of a reference without requiring a consumer + * of its value shape. Direct definition references already identify the target; + * overload sets need listeners, including when their definitions arrive later. + */ + def requireTerm(trm: Term)(using NewResolverState): Unit = trm.withoutCaptures match + case direct @ MemberRef(sym: TermSymbol) => + // Selecting a known field does not require resolving the field's value. + softAssert(direct.resolvedTargets.forall(_ is sym)) + direct.resolvedTargets = sym :: Nil + case _: NewResolvable => listenTerm(trm)(_ => ()) + case _ => () + + /** Annotation identity is needed before elaborating the annotated body. Read + * the main symbol now, without waiting for a value or inspecting unfinished + * definitions. Keep validating selections/opens: a later distinct candidate + * must be an error, since it cannot change an already interpreted annotation. + */ + def annotationSymbol(trm: Term)(using NewResolverState): Opt[Symbol] = trm match + case Capture(base, _) => annotationSymbol(base) + case TyApp(base, _) => annotationSymbol(base) + case App(base, _) => annotationSymbol(base) + case ref: NewRefImpl => S(ref.sym) + case _: NewSel | _: UnresolvedRef => + val symbols = mutable.LinkedHashSet.empty[BlockMemberSymbol] + var collecting = true + var failed = false + def fail()(using NewResolverState): Unit = if !failed then + failed = true + resolError(trm, msg"An annotation's main symbol must be uniquely known when the annotation is elaborated." -> N :: Nil) + // This listener validates an elaboration decision; it transports no values. + // Keep checking late candidates in the defining block, but never mutate its + // local bookkeeping when a completed block's listeners run in a consumer. + listen(trm): shape => + if rstate.canResolve(trm) then shape match + case sh: SymShape => + if symbols.add(sh.sym) && !collecting then fail() + case _ => fail() + collecting = false + symbols.toList match + case symbol :: Nil if !failed => S(symbol) + case _ => fail(); N + // TODO: Ref(sym: BuiltinSymbol) is a legacy representation that still needs + // updating to the new reference forms, even when using new resolution. + case Ref(sym: BuiltinSymbol) => S(sym) + case _ => + resolError(trm, msg"An annotation must have a known main symbol." -> N :: Nil) + N + + /** Values prefer the function/constructor overload; selection receivers prefer + * the module overload. Keep that choice through captures so nested functions + * and opened names use the same interpretation as direct references. + */ + private def valueTarget(member: BlockMemberSymbol, receiver: Bool): Opt[DefinitionSymbol[?]] = + if receiver then member.asModOrObj.orElse(member.asTrm).orElse(member.asCls).orElse(member.asPat) + else member.asTrm.orElse(member.asModOrObj).orElse(member.asCls).orElse(member.asPat) + + /** Syntactic control transfers within one evaluation scope. Calls may return, + * but an explicit return/break/throw cannot also supply a normal value. Keep + * exit operands at their lexical endpoint; the function or label observing + * them applies the same shape transport as for its normal body result. + */ + private case class ResultFlow(normal: Bool, exits: Map[Opt[LabelSymbol], Vector[Term]]): + // None identifies the current function's return; Some(label) identifies a break. + def or(that: ResultFlow): ResultFlow = ResultFlow(normal || that.normal, + exits ++ that.exits.map((target, values) => target -> (exits.getOrElse(target, Vector.empty) ++ values))) + def andThen(that: => ResultFlow): ResultFlow = + if normal then copy(normal = false).or(that) else this + def leave(target: Opt[LabelSymbol], value: Term): ResultFlow = + andThen(ResultFlow(false, Map(target -> Vector.single(value)))) + + private def resultFlow(term: Term): ResultFlow = + val normal = ResultFlow(true, Map.empty) + def sequence(terms: Iterable[Term]): ResultFlow = + terms.foldLeft(normal)((flow, term) => flow.andThen(resultFlow(term))) + def statement(stat: Statement): ResultFlow = stat match + // A definition's body belongs to its own invocation, not to the enclosing + // function. In particular, a nested getter's return must not escape here. + case _: Definition => normal + case term: Term => resultFlow(term) + case _ => sequence(stat.subTerms) + def split(branches: SimpleSplit, onEnd: ResultFlow): ResultFlow = branches match + case SimpleSplit.Else(value) => resultFlow(value) + case SimpleSplit.End => onEnd + case SimpleSplit.Cons(SimpleSplit.Head.Let(_, value), tail) => resultFlow(value).andThen(split(tail, onEnd)) + case SimpleSplit.Cons(SimpleSplit.Head.Match(scrutinee, pattern, consequent), tail) => + // A failed pattern can skip its guards. Retain that path as well as + // any control transfer from a guard which is evaluated. + val guards = pattern.subTerms.foldLeft(normal)((flow, term) => flow.or(resultFlow(term))) + resultFlow(scrutinee).andThen(guards.andThen(split(consequent, onEnd).or(split(tail, onEnd)))) + def quoted(term: Term, depth: Int): ResultFlow = term match + // Only splices crossing back to the current stage execute now. Nested + // quotations delay their own splices, including any returns they contain. + case Unquoted(body) if depth == 1 => resultFlow(body) + case Unquoted(body) => quoted(body, depth - 1) + case Quoted(body) => quoted(body, depth + 1) + case _ => term.subTerms.foldLeft(normal)((flow, term) => flow.andThen(quoted(term, depth))) + term match + case Ret(value) => resultFlow(value).leave(N, value) + case Break(label, _, value) => value.fold(normal)(resultFlow).leave(S(label), value.getOrElse(unit)) + case Throw(value) => resultFlow(value).copy(normal = false) + case _: Continue => ResultFlow(false, Map.empty) + case Blk(stats, value) => stats.foldLeft(normal)((flow, stat) => flow.andThen(statement(stat))).andThen(resultFlow(value)) + case Label(label, _, body, _) => + val flow = resultFlow(body) + ResultFlow(flow.normal || flow.exits.contains(S(label)), flow.exits - S(label)) + case Try(body, cleanup) => + val first = resultFlow(body) + val last = resultFlow(cleanup) + // An abrupt cleanup replaces the pending return/break as well as the + // normal body value. A normally completing cleanup contributes no value. + if last.normal then first.or(last.copy(normal = false)) else last + case IfLike(_, form, branches) => + // Falling off an imperative split returns unit; falling off a returning + // split throws a match error. A loop may finish without running its body. + val flow = split(branches, if form.isImperative then normal else ResultFlow(false, Map.empty)) + if form == IfLikeForm.While then flow.or(normal) else flow + case Quoted(body) => quoted(body, 1) + case _: Lam | _: Unquoted | _: FunTy | _: Forall | _: Constrained | _: WildcardTy + | _: CompType | _: Neg | _: DynTy => normal + case Annotated(Annot.Async(), _) => normal + case Annotated(_, target) => resultFlow(target) + case Handle(_, rhs, args, _, _, _) => sequence(rhs :: args) + case New(cls, args, _) => sequence(cls :: args) + case Rcd(_, stats) => stats.foldLeft(normal)((flow, stat) => flow.andThen(statement(stat))) + case Asc(value, _) => resultFlow(value) + case TyApp(value, _) => resultFlow(value) + case _ => sequence(term.subTerms) + + /** Function results include explicit returns, whereas listenTerm observes only + * normal completion. Share this rule between calls, result checking, getters, + * and interface exposure so a returned closure is checked even when no caller + * observes it locally. + */ + private[semantics] def listenResult(body: Term)(listener: Listener)(using NewResolverState): Unit = + val flow = resultFlow(body) + if flow.normal then listenTerm(body)(listener) + flow.exits.getOrElse(N, Vector.empty).foreach(value => listenTerm(value)(listener)) + + def listenTerm(trm: Term)(listener: Listener)(using NewResolverState): Unit = + log(s"listenTerm: trm = ${trm.showDbg}") + listenValue(trm, false)(listener) + + def listenReceiver(trm: Term)(listener: Listener)(using NewResolverState): Unit = + listenValue(trm, true)(shape => listenInstanceViews(shape)(listener)) + + private def listenValue(trm: Term, receiver: Bool)(listener: Listener)(using NewResolverState): Unit = trm match + case Capture(base, thru) => + listenValue(base, receiver): shape => + listener(MarkedShape.enter(shape, ResolutionBoundary(thru), N)) + case _ => listen(trm): + case sh: TermShape => listener(sh) + case ss: SymShape => + fromSymbol(ss, listener, trm, recordValueTarget(trm, _), receiver) + + private def recordValueTarget(term: Term, symbol: DefinitionSymbol[?])(using NewResolverState): Unit = + term.withoutCaptures match + case ref: NewResolvable => + rstate.recordResolution(ref, ref.resolvedTargets.contains(symbol))(ref.resolvedTargets ::= symbol) + case _ => () + + /** Subscribe to spread operands once for each tuple or record AST node. Record + * the node before calling start: a recursive spread can call listen on the same + * node before start returns. Testing shapes.isEmpty would not prevent duplicate + * subscriptions while the node is waiting for a forward definition. Deliver + * cached shapes to each listener, which is already registered for future shapes. + */ + private def listenAggregate(aggregate: Tup | CtxTup | Rcd, listener: ShapeListener[ShapeEvent]) + (start: (Listener) => Unit)(using NewResolverState): Unit = + val first = aggregateProducers.add(new Identity(aggregate)) + // An imported definition can already have shapes computed by its own elaborator. + // Send those shapes to this listener even if first is true; recomputing the same + // shapes below will not notify it, because shapes.add rejects duplicates. + aggregate.replayShapes(listener) + if first then + start: shape => + if aggregate.currentShapes.add(shape) then aggregate.notifyShapeListeners(shape) + + private def activationListener(receive: ShapeListener[Shape])(using NewResolverState): ShapeListener[ShapeEvent] = + // A request and event are compatible when their activation maps agree on + // shared keys; an empty source request accepts all activations. Unwrap the + // event and supply that activation to the receiving operation, without + // replacing the enclosed value's independent caller-side substitution. + val requestedInstances = rstate.instances + def compatible(instances: TypeSubstitution): Bool = + requestedInstances.forall((parameter, instance) => instances.get(parameter).forall(_ eq instance)) + event => event match + case ActivatedShapeEvent(value, instances) => + if compatible(instances) then receive(value)(using rstate.withInstances(requestedInstances.withOverrides(instances))) + case value: TermShape => receive(instantiateShape(value, rstate.instances)) + case symbol: SymShape => receive(symbol) + + private def listenValueHost(host: ShapeHost)(receive: Listener)(using NewResolverState): Unit = + host.subscribeToShapes(activationListener: + case value: TermShape => receive(value) + case _: SymShape => softAssert(false, "Value hosts contain only term shapes") + ) + + private def listenMemberVariable(sym: TermSymbol)(receive: Listener)(using NewResolverState): Unit = + listenValueHost(rstate.memberVariable(sym))(receive) + + def listen(trm: Term, discardMarks: Bool = false)(receive: ShapeListener[Shape])(using NewResolverState): Unit = + val listener = activationListener(receive) + log(s"listen: trm = ${trm.showDbg}") + trm.addShapeListener(listener) + trm match + case _: SynthSel => + lastWords("Synthetic selections must not enter new resolution") + case Asc(_, sign) => listenTypeInstances(sign)(listener) + case sel @ DynSel(prefix, _, true, true) => listenArrayIndex(sel, prefix)(listener) + case _: DynSel | _: DynNew => listener(DynShape()) + case application @ TyApp(underlying, args) => + def checkArity(callee: TermShape, count: Int)(using NewResolverState): Unit = + if count != args.length && rstate.typeArgumentArityErrors.add((new Identity(application), count)) then + resolError(trm, callee.diagnostic(msg"${callee.describe.capitalize} expected ${count} type ${ + "argument".pluralized(count)}, but got ${args.length}")) + def instantiate(value: TermShape)(receive: Listener)(using NewResolverState): Unit = listenInstanceViews(value): viewed => + val ShapeParts(actual, captured, supplied) = shapeParts(viewed) + if supplied.nonEmpty then + checkArity(actual, 0) + receive(value) + else actual.applicationHead match + case (callable: CallableTypeShape, marks) => + checkArity(callable, callable.tparams.length) + val supplied = args.map(arg => declaredType(typeResolution(arg), Map.empty).instantiate(rstate.instances)) + instantiateCallable(callable.copy(supplied = S(supplied)), + rstate.typeApplicationSite(application), marks).exit(marks) match + case value: TermShape => receive(value) + case NoShape => () + case (callee: DefnShape, marks) => + val (_, parameters) = definitionParameters(callee) + // The first consumed parameter list has already bound this scheme. + // Looking through an application's head must not turn its result + // (especially a constructed object) back into a generic function. + val unapplied = actual match + case Marked(_: DefnShape, _) => true + case Marked(construction: NewShape, _) => construction.argss.isEmpty && !construction.isSaturated + case _ => false + checkArity(callee, if unapplied then parameters.length else 0) + if !unapplied then receive(value) + else + val supplied = args.map(arg => declaredType(typeResolution(arg), Map.empty).instantiate(rstate.instances)) + val instances = instantiateDefinition(callee, rstate.typeApplicationSite(application), marks, + rstate.instances.withOverrides(captured), S(supplied)) + SpecializedShape(callee, supplied, instances).exit(marks) match + case specialized: TermShape => receive(specialized) + case NoShape => () + case _ => receive(value) + // A value specialization binds its arguments in the type application's + // scope, after the reference has crossed all intervening captures. By-name + // definitions execute at the reference itself: supply their arguments there, + // rebasing from the application scope with the ordinary mark operations. + // Their result may have an independent scheme and must not be specialized + // again with the arguments belonging to the by-name definition. + def observe(base: Term, captures: Ls[Marks]) + (receive: (TermShape, Bool) => NewResolverState ?=> Unit)(using NewResolverState): Unit = base match + case Capture(inner, thru) => + val crossing = EntryMark(ResolutionBoundary(thru), N, NoMarks) + observe(inner, crossing :: captures): (shape, invoked) => + receive(if discardMarks then shape else MarkedShape.enter(shape, ResolutionBoundary(thru), N), invoked) + case _ => listen(base, discardMarks): + case sym: SymShape => sym.sym.onComplete: () => + valueTarget(sym.sym, false).flatMap(_.defn) match + case S(td: TermDefinition) if isByName(td) => + val definition = defnShapes.getOrElseUpdate(td.tsym, DefnShape(td, N)) + checkArity(definition, definitionBinders(td).length) + val arguments = args.map: arg => + val caller = declaredType(typeResolution(arg), Map.empty).instantiate(rstate.instances) + transportType(caller, inverseMarks(captures)) + fromSymbolAt(sym, value => receive(value, true), base, recordValueTarget(base, _), false, + S((rstate.typeApplicationSite(application), arguments))) + case _ => fromSymbol(sym, value => receive(value, false), base, recordValueTarget(base, _), false) + case value: TermShape => receive(value, false) + observe(underlying, Nil): (value, invoked) => + if invoked then listener(value) else instantiate(value)(listener) + case mut @ Mut(underlying: Tup) => listener(mutableArray(mut, underlying)) + case Mut(underlying) => listenTerm(underlying)(listener) + case tuple: (Tup | CtxTup) => listenAggregate(tuple, listener): publish => + val elements = tuple match + case Tup(fields) => fields + case CtxTup(fields) => fields + val fields = elements.collect { case Fld(_, key, S(value)) => (key, value) } + val named = if fields.isEmpty then Nil else + // Reuse the defining graph's property symbols when an imported tuple + // is observed directly, as well as when its copied listeners fire. + val graph = tuple.originalData.owner + assert(graph != null, "A tuple producer must have an inference owner") + val record = rstate.inGraph(graph.nn).namedTupleRecords.getOrElseUpdate(new Identity(tuple), { + val record: Rcd = Rcd(false, fields.map((key, value) => RcdField(key, value)(using rstate.owner))) + record.withLocOf(tuple) + record + }) + TupleShape.ValueField(RecordShape(record, record.stats.collect { + case field: RcdField => RecordShape.Field(field) + }), Nil) :: Nil + def expand(elems: Ls[Elem], reversed: Ls[TupleShape.Element])(using NewResolverState): Unit = elems match + case Nil => + // A sole spread preserves its operand's shape and context exactly. + // Besides avoiding wrappers, this lets recursive rest forwarding + // reach the same fixed point as forwarding an ordinary parameter. + val shape = (reversed, named) match + case (TupleShape.Spread(shape, NoMarks) :: Nil, Nil) => shape + case (TupleShape.Spread(shape, marks: SomeMarks) :: Nil, Nil) => MarkedShape(shape, marks) + case _ => TupleShape(tuple, reversed.reverse ::: named)(this) + publish(shape) + // Lowering evaluates fields in source order, but packs all named + // fields into one trailing record. Shape positions must match that layout. + case Fld(_, _, S(_)) :: rest => expand(rest, reversed) + case (field: Fld) :: rest => expand(rest, TupleShape.Field(field, Nil) :: reversed) + case Spd(_, term) :: rest => + val spreadKey = new Object + listenTermViews(term): sh => + if rstate.spreadInputs.getOrElseUpdate(spreadKey, mutable.Set.empty).add(sh) then sh match + case Marked(shape: TupleShape, marks) => + // Widen an incoming candidate that already contains its own + // producer in this context. For `fun growing(n) = β€Ή...β€Ί [n, ...growing(n - 1)] β€Ή...β€Ί`, + // this replaces the recursive operand with an arbitrary sequence, + // retaining the surrounding `n` field and the spread's marks. + // Further feedback in the same context produces the same widened + // candidate, so the host's deduplication stops that expansion. + // Earlier candidates remain valid alternatives. A pending spread + // never reaches this branch: it waits for a shape notification. + val spread = if shape.containsSpread(shape.source, marks) + then TupleShape.unknown(shape.source)(this) + else shape + expand(rest, TupleShape.Spread(spread, marks) :: reversed) + case Marked(_: DynShape, marks) => + val spread = TupleShape(term, TupleShape.Unknown(term, TupleShape.ValueField(DynShape(), Nil)) :: Nil)(this) + expand(rest, TupleShape.Spread(spread, marks) :: reversed) + case shape @ Marked(_, marks) => + val typed = listenArrayElements(shape): + case Marked(element, context) => + val field = TupleShape.ValueField(element, context :: Nil) + val spread = TupleShape(term, TupleShape.Unknown(term, field) :: Nil)(this) + expand(rest, TupleShape.Spread(spread, NoMarks) :: reversed) + if !typed then + // Other opaque iterables have no known element type. Their + // runtime spread remains permitted without authorizing calls. + val field = TupleShape.ValueField(UnknownValueShape.spread(term, shape), Nil) + val unknown = TupleShape(term, TupleShape.Unknown(term, field) :: Nil)(this) + expand(rest, TupleShape.Spread(unknown, marks) :: reversed) + expand(elements, Nil) + case record: Rcd => listenAggregate(record, listener): publish => + def expand(stats: Ls[Statement], reversed: Ls[RecordShape.Element])(using NewResolverState): Unit = stats match + case Nil => + val shape = RecordShape(record, reversed.reverse) + publish(shape) + case (field: RcdField) :: rest => expand(rest, RecordShape.Field(field) :: reversed) + case RcdSpread(term) :: rest => + val spreadKey = new Object + listenTermViews(term): shape => + if rstate.spreadInputs.getOrElseUpdate(spreadKey, mutable.Set.empty).add(shape) then shape match + case Marked(shape: RecordShape, marks) => + // Bound recursive record producers just as for tuple spreads. + // Keep surrounding explicit fields even when the spread widens. + val spread = if shape.containsSpread(shape.source, marks) + then RecordShape(shape.source, RecordShape.Unknown(UnknownValueShape.spread(term, shape)) :: Nil) + else shape + expand(rest, RecordShape.Spread(spread, marks) :: reversed) + case Marked(_: DynShape, marks) => expand(rest, RecordShape.Dynamic(marks :: Nil) :: reversed) + case _ => expand(rest, RecordShape.Unknown(UnknownValueShape.spread(term, shape)) :: reversed) + case _ :: rest => expand(rest, reversed) + expand(record.stats, Nil) + case intro: IntroTerm => + // Literals have the declared primitive interface, including inherited + // members. Retain their literal shape for pattern tests and provenance. + val primitive = intro match + case Lit(_: Tree.StrLit) => S(prelude.builtins.Str) + case Lit(_: Tree.IntLit) => S(prelude.builtins.Int) + case Lit(_: Tree.DecLit) => S(prelude.builtins.Num) + case Lit(_: Tree.BoolLit) => S(prelude.builtins.Bool) + case _ => N + def publish(parent: Opt[NominalInstanceView])(using NewResolverState): Unit = + listener(introShapes.getOrElseUpdate(new Identity(intro), { + log(s"introShape: intro = $intro") + IntroShape(intro, parent) + })) + primitive match + case N => publish(N) + case S(cls) => + val tpe = rstate.primitiveTypes.getOrElseUpdate(cls, { + val resolution = new TypeResolution(intro, messages => resolError(intro, messages)) + resolution.publish(TypeShape.Nominal(cls.defn.get)) + declaredType(resolution, Map.empty) + }) + listenTypeViews(tpe): + case nominal: NominalInstanceView => publish(S(nominal)) + case _ => softAssert(false, "Primitive class must expose a nominal interface") + case Ref(sym) if sym is sym.getState.globalThisSymbol => listener(DynShape()) + case SelfRef(sym) if sym is sym.getState.globalThisSymbol => listener(DynShape()) + case ref @ Ref(loc: LocalSymbol) => + loc.subscribeToShapes(listener) + case ref @ SimpleRef(sym) => + sym match + case loc: LocalSymbol => + loc.subscribeToShapes(listener) + case _: BuiltinSymbol => + lastWords("Builtin symbols must not enter new resolution as SimpleRef") + case SelfRef(sym) => + // A receiver can be referenced before its body is complete or after its + // definition has already been published. The definition listener handles both. + def completed(defn: ClassLikeDef)(using NewResolverState): Unit = + listenExt(defn, ext => + // Each inner symbol has one self shape; repeated notifications must agree. + val shape = selfShapes.getOrElseUpdate(sym, BaseShape(defn, ext)) + softAssert(shape.defn is defn) + softAssert(shape.ext == ext) + listener(shape)) + val symbol = sym.asDefnSym + symbol.defn match + case S(defn) => completed(defn) + case N => symbol.defnListeners += completed + case ref @ MemberRef(sym: TermSymbol) if sym.k is LetBind => + // A let-binding has no evaluation boundary of its own. Surrounding + // function/class captures still enter and exit through Capture normally. + recordValueTarget(ref, sym) + rstate.memberVariable(sym).subscribeToShapes(listener) + case ref @ MemberRef(sym: TermSymbol) => + listenDefn(sym, sh => + MarkedShape.exit(sh, ResolutionBoundary(sym), S(ref.resSym)) match + case sh: TermShape => listener(sh) + case NoShape => ()) + case ref @ MemberRef(sym: BlockMemberSymbol) => + val fs = ref.resSym + val sh = symShapes.getOrElseUpdate((sym, fs, Nil), CoreSymShape(sym, fs, Nil)) + listener(sh) + case ref @ MemberRef(_: ErrorSymbol) => rstate.markError(ref) + case ref @ MemberRef(sym: TypeAliasSymbol) => + rstate.markError(ref) + resolError(ref, msg"Type alias '${sym.nme}' cannot be used as a runtime value." -> sym.toLoc :: Nil) + case MemberRef(_: InnerSymbol) => + // Ctx.RefElem uses SelfRef for class/module receivers, not MemberRef. + lastWords("Direct inner-symbol references must use SelfRef") + case Capture(base, thru) => + if discardMarks then + listen(base, discardMarks = true)(listener) + else listenTerm(base): sh => + listener(MarkedShape.enter(sh, ResolutionBoundary(thru), N)) + case ref @ Ref(sym: InnerSymbol) => // TODO: remove remaining occurrences of such refs + sym.addShapeListener(listener) + case ref @ Ref(bsym: BlockMemberSymbol) => + lastWords("Legacy block member references must not enter new resolution") + case res: (App | New | Ref | Sel | SelProj | Resolved | LeadingDotSel) => + res.replayShapes(listener) + case sh: (NewSel | UnresolvedRef | Super | IfLike) => + sh.replayShapes(listener) + case block @ Blk(_, result) => + if resultFlow(block).normal then listen(result, discardMarks)(listener) + case cleanup @ Try(body, _) => + // Finally is evaluated for effects; normal completion returns the body's + // value. Its cleanup value must not contribute candidates to this result. + if resultFlow(cleanup).normal then listen(body, discardMarks)(listener) + case Label(label, _, body, _) => + val flow = resultFlow(body) + if flow.normal then listen(body, discardMarks)(listener) + flow.exits.getOrElse(S(label), Vector.empty).foreach(value => listen(value, discardMarks)(listener)) + case Annotated(annotation @ Annot.Async(), body) => + listener(promiseShape(annotation, resultType(body, rstate.instances)(listenResult(body)))) + case Annotated(_, target) => listen(target, discardMarks)(listener) + case _: Assgn | _: Drop => listener(unitResultShape) + case _: Ret | _: Break | _: Throw | _: Continue => () // These expressions do not complete normally. + case _: Quoted | _: Unquoted => + // Quotation constructs a runtime syntax value; splicing supplies syntax + // for a different stage. Neither has the value interface of its operand. + listener(UnknownValueShape.at(trm)) + case _: Handle => + // An effect handler can abort or replace the continuation's result. The + // normal body's interface alone therefore cannot describe this value. + listener(UnknownValueShape(trm, fromPublicInterface = false)(ShapeProvenance( + msg"An effect handler can replace the handled body's result." -> trm.toLoc :: Nil))) + case _: DynTy | _: FunTy | _: Forall | _: Constrained | _: WildcardTy | _: CompType | _: Neg => + resolError(trm, msg"A type cannot be used as a runtime value." -> N :: Nil) + case Error() => () // Elaboration has already reported the error; it supplies no candidate. + case Missing => + () // FIXME: Currently get this from light-elaborated Predef import + case _: SynthIf | _: SynthWhile => + lastWords("Lowering-generated control flow must not enter resolution") + case _: Region | _: RegRef | _: Deref | _: SetRef => + lastWords("InvalML reference operations require legacy resolution") + +end NewResolver diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/NewResolverState.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/NewResolverState.scala new file mode 100644 index 0000000000..164cfe2ffd --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/NewResolverState.scala @@ -0,0 +1,433 @@ +package hkmc2 +package semantics + +import scala.collection.mutable +import hkmc2.utils.*, shorthands.* +import hkmc2.Message.MessageContext +import Term.* + +inline def rstate(using state: NewResolverState): NewResolverState = state + +object NewResolverState: + type Listener = TermShape => NewResolverState ?=> Unit + + /** Lazy source lookup preserves existing graph-node identities without copying + * a unit's memo tables. Mutable bookkeeping values supply an explicit copier. + */ + final class Cache[K, V](source: Opt[Cache[K, V]], copy: V => V): + private val entries = mutable.Map.empty[K, V] + private def peek(key: K): Opt[V] = entries.get(key).orElse(source.flatMap(_.peek(key))) + def get(key: K): Opt[V] = entries.get(key).orElse: + source.flatMap(_.peek(key)).map: value => + val local = copy(value) + entries(key) = local + local + def getOrElseUpdate(key: K, value: => V): V = get(key).getOrElse: + val result = value + entries(key) = result + result + def update(key: K, value: V): Unit = entries(key) = value + + final class Seen[A](source: Opt[Seen[A]]): + private val entries = mutable.Set.empty[A] + def apply(value: A): Bool = entries(value) || source.exists(_(value)) + def add(value: A): Bool = if apply(value) then false else entries.add(value) + +/** Inference belongs to the consuming compilation unit. Imported hosts retain their + * completed candidates and contextual listeners; this state supplies private copies + * before a consumer can extend either collection. + */ +final class NewResolverState private ( + val owner: Elaborator.State, private val consumer: Opt[NewResolverState], private val source: Opt[NewResolverState], + private val contextBase: Opt[NewResolverState], val instances: TypeSubstitution): + import NewResolverState.{Cache, Seen} + + def this(owner: Elaborator.State) = this(owner, N, N, N, TypeSubstitution.empty) + private def root: NewResolverState = consumer.getOrElse(this) + private def inherited: Opt[NewResolverState] = contextBase.orElse(source) + private val contexts = mutable.Map.empty[(NewResolverState, TypeSubstitution), NewResolverState] + /** Activations share the consuming unit's hosts and memoized source graph. + * Only the finite substitution varies; composing views never adds a parent + * context or changes either endpoint's originating graph. + */ + def withInstances(substitution: TypeSubstitution): NewResolverState = + val base = contextBase.getOrElse(this) + if substitution.isEmpty then base + else root.contexts.getOrElseUpdate((base, substitution), + new NewResolverState(owner, S(root), source, S(base), substitution)) + private var reporter: Raise | Null = null + def withReporter(raise: Raise): this.type = + root.reporter = raise + this + def report(diagnostic: Diagnostic): Unit = + assert(root.reporter != null, "Resolution requires a diagnostic collector") + root.reporter.nn(diagnostic) + def reportResolutionError(src: Term | Pattern, messages: Ls[(Message, Opt[Loc])]): Unit = + report(ErrorReport(msg"Resolution error in ${src.describe}" -> src.toLoc :: messages, source = Diagnostic.Source.Compilation)) + + def isOwnedSym(symbol: Symbol): Bool = symbol.getState is owner + + // Annotation constraints record escaping implementations in the consuming + // unit, including their activation context. The block's exposure pass drains + // these roots before sealing resolution; they are neither copied from imports + // nor retained across worksheet blocks. One witness suffices for each value. + private val exposureInputs = mutable.LinkedHashMap.empty[(TermShape, NewResolverState), ShapeProvenance] + def exposeValue(shape: TermShape, provenance: ShapeProvenance): Unit = + root.exposureInputs.getOrElseUpdate((shape, this), provenance) + def takeExposureInputs(): Ls[InterfaceExposure.Input] = + val inputs = root.exposureInputs.iterator.map: + case ((shape, state), provenance) => InterfaceExposure.Input(shape, state)(provenance) + .toList + root.exposureInputs.clear() + inputs + + // A listener carries its defining graph, not its defining mutable resolver. + // Views share the consumer's host copies, but consult the source's bindings and + // memoized nodes individually. Creating a view never traverses a source map. + private val views = mutable.Map.empty[NewResolverState, NewResolverState] + private val inheritedViews = mutable.Map.empty[(NewResolverState, NewResolverState), NewResolverState] + private[hkmc2] def inGraph(graph: NewResolverState): NewResolverState = + rebase(graph.contextBase.getOrElse(graph), root).withInstances(instances) + private def rebase(graph: NewResolverState, destination: NewResolverState): NewResolverState = + // A callback registered in this consuming unit already names its graph. + // Sending it through the current import view would create X(Y(X(...))) + // when an imported callback invokes a consumer callback and returns. + if graph.root eq root then graph + else + val origin = source.fold(graph)(_.rebase(graph, destination)) + if origin.root eq root then origin + else if root eq destination then + root.views.getOrElseUpdate(origin, new NewResolverState(owner, S(root), S(origin), N, TypeSubstitution.empty)) + else root.views.get(origin).getOrElse: + // A previously unused path can require a view of an intermediate exporter. + // Memoize that read-only view in the consumer, never in the exporter. + destination.inheritedViews.getOrElseUpdate((root, origin), + new NewResolverState(owner, S(root), S(origin), N, TypeSubstitution.empty)) + + private val copies = mutable.Map.empty[Publisher.Data[?], Publisher.Data[?]] + private val pending = mutable.Map.empty[Identity[Publisher[?]], Publisher.Data[?]] + private[hkmc2] def local[A](ref: Publisher.Data[A]): Publisher.Data[A] = + // Explicit references (notably type parameters) need the same intermediate + // exporter lookup as references through their syntax or symbol host. + val origin = source.fold(ref)(_.peekReference(ref)) + if (origin.owner eq root) && !origin.completed then origin + else + // Each key and its copy share A; recover that type when looking up the + // heterogeneous graph-node map. + root.copies.getOrElseUpdate(origin, origin.copy(root)).asInstanceOf[Publisher.Data[A]] + + private def peekReference[A](ref: Publisher.Data[A]): Publisher.Data[A] = + val origin = source.fold(ref)(_.peekReference(ref)) + root.copies.get(origin).fold(origin)(_.asInstanceOf[Publisher.Data[A]]) + + private def peek[A](publisher: Publisher[A]): Publisher.Data[A] = + peekReference(publisher.originalData) + + private[hkmc2] def data[A](publisher: Publisher[A]): Publisher.Data[A] = + val ref = source.fold(publisher.initialData(root))(_.peek(publisher)) + if ref.owner == null then publisher.initialData(root) + if publisher.isOwnedBy(root) && !root.completedNodes(new Identity(publisher)) then + root.pending.getOrElseUpdate(new Identity(publisher), ref) + local(ref) + + /** Number of lazily copied graph nodes, for isolation/scale regressions. */ + private[hkmc2] def copiedHostCount: Int = root.copies.size + + private val completedNodes = mutable.Set.empty[Identity[Publisher[?]]] + + // A receiver shape can lack a member that another shape supplies later in + // inference. Retain lazy witnesses by selection, including those from later + // activations of completed syntax, and report them together at block completion. + // Eager problems invalidate the selection immediately, regardless of its targets; + // delaying their report lets the same error include every relevant witness. + private val missingMembers = mutable.LinkedHashMap.empty[Identity[NewSel], + mutable.ListBuffer[(Bool, () => Ls[(Message, Opt[Loc])])]] + private def hasSelectionTarget(selection: NewSel): Bool = + selection.resolvedTargets.nonEmpty || selection.hasDynamicTarget || selection.tupleIndex.nonEmpty + def missingMember(selection: NewSel, eager: Bool)(messages: => Ls[(Message, Opt[Loc])]): Unit = + val key = new Identity(selection) + // Accumulate witnesses for this block's error without replaying errors on + // completed selections or adding follow-on errors to unrelated failures. + if !hasError(selection) || root.missingMembers.contains(key) then + if eager then markError(selection) + root.missingMembers.getOrElseUpdate(key, mutable.ListBuffer.empty) += ((eager, () => messages)) + + /** Seal the decisions and original host data read by erasure/lowering. The + * inference graph keeps private, live host data and all its listeners, so + * later calls can still transport arguments and results through these nodes. + * This boundary is per block, even when a worksheet reuses its symbol state. + * Legacy blocks also need sealing: new-resolution consumers may import their + * syntax concurrently, so no consumer may claim an unowned original host. + */ + def completeBlock(block: Statement): Unit = + val visited = mutable.Set.empty[Identity[Statement]] + def pattern(pat: Pattern): Unit = + pat match + case ctor: Pattern.Constructor => data(ctor) + case _ => () + pat.children.foreach: + case child: Pattern => pattern(child) + case term: Term => visit(term) + case _ => () + def split(branches: SimpleSplit): Unit = branches match + case SimpleSplit.Cons(SimpleSplit.Head.Match(_, pat, consequent), tail) => + pattern(pat) + split(consequent) + split(tail) + case SimpleSplit.Cons(_, tail) => split(tail) + case _ => () + def visit(statement: Statement): Unit = if visited.add(new Identity(statement)) then + statement match + // Bind even unused syntax to this unit. Otherwise its first consumer + // could become the owner of an imported node's original listener buffer. + case term: Term => data(term) + case _ => () + statement match + case cls: ClassLikeDef => + cls.ext.foreach(visit) + cls.auxParams.foreach(_.subTerms.foreach(visit)) + cls match + case pat: PatternDef => pattern(pat.pattern) + case _ => () + case IfLike(_, _, branches) => split(branches) + case Rcd(_, stats) => stats.foreach(visit) + case Forall(params, _, _) => + params.foreach: param => + param.lb.foreach(visit) + param.ub.foreach(visit) + case New(_, _, refinement) => refinement.foreach(r => visit(r._2.blk)) + case _ => () + statement.subStatements.foreach(visit) + visit(block) + root.missingMembers.foreach: (key, problems) => + val selection = key.value + val unresolved = !hasSelectionTarget(selection) + val messages = problems.iterator.collect: + case (eager, messages) if eager || unresolved => messages() + .flatten.toList.distinct + if messages.nonEmpty then + markError(selection) + reportResolutionError(selection, messages) + root.missingMembers.clear() + root.pending.foreach: (key, value) => + if !key.value.isInstanceOf[Symbol] && root.completedNodes.add(key) then value.completed = true + root.pending.clear() + + def canResolve(host: Publisher[?]): Bool = + data(host) + host.isOwnedBy(root) && !root.completedNodes(new Identity(host)) + + private val errors = mutable.Set.empty[Identity[PossiblyErroneous]] + def hasError(host: PossiblyErroneous): Bool = host.isErroneous || root.errors(new Identity(host)) + def markError(host: Publisher[?] & PossiblyErroneous): Unit = + root.errors += new Identity(host) + if canResolve(host) then host.isErroneous = true + + /** Inference may refine an imported result, but the imported code has already + * been compiled. Its member/constructor targets must remain unchanged. + */ + def recordResolution(host: Publisher[?], unchanged: Bool)(update: => Unit): Unit = + if !unchanged then + assert(canResolve(host), "Inference changed a completed reference target") + update + + val membersCache: Cache[(Identity[TermShape], Str), MemberLookup] = + new Cache(inherited.map(_.membersCache), identity) + val spreadInputs: Cache[Object, mutable.Set[TermShape]] = + new Cache(inherited.map(_.spreadInputs), _.clone()) + val reportedArities: Cache[Object, mutable.Set[Int]] = + new Cache(inherited.map(_.reportedArities), _.clone()) + val appShapes: Cache[(CoreTermShape, FlowSymbol), AppShape] = + new Cache(inherited.map(_.appShapes), identity) + val newShapes: Cache[(ClassValueTarget, Ls[Marks], FlowSymbol, Opt[Ls[DeclaredType]]), NewShape] = + new Cache(inherited.map(_.newShapes), identity) + val constructorApplications: Seen[(NewShape, TypeSubstitution)] = + new Seen(inherited.map(_.constructorApplications)) + val introShapes: Cache[Identity[IntroTerm], IntroShape] = + new Cache(inherited.map(_.introShapes), identity) + val symShapes: Cache[(BlockMemberSymbol, FlowSymbol, Ls[Marks]), CoreSymShape] = + new Cache(inherited.map(_.symShapes), identity) + // Member `let`s have no TermDefinition body. Their initializers and subsequent + // assignments feed this host, just as assignments feed a local variable. + // Keep the host in resolver state so imported observations use the existing + // consumer-local publisher machinery rather than mutating the source symbol. + private val memberVariables: Cache[TermSymbol, ShapeHost] = + new Cache(inherited.map(_.memberVariables), identity) + def memberVariable(symbol: TermSymbol): ShapeHost = + root.memberVariables.getOrElseUpdate(symbol, + symbol.getState.newResolverState.memberVariables.get(symbol).getOrElse( + new Host[ShapeEvent]: + def showDbg(using DebugPrinter): Str = s"assignments to ${symbol.nme}" + )) + val declaredSymShapes: Cache[(BlockMemberSymbol, FlowSymbol, Ls[Marks], Map[VarSymbol, DeclaredType], Bool), DeclaredSymShape] = + new Cache(inherited.map(_.declaredSymShapes), identity) + val selfShapes: Cache[InnerSymbol, BaseShape] = + new Cache(inherited.map(_.selfShapes), identity) + val defnShapes: Cache[DefinitionSymbol[?], DefnShape] = + new Cache(inherited.map(_.defnShapes), identity) + // Parameterless definition bodies share a result node before following their + // references. Keep distinct generic activations separate, and inherit the host + // identity so imported listeners use the consumer's private publisher data. + val byNameResults: Cache[(TermSymbol, TypeSubstitution), ShapeHost] = + new Cache(inherited.map(_.byNameResults), identity) + // Async payloads are deferred type edges, not snapshots of the body's current + // candidates. Register each host before following recursive function results. + val resultTypes: Cache[(Identity[Term], TypeSubstitution), DeclaredType] = + new Cache(inherited.map(_.resultTypes), identity) + val awaitedTypes: Cache[DeclaredType, DeclaredType] = + new Cache(inherited.map(_.awaitedTypes), identity) + val awaitedValues: Cache[(TermShape, TypeSubstitution), DeclaredType] = + new Cache(inherited.map(_.awaitedValues), identity) + val typeInterpretations: Cache[Identity[Term], TypeResolution] = + new Cache(inherited.map(_.typeInterpretations), identity) + // Qualification can deliver the same declaration in several environments. + // Its open source type is shared before any environment observes the body. + val selectedTypes: Cache[(Identity[Term], TypeSymbol), TypeResolution] = + new Cache(inherited.map(_.selectedTypes), identity) + // Recorded before elaborating a declaration's members, including in legacy + // exporters. Nominal interfaces may use any enclosing explicit type binder. + val lexicalTypeBinders: Cache[AnyDefinitionSymbol, Set[VarSymbol]] = + new Cache(inherited.map(_.lexicalTypeBinders), identity) + // Recorded with lexicalTypeBinders: the resolution scopes enclosing a type + // definition. Declared members are located at their class's definition. + val lexicalBoundaries: Cache[AnyDefinitionSymbol, Set[ResolutionBoundary]] = + new Cache(inherited.map(_.lexicalBoundaries), identity) + val typeDependencies: Cache[TypeResolution, Set[VarSymbol]] = + new Cache(inherited.map(_.typeDependencies), identity) + val unguardedTypeDependencies: Cache[TypeResolution, Set[VarSymbol]] = + new Cache(inherited.map(_.unguardedTypeDependencies), identity) + val regularTypes: Cache[TypeResolution, Bool] = + new Cache(inherited.map(_.regularTypes), identity) + val combinedTypes: Cache[TypeFormula[DeclaredType], DeclaredType] = + new Cache(inherited.map(_.combinedTypes), identity) + val argumentTypes: Cache[(TypeResolution, TypeArgument), DeclaredType] = + new Cache(inherited.map(_.argumentTypes), identity) + val pendingTypeDependencies: Cache[TypeResolution, TypeDependencyHost] = + new Cache(inherited.map(_.pendingTypeDependencies), identity) + val dependencySubscriptions: Seen[(TypeResolution, TypeResolution)] = + new Seen(inherited.map(_.dependencySubscriptions)) + val quantifiedTypes: Cache[(TypeResolution, Ls[VarSymbol]), TypeResolution] = + new Cache(inherited.map(_.quantifiedTypes), identity) + val instantiatedCallables: Cache[(CallableTypeShape, FlowSymbol, Ls[Marks]), CallableTypeShape] = + new Cache(inherited.map(_.instantiatedCallables), identity) + val contextualSymbols: Cache[(CoreSymShape, TypeSubstitution), ContextualSymShape] = + new Cache(inherited.map(_.contextualSymbols), identity) + val shapeViews: Cache[(TermShape, TypeSubstitution), TermShape] = + new Cache(inherited.map(_.shapeViews), identity) + val inferredInstantiations: Seen[(AnyDefinitionSymbol, FlowSymbol, TypeSubstitution, Ls[Marks])] = + new Seen(inherited.map(_.inferredInstantiations)) + // A scheme is owned by its source definition, or by the original interpretation + // of an anonymous quantified annotation. Neither a view nor an instance is an owner. + private val typeInstances: Cache[(AnyDefinitionSymbol | TypeResolution, FlowSymbol), TypeSubstitution] = + new Cache(inherited.map(_.typeInstances), identity) + private var allocatedTypeInstances: Int = 0 + private[hkmc2] def allocatedTypeInstanceCount: Int = root.allocatedTypeInstances + private val typeApplicationSites: Cache[Identity[TyApp], FlowSymbol] = + new Cache(inherited.map(_.typeApplicationSites), identity) + /** A type application consumes a scheme even when no term argument follows. + * Its site is shared across observations and activation views of the source. + */ + def typeApplicationSite(application: TyApp): FlowSymbol = + val key = new Identity(application) + root.typeApplicationSites.getOrElseUpdate(key, typeApplicationSites.get(key).getOrElse( + FlowSymbol("type application")(using owner))) + private val fieldProjectionSites: Cache[BlockMemberSymbol, FlowSymbol] = + new Cache(inherited.map(_.fieldProjectionSites), identity) + /** A structural field constraint is a static projection site. Recursive + * observations share it across substitution views; ordinary receiver marks + * distinguish activations, as they do for a written member selection. + */ + def fieldProjectionSite(field: BlockMemberSymbol): FlowSymbol = + root.fieldProjectionSites.getOrElseUpdate(field, fieldProjectionSites.get(field).getOrElse( + FlowSymbol.memSym(field)(using owner))) + private[hkmc2] def instantiateTypeParameters(scheme: AnyDefinitionSymbol | TypeResolution, + site: FlowSymbol, parameters: Ls[VarSymbol]): TypeSubstitution = + require(parameters.distinct.length == parameters.length, "A scheme cannot bind a parameter twice") + val origin = scheme match + case constructor: ClassCtorSymbol => constructor.associatedCls + case original => original + val key = (origin, site) + // Graph views in one consumer must agree even when they reach the same + // source definition through different imports. Adopt an inherited group + // before allocating, and put either result in the consumer's canonical cache. + val instances = root.typeInstances.getOrElseUpdate(key, typeInstances.get(key).getOrElse { + // Construct the complete group without activating constraints. Recursive + // subscribers may use it only after the cache contains every binder. + val result = parameters.map: parameter => + new TypeParameterInstance(parameter)(using owner) + root.allocatedTypeInstances += result.length + TypeSubstitution(result) + }) + assert(instances.keySet == parameters.toSet, "A source scheme's binders must remain stable") + instances + // The flag selects merged member interfaces versus independent constraint inputs. + val typeViews: Cache[(DeclaredType, Bool), TermShapeHost] = + new Cache(inherited.map(_.typeViews), identity) + val patternTypes: Cache[(Identity[Pattern.Constructor], InnerSymbol), DeclaredType] = + new Cache(inherited.map(_.patternTypes), identity) + // Repeated refinement of one input must reuse its synthesized type hosts. + // Constructor paths distinguish references to the same captured class. + val patternRefinements: Cache[(Identity[Pattern.Constructor], InnerSymbol, Ls[Marks], TermShape), DeclaredType] = + new Cache(inherited.map(_.patternRefinements), identity) + val primitiveTypes: Cache[ClassSymbol, DeclaredType] = + new Cache(inherited.map(_.primitiveTypes), identity) + val extremeTypes: Cache[(Bool, Opt[TypeResolution]), DeclaredType] = + new Cache(inherited.map(_.extremeTypes), identity) + val variantTypes: Cache[(DeclaredType, Bool), DeclaredType] = + new Cache(inherited.map(_.variantTypes), identity) + val selectedArguments: Cache[(DeclaredType, Bool), DeclaredType] = + new Cache(inherited.map(_.selectedArguments), identity) + val abstractTypes: Cache[TypeResolution, DeclaredType] = + new Cache(inherited.map(_.abstractTypes), identity) + val omittedTypes: Cache[(TypeResolution, VarSymbol), DeclaredType] = + new Cache(inherited.map(_.omittedTypes), identity) + val exposedTypeHoles: Seen[(DeclaredType, TermShape, Ls[Marks])] = + new Seen(inherited.map(_.exposedTypeHoles)) + val signatureParameters: Cache[VarSymbol, DeclaredType] = + new Cache(inherited.map(_.signatureParameters), identity) + val contextualTypes: Cache[ContextualType, DeclaredType] = + new Cache(inherited.map(_.contextualTypes), identity) + val tupleArrayParents: Cache[Identity[TupleShape], NominalInstanceView] = + new Cache(inherited.map(_.tupleArrayParents), identity) + val parameterTypes: Cache[TypeShape.Parameter, DeclaredType] = + new Cache(inherited.map(_.parameterTypes), identity) + val mutableArrays: Cache[Identity[Term.Mut], TermShape] = + new Cache(inherited.map(_.mutableArrays), identity) + // Named tuple fields have stable property identities, shared with consumers + // through the tuple's original graph rather than allocated per spread candidate. + val namedTupleRecords: Cache[Identity[Tup | CtxTup], Rcd] = + new Cache(inherited.map(_.namedTupleRecords), identity) + val aggregateProducers: Seen[Identity[Tup | CtxTup | Rcd]] = + new Seen(inherited.map(_.aggregateProducers)) + // Whether a binder is supplied is fixed by its authoritative syntactic site. + // Each definition/site has its own parameter instances; marks distinguish the + // enclosing activations of their bounds, not whether the slot is explicit. + // A method projection or nested constraint can change the observation scope + // without changing this property. Missing supplied positions remain inferred. + private val explicitTypeArguments = mutable.Set.empty[VarSymbol] + def markExplicitTypeArgument(symbol: VarSymbol): Unit = + root.explicitTypeArguments += symbol + def hasExplicitTypeArgument(symbol: VarSymbol): Bool = + root.explicitTypeArguments(symbol) || source.exists(_.hasExplicitTypeArgument(symbol)) + val typeConstraints: Seen[(DeclaredType, TermShape, Ls[Marks])] = + new Seen(inherited.map(_.typeConstraints)) + val typeRelations: Seen[(ContextualType, ContextualType)] = + new Seen(inherited.map(_.typeRelations)) + val typeArgumentArityErrors: Seen[(Identity[TyApp], Int)] = + new Seen(inherited.map(_.typeArgumentArityErrors)) + val arrayIndexErrors: Seen[Identity[Term]] = + new Seen(inherited.map(_.arrayIndexErrors)) + +private[semantics] final class TermShapeHost extends Host[TermShape]: + def showDbg(using DebugPrinter): Str = "instance views" + def publish(shape: TermShape)(using NewResolverState): Unit = + if currentShapes.add(shape) then notifyShapeListeners(shape) + def listen(listener: NewResolverState.Listener)(using NewResolverState): Unit = + subscribeToShapes(listener) + +private[semantics] final class TypeDependencyHost extends Host[Set[VarSymbol]]: + def showDbg(using DebugPrinter): Str = "type dependencies" + def publish(binders: Set[VarSymbol])(using NewResolverState): Unit = + if currentShapes.add(binders) then notifyShapeListeners(binders) + def listen(listener: ShapeListener[Set[VarSymbol]])(using NewResolverState): Unit = + subscribeToShapes(listener) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Pattern.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Pattern.scala index 4fc5e5b055..a133060a25 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Pattern.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Pattern.scala @@ -7,6 +7,8 @@ import syntax.{Keyword, SpreadKind, Tree}, Tree.{Ident, StrLit} import Elaborator.State, Message.MessageContext, ucs.error import scala.annotation.tailrec, util.chaining.* import utils.{TraceLogger, tl} +import hkmc2.document.* +import hkmc2.document.Document.* object Pattern: /** The reason why a variable obtained from `Pattern.variables` is invalid. */ @@ -41,7 +43,12 @@ object Pattern: invalidVars: Ls[(Pattern.Alias, InvalidReason)] ): /** Allocate symbols for all variables. */ - def allocate(using State, TraceLogger): Seq[(Str, VarSymbol)] = + def allocate(using State, TraceLogger): Seq[(Str, VarSymbol)] = allocate(Map.empty) + + /** Extraction parameters already own their binding symbols. Guards must use + * those symbols from the start, before their references are elaborated. + */ + def allocate(preallocated: Map[Str, VarSymbol])(using State, TraceLogger): Seq[(Str, VarSymbol)] = varMap.iterator.map: (name, aliases) => // We need to assign the same symbol to the same name. But I realize // that the following cases will break this constraint: `(x where x > 0) @@ -52,8 +59,10 @@ object Pattern: val symbols = aliases.iterator.flatMap(_.symbolOption).toSet // TODO: The above edge case would fail the following assertion. assert(symbols.size <= 1) + preallocated.get(name).foreach: expected => + assert(symbols.forall(_ is expected), "Guard bindings must retain their extraction parameter symbol") // If no symbol had been created before, create a new symbol now. - val symbol = symbols.headOption.getOrElse(VarSymbol(Ident(name), erasedType = N)) + val symbol = symbols.headOption.orElse(preallocated.get(name)).getOrElse(VarSymbol(Ident(name), erasedType = N)) aliases.foreach: alias => // For guarded patterns (`p where t`), the variables in `p` have to be // allocated before `t` is elaborated. In that case, we don't need to @@ -145,15 +154,11 @@ object Pattern: /** A shorthand for creating a variable pattern. */ def Variable = Pattern.Wildcard() binds (_: Ident) - trait ConstructorImpl: + trait ConstructorImpl extends PatternShapeHost, PossiblyErroneous: self: Pattern.Constructor => - /** Get the resolved symbol of the target term. */ - def symbol: Opt[Symbol] = self.target.resolvedSym - - /** Expect the `symbol` to be set. */ - def symbol_! : Symbol = symbol.getOrElse: - lastWords(s"target term `${self.target}` does not resolve to a symbol") + // val resSym: FlowSymbol = FlowSymbol.app + var resolvedTargets: Ls[DefinitionSymbol[?] | VarSymbol] = Nil /** Add a mutable field to the `Alias` pattern to store the symbol for the * variable. Note that NOT every `Alias` pattern has a symbol. */ @@ -171,7 +176,7 @@ object Pattern: import Pattern.*, InvalidReason.* /** An inductive data type for patterns. */ -enum Pattern extends AutoLocated: +enum Pattern extends AutoLocated, Describable, PatternShapePublisher: /** A pattern that matches a constructor and its arguments. * @param target The term representing the constructor. * @param arguments `None` if the pattern does not have a parameter list. The @@ -250,6 +255,12 @@ enum Pattern extends AutoLocated: * `and` in split. */ case Guarded(pattern: Pattern, guard: Term) + + // val trmHost: ShapeHost = new ShapeHost{} + object trmHost extends ShapeHost: + def showDbg(using DebugPrinter): Str = s"trmHost(${Pattern.this.showDbg})" + end trmHost + infix def binds(id: Ident): Pattern.Alias = Pattern.Alias(this, id) /** Annotate the pattern using the given term. If the term is `Error`, then @@ -302,6 +313,34 @@ enum Pattern extends AutoLocated: case p: Pattern => p.varNamesUsedInGuards .foldLeft(Set.empty[Str])(_ ++ _) + def show(using Scope, ShowCfg, Raise): Document = this match + case Composition(true, left, right) => doc"${left.show} | ${right.show}" + case Composition(false, left, right) => doc"${left.show} & ${right.show}" + case Negation(pattern) => doc"not ${pattern.show}" + case Wildcard() => "_" + case Literal(literal) => literal.idStr + case Range(lower, upper, rightInclusive) => + doc"${lower.idStr} ${if rightInclusive then "to" else "until"} ${upper.idStr}" + case Concatenation(left, right) => doc"${left.show} ~ ${right.show}" + case Tuple(leading, spread) => + (leading.iterator.map(_.show) ++ spread.fold(Iterator.empty): + case (_, middle, trailing) => + Iterator.single(middle.show) ++ trailing.iterator.map(_.show) + ).toList.mkDocument("[", ", ", "]") + case Record(fields) => doc"{${fields.map((k, v) => doc"${k.name}: ${v.show}").mkString(", ")}}" + case Chain(first, second) => doc"${first.show} >> ${second.show}" + // case al @ Alias(pattern, alias) => doc"${pattern.show} as ${alias.name}β€Ή${al.symbolOption.fold("")(_.showName)}β€Ί" + case al @ Alias(pattern, alias) => doc"${pattern.show} as ${al.symbolOption.fold(alias.name+"Λ€Λ€Λ€")(_.showName)}" + case Transform(pattern, _, transform) => doc"${pattern.show} => ${transform.show}" + case Annotated(pattern, annotations) => annotations.iterator.map: + case L(errorLoc) => "error" + case R(term) => term.show + .mkString("@", " @", " ") + pattern.show + case Guarded(pattern, guard) => pattern.show + " where " + guard.show + case Constructor(target, arguments) => + target.show :: arguments.fold(doc""): + args => doc"(${args.map(_.show).mkDocument(", ")})" + def children: Vector[Located] = this match case Constructor(target, arguments) => target +: arguments.fold(Vector.empty)(_.toVector) case Composition(polarity, left, right) => Vector.double(left, right) @@ -382,9 +421,9 @@ enum Pattern extends AutoLocated: Iterator.single(spreadKind.str + middle.showDbg) ++ trailing.iterator.map(_.showDbg)).mkString("[", ", ", "]") case Record(fields) => s"{${fields.map((k, v) => s"${k.name}: ${v.showDbg}").mkString(", ")}}" - case Chain(first, second) => s"${first.showDbgWithPar} as ${second.showDbgWithPar}" - case Alias(Wildcard(), alias) => alias.name - case Alias(pattern, alias) => s"${pattern.showDbgWithPar} as ${alias.name}" + case Chain(first, second) => s"${first.showDbgWithPar} >> ${second.showDbgWithPar}" + case al @ Alias(Wildcard(), alias) => s"${alias.name}β€Ή${al.symbolOption.fold("?")(_.showDbg)}β€Ί" + case al @ Alias(pattern, alias) => s"${pattern.showDbgWithPar} as ${alias.name}β€Ή${al.symbolOption.fold("")(_.showDbg)}β€Ί" case Transform(pattern, _, transform) => s"${pattern.showDbgWithPar} => ${transform.showDbg}" case Annotated(pattern, annotations) => annotations.iterator.map: case L(errorLoc) => "error" diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/PatternShape.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/PatternShape.scala new file mode 100644 index 0000000000..ac9654ccf5 --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/PatternShape.scala @@ -0,0 +1,39 @@ +package hkmc2 +package semantics + +import scala.collection.mutable.{Buffer, Set as MutSet, LinkedHashSet} + +import hkmc2.utils.*, shorthands.* +import syntax.* +import hkmc2.utils.Scope +import hkmc2.utils.Scope.scope +import hkmc2.document.* +import hkmc2.document.Document.* + +import Elaborator.State +import hkmc2.typing.Type +import hkmc2.semantics.Elaborator.{Ctx, ctx} +import hkmc2.Message.MessageContext +import hkmc2.semantics.flow.{SelectionTarget, AppTarget} + + + +type PatternShapePublisher = Publisher[PatternShape] +type PatternShapeHost = Host[PatternShape] + + +sealed abstract class PatternShape: + def showDbg(using DebugPrinter): Str + + +// TODO: not a case class...? +case class CtorPatternShape( + cls: ClassDef | ModuleOrObjectDef, + fs: Ls[BlockMemberSymbol -> Pattern], + src: Pattern.Constructor, + resSym: FlowSymbol, + marks: Ls[Marks], +) extends PatternShape: + def showDbg(using DebugPrinter): Str = src.showDbg + + diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala index c0e34d2d69..b18658e602 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala @@ -360,7 +360,7 @@ class Resolver(tl: TraceLogger) trace(s"Resolving definition: $defn"): def traverseTermDef(tdf: TermDefinition) = val TermDefinition(_k, _sym, _tsym, - pss, tps, sign, body, TermDefFlags(isMethod), modulefulness, annotations, comp + pss, tps, sign, body, TermDefFlags(isMethod, _), modulefulness, annotations, comp ) = tdf /** * Add the contextual parameters in pss to the ICtx so that they @@ -423,7 +423,7 @@ class Resolver(tl: TraceLogger) defn match // Case: instance definition. Add the instance to the context. - case defn @ TermDefinition(k = Ins, sym = sym, tsym = tsym, flags = TermDefFlags(isMethod), sign = sign) => + case defn @ TermDefinition(k = Ins, sym = sym, tsym = tsym, flags = TermDefFlags(isMethod, _), sign = sign) => softAssert(defn.owner.isEmpty) log(s"Resolving instance definition ${defn.showDbg}") traverseTermDef(defn) @@ -525,7 +525,7 @@ class Resolver(tl: TraceLogger) def resolve(t: Resolvable, prefer: Expect, inAppPrefix: Bool, inCtxPrefix: Bool, inTyPrefix: Bool)(using ICtx): (Opt[CallableDefinition], ICtx) = trace[(Opt[CallableDefinition], ICtx)]( s"Resolving resolvable term: ${t}, (prefer = ${prefer}, inAppPrefix = ${inAppPrefix}, inCtxPrefix = ${inCtxPrefix}, inTyPrefix = ${inTyPrefix})", - _ => s"~> ${t.expanded} (sym = ${t.resolvedSym}, typ = ${t.resolvedTyp})" + _ => s"~> ${t.expanded} (sym = ${t.legacyResolvedSym}, typ = ${t.resolvedTyp})" ): // Resolve the sub-resolvable-terms of the term. val (defn, newICtx1) = @@ -590,7 +590,7 @@ class Resolver(tl: TraceLogger) (N, ictx) t.expandedResolvableIn: t => - log(s"Resolving resolvable term ${t} with sym = ${t.resolvedSym}, typ = ${t.resolvedTyp}") + log(s"Resolving resolvable term ${t} with sym = ${t.legacyResolvedSym}, typ = ${t.resolvedTyp}") // Fill the context with possibly the type arguments information. val newICtx2 = newICtx1.givenIn: @@ -848,7 +848,7 @@ class Resolver(tl: TraceLogger) def resolveSymbol(t: Resolvable, prefer: Expect, sign: Bool)(using ictx: ICtx): Unit = trace[Unit]( s"Resolving symbol for term: ${t} (prefer = ${prefer})", - _ => s"-> (sym = ${t.resolvedSym}, typ = ${t.resolvedTyp})" + _ => s"-> (sym = ${t.legacyResolvedSym}, typ = ${t.resolvedTyp})" ): // If the term has an expansion already, it is likely that there is // an internal error because otherwise we should resolve the symbol @@ -879,7 +879,7 @@ class Resolver(tl: TraceLogger) log(s"Resolving symbol for selection ${t}, defn = ${lhs.defn}") val clsDefn = cls.flatMap: sym => - sym.resolvedSym.flatMap(_.asCls).map: clsSym => + sym.legacyResolvedSym.flatMap(_.asCls).map: clsSym => clsSym.defn.getOrElse(die) // Singleton Definitions @@ -934,7 +934,7 @@ class Resolver(tl: TraceLogger) case t @ Term.New(cls, _, N) => // cls is already resolved, so the symbol should be present; // otherwise, there is already an error raised. - cls.resolvedSym match + cls.legacyResolvedSym match case S(clsSym: ClassSymbol) => t.expand(S(t.duplicate.resolved(clsSym))) case S(sym) => @@ -953,7 +953,7 @@ class Resolver(tl: TraceLogger) */ def resolveType(t: Resolvable, prefer: Expect)(using ictx: ICtx): Unit = t.expandedResolvableIn: t => trace[Unit]( - s"Resolving the type for term: ${t} (prefer = ${prefer}, sym = ${t.resolvedSym})", + s"Resolving the type for term: ${t} (prefer = ${prefer}, sym = ${t.legacyResolvedSym})", _ => s"-> typ = ${t.resolvedTyp}" ): def disambSym(bms: BlockMemberSymbol): Opt[DefinitionSymbol[?]] = prefer match @@ -964,7 +964,7 @@ class Resolver(tl: TraceLogger) case _: PatternConstructor => TODO("disambSym for PatternConstructor") t match - case Term.New(cls, _, N) => cls.resolvedSym match + case Term.New(cls, _, N) => cls.legacyResolvedSym match case S(clsSym: ClassSymbol) => val ty = Type.Ref(clsSym, Nil) t.expand(S(t.withTyp(ty))) @@ -973,7 +973,7 @@ class Resolver(tl: TraceLogger) // otherwise, there is already an error raised. () case t @ Apps(base: Resolvable, ass) => - val decl = base.resolvedSym match + val decl = base.legacyResolvedSym match case S(bms: BlockMemberSymbol) => val disambBms = disambSym(bms) log(s"Disambiguate ${bms} (${bms.asTrm}) into ${disambBms} (defn = ${disambBms.map(_.defn)}), preferring ${prefer}") @@ -1062,9 +1062,17 @@ class Resolver(tl: TraceLogger) // * Traverse through the sub-terms. t match + // Captures retain lexical scopes for consumers using new resolution; + // they do not change the type interpreted by legacy resolution. + case Term.Capture(base, _) => + traverseSign(base, expect, inAppPrefix) + break() case Term.Ref(_) => case Term.Lit(_) => case Term.Tup(_) => t.subTerms.foreach(traverse(_, expect = NonModule(N))) + case Term.Rcd(_, stats) => stats.foreach: + case field: RcdField => traverseSign(field.rhs, expect = NonModule(N)) + case stat => raise(ErrorReport(msg"Expected a field declaration in record type." -> stat.toLoc :: Nil)) case Term.UnitVal() => // Literals with operators. e.g., -42 case Term.App(Term.Ref(_: BuiltinSymbol), Term.Tup(Fld(term = Term.Lit(_)) :: Nil)) => @@ -1082,7 +1090,7 @@ class Resolver(tl: TraceLogger) // Complex type: Function type, Wildcard type, Composed type, // Negation type, Forall type, - case t: (Term.FunTy | Term.WildcardTy | Term.CompType | Term.Neg | Term.Forall | Term.Constrained | Term.Tup) => + case t: (Term.DynTy | Term.FunTy | Term.WildcardTy | Term.CompType | Term.Neg | Term.Forall | Term.Constrained | Term.Tup) => t.subTerms.foreach(traverseSign(_, expect = Expect.NonModule(N))) // t is not a type. @@ -1150,6 +1158,7 @@ class Resolver(tl: TraceLogger) Type.Error t match + case Term.Capture(base, _) => resolveSign(base, expect) // If the term is a type application, e.g., T[A, ...], resolve the // type constructor and arguments respectively. case Term.TyApp(con, args) => resolveSign(con, expect = expect) match @@ -1168,13 +1177,13 @@ class Resolver(tl: TraceLogger) case Term.App(Term.Ref(_: BuiltinSymbol), Term.Tup(Fld(term = Term.Lit(_)) :: Nil)) => if expect.module then raiseError() else Type.NotImplemented // TODO: Support Lit with operator - case _: (Term.FunTy | Term.WildcardTy | Term.CompType | Term.Neg | Term.Forall | Term.Constrained | Term.Tup | Term.Lit) => + case _: (Term.DynTy | Term.FunTy | Term.WildcardTy | Term.CompType | Term.Neg | Term.Forall | Term.Constrained | Term.Tup | Term.Rcd | Term.Lit) => if expect.module then raiseError() else Type.NotImplemented // TODO: Support complex types // Otherwise, resolve the term directly. - case _ => t.resolvedSym match + case _ => t.legacyResolvedSym match // A VarSymbol is probably a type parameter. case S(sym: VarSymbol) if ModuleChecker.isTypeParam(sym) => if expect.module @@ -1247,6 +1256,6 @@ object ModuleChecker: case N => false case _ => false - def isStaticClass(t: Term): Bool = t.resolvedSym.exists(_.asCls.isDefined) + def isStaticClass(t: Term): Bool = t.legacyResolvedSym.exists(_.asCls.isDefined) end ModuleChecker diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Shape.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Shape.scala new file mode 100644 index 0000000000..73fce7489f --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Shape.scala @@ -0,0 +1,997 @@ +package hkmc2 +package semantics + +import hkmc2.utils.*, shorthands.* +import syntax.* +import hkmc2.Message.MessageContext +import hkmc2.document.* +import hkmc2.document.Document.* +import scala.collection.mutable + + +/** Publisher payloads distinguish ordinary shapes from activation-tagged events. + * Only Shape supports value operations; an event must be dispatched first. + * Plain shapes need no allocation to participate in activation-independent flow. + */ +sealed trait ShapeEvent + +sealed trait Shape extends ShapeEvent, ShapeLike: + def describe: Str + /** Origin of the value or symbol described by this shape, independently of its use site. */ + def toLoc: Opt[Loc] + /** Keep the operation's explanation at its interface witness, after any path + * from the observed value to that witness. Wrappers preserve this ordering. + * An annotation restricting a value and its class declaration are different + * witnesses: interface errors use the annotation, while toLoc identifies the + * declaration. Diagnostic witnesses do not change shape equality. + */ + final def diagnostic(message: Message): Ls[(Message, Opt[Loc])] = this match + case value: TermShape => value.applicationHead._1 match + case view: NominalInstanceView => view.provenance.diagnostic(message, view.annotation.flatMap(_.toLoc).orElse(toLoc)) + case view: ContextualShape => view.source.diagnostic(message) + case view: SpecializedShape => view.declaration.diagnostic(message) + case unknown: UnknownValueShape => unknown.provenance.diagnostic(message, toLoc) + case opaque: OpaqueTypeShape => opaque.provenance.diagnostic(message, toLoc) + case rigid: RigidTypeShape => rigid.provenance.diagnostic(message, toLoc) + case _ => (message -> toLoc) :: Nil + case _: SymShape => (message -> toLoc) :: Nil + def shwDbg(using DebugPrinter): Str = this match + // case ds: DefnShape => s"DefnShape(${ds.defn.describe} ${ds.defn.sym.showDbg})" + case ds: DefnShape => ds.defn.sym.showDbg + case as: AppShape => s"AppShape(${as.receiver.shwDbg}, ${as.args.showDbg})" + case ms: MarkedShape[?] => s"MarkedShape(${ms.sh.shwDbg}, ${ms.mark.showDbg})" + case ns: SymShape => s"SymShape(${ns.sym.showDbg})" + case ns: NewShape => s"NewShape(${ns.cls.showDbg}, ${ns.argss.map(_.showDbg).mkString(", ")})" + case is: IntroShape => s"IntroShape(${is.trm.showDbg})" + case _: DynShape => "DynShape" + case us: UnknownValueShape => s"UnknownValueShape(${us.source.showDbg})" + case ts: TupleShape => s"TupleShape(${ts.source.showDbg})" + case rs: RecordShape => s"RecordShape(${rs.source.showDbg})" + case rs: RecordTypeShape => s"RecordTypeShape(${rs.source.showDbg})" + case ts: InstanceShape => s"InstanceShape(${ts.tpe.resolution.source.showDbg})" + case ts: NominalInstanceView => s"NominalInstanceView(${ts.defn.sym.showDbg})" + case ts: OpaqueTypeShape => s"OpaqueTypeShape(${ts.source.showDbg})" + case ts: CallableTypeShape => s"CallableTypeShape(${ts.source.showDbg})" + case view: ContextualShape => s"ContextualShape(${view.source.shwDbg})" + case specialized: SpecializedShape => s"SpecializedShape(${specialized.declaration.shwDbg})" + case rigid: RigidTypeShape => s"RigidTypeShape(${rigid.parameter.showDbg})" + case bs: BaseShape => s"BaseShape(${bs.defn.sym.showDbg})" + case es: ErrShape => es.describe + +sealed trait NonMarkedShape extends TermShape +sealed trait NonAppTermShape extends NonMarkedShape + +/** A shape with its outer contextual/specialization view removed. Application + * receivers use this family, so a view cannot hide inside an application chain. + * Marks remain on the receiver and are accumulated by applicationHead. + */ +sealed trait CoreShape extends NonMarkedShape +sealed trait CoreHeadShape extends CoreShape, NonAppTermShape +type CoreTermShape = CoreShape | MarkedShape[CoreShape] + +// Only these shapes defer substitution to a ContextualShape. All other cores +// either store substitutions in their own fields or are independent of binders. +type ContextualSource = AppShape | NewShape | DefnShape | BaseShape | IntroShape + +/** The value's lexical substitution and any already consumed type arguments. + * Removing these views preserves the full application chain and its marks. + */ +final case class ShapeParts(value: CoreTermShape, instances: TypeSubstitution, + supplied: Opt[Ls[DeclaredType]]) + +/** A lexical resolution boundary, independent of a definition's term/type interpretation. + * A class and its companion constructor enter the same instance scope. Keeping their + * symbol identities distinct is still necessary for overload selection and lowering. + */ +final case class ResolutionBoundary private (symbol: AnyDefinitionSymbol): + def showDbg(using DebugPrinter): Str = symbol.showDbg + override def toString: Str = symbol.toString +object ResolutionBoundary: + def apply(symbol: AnyDefinitionSymbol): ResolutionBoundary = + assert(!symbol.isInstanceOf[TypeAliasSymbol], "Type aliases do not introduce value resolution scopes") + new ResolutionBoundary(symbol match + case ctor: ClassCtorSymbol => ctor.associatedCls + case symbol => symbol) + +// Scanning each new tail makes chain construction quadratic in its depth. +// Keep the checks enabled so missing scope transfers fail at their source instead +// of allowing repeated boundaries to accumulate in contextual reference keys. +private val checkMarkPaths = true + +/** A reduced lexical path: entries followed by exits, most recent crossing first. + * Exiting cancels the leading entry when their sites agree (an absent site is + * a capture, compatible with any activation). Entering never cancels an exit: + * that pair records an inner value's provenance until a consumer accesses it. + * Each direction traverses distinct lexical scopes, so recursive calls cannot + * lengthen a normalized path indefinitely. Debug assertions check this invariant + * without widening paths. + * + * A well-scoped walk's adjacent entry/exit steps traverse the same tree edge. + * Cancelling them preserves endpoints; a reduced walk only ascends, then + * descends. Neither run can revisit a boundary, and its length is bounded by + * the sum of the endpoint depths. Producers must preserve those endpoints, + * including through qualified type selections and deferred type references. + * See doc/new-resolution-scopes.md for the proof and the producer contracts. + */ +sealed abstract class Marks: + def showDbg(using DebugPrinter): Str = this match + case EntryMark(boundary, id, rest) => s"β†˜βŸ¨${boundary.showDbg}⟩${id.fold("")("%⟨"+_.showDbg+"⟩")}${rest.showDbg}" + case ExitMark(boundary, id, rest) => s"β†—βŸ¨${boundary.showDbg}⟩${id.fold("")("%⟨"+_.showDbg+"⟩")}${rest.showDbg}" + case NoMarks => "Ο΅" + @scala.annotation.tailrec + final def hasEntry(boundary: ResolutionBoundary): Bool = this match + case EntryMark(b, _, rest) => b == boundary || rest.hasEntry(boundary) + case _ => false + +type SomeMarks = EntryMark | ExitMark +case class EntryMark(boundary: ResolutionBoundary, id: Opt[FlowSymbol], rest: Marks) extends Marks: + // Consecutive entries descend lexical scopes; consecutive exits ascend them. + // Repeating a scope in either direction indicates a missing capture/exit, not + // another recursive activation to retain or silently truncate. + if checkMarkPaths then + assert(!rest.hasEntry(boundary), "Repeated entry into the same lexical resolution scope") +sealed abstract class ExitMarks extends Marks: + @scala.annotation.tailrec + final def hasExit(boundary: ResolutionBoundary): Bool = this match + case ExitMark(b, _, rest) => b == boundary || rest.hasExit(boundary) + case NoMarks => false +case class ExitMark(boundary: ResolutionBoundary, id: Opt[FlowSymbol], rest: ExitMarks) extends ExitMarks: + if checkMarkPaths then + assert(!rest.hasExit(boundary), "Repeated exit from the same lexical resolution scope") +case object NoMarks extends ExitMarks + +sealed trait ShapeLike: + def exit(revMarkss: Ls[Marks])(using TL): TermShape | NoShape + def exit(marks: Marks)(using TL): TermShape | NoShape + def enter(revMarkss: Ls[Marks])(using TL): TermShape | NoShape + def enter(marks: Marks)(using TL): TermShape | NoShape + def shwDbg(using DebugPrinter): Str + +case object NoShape extends ShapeLike: + def exit(revMarkss: Ls[Marks])(using TL): TermShape | NoShape = NoShape + def exit(marks: Marks)(using TL): TermShape | NoShape = NoShape + def enter(revMarkss: Ls[Marks])(using TL): TermShape | NoShape = NoShape + def enter(marks: Marks)(using TL): TermShape | NoShape = NoShape + def shwDbg(using DebugPrinter): Str = toString + +type NoShape = NoShape.type + +object Marked: + // Reattach an already normalized outer path without erasing the core's type. + def apply[T <: NonMarkedShape](shape: T, marks: Marks): T | MarkedShape[T] = marks match + case NoMarks => shape + case marks: SomeMarks => MarkedShape(shape, marks) + def unapply(sh: TermShape): S[(NonMarkedShape, Marks)] = + sh match + case sh: NonMarkedShape => S((sh, NoMarks)) + case MarkedShape(sh, mark) => S((sh, mark)) +end Marked + +case class MarkedShape[+T <: NonMarkedShape](sh: T, mark: SomeMarks) extends TermShape: + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = + sh.getMemberThrough(name, mark) + override def isInstanceOfClass(cls: ClassLikeDef)(using NewResolverState): Bool = sh.isInstanceOfClass(cls) + def describe: Str = sh.describe + def toLoc: Opt[Loc] = sh.toLoc +object MarkedShape: + def enter(sh: TermShape, boundary: ResolutionBoundary, id: Opt[FlowSymbol])(using TL): MarkedShape[NonMarkedShape] = + sh match + case sh: NonMarkedShape => MarkedShape(sh, EntryMark(boundary, id, NoMarks)) + case MarkedShape(sh, marks) => MarkedShape(sh, EntryMark(boundary, id, marks)) + def exit(sh: TermShape, boundary: ResolutionBoundary, id: Opt[FlowSymbol])(using TL): TermShape | NoShape = + tl.trace[TermShape | NoShape](s".exit MarkedShape (${sh.shwDbg}, ${boundary.showDbg}, ${id.fold("")(_.showDbg)})", res => s"= ${res.shwDbg}"): + sh match + case sh: NonMarkedShape => MarkedShape(sh, ExitMark(boundary, id, NoMarks)) + case MarkedShape(sh, marks) => + marks match + case marks: ExitMark => MarkedShape(sh, ExitMark(boundary, id, marks)) + case EntryMark(entered, id2, rest) => + assert(entered == boundary, s"Entry and exit cross different lexical resolution scopes: ${entered.showDbg} vs ${boundary.showDbg} for ${sh.shwDbg}") + if id.forall(i1 => id2.forall(i2 => i1 is i2)) then + rest match + case NoMarks => sh + case rest: SomeMarks => MarkedShape(sh, rest) + else NoShape +end MarkedShape + +sealed trait TermShape extends Shape: + // Cache all lookup outcomes per shape, without materializing all inherited members. + final def getMember(name: Str)(using state: NewResolverState): MemberLookup = + state.membersCache.getOrElseUpdate((new Identity[TermShape](this), name), getMemberImpl(name)) + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup + /** Lookup on this shape as reached through the receiver path `receiver`. + * Shapes whose members are declared by a nominal class override this to + * locate the member at the class's definition (see NewResolver.nominalMember); + * other members are transported through the whole path. + */ + def getMemberThrough(name: Str, receiver: Marks)(using NewResolverState): MemberLookup = receiver match + case NoMarks => getMember(name) + case receiver: SomeMarks => getMember(name).withMarks(receiver :: Nil) + + /** Whether this value is an instance of the nominal class. A saturated class + * value (e.g. a class without parameters) is still not an instance. */ + def isInstanceOfClass(cls: ClassLikeDef)(using NewResolverState): Bool = false + + // Cache the head independently: following receivers does not need to construct + // or unpack a (head, marks) pair at every intermediate shape. + private lazy val applicationHeadShape: NonAppTermShape = this match + case as: AppShape => as.receiver.applicationHeadShape + case ns: NewShape => ns.receiver + case na: NonAppTermShape => na + case MarkedShape(sh, _) => (sh: TermShape).applicationHeadShape + // Public contexts run from the callable definition to its consumer, as in + // MemberLookup.withMarks. Build the reverse privately so each wrapper prepends + // in constant time; materialize the forward view only when a consumer needs it. + private lazy val reversedApplicationMarks: Ls[Marks] = this match + case as: AppShape => as.receiver.reversedApplicationMarks + case ns: NewShape => ns.clsMarks.reverse + case _: NonAppTermShape => Nil + case MarkedShape(sh, mark) => mark :: (sh: TermShape).reversedApplicationMarks + private lazy val applicationMarks: Ls[Marks] = this match + case as: AppShape => as.receiver.applicationMarks + case ns: NewShape => ns.clsMarks + case _ => reversedApplicationMarks.reverse + lazy val applicationHead: (NonAppTermShape, Ls[Marks]) = this match + case as: AppShape => as.receiver.applicationHead + case _ => (applicationHeadShape, applicationMarks) + // All remaining parameter lists belong to the same callable and carry its + // context. Keep the lists separate until the public view needs their pairs. + private lazy val unappliedParamLists: Ls[ParamList] = this match + case specialized: SpecializedShape => (specialized.declaration: TermShape).unappliedParamLists + case ds: DefnShape => ds.defn match + case defn: TermDefinition => defn.params + case defn: ClassDef => + if defn.paramsOpt.isDefined then // whether the class can receive direct applications + defn.paramsOpt.toList ::: defn.auxParams + else Nil + case _ => Nil + case as: AppShape => as.receiver.unappliedParamLists.drop(1) + case ns: NewShape => (ns.receiver: TermShape).unappliedParamLists.drop(ns.argss.length) + case MarkedShape(sh, _) => (sh: TermShape).unappliedParamLists + case is: IntroShape => + is.trm match + case Term.Lam(params, _) => params :: Nil + case _ => Nil + case _ => Nil + lazy val unappliedParams: Ls[(ParamList, Ls[Marks])] = this match + // Applying arguments removes a list without changing the context; retain + // sharing of the already paired tail when this public view is requested. + case as: AppShape => as.receiver.unappliedParams.drop(1) + case _ => unappliedParamLists.map(_ -> applicationMarks) + + def exit(marks: Marks)(using TL): TermShape | NoShape = + tl.trace[TermShape | NoShape](s".exit $shwDbg (${marks.showDbg})", res => s"= ${res.shwDbg}"): + marks match + case NoMarks => this + // case EntryMark(sym, id, rest) => + // MarkedShape.enter(this, sym, id).exit(rest) + // case ExitMark(sym, id, rest) => + // MarkedShape.exit(this, sym, id).exit(rest) + case EntryMark(sym, id, rest) => + exit(rest) match + case NoShape => NoShape + case ex: TermShape => MarkedShape.enter(ex, sym, id) + case ExitMark(sym, id, rest) => + exit(rest) match + case NoShape => NoShape + case ex: TermShape => MarkedShape.exit(ex, sym, id) + + def exit(revMarkss: Ls[Marks])(using TL): TermShape | NoShape = revMarkss match + case Nil => this + case mark :: rest => + exit(mark).exit(rest) + + def enter(marks: Marks)(using tl: TL): TermShape | NoShape = + tl.trace[TermShape | NoShape](s".enter $shwDbg (${marks.showDbg})", res => s"= ${res.shwDbg}"): + marks match + case NoMarks => this + case EntryMark(sym, id, rest) => + // MarkedShape.enter(this, sym, id).enter(rest) + // ??? + // MarkedShape.exit(this, sym, id) match + // case NoShape => this // TODO: should this be an error? + // case sh: TermShape => sh.enter(rest) + MarkedShape.exit(this, sym, id).enter(rest) + case ExitMark(sym, id, rest) => + // MarkedShape.exit(this, sym, id) match + // case NoShape => this // TODO: should this be an error? + // case sh => sh.enter(rest) + MarkedShape.enter(this, sym, id).enter(rest) + // marks match + // case NoMarks => this + // case marks: SomeMarks => + // this match + // case sh: NonMarkedShape => MarkedShape(sh, marks) + // case MarkedShape(sh, mark) => + // ??? + + def enter(revMarkss: Ls[Marks])(using TL): TermShape | NoShape = revMarkss match + case Nil => this + case mark :: rest => + // Fragments describe travel from the definition out to its consumer. + // Passing an argument back in must undo that composition in reverse order. + enter(rest).enter(mark) + + def isSaturated: Bool = unappliedParamLists.isEmpty + +end TermShape + +// sealed trait TermShape: +// def describe: Str = this match +// case app: AppShape => s"application of ${app.lhs.describe} to ${app.args.describe}" +// case sel: SelShape => s"selection of ${sel.nme.name} from ${sel.receiver.describe}" +// case sym: SymShape => s"symbol ${sym.sym.describe}" + +/** Missing means definitely absent; Unknown must not be treated as a miss when + * searching wildcard opens, since it can introduce an additional candidate. */ +enum MemberLookup: + case Contextual(source: MemberLookup, instances: TypeSubstitution) + case Found(member: BlockMemberSymbol | RecordMember, marks: Ls[Marks]) + // Declared members expose signatures only. Their marks transport dependent + // type arguments; they never authorize reading an implementation's value flow. + case Declared(member: BlockMemberSymbol, bindings: Map[VarSymbol, DeclaredType], marks: Ls[Marks], annotation: Opt[Term], positive: Bool) + // A structural field already has its lexical bindings and intersection applied. + case Typed(field: RcdField, tpe: DeclaredType, marks: Ls[Marks]) + // A preceding spread can place several different fields at the same index. + case Indexed(fields: Ls[TupleShape.Fixed], marks: Ls[Marks]) + case Dynamic(marks: Ls[Marks]) + case Missing + case Unknown(reason: MemberLookup.Uncertainty, fromPublicInterface: Bool, provenance: ShapeProvenance) + + def withMarks(marks: Ls[Marks]): MemberLookup = this match + case Contextual(source, instances) => Contextual(source.withMarks(marks), instances) + case Found(member, inner) => Found(member, inner ::: marks) + case Declared(member, bindings, inner, annotation, positive) => Declared(member, bindings, inner ::: marks, annotation, positive) + case Typed(field, tpe, inner) => Typed(field, tpe, inner ::: marks) + case Indexed(fields, inner) => Indexed(fields, inner ::: marks) + case Dynamic(inner) => Dynamic(inner ::: marks) + case _ => this + + /** The receiver's annotation remains the diagnostic origin when lookup visits a parent. */ + def withAnnotation(annotation: Opt[Term]): MemberLookup = this match + case Contextual(source, instances) => Contextual(source.withAnnotation(annotation), instances) + case Declared(member, bindings, marks, _, positive) => Declared(member, bindings, marks, annotation, positive) + case _ => this + + def instantiate(instances: TypeSubstitution): MemberLookup = + if instances.isEmpty then this else this match + case Contextual(source, previous) => Contextual(source, instances.withOverrides(previous)) + case Missing | Unknown(_, _, _) | Dynamic(_) => this + case _ => Contextual(this, instances) + +object MemberLookup: + enum Uncertainty: + case ValueShape, RecordOverwrite + + /** Own members take precedence over inherited ones, including unknown spreads. */ + def inClass(defn: ClassLikeDef, ext: Opt[TermShape], name: Str)(using NewResolverState): MemberLookup = + defn.body.members.get(name) match + case S(member) => Found(member, Nil) + case N => ext.fold[MemberLookup](Missing)(_.getMember(name)) + +extension (member: BlockMemberSymbol | RecordMember) + def memberSymbol: BlockMemberSymbol = member match + case symbol: BlockMemberSymbol => symbol + case member: RecordMember => member.field.sym + +class ErrShape(val err: ErrorReport) extends CoreHeadShape: + def describe: Str = s"error: ${err.mainMsg}" + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = MemberLookup.Missing + def toLoc: Opt[Loc] = N + +class AppShape(val receiver: CoreTermShape, val args: Term, val src: Term.App)(using DebugPrinter) extends CoreShape: + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = + // An unsaturated term definition is just a concrete function shape + if !isSaturated then MemberLookup.Missing + else + applicationHead match + case (ds: DefnShape, mss) => + ds.getInstanceMember(name).withMarks(mss) + case _ => MemberLookup.Missing + override def isInstanceOfClass(cls: ClassLikeDef)(using NewResolverState): Bool = + isSaturated && (applicationHead._1 match + case ds: DefnShape => ds.classExtends(cls) + case _ => false) + def describe: Str = + // s"application of ${receiver.describe}" + s"instance of ${applicationHead._1.describe}" + def toLoc: Opt[Loc] = src.toLoc + override def toString: String = s"AppShape($receiver, ${args.showDbg})" + // def target: Opt[AppTarget] + +class NewShape(val receiver: DefnShape, val classTarget: ClassValueTarget, val clsMarks: Ls[Marks], val argss: Ls[Term], val src: Term.New, + val supplied: Opt[Ls[DeclaredType]])(using DebugPrinter) extends CoreShape: + def cls: ClassSymbol = ClassValueShape.definitionOf(classTarget).sym + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = + if isSaturated then receiver.getInstanceMember(name).withMarks(clsMarks) + else MemberLookup.Missing + override def isInstanceOfClass(cls: ClassLikeDef)(using NewResolverState): Bool = + isSaturated && receiver.classExtends(cls) + def describe: Str = + // s"instantiation of ${receiver.describe}" + s"instance of ${cls.defn.get.describeRef}" + override def toString: String = s"NewNewShape(${cls.showDbg}, $argss)" + def toLoc: Opt[Loc] = src.toLoc + +/** A class expression can denote a class directly or recover it from its + * generated constructor function. Keep the source definition in the inference + * shape so lowering does not need a separate mutable access-mode annotation. + */ +type ClassValueTarget = ClassSymbol | ClassCtorSymbol + +final case class ClassValueShape(target: ClassValueTarget, parent: Opt[TermShape]) + extends DefnShape(ClassValueShape.definitionOf(target), parent) + +object ClassValueShape: + def definitionOf(target: ClassValueTarget): ClassDef = target match + case cls: ClassSymbol => cls.defn.get + case ctor: ClassCtorSymbol => ctor.associatedCls.defn.get + + /** Read the originating unit's completed interpretation. Later inference must + * preserve both the class identity and whether its operand needs unwrapping. + */ + def targetsOf(term: Term): Ls[ClassValueTarget] = + def targets(event: ShapeEvent): Ls[ClassValueTarget] = event match + case ActivatedShapeEvent(value, _) => targets(value) + case MarkedShape(value, _) => targets(value) + case ContextualShape(value, _) => targets(value) + case SpecializedShape(value, _, _) => targets(value) + case shape: NewShape => shape.classTarget :: Nil + case shape: ClassValueShape => shape.target :: Nil + case _ => Nil + term.shapes.toList.flatMap(targets).distinct + +sealed abstract class SymShape(val sym: BlockMemberSymbol, val resSym: FlowSymbol, val markss: Ls[Marks]) extends Shape: + def describe: Str = s"${sym.describe} symbol '${sym.nme}'" + def toLoc: Opt[Loc] = sym.toLoc + override def toString: String = s"SymShape($sym)" + def exit(revMarkss: Ls[Marks])(using TL): TermShape | NoShape = ??? + def exit(marks: Marks)(using TL): TermShape | NoShape = ??? + def enter(revMarkss: Ls[Marks])(using TL): TermShape | NoShape = ??? + def enter(marks: Marks)(using TL): TermShape | NoShape = ??? + +/** A symbolic value before its captured substitution is attached. Contextual + * symbol views compose substitutions instead of wrapping one another. + */ +sealed class CoreSymShape(symbol: BlockMemberSymbol, site: FlowSymbol, marks: Ls[Marks]) +extends SymShape(symbol, site, marks) + +/** The symbolic counterpart of ContextualShape: retain the value's substitution + * until overload selection produces a term shape. This does not consume a scheme. + */ +final class ContextualSymShape(val source: CoreSymShape, val instances: TypeSubstitution) +extends SymShape(source.sym, source.resSym, source.markss) + + +/** Keep a declared receiver's boundary while consumers choose between the term, + * type, and constructor interpretations of the same overload set. + */ +final class DeclaredSymShape(symbol: BlockMemberSymbol, site: FlowSymbol, marks: Ls[Marks], + val bindings: Map[VarSymbol, DeclaredType], val annotation: Opt[Term], val positive: Bool) extends CoreSymShape(symbol, site, marks) + +/* +class ThisShape(val defn: Definition) extends NonAppTermShape: + def describe: Str = s"Self-reference to ${defn.bsym.describe} '${defn.bsym.nme}'" + override def toString: String = s"ThisShape(${defn.describe})" + def members: Map[Str, MemberInfo] = ??? +*/ + +// TODO: make it not a TermShape? +class BaseShape(val defn: ClassLikeDef, val ext: Opt[TermShape]) extends CoreHeadShape: + override def isInstanceOfClass(cls: ClassLikeDef)(using NewResolverState): Bool = + (defn is cls) || ext.exists(_.isInstanceOfClass(cls)) + def describe: Str = s"${defn.describe}" + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = + MemberLookup.inClass(defn, ext, name) + def toLoc: Opt[Loc] = defn.toLoc + +/** A structural annotation exposes only its declared fields. Their symbols belong + * to the annotation, not to whichever record happens to be passed by a caller. + * Field types are deferred references, including for recursive intersections. + * Source declarations are witnesses for diagnostics and property selection, not + * part of semantic equality: a merged field may have several such witnesses. + */ +final case class RecordTypeShape(fields: Map[Str, DeclaredType])( + val source: Term.Rcd, val declarations: Map[Str, RcdField]) extends CoreHeadShape: + def describe: Str = "record type" + def toLoc: Opt[Loc] = source.toLoc + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = + fields.get(name).fold[MemberLookup](MemberLookup.Missing)(tpe => + MemberLookup.Typed(declarations(name), tpe, Nil)) + +/** An instance described by a type, in either position of a constraint. Keep the + * type reference intact during transport: expanding it to its current positive + * candidates would lose negative uses of generic arguments and compound types. + * Operations obtain concrete member/call views through listenInstanceViews. + */ +final case class InstanceShape(tpe: DeclaredType) extends CoreHeadShape: + def describe: Str = "value with a declared type" + def toLoc: Opt[Loc] = tpe.resolution.source.toLoc + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = + lastWords("Instance member lookup requires interpreting its type first") + +/** A deferred value view: interpret references in the shared `source` using + * `instances`, a flat map from original binders to canonical parameter instances. + * This does not consume the source's own generic scheme. For example, a returned + * inner[B] can capture outer's A while leaving B available for a later call. + * `instantiateShape` composes views instead of nesting them; already captured + * entries take precedence. Aggregates and declared types can store this view + * directly rather than using a ContextualShape wrapper. + * Unlike ActivatedShapeEvent.instances, this map belongs to the value, not its receiver. + * See doc/new-resolution-type-value-flow.md, "Value views, consumed schemes, and activation events". + */ +final case class ContextualShape(source: ContextualSource, instances: TypeSubstitution) extends NonAppTermShape: + def describe: Str = source.describe + def toLoc: Opt[Loc] = source.toLoc + override def isInstanceOfClass(cls: ClassLikeDef)(using NewResolverState): Bool = source.isInstanceOfClass(cls) + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = source.getMember(name).instantiate(instances) + override def getMemberThrough(name: Str, receiver: Marks)(using NewResolverState): MemberLookup = + source.getMemberThrough(name, receiver).instantiate(instances) + +/** Explicit type application, such as f[Int], has consumed this declaration's + * scheme before any term argument list need be applied. `arguments` retains the + * supplied caller type references; `instances` retains the chosen binder group + * and lexical captures. `shapeParts` tells `appShape` to reuse that group even + * through stored aliases, rather than instantiate the declaration again. + * A ContextualShape alone does not record scheme consumption. Complete annotated + * CallableTypeShape views record consumption by substituting and clearing `scheme`. + */ +final case class SpecializedShape(declaration: DefnShape, arguments: Ls[DeclaredType], + instances: TypeSubstitution) extends NonAppTermShape: + def describe: Str = declaration.describe + def toLoc: Opt[Loc] = declaration.toLoc + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = declaration.getMember(name).instantiate(instances) + +/** An inference-event envelope, not a value interface. `publishActivated` saves + * the current body's original-binder-to-instance map; `listen` checks agreement + * with the requested activation on shared keys, unwraps `value`, and invokes the + * receiver in the combined compatible activation. An empty request accepts all. + * The enclosed value separately retains its own substitution: in recursive f[A], + * it can refer to the caller's A@p while the receiving body uses A@q. Merging those + * maps would rebind the value or execute the operation in the wrong activation. + * The payload may be a value or an unresolved symbol; it cannot itself be an + * event. This envelope supports neither value operations nor mark transport. + * Equality includes the activation so distinct deliveries are not deduplicated. + * Neither map changes the ordinary mark algebra used for lexical scope crossings. + */ +final case class ActivatedShapeEvent(value: Shape, instances: TypeSubstitution) extends ShapeEvent: + require(instances.nonEmpty, "Publish an activation-independent shape without an envelope") + +/** A nominal annotation exposes only declarations, including inherited declarations. + * In particular, selecting an unannotated field does not inspect its initializer. + * The annotation is a diagnostic witness, excluded from shape equality; synthesized + * interfaces such as a tuple's Array parent have no written annotation. + * Provenance can instead identify the construct that introduces an interface, + * such as the async annotation that creates a Promise result. + */ +final case class NominalInstanceView(defn: ClassLikeDef, bindings: Map[VarSymbol, DeclaredType], + parent: Opt[TermShape])(val annotation: Opt[Term], val provenance: ShapeProvenance)(resolver: NewResolver) extends CoreHeadShape: + def describe: Str = s"value of type '${defn.sym.nme}'" + def toLoc: Opt[Loc] = defn.toLoc + override def isInstanceOfClass(cls: ClassLikeDef)(using NewResolverState): Bool = + (defn is cls) || parent.exists(_.isInstanceOfClass(cls)) + def ancestor(cls: ClassLikeDef): Opt[NominalInstanceView] = + if defn is cls then S(this) + else parent.flatMap: + case Marked(parent: NominalInstanceView, _) => parent.ancestor(cls) + case _ => N + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = + resolver.nominalMember(this, name, NoMarks) + override def getMemberThrough(name: Str, receiver: Marks)(using NewResolverState): MemberLookup = + resolver.nominalMember(this, name, receiver) + +/** Parameter types and arity exposed by one list in a declared calling interface. + * A rest annotation describes the whole trailing array; hasRest also distinguishes + * an unannotated rest parameter from a fixed-arity list. + */ +final case class DeclaredParams(params: Ls[Opt[DeclaredType]], hasRest: Bool, rest: Opt[DeclaredType]) + +/** Async lowering wraps the result only after all written parameter lists have + * been applied. A separate signature can encode those lists as nested arrows, + * so the pending wrapper must survive observing the next callable interface. + */ +final case class AsyncResult(annotation: Annot.Async, remainingLists: Int): + require(remainingLists > 0) + +/** Calls through annotations expose only the declared result. Argument shapes + * constrain type parameters in the interface; they do not recover its implementation. + * `scheme` retains binders that are still available for instantiation; capture + * substitution leaves those binders alone. `instantiateCallable` consumes the + * scheme by substituting its references and clearing it, so stored values and + * remaining curried lists reuse the chosen instances. This is the annotated + * counterpart of SpecializedShape, not an inference-event activation. + */ +final case class CallableTypeShape(source: Term, paramLists: NELs[DeclaredParams], + result: Opt[DeclaredType], scheme: Opt[TypeScheme], supplied: Opt[Ls[DeclaredType]], + declaration: Opt[TermDefinition], asyncResult: Opt[AsyncResult]) extends CoreHeadShape: + def tparams: Ls[DeclaredTypeParameter] = scheme.toList.flatMap(_.parameters) + def describe: Str = declaration.fold("function with a declared signature")(d => s"function '${d.bsym.nme}'") + def toLoc: Opt[Loc] = declaration.fold(source.toLoc)(_.toLoc) + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = MemberLookup.Missing + +/** Why observing a type did not expose a member interface. Keep this evidence + * separate from semantic candidates: normalization can synthesize Any without + * making the written input bound an abstract type. Each reason retains a finite + * source witness, not a path that grows when recursive aliases are revisited. + */ +enum TypeInterfaceReason: + case MissingOutput(source: Term) + case ContravariantParameter(parameter: TyParam, source: Term) + case Unrestricted(source: Term) + case AbstractDeclaration(symbol: TypeAliasSymbol, source: Term) + case RecursiveAlias(symbol: TypeAliasSymbol) + case Negated(source: Term) + case Unavailable(source: Term) + + def provenance: ShapeProvenance = + val result = ShapeProvenance(this match + case MissingOutput(source) => + (msg"This type argument specifies only an input bound." -> source.toLoc) :: + (msg"Values read through it have no output bound more specific than 'Any'." -> N) :: Nil + case ContravariantParameter(parameter, source) => + (msg"This type argument supplies only an input bound for '${parameter.sym.nme}'." -> source.toLoc) :: + (msg"Type parameter '${parameter.sym.nme}' is declared contravariant here." -> parameter.sym.toLoc) :: + (msg"Values read through it have no output bound more specific than 'Any'." -> N) :: Nil + case Unrestricted(source) => + msg"This type permits values of any type, so no member interface is guaranteed." -> source.toLoc :: Nil + case AbstractDeclaration(symbol, _) => + (msg"Type '${symbol.nme}' is declared without a member interface." -> symbol.toLoc) :: Nil + case RecursiveAlias(symbol) => + msg"Following type alias '${symbol.nme}' does not expose a member interface." -> symbol.toLoc :: Nil + case Negated(source) => + msg"This negated type does not specify a member interface." -> source.toLoc :: Nil + case Unavailable(source) => + msg"No member interface is known for this type." -> source.toLoc :: Nil + ) + this match + case AbstractDeclaration(_, source) => result.withTypeOrigin(source) + case _ => result + +/** A type view that authorizes no member operations. The diagnostic reason is + * excluded from equality so alternative witnesses do not add inference flow. + */ +final case class OpaqueTypeShape(source: Term)(val reason: TypeInterfaceReason) extends CoreHeadShape: + def describe: Str = "value with no known interface" + def toLoc: Opt[Loc] = source.toLoc + def provenance: ShapeProvenance = reason.provenance + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = + MemberLookup.Unknown(MemberLookup.Uncertainty.ValueShape, false, provenance) + +class DefnShape(val defn: Definition, val ext: Opt[TermShape]) extends CoreHeadShape: + /** Instance lookup is shared by constructor calls and explicit `new`. + * Inherited members retain their marks before the caller adds its captures. */ + def getInstanceMember(name: Str)(using NewResolverState): MemberLookup = defn match + case cd: ClassDef => MemberLookup.inClass(cd, ext, name) + case _: TermDefinition => ext.fold[MemberLookup](MemberLookup.Missing)(_.getMember(name)) + case _ => MemberLookup.Missing + /** Nominal ancestry of this definition, independent of whether its value is an instance. */ + def classExtends(cls: ClassLikeDef)(using NewResolverState): Bool = + clsDef.contains(cls) || ext.exists(_.isInstanceOfClass(cls)) + override def isInstanceOfClass(cls: ClassLikeDef)(using NewResolverState): Bool = defn match + case _: ModuleOrObjectDef => classExtends(cls) + case _ => false + lazy val clsDef = defn match + case defn: ClassLikeDef => S(defn) + case defn: TermDefinition => + defn.tsym match + case ts: ClassCtorSymbol => S(ts.associatedCls.defn.get) + case _ => N + case _ => N + // def describe: Str = s"${defn.describe}" + def describe: Str = + // s"${defn.bsym.describe} '${defn.bsym.nme}'" + // s"${defn.describe}" + // s"${defn.bsym.asCls.fold("")(_.defn.get.kind.desc+" ")}'${defn.bsym.nme}'" + s"${defn match + case defn: ClassDef => return defn.kind.desc + " value" + case defn: TypeLikeDef => defn.kind.desc + " " + case defn: TermDefinition => + defn.tsym match + case _: ClassCtorSymbol => "class " + case s => s.k.desc + " " + }'${defn.bsym.nme}'" + // override def toString: String = s"DefnShape(${defn.describe} ${defn.bsym.nme})" + override def toString: String = s"DefnShape(${defn.describe})" + /** Shape-only counterparts of BufferableTransform's static members. The extra + * (buffer, index) list precedes the original method/constructor lists; it must + * not consume their arguments or expose instance fields as static members. + * These fields are never lowered: their symbols identify the generated names. + */ + private lazy val bufferedMembers: Map[Str, RecordMember] = defn match + case cls: ClassDef if cls.annotations.exists(_.isInstanceOf[Annot.Bufferable]) => + given Elaborator.State = cls.sym.getState + def field(name: Str, value: Term): (Str, RecordMember) = + name -> RecordMember(RcdField(Term.Lit(syntax.Tree.StrLit(name)), value), false) + def withBuffer(body: Term): Term = + val params = PlainParamList(List("buffer", "index").map: name => + Param.simple(VarSymbol(new syntax.Tree.Ident(name), erasedType = N)))(cls.toLoc) + Term.Lam(params, body) + // Constructors return the allocated index after all their original lists. + val ctor = (cls.paramsOpt.toList ::: cls.auxParams).foldRight[Term](Term.Lit(syntax.Tree.IntLit(0))): + (params, body) => Term.Lam(params, body) + val methods = cls.body.methods.filter(method => method.body.nonEmpty && !method.tsym.isPrivate).map: method => + val ref = Term.MemberRef(method.bsym)(new syntax.Tree.Ident(method.bsym.nme), FlowSymbol.memSym(method.bsym)) + field(method.bsym.nme, withBuffer(ref)) + (field("size", Term.Lit(syntax.Tree.IntLit(0))) :: + field("ctor", withBuffer(ctor)) :: methods).toMap + case _ => Map.empty + + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = + defn match + case defn: ModuleOrObjectDef => + MemberLookup.inClass(defn, ext, name) + case cls: ClassDef if cls.annotations.exists(_.isInstanceOf[Annot.Bufferable]) => + bufferedMembers.get(name).fold[MemberLookup](MemberLookup.Missing)(MemberLookup.Found(_, Nil)) + case _: TermDefinition => MemberLookup.Missing + case _ => MemberLookup.Missing + def toLoc: Opt[Loc] = defn.sym.toLoc + +/* +class RefinedShape(val base: TermShape, val refinements: Ls[Str -> Term]) extends NonAppTermShape: + def describe: Str = base.describe + lazy val members: Map[Str, MemberInfo] = + base.members ++ refinements.iterator.map: (nme, trm) => + // nme -> BlockMemberSymbol(nme, trm) + ??? +*/ +/** A tuple candidate selects a value shape for each spread. Ordinary fields stay + * lazy. Recursive spreads can have unknown length; retain the known fields around + * them instead of discarding either those fields or the unresolved possibilities. + */ +final case class TupleShape(source: Term, elements: Ls[TupleShape.Element], + instances: TypeSubstitution)(resolver: NewResolver) extends CoreHeadShape: + def arrayParent(using NewResolverState): NominalInstanceView = resolver.tupleArrayParent(this) + override def isInstanceOfClass(cls: ClassLikeDef)(using NewResolverState): Bool = arrayParent.isInstanceOfClass(cls) + private lazy val sourceSegments: Ls[TupleShape.Segment] = elements.flatMap: + case segment: TupleShape.Segment => segment :: Nil + case TupleShape.Rest(_, segments) => segments + case TupleShape.Spread(shape, marks) => shape.segments.map: + case field: TupleShape.Fixed => field.withMarks(marks :: Nil) + case TupleShape.Unknown(source, element) => TupleShape.Unknown(source, element.withMarks(marks :: Nil)) + lazy val segments: Ls[TupleShape.Segment] = sourceSegments.map: + case field: TupleShape.Fixed => field.instantiate(instances) + case TupleShape.Unknown(source, element) => TupleShape.Unknown(source, element.instantiate(instances)) + /** Does this candidate already depend on the given producer in the given context? + * `source` identifies the producer by syntax-node identity; `marks` distinguish + * its spread contexts. Inspect the selected dependency tree, not flattened + * segments, since flattening would erase the evidence of recursive production. + * Rest views retain that evidence even when they consume the relevant fields. + * + * The tuple listener uses this to widen repeated dependencies: for a producer + * like `[n, ...growing(n - 1)]`, repeatedly prefixing its own candidates would + * otherwise create unboundedly many shapes. This is an approximation, not a + * proof of runtime recursion: contexts have only the precision of their marks. + */ + def containsSpread(source: Term, marks: Marks): Bool = elements.exists: + case _: TupleShape.Segment => false + case TupleShape.Rest(shape, _) => shape.containsSpread(source, marks) + case TupleShape.Spread(shape, inner) => + ((shape.source is source) && inner == marks) || shape.containsSpread(source, marks) + def describe: Str = "tuple literal" + def toLoc: Opt[Loc] = source.toLoc + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = name.toIntOption match + case S(index) if index >= 0 => + // An unknown-length segment can be empty. After reaching one, every + // following field whose minimum position is at most `index` is possible. + // Keep an exact prefix precise, and stop once fixed fields alone have + // passed the index; later segments cannot move those fields backwards. + def loop(rest: Ls[TupleShape.Segment], index: Int, uncertain: Bool): Ls[TupleShape.Fixed] = rest match + case (field: TupleShape.Fixed) :: tail => + if index == 0 then field :: Nil + else if uncertain then field :: loop(tail, index - 1, true) + else loop(tail, index - 1, false) + case (segment: TupleShape.Unknown) :: tail => + segment.element :: loop(tail, index, true) + case Nil => Nil + loop(segments, index, false) match + case Nil => MemberLookup.Missing + case fields => MemberLookup.Indexed(fields, Nil) + case _ => arrayParent.getMember(name) + override def getMemberThrough(name: Str, receiver: Marks)(using NewResolverState): MemberLookup = name.toIntOption match + case S(index) if index >= 0 => super.getMemberThrough(name, receiver) + case _ => arrayParent.getMemberThrough(name, receiver) + +/** Values whose members and call results are deliberately checked only at runtime. + * Unlike UnknownValueShape, this authorizes dynamic operations; it is introduced + * by JavaScript interop and explicit `dyn` types, not by failed inference. + */ +final case class DynShape() extends CoreHeadShape: + def describe: Str = "dynamic value" + def toLoc: Opt[Loc] = N + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = MemberLookup.Dynamic(Nil) + +/** A shared diagnostic witness, independent of resolution marks. Messages, + * locations, and the flattened chain are evaluated only when reporting an error. + * Discovery retains one witness per reached shape to bound recursive paths. + */ +final class ShapeProvenance private (path: => Ls[(Message, Opt[Loc])], notes: => Ls[(Message, Opt[Loc])], + typeOrigin: Opt[Located]): + private lazy val pathNotes = path + private lazy val originNotes = notes + /** A type origin anchors the operation's explanation: first trace the value + * back to that annotation, then explain the interface it provides. Without + * an annotation witness, report the operation at its fallback location first + * and follow it with the reasons the value's shape is unknown. + */ + def diagnostic(message: Message, fallback: Opt[Loc]): Ls[(Message, Opt[Loc])] = typeOrigin match + case S(source) => pathNotes ::: (message -> source.toLoc.orElse(fallback)) :: originNotes + case N => (message -> fallback) :: pathNotes ::: originNotes + def via(note: => (Message, Opt[Loc])): ShapeProvenance = + new ShapeProvenance(note :: pathNotes, originNotes, typeOrigin) + /** Keep the annotation providing this interface separate from the path through + * parameters, returns, and storage that led to the observation. Besides written + * types, an async annotation provides the Promise interface of its result. + */ + def withTypeOrigin(source: Located): ShapeProvenance = + new ShapeProvenance(pathNotes, originNotes, S(source)) + +object ShapeProvenance: + def apply(notes: => Ls[(Message, Opt[Loc])]): ShapeProvenance = new ShapeProvenance(Nil, notes, N) + val empty = ShapeProvenance(Nil) + +/** An unknown input or the element of an opaque or widened spread can be any + * value. Inputs admitted by an exposed interface always require checking, even + * when local calls resolve the same selection. Keep that distinction in shape + * equality so an ordinary unknown cannot hide the public input's obligation. + * Provenance is outside case-class equality: another diagnostic witness must + * not turn the same unknown into a new inference candidate. + */ +final case class UnknownValueShape(source: Term, fromPublicInterface: Bool)(val provenance: ShapeProvenance) extends CoreHeadShape: + def describe: Str = "value of unknown shape" + def toLoc: Opt[Loc] = source.toLoc + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = + MemberLookup.Unknown(MemberLookup.Uncertainty.ValueShape, fromPublicInterface, provenance) + +/** A generic definition must be valid without choosing a caller's type. This + * checking witness is not an inferred bound on any call-site parameter. + */ +final case class RigidTypeShape(parameter: VarSymbol, source: Term) extends CoreHeadShape: + def describe: Str = "value of a type parameter" + def toLoc: Opt[Loc] = source.toLoc + def provenance: ShapeProvenance = ShapeProvenance( + msg"Type parameter '${parameter.nme}' does not specify a member interface." -> parameter.toLoc :: Nil) + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = + MemberLookup.Unknown(MemberLookup.Uncertainty.ValueShape, false, provenance) + +object UnknownValueShape: + def at(source: Term): UnknownValueShape = + UnknownValueShape(source, fromPublicInterface = false)(ShapeProvenance(msg"The shape of this value is unknown." -> source.toLoc :: Nil)) + def spread(source: Term, value: TermShape): UnknownValueShape = + val origin = value.applicationHead._1 match + case unknown: UnknownValueShape => unknown + case _ => UnknownValueShape(source, fromPublicInterface = false)(ShapeProvenance.empty) + origin.copy(source = source)(origin.provenance.via(msg"This spread has no known element shape." -> source.toLoc)) + +object TupleShape: + def apply(source: Term, elements: Ls[Element])(resolver: NewResolver): TupleShape = + new TupleShape(source, elements, TypeSubstitution.empty)(resolver) + sealed trait Element + sealed trait Segment extends Element: + /** The deferred value of one element, independently of the segment's length. */ + def element: Fixed + sealed trait Fixed extends Segment: + final def element: Fixed = this + def marks: Ls[Marks] + def withMarks(outer: Ls[Marks]): Fixed = this match + case Field(field, inner) => Field(field, inner ::: outer) + case TypedField(tpe, inner) => TypedField(tpe, inner ::: outer) + case UnknownField(source, inner) => UnknownField(source, inner ::: outer) + case ValueField(value, inner) => ValueField(value, inner ::: outer) + case ViewedField(source, instances, inner) => ViewedField(source, instances, inner ::: outer) + def instantiate(instances: TypeSubstitution): Fixed = + if instances.isEmpty then this else this match + case ViewedField(source, previous, marks) => ViewedField(source, instances.withOverrides(previous), marks) + case _ => ViewedField(this, instances, Nil) + final case class ViewedField(source: Fixed, instances: TypeSubstitution, marks: Ls[Marks]) extends Fixed + final case class Field(field: Fld, marks: Ls[Marks]) extends Fixed + final case class ValueField(value: TermShape, marks: Ls[Marks]) extends Fixed + final case class TypedField(tpe: DeclaredType, marks: Ls[Marks]) extends Fixed + // Mutable tuple slots retain their positions, but their initializer cannot + // supply the shape of later reads (including reads through a spread copy). + final case class UnknownField(source: Term, marks: Ls[Marks]) extends Fixed + /** An unknown number of elements described by the same deferred field as a + * fixed position. Its marks and retained substitution must survive tuple + * views, rest slicing, and spreading, even when its length is unknown. + * Use this for typed arrays, opaque layouts, and recursive widening, never + * for a spread whose shape has not arrived yet. */ + final case class Unknown(source: Term, element: Fixed) extends Segment + final case class Spread(shape: TupleShape, marks: Marks) extends Element + def unknown(source: Term)(resolver: NewResolver): TupleShape = + TupleShape(source, Unknown(source, UnknownField(source, Nil)) :: Nil)(resolver) + /** Retain the original candidate as well as the selected residual segments: + * flattening away the parent would hide recursive producer dependencies from + * containsSpread, allowing recursion through rest slicing to evade widening. */ + final case class Rest(shape: TupleShape, segments: Ls[Segment]) extends Element + /** A view of `tuple` retaining only `segments`, which must be taken from + * `tuple.segments`. A view of another view refers to that view's parent: + * a view contributes no spreads of its own, so containsSpread gives the same + * answer for the parent as for the intermediate view. Nesting views instead + * would make the candidate's identity record the order in which fields were + * removed. For `if xs is [x, ...rest] then f(rest); [...rest, x] then f(rest)` + * on an n-tuple, that yields 2^n candidates for only O(n^2) distinct layouts. */ + def restView(tuple: TupleShape, segments: Ls[Segment])(resolver: NewResolver): TupleShape = + val parent = tuple.elements match + case Rest(parent, _) :: Nil => parent + case _ => tuple + TupleShape(tuple.source, Rest(parent, segments) :: Nil)(resolver) + + +/** A field found by record member lookup. Spreading r into `mut {...r}` reuses + * its field symbols, but writes can change the copied values even if r is immutable. + * Store mutability on this lookup result: the mutable copy's initializer no longer + * determines its value shape, while reads from an immutable r can still use it. + */ +final case class RecordMember(field: RcdField, mutable: Bool) + +/** Keep spread candidates and their contexts rather than flattening away their + * provenance. Lookup follows runtime's last-write-wins order. Unknown entries + * are barriers: an opaque spread or computed key can overwrite earlier fields. + */ +final case class RecordShape(source: Term.Rcd, elements: Ls[RecordShape.Element], + instances: TypeSubstitution) extends CoreHeadShape: + def describe: Str = "record literal" + def toLoc: Opt[Loc] = source.toLoc + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = + import MemberLookup.* + def unknown(source: Located) = Unknown(Uncertainty.RecordOverwrite, false, + ShapeProvenance(msg"This computed key can overwrite the selected member." -> source.toLoc :: Nil)) + def loop(rest: Ls[RecordShape.Element]): MemberLookup = rest match + case Nil => Missing + case RecordShape.Field(field) :: rest => field.field match + case Term.Lit(Tree.StrLit(key)) => + if key == name then Found(RecordMember(field, source.mut), Nil) else loop(rest) + case _ => unknown(field.field) + case RecordShape.Unknown(shape) :: _ => Unknown(Uncertainty.RecordOverwrite, shape.fromPublicInterface, shape.provenance) + case RecordShape.Dynamic(marks) :: _ => Dynamic(marks) + case RecordShape.Spread(shape, marks) :: rest => + def spread(info: MemberLookup): Opt[MemberLookup] = info match + case Contextual(source, instances) => spread(source).map(_.instantiate(instances)) + case Dynamic(inner) => S(Dynamic(inner ::: marks :: Nil)) + case Unknown(_, fromPublicInterface, provenance) => S(Unknown(Uncertainty.RecordOverwrite, fromPublicInterface, provenance)) + case Missing => N + case Found(member: RecordMember, inner) => + S(Found(member.copy(mutable = member.mutable || source.mut), inner ::: marks :: Nil)) + case Found(_: BlockMemberSymbol, _) | Declared(_, _, _, _, _) | Typed(_, _, _) | Indexed(_, _) => + // Record spreads recursively look up RecordShapes, which only create RecordMembers. + lastWords("Record lookup returned a nominal member") + spread(shape.getMember(name)).getOrElse(loop(rest)) + loop(elements.reverse).instantiate(instances) + def containsSpread(record: Term.Rcd, marks: Marks): Bool = elements.exists: + case RecordShape.Spread(shape, inner) => + ((shape.source is record) && inner == marks) || shape.containsSpread(record, marks) + case _ => false + +object RecordShape: + def apply(source: Term.Rcd, elements: Ls[Element]): RecordShape = new RecordShape(source, elements, TypeSubstitution.empty) + enum Element: + case Field(field: RcdField) + case Spread(shape: RecordShape, marks: Marks) + case Unknown(shape: UnknownValueShape) + case Dynamic(marks: Ls[Marks]) + export Element.* + + +type IntroTerm = Term.Lit | Term.UnitVal | Term.Lam //| Term.New +class IntroShape(val trm: IntroTerm, val primitive: Opt[NominalInstanceView]) extends CoreHeadShape: + def describe: Str = trm.describe + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = trm match + case _: Term.Lit | _: Term.UnitVal => primitive.fold[MemberLookup](MemberLookup.Missing)(_.getMember(name)) + case lam: Term.Lam => MemberLookup.Missing // TODO: methods on lambdas + // case newTerm: Term.New => + // Map.empty // TODO + override def getMemberThrough(name: Str, receiver: Marks)(using NewResolverState): MemberLookup = trm match + case _: Term.Lit | _: Term.UnitVal => primitive.fold[MemberLookup](MemberLookup.Missing)(_.getMemberThrough(name, receiver)) + case _: Term.Lam => super.getMemberThrough(name, receiver) + override def isInstanceOfClass(cls: ClassLikeDef)(using NewResolverState): Bool = + primitive.exists(_.isInstanceOfClass(cls)) + def toLoc: Opt[Loc] = trm.toLoc + override def toString: Str = s"IntroShape(${trm})" + +sealed trait LitShape extends CoreHeadShape: + self: Term.Lit => + protected def getMemberImpl(name: Str)(using NewResolverState): MemberLookup = MemberLookup.Missing // TODO: methods on literals, e.g. string methods + + +type ShapePublisher = Publisher[ShapeEvent] +type ShapeHost = Host[ShapeEvent] + diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/SimpleSplit.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/SimpleSplit.scala index b2ee9e73f2..e31d8e6475 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/SimpleSplit.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/SimpleSplit.scala @@ -3,6 +3,8 @@ package semantics import hkmc2.utils.*, shorthands.*, syntax.*, Tree.{BoolLit, Keywrd} import Keyword.{`do`, `else`, `then`}, utils.TL, Elaborator.{Ctx, State} +import hkmc2.document.* +import hkmc2.document.Document.* /** * Similar to `Split` but contains nested patterns (i.e., class `Pattern`). @@ -29,6 +31,20 @@ enum SimpleSplit extends AutoLocated with ProductWithTail: case els: Else => els case End => this + def showSub(using Scope, ShowCfg, Raise, IfLikeForm): Document = this match + // doc"${scrutinee.show} is ${pattern.show} and { #{ # ${consequent.show} #} # }" + case Cons(head, End) => doc"${summon[IfLikeForm].midStr} ${head.show}" + case Cons(head, tail) => doc" and { #{ # ${head.show}; # ${tail.show} #} # }" + // case Else(default) => doc" else ${default.show}" + case Else(default) => doc" ${summon[IfLikeForm].midStr} ${default.show}" + case End => "β€Ήunhandledβ€Ί" + def show(using Scope, ShowCfg, Raise, IfLikeForm): Document = this match + case Cons(head, End) => doc"${head.show}" + case Cons(head, tail) => doc"${head.show}; # ${tail.show}" + // case Else(default) => doc"${summon[IfLikeForm].midStr} ${default.show}" + case Else(default) => doc"else ${default.show}" + case End => "" + protected def children: Vector[Located] = this match case Cons(branch, tail) => Vector.double(branch, tail) case els @ Else(default) => els.kw match @@ -74,13 +90,13 @@ enum SimpleSplit extends AutoLocated with ProductWithTail: /** This field is designed to be compatible with bbML. */ private var _expandedSplit: Opt[Split] = N - /** */ - def getExpandedSplit(using TL, Ctx, State, Raise): Split = _expandedSplit.getOrElse: + /** Expand patterns after resolution, with symbol queries authorized by erasure. */ + def getExpandedSplit(using codegen.Erasure, TL, Ctx, State, Raise, Config): Split = _expandedSplit.getOrElse: val split = Split.from(this) _expandedSplit = S(split) split - def mkClone(using State): SimpleSplit = this match + def mkClone(using State, codegen.Erasure): SimpleSplit = this match case Cons(head, tail) => Cons(head match case Head.Match(scrutinee, pattern, consequent) => Head.Match(scrutinee.mkClone.asInstanceOf, pattern, consequent.mkClone) // TODO: clone `pattern`? @@ -110,6 +126,12 @@ object SimpleSplit: case Match(scrutinee: Term.Ref, pattern: Pattern, consequent: SimpleSplit) case Let(binding: LocalVarSymbol, term: Term) + def show(using Scope, ShowCfg, Raise, IfLikeForm): Document = this match + case Match(scrutinee, pattern, consequent) => + // doc"${scrutinee.show} is ${pattern.show} and { #{ # ${consequent.show} #} # }" + doc"${scrutinee.show} is ${pattern.show}${consequent.showSub}" + case Let(binding, term) => doc"let ${binding.showName} = ${term.show}" + def subTerms: Vector[Term] = this match case Match(scrutinee, pattern, consequent) => scrutinee +: (pattern.subTerms ++ consequent.subTerms) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Split.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Split.scala index 06fd034360..55419f4a17 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Split.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Split.scala @@ -5,7 +5,7 @@ import hkmc2.utils.*, shorthands.* import syntax.*, Elaborator.State, ucs.FlatPattern final case class Branch(scrutinee: Term.Ref, pattern: FlatPattern, continuation: Split) extends AutoLocated: - def mkClone(using State): Branch = + def mkClone(using State, codegen.Erasure): Branch = val scrutineeClone = new Term.Ref(scrutinee.sym) (Tree.Ident(scrutinee.tree.name), scrutinee.typ) Branch(scrutineeClone, pattern.mkClone, continuation.mkClone) @@ -31,7 +31,7 @@ enum Split extends AutoLocated with ProductWithTail: inline def ~:(head: Branch): Split = Split.Cons(head, this) - def mkClone(using State): Split = this match + def mkClone(using State, codegen.Erasure): Split = this match case Cons(head, tail) => Cons(head.mkClone, tail.mkClone) case Let(sym, term, tail) => Let(sym, term.mkClone, tail.mkClone) case Else(default) => Else(default.mkClone) @@ -126,6 +126,17 @@ enum Split extends AutoLocated with ProductWithTail: (sym.body.freeSplitSyms ++ tail.freeSplitSyms) - sym case Split.UseSplit(sym) => sym.body.freeSplitSyms + sym + def show(using utils.Scope, ShowCfg, Raise): document.Document = + import document.*, Document.* + this match + case Cons(Branch(scrutinee, pattern, continuation), tail) => + doc"${scrutinee.show} is ${pattern.show} then { #{ # ${continuation.show} #} # } # ${tail.show}" + case Let(sym, term, tail) => doc"let ${sym.showName} = ${term.show}; # ${tail.show}" + case Else(default) => doc"else ${default.show}" + case End => doc"" + case LetSplit(sym, tail) => doc"let-split ${sym.showName} = { #{ # ${sym.body.show} #} # }; # ${tail.show}" + case UseSplit(sym) => doc"$${${sym.showName}}" + final def showDbg(using DebugPrinter): String = this match case Split.Cons(head, tail) => s"${head.showDbg}; ${tail.showDbg}" case Split.Let(name, term, tail) => s"let ${name} = ${term.showDbg}; ${tail.showDbg}" @@ -175,7 +186,7 @@ object Split: import Term.Ref /** Desugar a `SimpleSplit` into a `Split`. */ - def from(rootSplit: SS)(using tl: TL)(using Ctx, Raise, State): Split = + def from(rootSplit: SS)(using codegen.Erasure)(using tl: TL)(using Ctx, Raise, State, Config): Split = val compiler = new SplitCompiler() val scrutCache = MutMap.empty[Ref, Scrut] def go(split: SS): Split = split match diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala index 3c2c3bc4ec..1b14bc5846 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala @@ -2,7 +2,7 @@ package hkmc2 package semantics import scala.collection.mutable -import scala.collection.mutable.{Set => MutSet} +import scala.collection.mutable.{Buffer, LinkedHashSet} import hkmc2.utils.*, shorthands.* import syntax.* @@ -10,7 +10,7 @@ import hkmc2.utils.* import Elaborator.State import Tree.Ident -import hkmc2.codegen.{ErasedType, ErasedFuncType, ErasedValueType, HasErasedType, HasOnceMutableErasedType} +import hkmc2.codegen.{ErasedType, ErasedFuncType, ErasedValueType, HasErasedType, HasOnceMutableErasedType, HasDelayedErasedType} import hkmc2.utils.SymbolSubst @@ -186,14 +186,25 @@ object FlowSymbol: // FlowSymbol("β€Ήapp-resβ€Ί") // FlowSymbol("@") FlowSymbol("app") - + + def pat()(using State) = + FlowSymbol("pat") + + def neww()(using State) = + FlowSymbol("new") + def sel(nme: Str)(using State) = FlowSymbol(s"β‹…$nme") def synthSel(nme: Str)(using State) = FlowSymbol(s"(β‹…)$nme") def selProj(nme: Str)(using State) = FlowSymbol(s"#β‹…$nme") - + + // def memSym(sym: MemberSymbol, nme: Str)(using State) = + // FlowSymbol(s"$nme(${sym.nme})") + def memSym(sym: MemberSymbol)(using State) = + FlowSymbol(sym.nme) + def lds(nme: Str)(using State) = FlowSymbol(s"Ɛ⋅$nme") @@ -203,7 +214,13 @@ class ConcreteFlowSymbol(label: Str)(using State) extends FlowSymbol(label): def subst(using s: SymbolSubst): FlowSymbol = s.mapFlowSym(this) -sealed trait LocalSymbol extends Symbol +// sealed trait LocalSymbol extends Symbol, ShapePublisher: +// /** Shapes are published in discovery order. Resolution replays this collection to late +// * listeners, so an unordered set would make ambiguous-target diagnostics depend on hash and +// * parallel compilation timing. */ +// private[semantics] val shapes: LinkedHashSet[Shape] = LinkedHashSet.empty +sealed trait LocalSymbol extends Symbol, ShapeHost + sealed trait NamedSymbol extends Symbol: def name: Str def id: Ident @@ -254,15 +271,28 @@ class InstSymbol(val origin: Symbol)(using State) extends LocalSymbol: def subst(using sub: SymbolSubst): InstSymbol = sub.mapInstSym(this) -class VarSymbol(val id: Ident, override val erasedType: Opt[ErasedValueType])(using State) +class VarSymbol(val id: Ident)(using State) extends LocalVarSymbol(id.name) + with HasDelayedErasedType[ErasedValueType] with NamedSymbol: + def this(id: Ident, erasedType: Opt[ErasedValueType])(using State) = + this(id) + this.erasedType = erasedType val name: Str = id.name var sourceAliases: Ls[Str] = Nil override def toLoc: Opt[Loc] = id.toLoc // override def toString: Str = s"$name@$uid" override def subst(using s: SymbolSubst): VarSymbol = s.mapVarSym(this) +/** A source type binder instantiated at a static application site. Its inference + * host starts empty: the source binder's checking witnesses and subscriptions + * belong to the generic definition, not to any of its applications. + */ +final class TypeParameterInstance(val origin: VarSymbol)(using State) extends VarSymbol(origin.id): + require(!origin.isInstanceOf[TypeParameterInstance], "Instantiate original type binders only") + decl = origin.decl + val reference: Term.SimpleRef = Term.SimpleRef(this)(origin.id) + class BuiltinSymbol (val nme: Str, val binary: Bool, val unary: Bool, val nullary: Bool, val functionLike: Bool, val isPure: Bool)(using State) extends Symbol: @@ -301,7 +331,23 @@ class BlockMemberSymbol(val nme: Str, val trees: Ls[TypeOrTermDef], val nameIsMe var tsym: Opt[TermSymbol] = N var sourceAliases: Ls[Str] = Nil - def toLoc: Option[Loc] = Loc(trees) + private var defnListeners: Buffer[() => Unit] = Buffer.empty + def complete(): Unit = if defnListeners isnt null then + val listeners = defnListeners + // Completion callbacks may request this member again. Publish completion + // first so reentrant listeners run immediately instead of mutating the queue. + defnListeners = null // free memory and prevent further listening + listeners.foreach(_()) + /** Called when all the symbols covered by this BMS are present, with their definitions set. */ + def onComplete(f: () => Unit): Unit = + if defnListeners is null then + f() + else + defnListeners += f + + // For constructor and record fields, trees is empty because the member was + // synthesized. Use the source location of its TermSymbol to locate diagnostics. + def toLoc: Option[Loc] = Loc(trees).orElse(tsym.flatMap(_.toLoc)) def symbols = tsym.toList ::: trees.collect: case t: Tree.TypeDef => t.symbol @@ -343,13 +389,25 @@ end BlockMemberSymbol sealed abstract class MemberSymbol(using State) extends Symbol: def nme: Str def subst(using SymbolSubst): MemberSymbol + + /** Resolution publishes direct override edges; only resolver subscriptions may observe the + * growing graph. Erasure/lowering read the completed declaration identities, never value flow. + * The host uses the same consumer-local copies as other inference publishers on imported code. + * These edges belong to source declarations and are consumed before/while lowering. MIR + * symbol refreshing does not copy them: lowered selections already contain their chosen target. + */ + private[semantics] val overrideLinks: OverrideLinks = new OverrideLinks + def overriddenMembers(using codegen.Erasure): Ls[TermSymbol] = overrideLinks.members -class TermSymbol(val k: TermDefKind, val owner: Opt[InnerSymbol], val id: Tree.Ident, override var erasedType: Opt[ErasedType])(using State) +class TermSymbol(val k: TermDefKind, val owner: Opt[InnerSymbol], val id: Tree.Ident)(using State) extends MemberSymbol with DefinitionSymbol[TermDefinition] - with HasOnceMutableErasedType + with HasDelayedErasedType[ErasedType] with NamedSymbol: + def this(k: TermDefKind, owner: Opt[InnerSymbol], id: Tree.Ident, erasedType: Opt[ErasedType])(using State) = + this(k, owner, id) + this.erasedType = erasedType var sourceAliases: Ls[Str] = Nil def nme: Str = id.name def name: Str = nme @@ -440,6 +498,13 @@ sealed trait ClassLikeSymbol extends IdentifiedSymbol, HasErasedType: val tree: Tree.TypeDef def subst(using sub: SymbolSubst): ClassLikeSymbol + /** Published by erasure before class bodies are lowered, including for external declarations. + * Full IR class definitions refresh this header when rewritten, just as they own `irDefn`. + * Keeping the header separately is necessary for forward parents and declarations without + * executable bodies; consumers must not reconstruct it from semantic terms. + */ + var irClassHeader: Opt[codegen.ClassHeader] = N + type AnyDefinitionSymbol = DefinitionSymbol[?] @@ -458,9 +523,15 @@ sealed trait DefinitionSymbol[Defn <: Definition] extends MemberSymbol: def defn_=(d: S[Defn]): Unit = require(_defn.isEmpty, s"Cannot reassign defn of ${this} from ${_defn} to ${d}") _defn = d + defnListeners.foreach(_(d.value)) + defnListeners = null // free memory and prevent further listening + var decl: Opt[Declaration] = N // NOTE: currently only assigned for class params and only used by deforestation; may want to just remove it once deforestation is improved def bms: Opt[BlockMemberSymbol] = defn.map(_.bsym) + // TODO: rm + private[semantics] var defnListeners: Buffer[Defn => Unit] = Buffer.empty + // * Although the IR is immutable, // * we consider that a given symbol is *owned* by the IR Defn node that defines it. var irDefn: Opt[codegen.Defn] = N @@ -498,7 +569,7 @@ end DefinitionSymbol * One overloaded `BlockMemberSymbol` may correspond to multiple `InnerSymbol`s * A `Ref(_: InnerSymbol)` represents a `this`-like reference to the current object. */ // TODO prevent from appearing in Ref -sealed trait InnerSymbol(using State) extends Symbol: +sealed trait InnerSymbol(using State) extends Symbol, ShapePublisher: // Ideally, InnerSymbol should extend DefinitionSymbol, but that requires us to specify the type // parameter to all occurrences of InnerSymbol. So, we use a self-type annotation instead to // ensure that any implementation of InnerSymbol is also a DefinitionSymbol. diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala index 947c56a25b..22b21925d8 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala @@ -1,7 +1,7 @@ package hkmc2 package semantics -import scala.collection.mutable.{Buffer, Set as MutSet} +import scala.collection.mutable.{Buffer, Set as MutSet, LinkedHashSet} import hkmc2.utils.*, shorthands.* import syntax.* @@ -14,6 +14,8 @@ import Elaborator.State import hkmc2.typing.Type import hkmc2.semantics.Elaborator.{Ctx, ctx} import hkmc2.Message.MessageContext +import hkmc2.semantics.flow.{SelectionTarget, AppTarget} +import hkmc2.codegen.Erasure final case class QuantVar(sym: VarSymbol, ub: Opt[Term], lb: Opt[Term]) @@ -38,6 +40,8 @@ enum Annot extends Located: case RaiseEffects()(val toLoc: Opt[Loc]) // Whether the function is guaranteed to not raise effects. case Pure()(val toLoc: Opt[Loc]) + // Buffered classes expose generated allocation and access methods on the class value. + case Bufferable(keepInstances: Bool)(val toLoc: Opt[Loc]) case Config(modify: hkmc2.Config => hkmc2.Config)(val toLoc: Opt[Loc]) // Marks if a function or lambda is one-shot, i.e. called at most once. // Functions with multiple parameter lists are considered here as a chain of @@ -58,7 +62,7 @@ enum Annot extends Located: def subTerms: Vector[Term] = this match case Trm(trm) => Vector.single(trm) case _: Modifier | Untyped() | TailRec() | TailCall() | Inline() | NoInline() - | Generator() | Async() | RaiseEffects() | Pure() | _: Config | _: Affine => Vector.empty + | Generator() | Async() | RaiseEffects() | Pure() | _: Bufferable | _: Config | _: Affine => Vector.empty def show(using Scope, ShowCfg, Raise): Document = this match case Untyped() => doc"@untyped" @@ -72,10 +76,11 @@ enum Annot extends Located: case Affine(n) => doc"@affine($n)" case Modifier(mod) => doc"@${mod.name}" case Pure() => doc"@pure" + case Bufferable(keepInstances) => if keepInstances then doc"@bufferable" else doc"@buffered" case Trm(trm) => doc"@${trm.show}" case Config(_) => doc"@config(...)" - def mkClone(using State): Annot = this match + def mkClone(using State, Erasure): Annot = this match case Trm(trm) => Trm(trm.mkClone)(toLoc) case _ => this @@ -102,7 +107,9 @@ sealed trait AnySel extends ResolvableImpl: val originalCtx: Opt[SrcScope] var resolvedTargets: Ls[flow.SelectionTarget] = Nil // * filled during flow analysis - var isErroneous: Bool = false // * to avoid reporting follow-on errors after a flow/resolution error + def validResolvedTargets = resolvedTargets.filterNot: + case flow.SelectionTarget.Err(_) => true + case _ => false end AnySel object AnySel: @@ -112,13 +119,23 @@ object AnySel: case Term.SelProj(lhs, cls, proj) => S((lhs, proj, S(cls))) end AnySel + +sealed trait AppImpl extends ResolvableImpl: + self: Term.App => + var resolvedTargets: Ls[flow.AppTarget] = Nil // * filled during flow analysis +end AppImpl + + type Resolvable = Term & ResolvableImpl -sealed trait ResolvableImpl: +// TODO: remove when the new resolution migration is completed. +sealed trait ResolvableImpl extends ShapeHost, PossiblyErroneous: this: Term => import Resolvable.CallableDefinition + def getShapes: Ls[ShapeEvent] = shapes.toList + /** * The expanded form of the term, if it exists. * @@ -139,7 +156,7 @@ sealed trait ResolvableImpl: case t: Term.SynthSel => t.copy()(t.sym, t.resSym, t.typ, t.originalCtx) case t: Term.LeadingDotSel => t.copy()(t.originalCtx) case t: Term.SelProj => t.copy()(t.sym, t.resSym, t.typ, t.originalCtx) - case t: Term.New => t.copy()(t.typ) + case t: Term.New => t.copy()(t.resSym, t.typ) .withLocOf(this) .asInstanceOf @@ -162,7 +179,7 @@ sealed trait ResolvableImpl: case t: Term.SynthSel => t.copy()(t.sym, t.resSym, S(typ), t.originalCtx) case _: Term.LeadingDotSel => lastWords(s"Cannot attach a type to leading dot selection: ${this.showDbg}") case t: Term.SelProj => t.copy()(t.sym, t.resSym, S(typ), t.originalCtx) - case t: Term.New => t.copy()(S(typ)) + case t: Term.New => t.copy()(t.resSym, S(typ)) .withLocOf(this) .asInstanceOf @@ -210,7 +227,7 @@ sealed trait ResolvableImpl: def hasExpansion = expansion.isDefined - def defn: Opt[Definition] = resolvedSym match + def defn: Opt[Definition] = legacyResolvedSym match case S(sym: BlockMemberSymbol) => N case S(sym: AnyDefinitionSymbol) => sym.defn case _ => N @@ -273,11 +290,6 @@ object Resolvable: defn, )) -trait LeadingDotSelImpl(using State): - self: Term.LeadingDotSel => - val resSym: FlowSymbol = FlowSymbol.lds(self.nme.name) - var resolvedTargets: Ls[flow.SelectionTarget.CompanionMember] = Nil // * filled during flow analysis - case class SrcScope(outer: Elaborator.OuterCtx, parent: Opt[SrcScope]): /** Computes the outermost scope from which the current scope can still be accessed. @@ -307,27 +319,41 @@ case class SrcScope(outer: Elaborator.OuterCtx, parent: Opt[SrcScope]): case S(par) => val (base, path) = par.outermostAcessibleBase (base, inner :: path) - case _: (Function | LocalScope) | LambdaOrHandlerBlock | NonReturnContext => + case _: (Function | LocalScope | NonReturnContext) | LambdaOrHandlerBlock => (this, Nil) object SrcScope: given s: Ctx => SrcScope = summon[Ctx].scope -enum Term extends Statement, AutoLocated: + + +enum Term extends Statement, AutoLocated, ShapePublisher: + /** Filled by the type interpreter during elaboration; erasure validates the completed result. */ + private[hkmc2] var typeInterpretation: Opt[TypeResolution] = N + case Error() case UnitVal() + case DynTy() case Missing // Placeholder terms that were not elaborated due to the "lightweight" elaboration mode `Mode.Light` - case Lit(lit: Literal) + case Lit(lit: Literal) extends Term + + // --- NEW --- + case SimpleRef(sym: codegen.SimpleSymbol)(val tree: Tree.Ident) extends Term, NewRefImpl + case SelfRef(sym: InnerSymbol)(val tree: Tree.Ident) extends Term, NewRefImpl + case MemberRef(sym: MemberSymbol)(val tree: Tree.Ident, val resSym: FlowSymbol) extends Term, NewResolvableImpl, NewRefImpl + /** A direct reference to an inherited implementation, retaining the lexical receiver. */ + case Super(receiver: Term, owner: InnerSymbol, id: Tree.Ident)(val resSym: FlowSymbol) extends Term, NewResolvableImpl, ShapeHost + /** An optional class selection fixes the lookup scope for an explicit member projection. */ + case NewSel(prefix: Term, id: Tree.Ident, cls: Opt[NewSel])(val resSym: FlowSymbol) extends Term, NewSelImpl, ShapeHost + case UnresolvedRef(prefixes: Ls[Term], id: Tree.Ident)(val resSym: FlowSymbol) extends Term, UnresolvedRefImpl, ShapeHost + case Capture(base: Term, thru: AnyDefinitionSymbol) extends Term + // --- LEGACY --- /** A term that wraps another term, indicating that the symbol of the inner term is resolved. * This is mainly used to disambiguate overloaded definitions. */ - case Resolved(t: Term, sym: DefinitionSymbol[?]) + case Resolved(t: Term, sym: AnyDefinitionSymbol) (val typ: Opt[Type]) extends Term, ResolvableImpl case Ref(sym: Symbol) - (val tree: Tree.Ident, val typ: Opt[Type]) extends Term, ResolvableImpl - case App(lhs: Term, rhs: Term) - (val tree: Tree.App, val typ: Opt[Type], val resSym: FlowSymbol) extends Term, ResolvableImpl - case TyApp(lhs: Term, targs: Ls[Term]) - (val typ: Opt[Type]) extends Term, ResolvableImpl + (val tree: Tree.Ident, val typ: Opt[Type]) extends Term, ResolvableImpl, AnyRefImpl case Sel(prefix: Term, nme: Tree.Ident) (val sym: Opt[MemberSymbol], val resSym: FlowSymbol, val typ: Opt[Type], val originalCtx: Opt[SrcScope]) extends Term, AnySel @@ -337,11 +363,20 @@ enum Term extends Statement, AutoLocated: case SelProj(prefix: Term, cls: Term, nme: Tree.Ident) (val sym: Opt[MemberSymbol], val resSym: FlowSymbol, val typ: Opt[Type], val originalCtx: Opt[SrcScope]) extends Term, AnySel - case DynSel(prefix: Term, fld: Term, arrayIdx: Bool) - case Tup(fields: Ls[Elem])(val tree: Tree.Tup) + // --- END --- + + case App(lhs: Term, rhs: Term) + (val tree: Tree.App, val typ: Opt[Type], val resSym: FlowSymbol) extends Term, AppImpl + case TyApp(lhs: Term, targs: Ls[Term]) + (val typ: Opt[Type]) extends Term, ResolvableImpl + /** `checked` asks resolution to validate the receiver and determine the element + * interface (`obj.[idx]`); it does not require a compile-time index value. + * `arrayIdx` independently selects array indexing in the generated code. */ + case DynSel(prefix: Term, fld: Term, arrayIdx: Bool, checked: Bool) + case Tup(fields: Ls[Elem])(val tree: Tree.Tup) extends Term, ShapeHost case Mut(underlying: Tup | Rcd | New | DynNew) - case CtxTup(fields: Ls[Elem])(val tree: Tree.Tup) - case IfLike(kw: Keyword.SplitLike, form: IfLikeForm, split: SimpleSplit) + case CtxTup(fields: Ls[Elem])(val tree: Tree.Tup) extends Term, ShapeHost + case IfLike(kw: Keyword.SplitLike, form: IfLikeForm, split: SimpleSplit) extends Term, ShapeHost /** `If` expressions synthesized by the pattern compiler. It should only be * created and used in `Lowering`. One must make sure that all terms in the * split are correctly resolved. In the future, we might look for a way to @@ -359,11 +394,11 @@ enum Term extends Statement, AutoLocated: case Constrained(constraints: Ls[SubConstraint], body: Term) case WildcardTy(in: Opt[Term], out: Opt[Term]) case Blk(stats: Ls[Statement], res: Term) extends Term, BlkImpl - case Rcd(mut: Bool, stats: Ls[Statement]) + case Rcd(mut: Bool, stats: Ls[Statement]) extends Term, ShapeHost case Quoted(body: Term) case Unquoted(body: Term) case New(cls: Term, args: Ls[Term], rft: Opt[ClassSymbol -> ObjBody]) - (val typ: Opt[Type]) extends Term, ResolvableImpl + (val resSym: FlowSymbol, val typ: Opt[Type]) extends Term, ResolvableImpl case DynNew(cls: Term, args: Ls[Term]) case Asc(term: Term, ty: Term) case CompType(lhs: Term, rhs: Term, pol: Bool) @@ -395,6 +430,18 @@ enum Term extends Statement, AutoLocated: override def hashCode: Int = System.identityHashCode(this) */ + def withoutCaptures: Term = this match + case Capture(base, _) => base.withoutCaptures + case _ => this + + /** Class target validation looks through wrappers with no runtime representation. + * Shape propagation still processes captures to retain their provenance. + */ + def classHead: Term = this match + case Capture(base, _) => base.classHead + case TyApp(base, _) => base.classHead + case _ => this + def expanded: Term = this match case t: Resolvable => t.expansion match case S(S(t)) => t.expanded @@ -420,6 +467,7 @@ enum Term extends Statement, AutoLocated: * elaboration. */ lazy val symbol: Opt[Symbol] = this match + case Capture(base, _) => base.symbol case res: Resolved => S(res.sym) case Ref(sym) => S(sym) case sel: Sel => sel.sym @@ -430,17 +478,42 @@ enum Term extends Statement, AutoLocated: /** * The symbol representing the evaluation result of the term. This - * symbol is resolved during the resolution stage. + * symbol is resolved during the resolution stage. Reading its final value requires + * erasure; elaboration must listen for shapes instead. */ - def resolvedSym: Opt[Symbol] = expanded match + def resolvedSym(using Erasure): Opt[Symbol] = resolvedSymImpl + + /** Read-only lookup shared by erasure and legacy consumers of completed imports. + * The public accessors establish completion before reading new-resolution targets. + */ + private def resolvedSymImpl: Opt[Symbol] = expanded match case res: Resolved => S(res.sym) + case SimpleRef(sym) => S(sym) + case Capture(base, _) => base.resolvedSymImpl + case ref: UnresolvedRef if ref.resolvedMembers.distinct.sizeCompare(1) =/= 0 => N + case sel: NewSel if sel.hasAmbiguousClassImpl => N + case ref: NewResolvable => + if ref.isErroneous then N else ref.resolvedTargets.distinct match + case sym :: Nil => S(sym) + case _ => N case ref: Ref => ref.symbol case sel: Sel => sel.sym case sel: SynthSel => sel.sym case sel: SelProj => sel.sym - case app: TyApp => app.lhs.resolvedSym + case app: TyApp => app.lhs.resolvedSymImpl case _ => N + /** The old resolver may inspect its own expansions while resolving legacy terms, + * and completed references in imported new-resolution signatures. Never observe + * a new reference while its defining block can still change its selected targets. + */ + private[semantics] def legacyResolvedSym: Opt[Symbol] = expanded match + case term: (NewResolvable | NewRefImpl | Capture) => + assert(term.originalData.completed, "Legacy symbol query on an incomplete new-resolution term") + term.resolvedSymImpl + case app: TyApp => app.lhs.legacyResolvedSym + case term => term.symbol + def resolvedTyp: Opt[Type] = expanded match case res: Resolved => res.typ case ref: Ref => ref.typ @@ -457,6 +530,9 @@ enum Term extends Statement, AutoLocated: * (These definitions were generated as part of a slop PR.) */ lazy val freeVars: Set[Str] = this match case Ref(sym) => Set.single(sym.nme) + case SimpleRef(sym) => Set.single(sym.nme) + case MemberRef(sym) => Set.single(sym.nme) + case SelfRef(sym) => Set.single(sym.nme) case Lam(params, body) => val paramNames = params.allParams.iterator.map(_.sym.nme).toSet body.freeVars -- paramNames @@ -477,12 +553,16 @@ enum Term extends Statement, AutoLocated: val bodyFree = body.freeVars if bodyFree(result.nme) then bodyFree - result.nme else bodyFree case Forall(_, _, body) => body.freeVars - case Error | Missing | _: Lit | _: UnitVal | _: LeadingDotSel | _: Continue => Set.empty + case Error | Missing | _: Lit | _: DynTy | _: UnitVal | _: LeadingDotSel | _: Continue => Set.empty case Break(_, result, value) => value.iterator.flatMap(_.freeVars).toSet + result.nme case _ => subTerms.iterator.flatMap(_.freeVars).toSet def sel(id: Tree.Ident, sym: Opt[MemberSymbol])(using State, Elaborator.Ctx): Sel = Sel(this, id)(sym, FlowSymbol.sel(id.name), N, S(summon)) + /** Select a known synthetic member without performing source-level lookup. */ + def synthSel(sym: MemberSymbol)(using State): SynthSel = + SynthSel(this, Tree.Ident(sym.nme))(S(sym), FlowSymbol.synthSel(sym.nme), N, N) + def selNoSym(nme: Str, synth: Bool = false)(using State, Elaborator.Ctx): Sel | SynthSel = val id = new Tree.Ident(nme) if synth @@ -493,34 +573,91 @@ enum Term extends Statement, AutoLocated: App(this, Tup(args.toList.map(PlainFld(_)))(Tree.DummyTup)) (Tree.App(Tree.Dummy, Tree.Dummy), N, FlowSymbol("")) - override def mkClone(using State): Term = + /** Duplicate an elaborated expression after resolution, retaining its semantic + * identity and completed results. Listeners belong to elaboration and are not copied. + */ + override def mkClone(using State, Erasure): Term = + def copyMetadata(source: Term, copy: Term): copy.type = + copy.withLocOf(source) + copy.typeInterpretation = source.typeInterpretation + copy + def copyShapes(source: ShapeHost, copy: ShapeHost): copy.type = + copy.shapes ++= source.shapes + copy + def copyResolution(source: Resolvable, copy: Resolvable): copy.type = + copy.isErroneous = source.isErroneous + source.expansion.foreach(expansion => copy.expand(expansion.map(_.mkClone))) + copyShapes(source, copy) + def copySelection(source: Term & AnySel, copy: Term & AnySel): copy.type = + copy.resolvedTargets = source.resolvedTargets + copyResolution(source, copy) + def copyNewResolution(source: NewResolvable, copy: NewResolvable): copy.type = + copy.resolvedTargets = source.resolvedTargets + copy.isErroneous = source.isErroneous + copy + // Keep class qualifiers typed as selections while sharing all clone metadata copying. + def cloneSel(term: NewSel): NewSel = + val copy = new NewSel(term.prefix.mkClone, term.id, term.cls.map(cloneSel))(term.resSym) + copy.resolvedMembers = term.resolvedMembers + copy.hasDynamicTarget = term.hasDynamicTarget + copy.tupleIndex = term.tupleIndex + copyMetadata(term, copyNewResolution(term, copyShapes(term, copy))) val that = this match case Error() => Error() case UnitVal() => UnitVal() + case DynTy() => DynTy() case Missing => Missing case Lit(Tree.StrLit(value)) => Lit(Tree.StrLit(value)) case Lit(Tree.IntLit(value)) => Lit(Tree.IntLit(value)) case Lit(Tree.DecLit(value)) => Lit(Tree.DecLit(value)) case Lit(Tree.BoolLit(value)) => Lit(Tree.BoolLit(value)) case Lit(Tree.UnitLit(value)) => Lit(Tree.UnitLit(value)) - case term @ Resolved(t, sym) => Resolved(t.mkClone, sym)(term.typ) - case term @ Ref(sym) => Ref(sym)(Tree.Ident(term.tree.name), term.typ) - case term @ App(lhs, rhs) => App(lhs.mkClone, rhs.mkClone)(term.tree, term.typ, term.resSym) - case term @ TyApp(lhs, targs) => TyApp(lhs.mkClone, targs.map(_.mkClone))(term.typ) - case term @ Sel(prefix, nme) => Sel(prefix.mkClone, Tree.Ident(nme.name))(term.sym, term.resSym, term.typ, term.originalCtx) - case term @ SynthSel(prefix, nme) => SynthSel(prefix.mkClone, Tree.Ident(nme.name))(term.sym, term.resSym, term.typ, term.originalCtx) - case term @ LeadingDotSel(nme) => LeadingDotSel(Tree.Ident(nme.name))(term.originalCtx) - case DynSel(prefix, fld, arrayIdx) => DynSel(prefix.mkClone, fld.mkClone, arrayIdx) - case term @ Tup(fields) => Tup(fields.map { - case f: Fld => f.copy(term = f.term.mkClone, asc = f.asc.map(_.mkClone)) - case s: Spd => s.copy(term = s.term.mkClone) - })(term.tree) + case term @ SimpleRef(sym) => SimpleRef(sym)(term.tree) + case term @ SelfRef(sym) => SelfRef(sym)(term.tree) + case term @ MemberRef(sym) => copyNewResolution(term, MemberRef(sym)(term.tree, term.resSym)) + case term @ Super(receiver, owner, id) => + return copyMetadata(term, copyShapes(term, copyNewResolution(term, + Super(receiver.mkClone, owner, id)(term.resSym)))) + case term: NewSel => return cloneSel(term) + case term @ UnresolvedRef(prefixes, id) => + val clonedPrefixes = prefixes.map(_.mkClone) + val copy = UnresolvedRef(clonedPrefixes, id)(term.resSym) + def cloneReceiver(receiver: Term): Term = + val index = prefixes.indexWhere(_ is receiver) + assert(index >= 0, "A wildcard candidate must belong to an opened prefix") + clonedPrefixes(index) + copy.resolvedMembers = term.resolvedMembers.map((receiver, member) => cloneReceiver(receiver) -> member) + copy.dynamicPrefixes = term.dynamicPrefixes.map(cloneReceiver) + copyNewResolution(term, copyShapes(term, copy)) + case Capture(base, thru) => Capture(base.mkClone, thru) + case term @ Resolved(t, sym) => copyResolution(term, Resolved(t.mkClone, sym)(term.typ)) + case term @ Ref(sym) => copyResolution(term, Ref(sym)(Tree.Ident(term.tree.name), term.typ)) + case term @ App(lhs, rhs) => + val copy = App(lhs.mkClone, rhs.mkClone)(term.tree, term.typ, term.resSym) + copy.resolvedTargets = term.resolvedTargets + copyResolution(term, copy) + case term @ TyApp(lhs, targs) => copyResolution(term, TyApp(lhs.mkClone, targs.map(_.mkClone))(term.typ)) + case term @ Sel(prefix, nme) => copySelection(term, Sel(prefix.mkClone, Tree.Ident(nme.name))(term.sym, term.resSym, term.typ, term.originalCtx)) + case term @ SynthSel(prefix, nme) => copySelection(term, SynthSel(prefix.mkClone, Tree.Ident(nme.name))(term.sym, term.resSym, term.typ, term.originalCtx)) + case term @ LeadingDotSel(nme) => + val copy = LeadingDotSel(Tree.Ident(nme.name))(term.originalCtx) + copy.resolvedTargets = term.resolvedTargets + copyResolution(term, copy) + case DynSel(prefix, fld, arrayIdx, checked) => DynSel(prefix.mkClone, fld.mkClone, arrayIdx, checked) + case term @ Tup(fields) => + val copy = Tup(fields.map { + case f: Fld => f.copy(term = f.term.mkClone, asc = f.asc.map(_.mkClone)) + case s: Spd => s.copy(term = s.term.mkClone) + })(term.tree) + copyShapes(term, copy) case Mut(underlying) => Mut(underlying.mkClone.asInstanceOf[Tup | Rcd | New | DynNew]) - case term @ CtxTup(fields) => CtxTup(fields.map { - case f: Fld => f.copy(term = f.term.mkClone, asc = f.asc.map(_.mkClone)) - case s: Spd => s.copy(term = s.term.mkClone) - })(term.tree) - case IfLike(kw, form, split) => IfLike(kw, form, split.mkClone) + case term @ CtxTup(fields) => + val copy = CtxTup(fields.map { + case f: Fld => f.copy(term = f.term.mkClone, asc = f.asc.map(_.mkClone)) + case s: Spd => s.copy(term = s.term.mkClone) + })(term.tree) + copyShapes(term, copy) + case term @ IfLike(kw, form, split) => copyShapes(term, IfLike(kw, form, split.mkClone)) case SynthIf(split) => SynthIf(split.mkClone) case SynthWhile(split) => SynthWhile(split.mkClone) case Lam(params, body) => Lam(params, body.mkClone) @@ -529,14 +666,16 @@ enum Term extends Statement, AutoLocated: case Constrained(constraints, body) => Constrained(constraints, body.mkClone) case WildcardTy(in, out) => WildcardTy(in.map(_.mkClone), out.map(_.mkClone)) case blk: Blk => blk.mkBlkClone - case Rcd(mut, stats) => Rcd(mut, stats.map(_.mkClone)) + case term @ Rcd(mut, stats) => + val copy = Rcd(mut, stats.map(_.mkClone)) + copyShapes(term, copy) case Quoted(body) => Quoted(body.mkClone) case Unquoted(body) => Unquoted(body.mkClone) case term @ New(cls, args, rft) => - New(cls.mkClone, args.map(_.mkClone), rft.map { case (cs, ob) => cs -> ObjBody(ob.blk.mkBlkClone) })(term.typ) + copyResolution(term, New(cls.mkClone, args.map(_.mkClone), rft.map { case (cs, ob) => cs -> ObjBody(ob.blk.mkBlkClone) })(term.resSym, term.typ)) case DynNew(cls, args) => DynNew(cls.mkClone, args.map(_.mkClone)) case term @ SelProj(prefix, cls, proj) => - SelProj(prefix.mkClone, cls.mkClone, Tree.Ident(proj.name))(term.sym, term.resSym, term.typ, term.originalCtx) + copySelection(term, SelProj(prefix.mkClone, cls.mkClone, Tree.Ident(proj.name))(term.sym, term.resSym, term.typ, term.originalCtx)) case Asc(term, ty) => Asc(term.mkClone, ty.mkClone) case CompType(lhs, rhs, pol) => CompType(lhs.mkClone, rhs.mkClone, pol) case Neg(rhs) => Neg(rhs.mkClone) @@ -555,11 +694,12 @@ enum Term extends Statement, AutoLocated: case Annotated(annot, target) => Annotated(annot, target.mkClone) case Handle(lhs, rhs, args, derivedClsSym, defs, body) => Handle(lhs, rhs.mkClone, args.map(_.mkClone), derivedClsSym, defs, body.mkClone) - (this, that) match - case (self: Resolvable, that: Resolvable) if self.expansion.isDefined => - that.expand(self.expansion.get.map(_.mkClone)) - case _ => - that + copyMetadata(this, that) + + // // private[semantics] val reslListeners: Buffer[Resolution] = MutSet.empty + // private[semantics] val shapeListeners: Buffer[Shape => Unit] = Buffer.empty + // // private[semantics] def listen(f: Shape => Unit): Unit = + // // shapeListeners += f end Term @@ -634,347 +774,44 @@ object ShowCfg: end ShowCfg -sealed trait Statement extends Located, ProductWithExtraInfo: - - def mkClone(using State): Statement = this match - case t: Term => lastWords(s"overridden implementation") - case d: Definition => ??? - case imp: Import => Import(imp.sym, imp.str, imp.file)(imp.toLoc) - case LetDecl(sym, annotations) => LetDecl(sym, annotations.map(_.mkClone))(toLoc) - case RcdField(field, rhs) => RcdField(field.mkClone, rhs.mkClone) - case RcdSpread(rcd) => RcdSpread(rcd.mkClone) - case DefineVar(sym, rhs) => DefineVar(sym, rhs.mkClone)(toLoc) - case sc: SetConfig => sc - - def describe: Str = - val desc = this match - case Error() => "β€Ήerrorβ€Ί" - case UnitVal() => "unit value" - case Lit(lit) => lit.describeLit - case Ref(sym) => "reference" - case App(lhs, rhs) => "application" - case TyApp(lhs, targs) => "type application" - case Sel(pre, nme) => "selection" - case SynthSel(pre, nme) => "selection" - case DynSel(o, f, _) => "dynamic selection" - case Tup(fields) => "tuple literal" - case CtxTup(fields) => "contextual tuple literal" - case IfLike(_, IfLikeForm.ReturningIf, body) => "`if` expression" - case IfLike(_, IfLikeForm.ImperativeIf, body) => "`if` statement" - case IfLike(_, IfLikeForm.While, body) => "`while` statement" - case SynthIf(split) => "synthetic `if` expression" - case SynthWhile(split) => "synthetic `while` expression" - case Lam(params, body) => "function literal" - case FunTy(lhs, rhs, eff) => "function type" - case Forall(tvs, outer, body) => "universal quantification" - case Constrained(constraints, body) => "constrained type" - case WildcardTy(in, out) => "wildcard type" - case Blk(stats, res) => "block" - case Quoted(term) => "quoted term" - case Unquoted(term) => "unquoted term" - case New(cls, args, rft) => "object creation" - case SelProj(pre, cls, proj) => "field selection" - case Asc(term, ty) => "type ascription" - case CompType(lhs, rhs, pol) => if pol then "alternation" else "composition" - case Neg(rhs) => "negation type" - case Region(name, body) => "region expression" - case RegRef(reg, value) => "reference creation" - case Assgn(lhs, rhs) => "assignment" - case SetRef(ref, value) => "mutable reference assignment" - case Drop(ref) => "drop" - case Deref(ref) => "dereference" - case Throw(e) => "throw" - case Label(label, _, _, _) => s"label '${label.nme}'" - case Break(label, _, _) => s"break to label '${label.nme}'" - case Continue(label) => s"continue to label '${label.nme}'" - case Annotated(annotation, target) => "annotation" - case Ret(res) => "return" - case Try(body, finallyDo) => "try expression" - case Missing => "missing" - case LeadingDotSel(name) => "leading dot selection" - case Resolved(t, sym) => t.describe - case s => TODO(s) - this match - case self: Resolvable => self.resolvedTyp match - case S(typ) => s"${desc} of type ${typ.show}" - case N => desc - case _ => desc - - def extraInfo(using DebugPrinter): Str = this match - case r: Resolvable if r.resolvedSym.isDefined || r.resolvedTyp.isDefined => ( - r.resolvedSym.map(s => s"sym=${s.showAsPlain}") :: - r.resolvedTyp.map(s => s"typ=${s.showDbg}") :: Nil - ).flatten.mkString(",") - case r: SelProj => r.symbol.mkString - case _ => "" - - def subStatements: Vector[Statement] = this match - case Blk(stats, res) => stats.toVector :+ res - case _ => subTerms - def subTerms: Vector[Term] = this match - case Error() | Missing | _: Lit | _: Ref | _: UnitVal => Vector.empty - case Resolved(t, sym) => Vector.single(t) - case App(lhs, rhs) => Vector.double(lhs, rhs) - case RcdField(lhs, rhs) => Vector.double(lhs, rhs) - case RcdSpread(bod) => Vector.single(bod) - case FunTy(lhs, rhs, eff) => Vector.double(lhs, rhs) ++ eff.toVector - case TyApp(pre, tarsg) => pre +: tarsg.toVector - case Sel(pre, _) => Vector.single(pre) - case SynthSel(pre, _) => Vector.single(pre) - case DynSel(o, f, _) => Vector.double(o, f) - case Tup(fields) => fields.flatMap(_.subTerms).toVector - case Mut(und) => Vector.single(und) - case CtxTup(fields) => fields.flatMap(_.subTerms).toVector - case IfLike(_, _, split) => split.subTerms - case SynthIf(split) => split.subTerms - case SynthWhile(split) => split.subTerms - case Lam(params, body) => params.allParams.iterator.flatMap(_.sign).toVector :+ body - case Blk(stats, res) => stats.flatMap(_.subTerms).toVector :+ res - case Rcd(mut, stats) => stats.flatMap(_.subTerms).toVector - case Quoted(term) => Vector.single(term) - case Unquoted(term) => Vector.single(term) - case New(cls, args, rft) => (cls +: args.toVector) ++ rft.toVector.flatMap(_._2.blk.subTerms) - case DynNew(cls, args) => cls +: args.toVector - case SelProj(pre, cls, _) => Vector.double(pre, cls) - case Asc(term, ty) => Vector.double(term, ty) - case Ret(res) => Vector.single(res) - case Throw(res) => Vector.single(res) - case Label(_, _, body, _) => Vector.single(body) - case Break(_, _, value) => value.toVector - case Continue(_) => Vector.empty - case Forall(_, _, body) => Vector.single(body) - case Constrained(constraints, body) => constraints.flatMap(_.subTerms).toVector :+ body - case WildcardTy(in, out) => in.toVector ++ out.toVector - case CompType(lhs, rhs, _) => Vector.double(lhs, rhs) - case LetDecl(sym, annotations) => annotations.flatMap(_.subTerms).toVector - case DefineVar(sym, rhs) => Vector.single(rhs) - case Region(_, body) => Vector.single(body) - case RegRef(reg, value) => Vector.double(reg, value) - case Assgn(lhs, rhs) => Vector.double(lhs, rhs) - case SetRef(lhs, rhs) => Vector.double(lhs, rhs) - case Drop(term) => Vector.single(term) - case Deref(term) => Vector.single(term) - case TermDefinition(_, _, _, pss, tps, sign, body, _, _, annotations, _) => - pss.toVector.flatMap(_.subTerms) ++ tps.getOrElse(Nil).flatMap(_.subTerms).toVector ++ sign.toVector ++ body.toVector ++ annotations.flatMap(_.subTerms).toVector - case cls: ClassDef => - (cls.paramsOpt.toVector.flatMap(_.subTerms) :+ cls.body.blk) ++ cls.annotations.flatMap(_.subTerms).toVector - case mod: ModuleOrObjectDef => - ( mod.paramsOpt.toVector.flatMap(_.subTerms) :+ mod.body.blk) ++ mod.annotations.flatMap(_.subTerms).toVector - case td: TypeDef => - td.rhs.toVector ++ td.annotations.flatMap(_.subTerms).toVector - case pat: PatternDef => - (pat.paramsOpt.toVector.flatMap(_.subTerms) :+ pat.body.blk) ++ pat.annotations.flatMap(_.subTerms).toVector - case Import(sym, str, pth) => Vector.empty - case Try(body, finallyDo) => Vector.single(body) ++ Vector.single(finallyDo) - case Handle(lhs, rhs, args, derivedClsSym, defs, bod) => (rhs +: args.toVector) ++ defs.flatMap(_.td.subTerms).toVector :+ bod - case Neg(e) => Vector.single(e) - case Annotated(ann, target) => ann.subTerms ++ Vector.single(target) - case LeadingDotSel(nme) => Vector.empty - case SetConfig(_) => Vector.empty - - // private def treeOrSubterms(t: Tree, t: Term): Ls[Located] = t match - private def treeOrSubterms(t: Tree): Vector[Located] = t match - case Tree.DummyApp | Tree.DummyTup => subTerms - case _ => Vector.single(t) - - protected def children: Vector[Located] = this match - case t: Lit => Vector.single(t.lit.asTree) - case t: Ref => treeOrSubterms(t.tree) - case t: Tup => treeOrSubterms(t.tree) - case l: Lam => Vector.double(l.params, l.body) - case t: App => treeOrSubterms(t.tree) - case IfLike(_, _, split) => Vector.single(split) - case SynthIf(split) => Vector.single(split) - case SynthWhile(split) => Vector.single(split) - case SynthSel(pre, nme) => Vector.double(pre, nme) - case Sel(pre, nme) => Vector.double(pre, nme) - case SelProj(prefix, cls, proj) => Vector.triple(prefix, cls, proj) - case _ => - subTerms // TODO more precise (include located things that aren't terms) - - def show(using Scope, ShowCfg, Raise): Document = - def res: Document = this match - case lit: Lit => lit.lit.idStr - case r: Ref => - r.sym match - case _: BuiltinSymbol => r.sym.nme - case _ => r.sym.showName - case sel: Sel => - if summon[ShowCfg].showFlowSymbols - then doc"${sel.prefix.show}.${sel.sym.fold(doc"${sel.nme.name}β€Ή?β€Ί")(_.showName)}" - else doc"${sel.prefix.show}.${sel.nme.name}" - case sel: SynthSel => - if summon[ShowCfg].showFlowSymbols - then doc"⟨${sel.prefix.show}.⟩${sel.sym.fold(doc"${sel.nme.name}β€Ή?β€Ί")(_.showName)}" - else doc"${sel.prefix.show}.${sel.nme.name}" - case Resolved(trm, sym) => - trm.show - case app: App => - doc"${app.lhs.show}${app.rhs.showAsParams}${ - if summon[ShowCfg].showFlowSymbols - then - summon[ShowCfg].shownSymbols.add(app.resSym) - "β€Ή" :: app.resSym.showPlainName :: "β€Ί" - else "" - }" - case lam: Lam => doc"${lam.params.show} => ${lam.body.show}" - case nw: New => doc"new ${nw.cls.show}${nw.args.map(_.showAsParams).mkDocument()}${ - nw.rft.fold(doc"")(doc" with " :: _._2.blk.show)}" - case tup: Tup => bracketed("[", "]", insertBreak = true): - tup.fields.map(_.show).mkDocument(doc", # ") - case blk: Blk => braced: - doc" # " :: (blk.stats ::: - blk.res.match - case Lit(Tree.UnitLit(false)) => Nil - case res => res :: Nil - ).map(_.show).mkDocument(doc", # ") - case ld: LetDecl => - (ld.annotations.map(_.show :: " ") ::: doc"let ${ld.sym.showName}" :: Nil).mkDocument() - case df: DefineVar => - doc"${df.sym.showName} = ${df.rhs.show}" - case td: TermDefinition => - td.annotations.map(_.show :: " ").mkDocument() - :: doc"${td.k.str} ${td.bsym.showName}::${td.tsym.showName}" - :: (if td.tparams.isEmpty then doc"" - else doc"[${td.tparams.get.map(_.sym.showName).mkDocument(", ")}]") - :: td.params.map(_.show).mkDocument() - :: td.sign.fold(doc"")(s => doc": ${s.show}") - :: (if summon[ShowCfg].showFlowSymbols then doc" β€Ή${td.bsym.flow.showName}β€Ί" else doc"") - :: td.body.fold(doc"")(b => doc" = ${b.show}") - case cld: ClassLikeDef => - cld.annotations.map(_.show :: " ").mkDocument() - :: doc"${cld.ctorSym.fold(doc"")("fun "::_.showName::doc" # ")}${cld.kind.str} ${cld.bsym.showName}::${cld.sym.showName}" - :: (if cld.tparams.isEmpty then doc"" - else doc"[${cld.tparams.map(_.sym.showName).mkDocument(", ")}]") - :: cld.paramsOpt.map(_.show).toList.mkDocument() - :: cld.auxParams.map(_.show).mkDocument() - :: cld.ext.fold(doc"")(e => doc" extends ${e.show}") - :: doc" ${cld.body.blk.show}" - case imp: Import => - doc"import ${"\""}.../${imp.file.last}${"\""} as ${imp.sym.showName}" - case LeadingDotSel(name) => doc"_?_.${name.name}" - case Error() => doc"β€Ήerrorβ€Ί" - case _ => - doc"TODO[show:${getClass.getSimpleName}](${toString})" - this match - case t: Resolvable => t.expansion match - case S(S(exp)) => - val rhs = exp.show(using summon, summon[ShowCfg].copy(showExpansionMappings = false)) - if summon[ShowCfg].showExpansionMappings then - if exp === t then rhs - // ^ Some expansions only modify meta-data, such as types and symbols; - // we don't print them for conciseness - else res :: doc"{ ~> " :: rhs :: doc" }" - else exp.show - case _ => res - case _ => res - - def size: Int = this match - case Lit(Tree.StrLit(str)) => str.size / 4 + 1 - case _ => subTerms.iterator.map(_.size).sum + 1 - - def showDbg(using DebugPrinter): Str = this match - case r: Ref => r.sym.showAsPlain - case r: Resolved => - s"${r.showPlain}β€Ή${r.sym}β€Ί" - case trm: Term => - // s"$showPlainβ€Ή${trm.symbol.getOrElse("")}β€Ί" - s"$showPlain${trm.symbol.fold("")("β€Ή"+_+"β€Ί")}" - case _ => - showPlain - - def showAsParams(using Scope, ShowCfg, Raise): Document = this match - case tup: Tup => doc"(${tup.fields.map(_.show).mkDocument(", ")})" - case _ => doc"(...$show)" - - def showDbgAsParams(using DebugPrinter): Str = this match - case tup: Tup => s"(${tup.fields.map(_.showDbg).mkString(", ")})" - case _ => s"(...$showDbg)" - - def showPlain(using DebugPrinter): Str = this match - case Term.UnitVal() => "()" - case Lit(lit) => lit.idStr - case Resolved(t, sym) => t.showPlain - case r @ Ref(symbol) => symbol.toString - case App(lhs, rhs) => s"${lhs.showDbg}${rhs.showDbgAsParams}" - case RcdField(lhs, rhs) => s"${lhs.showDbg}: ${rhs.showDbg}" - case RcdSpread(bod) => s"...${bod.showDbg}" - case FunTy(lhs: Tup, rhs, eff) => - s"${lhs.fields.map(_.showDbg).mkString(", ")} ->${ - eff.map(e => s"{${e.showDbg}}").getOrElse("")} ${rhs.showDbg}" - case FunTy(lhs, rhs, eff) => - s"(...${lhs.showDbg}) ->${eff.map(e => s"{${e.showDbg}}").getOrElse("")} ${rhs.showDbg}" - case TyApp(lhs, targs) => s"${lhs.showDbg}[${targs.mkString(", ")}]" - case Forall(tvs, outer, body) => s"forall ${tvs.mkString(", ")}${outer.map(v => s", outer $v").mkString}: ${body.toString}" - case Constrained(constraints, body) => s"[${constraints.map(_.showDbg).mkString(", ")}] => ${body.showDbg}" - case WildcardTy(in, out) => s"in ${in.map(_.toString).getOrElse("βŠ₯")} out ${out.map(_.toString).getOrElse("⊀")}" - case Sel(pre, nme) => s"${pre.showDbg}.${nme.name}" - case SynthSel(pre, nme) => s"(${pre.showDbg}.)${nme.name}" - case DynSel(pre, fld, _) => s"${pre.showDbg}[${fld.showDbg}]" - case IfLike(kw, _, split) => s"${kw.name} { ${split.showDbg} }" - case SynthIf(split) => s"if { ${split.showDbg} }" - case SynthWhile(split) => s"while { ${split.showDbg} }" - case Lam(params, body) => s"Ξ»${params.showDbg}. ${body.showDbg}" - case Blk(stats, res) => - (stats.map(_.showDbg + "; ") :+ (res match { case Lit(Tree.UnitLit(false)) => "" case x => x.showDbg + " " })) - .mkString("{ ", "", "}") - case Rcd(mut, stats) => - (if mut then "mut " else "") + stats.map(_.showDbg + "; ").mkString("{ ", "", "}") - case Quoted(term) => s"""code"${term.showDbg}"""" - case Unquoted(term) => s"$${${term.showDbg}}" - case New(cls, args, rft) => - s"new ${cls.showDbg}${args.map(_.showDbgAsParams).mkString}${rft.fold("")(r => s"{ ${r._2.blk.showDbg} }")}" - case DynNew(cls, args) => - s"new! ${cls.showDbg}${args.map(_.showDbgAsParams).mkString}" - case SelProj(pre, cls, proj) => s"${pre.showDbg}.${cls.showDbg}#${proj.name}" - case Asc(term, ty) => s"${term.toString}: ${ty.toString}" - case LetDecl(sym, _) => s"let ${sym}" - case DefineVar(sym, rhs) => s"${sym} = ${rhs.showDbg}" - case Handle(lhs, rhs, args, derivedClsSym, defs, bod) => - s"handle ${lhs} = ${rhs}(${args.mkString(", ")}) ${defs} in ${bod}" - case Region(name, body) => s"region ${name.nme} in ${body.showDbg}" - case RegRef(reg, value) => s"(${reg.showDbg}).ref ${value.showDbg}" - case Assgn(lhs, rhs) => s"${lhs.showDbg} := ${rhs.showDbg}" - case SetRef(lhs, rhs) => s"${lhs.showDbg} := ${rhs.showDbg}" - case Drop(term) => s"drop $term" - case Deref(term) => s"!$term" - case Neg(ty) => s"~${ty.showDbg}" - case CompType(lhs, rhs, pol) => s"${lhs.showDbg} ${if pol then "|" else "&"} ${rhs.showDbg}" - case Error() => "" - case Tup(fields) => fields.map(_.showDbg).mkString("[", ", ", "]") - case Mut(und) => s"mut ${und.showDbg}" - case CtxTup(fields) => fields.map(_.showDbg).mkString("β€Ήusingβ€Ί[", ", ", "]") - case TermDefinition(k, sym, tsym, pss, tps, sign, body, flags, _, _, _) => - s"${flags.showDbg}${k.str} ${sym}${ - tps.map(_.map(_.showDbg)).mkStringOr(", ", "[", "]") - }${ - pss.map(_.showDbg).mkString("") - }${sign.fold("")(": "+_.showDbg)}${ - body match - case S(x) => " = " + x.showDbg - case N => "" - }" - case cls: ClassLikeDef => - s"${cls.kind} ${cls.sym.nme}${ - cls.tparams.map(_.showDbg).mkStringOr(", ", "[", "]")}${ - cls.paramsOpt.fold("")(_.toString)} ${cls.body}" - case Import(sym, str, file) => s"import $str from ${file}" - case Annotated(ann, target) => s"@${ann} ${target.showDbg}" - case Throw(res) => s"throw ${res.showDbg}" - case Label(label, _, body, _) => s"do ${label.nme}: ${body.showDbg}" - case Break(label, _, value) => s"${label.nme}.break${value.fold("")(v => s" ${v.showDbg}")}" - case Continue(label) => s"${label.nme}.continue" - case Try(body, finallyDo) => s"try ${body.showDbg} finally ${finallyDo.showDbg}" - case Ret(res) => s"return ${res.showDbg}" - case TypeDef(sym, _, tparams, rhs, _, _) => - s"type ${sym}${tparams.mkStringOr(", ", "[", "]")} = ${rhs.fold("")(x => x.showDbg)}" - case Missing => "missing" - case LeadingDotSel(nme) => s"_?_.${nme.name}" - case SetConfig(_) => "#config(...)" +sealed trait Statement extends StatementImpl + final case class LetDecl(sym: LocalVarSymbol | TermSymbol, annotations: Ls[Annot])(val toLoc: Opt[Loc]) extends Statement -final case class RcdField(field: Term, rhs: Term) extends Statement, AutoLocated +/** The symbol identifies the property, independently of any local binding used + * to evaluate its value. Computed keys also have an identity, but cannot be + * selected statically until their key is known. Cloning preserves this identity. + */ +final case class RcdField(field: Term, rhs: Term, sym: BlockMemberSymbol) extends Statement, AutoLocated: + // The two-argument RcdField.apply sets sym.tsym and its definition before + // constructing this node. Lowering clones reuse the same initialized symbols. + val tsym: TermSymbol = sym.tsym.get + require((tsym.k is RecordField) && tsym.owner.isEmpty && tsym.defn.exists(_.sym is sym)) + field match + case Term.Lit(Tree.StrLit(name)) => require(sym.nme == name) + case _ => () + +object RcdField: + def apply(field: Term, rhs: Term)(using State): RcdField = make(field, rhs, false) + def signature(field: Term, sign: Term)(using State): RcdField = make(field, sign, true) + private def make(field: Term, rhs: Term, signature: Bool)(using State): RcdField = + val name = field match + case Term.Lit(Tree.StrLit(name)) => name + case _ => "computed field" + val id = new Tree.Ident(name) + id.withLocOf(field) + val sym = new BlockMemberSymbol(name, Nil) + val tsym = TermSymbol(RecordField, N, id, erasedType = N) + sym.tsym = S(tsym) + // Value-field lookup removes the synthetic definition's capture mark, leaving + // enclosing call-site marks intact. A structural type field instead refers + // directly to its written type: projection does not invoke a value definition. + tsym.defn = S(TermDefinition(RecordField, sym, tsym, Nil, N, + if signature then S(rhs) else N, + if signature then N else S(Term.Capture(rhs, tsym)), TermDefFlags.empty, Modulefulness.none, Nil, N)(field.toLoc)) + sym.complete() + RcdField(field, rhs, sym) final case class RcdSpread(rcd: Term) extends Statement, AutoLocated final case class DefineVar(sym: LocalSymbol | TermSymbol, rhs: Term)(val toLoc: Opt[Loc]) extends Statement @@ -987,17 +824,18 @@ final case class SetConfig(modify: hkmc2.Config => hkmc2.Config)(val toLoc: Opt[ enum Visibility: case Public, Private -/** - * isMethod: if the term is a method (as opposed to a function) - */ -final case class TermDefFlags(isMethod: Bool): +/** `isMethod` distinguishes methods from functions. `hasResultAnnotation` records whether a + * signature describes just the result; erasure consumes leading arrows only for full signatures. + */ +final case class TermDefFlags(isMethod: Bool, hasResultAnnotation: Bool): def showDbg: Str = val flags = Buffer.empty[String] if isMethod then flags += "method " + if hasResultAnnotation then flags += "result annotation " flags.mkString override def toString: String = "β€Ή" + showDbg + "β€Ί" -object TermDefFlags { val empty: TermDefFlags = TermDefFlags(false) } +object TermDefFlags { val empty: TermDefFlags = TermDefFlags(false, false) } /** * A case class representing the modulefulness of a declaration. @@ -1050,6 +888,24 @@ final case class TermDefinition( require(k is tsym.k) def bsym: BlockMemberSymbol = sym val owner = tsym.owner + /** Only function bodies are wrapped by AsyncLowering; annotations on other + * definitions are ignored with a warning and must not change their interface. + */ + def asyncAnnotation: Opt[Annot.Async] = + if (k is syntax.Fun) && body.nonEmpty then annotations.collectFirst: + case annotation: Annot.Async => annotation + else N + /** A result annotation denotes the whole result; a separate function signature + * includes the arrows corresponding to the definition's written parameter lists. + */ + def resultSignature: Opt[Term] = + def strip(sign: Term, count: Int): Term = (sign, count) match + case (Term.Forall(_, _, body), _) => strip(body, count) + case (Term.FunTy(_, rhs, _), n) if n > 0 => strip(rhs, n - 1) + case _ => sign + sign.map: sign => + if (k is syntax.Fun) && !flags.hasResultAnnotation then strip(sign, params.length) + else sign def visibility: Visibility = annotations .collectFirst: case Annot.Modifier(Keyword.`private`) => Visibility.Private @@ -1156,6 +1012,14 @@ sealed abstract class Definition extends Declaration, Statement: def hasDeclareModifier: Opt[Annot.Modifier] = Annot.declareModifierOf(annotations) def hasStagedModifier: Opt[Annot.Modifier] = annotations.collectFirst: case mod @ Annot.Modifier(Keyword.`staged`) => mod + def describeRef: Str = this match + case td: TermDefinition => s"term '${td.sym.nme}'" + case cls: ClassDef => s"class '${cls.sym.nme}'" + case mod: ModuleOrObjectDef => + if mod.kind is Mod then s"module '${mod.sym.nme}'" + else s"object '${mod.sym.nme}'" + case td: TypeDef => s"type '${td.bsym.nme}'" + case pat: PatternDef => s"pattern '${pat.sym.nme}'" sealed trait CompanionValue extends Definition @@ -1191,10 +1055,7 @@ sealed abstract class ClassLikeDef extends TypeLikeDef: case _ => N def extraAnnotations(using Ctx): Ls[Annot] = annotations.filter: case Annot.Modifier(Keyword.`declare` | Keyword.`abstract` | Keyword.`data`) => false - case Annot.Trm(trm: SynthSel) if - (kind is Cls) && - (trm.sym.contains(ctx.builtins.annotations.bufferable) || - trm.sym.contains(ctx.builtins.annotations.buffered)) => false + case _: Annot.Bufferable if kind is Cls => false case _ => true @@ -1361,6 +1222,11 @@ enum IfLikeForm: def isImperative: Bool = this match case ReturningIf => false case ImperativeIf | While => true + def headStr: Str = this match + case ReturningIf => "if" + case ImperativeIf => "if" + case While => "while" + def midStr: Str = if isImperative then "do" else "then" sealed abstract class Elem extends AutoLocated: @@ -1467,17 +1333,6 @@ object ParamListFlags: val empty = ParamListFlags(false) -// trait FldImpl extends AutoLocated: -trait FldImpl: - self: Fld => - def children: Vector[Located] = self.term +: self.asc.toVector - def show(using Scope, ShowCfg, Raise): Document = flags.show(true) :: self.term.show - def showDbg(using DebugPrinter): Str = flags.show(true) + self.term.showDbg - def describe: Str = - (if self.flags.spec then "specialized " else "") + - (if self.flags.mut then "mutable " else "") + - self.term.describe - /** * Unwrapper that unwraps a term until it is no longer an App. */ @@ -1486,12 +1341,3 @@ object Apps: case Term.App(Apps(base, args), arg) => S(base, args :+ arg) case t => S(t, Nil) - -trait BlkImpl: - this: Blk => - def mkBlkClone(using State): Blk = Blk(stats.map(_.mkClone), res.mkClone) - def showTopLevel(using Scope, ShowCfg, Raise): Document = - (stats ::: (res match - case Lit(Tree.UnitLit(false)) => Nil - case res => res :: Nil)).map(_.show).mkDocument(doc", # ") - diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/TermImpl.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/TermImpl.scala new file mode 100644 index 0000000000..e78ff8d981 --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/TermImpl.scala @@ -0,0 +1,581 @@ +package hkmc2 +package semantics + +import scala.collection.mutable.{Buffer, Set as MutSet, LinkedHashSet} + +import hkmc2.utils.*, shorthands.* +import syntax.* +import hkmc2.utils.Scope +import hkmc2.utils.Scope.scope +import hkmc2.document.* +import hkmc2.document.Document.* + +import Elaborator.State +import hkmc2.typing.Type +import hkmc2.semantics.Elaborator.{Ctx, ctx} +import hkmc2.Message.MessageContext +import hkmc2.codegen.Erasure + +import Term.* + + + +trait Describable: + def describe: Str + + +type AnyRef_ = AnyRefImpl & Term + +trait AnyRefImpl: + self: Term.Ref | Term.SimpleRef | Term.MemberRef | Term.SelfRef => + def tree: Tree.Ident + def sym: Symbol + +trait NewRefImpl extends AnyRefImpl: + self: Term.SimpleRef | Term.MemberRef | Term.SelfRef => + def refNum: Int = 0 // TODO: track reference counts like in the old refs? + +trait NewSelImpl extends NewResolvableImpl: + self: Term.NewSel => + // At least one receiver permits runtime lookup without a static member symbol. + var hasDynamicTarget: Bool = false + // Resolution records a numeric tuple projection; lowering emits an indexed + // access rather than looking for a nominal field symbol. + var tupleIndex: Opt[Int] = N + var resolvedMembers: Ls[BlockMemberSymbol] = Nil // * filled during resolution + // Unlike a field whose value happens to be a class, C.class has a class + // target without an ordinary member. Its receiver remains the class reference. + def isClassValue(using Erasure): Bool = + !isErroneous && self.cls.isEmpty && self.id.name == "class" && resolvedMembers.isEmpty && + !hasDynamicTarget && resolvedTargets.exists(_.isInstanceOf[ClassSymbol]) + def hasAmbiguousClass(using Erasure): Bool = hasAmbiguousClassImpl + // Also used by the guarded symbol lookup for completed imports. + private[semantics] def hasAmbiguousClassImpl: Bool = projectionClassesImpl.sizeCompare(1) > 0 + def projectionClasses(using Erasure): Ls[ClassSymbol] = projectionClassesImpl + private def projectionClassesImpl: Ls[ClassSymbol] = + self.cls.toList.flatMap: qualifier => + qualifier.resolvedTargets.collect { case cls: ClassSymbol => cls }.distinct + +trait UnresolvedRefImpl extends NewResolvableImpl: + self: Term.UnresolvedRef => + // Retain the receiver as well as the definition: two instances can expose + // the same member symbol without denoting the same storage location. + var resolvedMembers: Ls[(Term, BlockMemberSymbol)] = Nil + var dynamicPrefixes: Ls[Term] = Nil + + +// trait FldImpl extends AutoLocated: +trait FldImpl: + self: Fld => + def children: Vector[Located] = self.term +: self.asc.toVector + def show(using Scope, ShowCfg, Raise): Document = flags.show(true) :: self.term.show + def showDbg(using DebugPrinter): Str = flags.show(true) + self.term.showDbg + def describe: Str = + (if self.flags.spec then "specialized " else "") + + (if self.flags.mut then "mutable " else "") + + self.term.describe + + +trait BlkImpl: + this: Blk => + def mkBlkClone(using State, Erasure): Blk = Blk(stats.map(_.mkClone), res.mkClone) + def showTopLevel(using Scope, ShowCfg, Raise): Document = + (stats ::: (res match + case Lit(Tree.UnitLit(false)) => Nil + case res => res :: Nil)).map(_.show).mkDocument(doc", # ") + + +trait LeadingDotSelImpl(using State): + self: Term.LeadingDotSel => + val resSym: FlowSymbol = FlowSymbol.lds(self.nme.name) + var resolvedTargets: Ls[flow.SelectionTarget.CompanionMember] = Nil // * filled during flow analysis + + +type AnyResolvable = Resolvable | NewResolvableImpl +// type AnyRef = Ref | SimpleRef | MemberRef + +type NewResolvable = NewResolvableImpl & Term + +/** Suppress follow-on diagnostics after a resolution or flow error. */ +trait PossiblyErroneous: + var isErroneous: Bool = false + +trait NewResolvableImpl extends PossiblyErroneous: + self: MemberRef | NewSel | UnresolvedRef | Super => + var resolvedTargets: Ls[DefinitionSymbol[?]] = Nil // * filled during flow analysis + // val resSym: FlowSymbol = FlowSymbol.simpleRef(self.sym.name) + // var disamb: Opt[Disambiguation] = None // * filled during flow analysis + + +trait StatementImpl extends Located, ProductWithExtraInfo, Describable: + this: Statement => + + def mkClone(using State, Erasure): Statement = this match + case t: Term => lastWords(s"overridden implementation") + case d: Definition => ??? + case imp: Import => Import(imp.sym, imp.str, imp.file)(imp.toLoc) + case LetDecl(sym, annotations) => LetDecl(sym, annotations.map(_.mkClone))(toLoc) + case RcdField(field, rhs, sym) => RcdField(field.mkClone, rhs.mkClone, sym) + case RcdSpread(rcd) => RcdSpread(rcd.mkClone) + case DefineVar(sym, rhs) => DefineVar(sym, rhs.mkClone)(toLoc) + case sc: SetConfig => sc + + def describe: Str = + val desc = this match + case Error() => "β€Ήerrorβ€Ί" + case UnitVal() => "unit value" + case DynTy() => "dynamic type" + case _: Rcd => "record literal" + case Lit(lit) => lit.describeLit + case Ref(sym) => "reference" + case Capture(base, thru) => base.describe + case SimpleRef(sym) => "reference" + case MemberRef(sym) => "member reference" + case UnresolvedRef(_, _) => "wildcard-open reference" + case App(lhs, rhs) => "application" + case TyApp(lhs, targs) => "type application" + case _: Super => "super reference" + case NewSel(pre, nme, _) => "selection" + case Sel(pre, nme) => "selection" + case SynthSel(pre, nme) => "selection" + case DynSel(o, f, _, checked) => if checked then "array index" else "dynamic selection" + case Tup(fields) => "tuple literal" + case CtxTup(fields) => "contextual tuple literal" + case IfLike(_, IfLikeForm.ReturningIf, body) => "`if` expression" + case IfLike(_, IfLikeForm.ImperativeIf, body) => "`if` statement" + case IfLike(_, IfLikeForm.While, body) => "`while` statement" + case SynthIf(split) => "synthetic `if` expression" + case SynthWhile(split) => "synthetic `while` expression" + case Lam(params, body) => "function literal" + case FunTy(lhs, rhs, eff) => "function type" + case Forall(tvs, outer, body) => "universal quantification" + case Constrained(constraints, body) => "constrained type" + case WildcardTy(in, out) => "wildcard type" + case Blk(stats, res) => "block" + case Quoted(term) => "quoted term" + case Unquoted(term) => "unquoted term" + case New(cls, args, rft) => "object instantiation" + case DynNew(cls, args) => "dynamic object instantiation" + case SelProj(pre, cls, proj) => "field selection" + case Asc(term, ty) => "type ascription" + case CompType(lhs, rhs, pol) => if pol then "alternation" else "composition" + case Neg(rhs) => "negation type" + case Region(name, body) => "region expression" + case RegRef(reg, value) => "reference creation" + case Assgn(lhs, rhs) => "assignment" + case SetRef(ref, value) => "mutable reference assignment" + case Drop(ref) => "drop" + case Deref(ref) => "dereference" + case Throw(e) => "throw" + case Label(label, _, _, _) => s"label '${label.nme}'" + case Break(label, _, _) => s"break to label '${label.nme}'" + case Continue(label) => s"continue to label '${label.nme}'" + case Annotated(annotation, target) => "annotation" + case Ret(res) => "return" + case Try(body, finallyDo) => "try expression" + case _: Handle => "handler expression" + case Missing => "missing" + case LeadingDotSel(name) => "leading dot selection" + case Resolved(t, sym) => t.describe + case td: TermDefinition => s"term definition '${td.sym.nme}'" + case cls: ClassDef => s"class definition '${cls.sym.nme}'" + case mod: ModuleOrObjectDef => s"module/object definition '${mod.sym.nme}'" + case td: TypeDef => s"type definition '${td.bsym.nme}'" + case s => TODO(s) + this match + case self: Resolvable => self.resolvedTyp match + case S(typ) => s"${desc} of type ${typ.show}" + case N => desc + case _ => desc + + def extraInfo(using DebugPrinter): Str = this match + case s: AnySel if s.resolvedTargets.nonEmpty => + s"targets=${s.resolvedTargets.map(_.showAsPlain).mkString("[", ",", "]")}" + case r: Resolvable if r.legacyResolvedSym.isDefined || r.resolvedTyp.isDefined => ( + r.legacyResolvedSym.map(s => s"sym=${s.showAsPlain}") :: + r.resolvedTyp.map(s => s"typ=${s.showDbg}") :: Nil + ).flatten.mkString(",") + case r: SelProj => r.symbol.mkString + case _ => "" + + def subStatements: Vector[Statement] = this match + case Blk(stats, res) => stats.toVector :+ res + case _ => subTerms + def subTerms: Vector[Term] = this match + case Error() | Missing | _: Lit | _: AnyRef_ | _: DynTy | _: UnitVal => Vector.empty + case Capture(base, thru) => Vector.single(base) + case Resolved(t, sym) => Vector.single(t) + case App(lhs, rhs) => Vector.double(lhs, rhs) + case RcdField(lhs, rhs, _) => Vector.double(lhs, rhs) + case RcdSpread(bod) => Vector.single(bod) + case FunTy(lhs, rhs, eff) => Vector.double(lhs, rhs) ++ eff.toVector + case TyApp(pre, tarsg) => pre +: tarsg.toVector + case Sel(pre, _) => Vector.single(pre) + case SynthSel(pre, _) => Vector.single(pre) + case Super(receiver, _, _) => Vector.single(receiver) + case NewSel(pre, _, cls) => Vector.single(pre) ++ cls.toVector + case UnresolvedRef(prefixes, _) => prefixes.toVector + case DynSel(o, f, _, _) => Vector.double(o, f) + case Tup(fields) => fields.flatMap(_.subTerms).toVector + case Mut(und) => Vector.single(und) + case CtxTup(fields) => fields.flatMap(_.subTerms).toVector + case IfLike(_, _, split) => split.subTerms + case SynthIf(split) => split.subTerms + case SynthWhile(split) => split.subTerms + case Lam(params, body) => params.allParams.iterator.flatMap(_.sign).toVector :+ body + case Blk(stats, res) => stats.flatMap(_.subTerms).toVector :+ res + case Rcd(mut, stats) => stats.flatMap(_.subTerms).toVector + case Quoted(term) => Vector.single(term) + case Unquoted(term) => Vector.single(term) + case New(cls, args, rft) => (cls +: args.toVector) ++ rft.toVector.flatMap(_._2.blk.subTerms) + case DynNew(cls, args) => cls +: args.toVector + case SelProj(pre, cls, _) => Vector.double(pre, cls) + case Asc(term, ty) => Vector.double(term, ty) + case Ret(res) => Vector.single(res) + case Throw(res) => Vector.single(res) + case Label(_, _, body, _) => Vector.single(body) + case Break(_, _, value) => value.toVector + case Continue(_) => Vector.empty + case Forall(_, _, body) => Vector.single(body) + case Constrained(constraints, body) => constraints.flatMap(_.subTerms).toVector :+ body + case WildcardTy(in, out) => in.toVector ++ out.toVector + case CompType(lhs, rhs, _) => Vector.double(lhs, rhs) + case LetDecl(sym, annotations) => annotations.flatMap(_.subTerms).toVector + case DefineVar(sym, rhs) => Vector.single(rhs) + case Region(_, body) => Vector.single(body) + case RegRef(reg, value) => Vector.double(reg, value) + case Assgn(lhs, rhs) => Vector.double(lhs, rhs) + case SetRef(lhs, rhs) => Vector.double(lhs, rhs) + case Drop(term) => Vector.single(term) + case Deref(term) => Vector.single(term) + case TermDefinition(_, _, _, pss, tps, sign, body, _, _, annotations, _) => + pss.toVector.flatMap(_.subTerms) ++ tps.getOrElse(Nil).flatMap(_.subTerms).toVector ++ sign.toVector ++ body.toVector ++ annotations.flatMap(_.subTerms).toVector + case cls: ClassDef => + (cls.paramsOpt.toVector.flatMap(_.subTerms) :+ cls.body.blk) ++ cls.annotations.flatMap(_.subTerms).toVector + case mod: ModuleOrObjectDef => + ( mod.paramsOpt.toVector.flatMap(_.subTerms) :+ mod.body.blk) ++ mod.annotations.flatMap(_.subTerms).toVector + case td: TypeDef => + td.rhs.toVector ++ td.annotations.flatMap(_.subTerms).toVector + case pat: PatternDef => + (pat.paramsOpt.toVector.flatMap(_.subTerms) :+ pat.body.blk) ++ pat.annotations.flatMap(_.subTerms).toVector + case Import(sym, str, pth) => Vector.empty + case Try(body, finallyDo) => Vector.single(body) ++ Vector.single(finallyDo) + case Handle(lhs, rhs, args, derivedClsSym, defs, bod) => (rhs +: args.toVector) ++ defs.flatMap(_.td.subTerms).toVector :+ bod + case Neg(e) => Vector.single(e) + case Annotated(ann, target) => ann.subTerms ++ Vector.single(target) + case LeadingDotSel(nme) => Vector.empty + case SetConfig(_) => Vector.empty + + // private def treeOrSubterms(t: Tree, t: Term): Ls[Located] = t match + private def treeOrSubterms(t: Tree): Vector[Located] = t match + case Tree.DummyApp | Tree.DummyTup => subTerms + case _ => Vector.single(t) + + protected def children: Vector[Located] = this match + case t: Lit => Vector.single(t.lit.asTree) + case t: AnyRef_ => treeOrSubterms(t.tree) + case t: Tup => treeOrSubterms(t.tree) + case l: Lam => Vector.double(l.params, l.body) + case t: App => treeOrSubterms(t.tree) + case IfLike(_, _, split) => Vector.single(split) + case SynthIf(split) => Vector.single(split) + case SynthWhile(split) => Vector.single(split) + case SynthSel(pre, nme) => Vector.double(pre, nme) + case Sel(pre, nme) => Vector.double(pre, nme) + case SelProj(prefix, cls, proj) => Vector.triple(prefix, cls, proj) + case _ => + subTerms // TODO more precise (include located things that aren't terms) + + def show(using Scope, ShowCfg, Raise): Document = + + def showSelTargets(sel: AnySel): Document = + val str = sel.nme.name + val ts = sel.resolvedTargets + ts match + case Nil => + // doc"$strβ€Ή?β€Ί" + doc"${str}Λ€Λ€Λ€" + case t :: Nil => t.show + case ts => doc"$strβ€Ή" :: ts.map(_.show).mkDocument(", ") :: doc"β€Ί" + + def res: Document = this match + case lit: Lit => lit.lit.idStr + case UnitVal() => doc"()" + case DynTy() => doc"dyn" + case r: SimpleRef => + r.sym match + case _: BuiltinSymbol => r.sym.nme + case _ => r.sym.showName + case r: MemberRef => r.sym.showName + case r: UnresolvedRef => doc"${r.id.name}" + case sr: SelfRef => s"${sr.sym.showName}.this" + case Capture(base, thru) => + // doc"${base.show}⟨${thru.showName}⟩" + doc"${base.show}^${thru.showName}" + case r: Ref => + r.sym match + case _: BuiltinSymbol => r.sym.nme + case _ => r.sym.showName + case sup: Super => doc"super.${sup.id.name}" + case sel: NewSel => + val str = sel.id.name + val pre = sel.cls.fold(doc"${sel.prefix.show}.")(cls => doc"${sel.prefix.show}.${cls.show}#") + if summon[ShowCfg].showFlowSymbols + then doc"$pre${ + sel.resolvedMembers match + case Nil if sel.resolvedTargets.nonEmpty => + doc"$strβ€Ή" :: sel.resolvedTargets.distinct.map(_.showName).mkDocument(", ") :: doc"β€Ί" + case Nil => doc"${str}Λ€Λ€Λ€" + case t :: Nil => t.showName + case ts => doc"$strβ€Ή" :: ts.map(_.showName).mkDocument(", ") :: doc"β€Ί" + }" + else doc"$pre$str" + case sel: Sel => + if summon[ShowCfg].showFlowSymbols + then doc"${sel.prefix.show}.${sel.sym.fold(doc"${ + showSelTargets(sel)}")(_.showName)}" + else doc"${sel.prefix.show}.${sel.nme.name}" + case sel: SynthSel => + if summon[ShowCfg].showFlowSymbols + then doc"⟨${sel.prefix.show}.⟩${sel.sym.fold(doc"${ + showSelTargets(sel)}")(_.showName)}" + else doc"${sel.prefix.show}.${sel.nme.name}" + case Resolved(trm, sym) => + trm.show + case app: App => + doc"${app.lhs.show}${app.rhs.showAsParams}${ + if summon[ShowCfg].showFlowSymbols + then + summon[ShowCfg].shownSymbols.add(app.resSym) + "β€Ή" :: app.resSym.showPlainName :: "β€Ί" + else "" + }" + case lam: Lam => doc"${lam.params.show} => ${lam.body.show}" + case nw: New => doc"new ${nw.cls.show}${nw.args.map(_.showAsParams).mkDocument()}${ + nw.rft.fold(doc"")(doc" with " :: _._2.blk.show)}" + case tup: Tup => bracketed("[", "]", insertBreak = true): + tup.fields.map(_.show).mkDocument(doc", # ") + case blk: Blk => braced: + doc" # " :: (blk.stats ::: + blk.res.match + case Lit(Tree.UnitLit(false)) => Nil + case res => res :: Nil + ).map(_.show).mkDocument(doc", # ") + case ld: LetDecl => + (ld.annotations.map(_.show :: " ") ::: doc"let ${ld.sym.showName}" :: Nil).mkDocument() + case df: DefineVar => + doc"${df.sym.showName} = ${df.rhs.show}" + case td: TermDefinition => + td.annotations.map(_.show :: " ").mkDocument() + :: doc"${td.k.str} ${td.bsym.showName}::${td.tsym.showName}" + :: (if td.tparams.isEmpty then doc"" + else doc"[${td.tparams.get.map(_.sym.showName).mkDocument(", ")}]") + :: td.params.map(_.show).mkDocument() + :: td.sign.fold(doc"")(s => doc": ${s.show}") + :: (if summon[ShowCfg].showFlowSymbols then doc" β€Ή${td.bsym.flow.showName}β€Ί" else doc"") + :: td.body.fold(doc"")(b => doc" = ${b.show}") + case cld: ClassLikeDef => + cld.annotations.map(_.show :: " ").mkDocument() + :: doc"${cld.ctorSym.fold(doc"")("fun "::_.showName::doc" # ")}${cld.kind.str} ${cld.bsym.showName}::${cld.sym.showName}" + :: (if cld.tparams.isEmpty then doc"" + else doc"[${cld.tparams.map(_.sym.showName).mkDocument(", ")}]") + :: cld.paramsOpt.map(_.show).toList.mkDocument() + :: cld.auxParams.map(_.show).mkDocument() + :: cld.ext.fold(doc"")(e => doc" extends ${e.show}") + :: doc" ${cld.body.blk.show}" + case imp: Import => + doc"import ${"\""}.../${imp.file.last}${"\""} as ${imp.sym.showName}" + case LeadingDotSel(name) => doc"_?_.${name.name}" + case Error() => doc"β€Ήerrorβ€Ί" + case IfLike(kw, form, split) => + // doc"${kw.name} ${form.headStr} ${split.show}" + given IfLikeForm = form + doc"${form.headStr} { #{ # ${split.show} #} # }" + // val fs = fomr match + case Missing => doc"β€Ήmissingβ€Ί" + case TyApp(lhs, targs) => doc"${lhs.show}[${targs.map(_.show).mkDocument(", ")}]" + case DynSel(prefix, field, arrayIdx, _) => + if arrayIdx then doc"${prefix.show}.[${field.show}]" + else doc"${prefix.show}.(${field.show})" + case SelProj(prefix, cls, field) => doc"${prefix.show}.${cls.show}#${field.name}" + case Mut(underlying) => doc"mut ${underlying.show}" + case CtxTup(fields) => doc"using (${fields.map(_.show).mkDocument(", ")})" + case SynthIf(split) => doc"if { #{ # ${split.show} #} # }" + case SynthWhile(split) => doc"while { #{ # ${split.show} #} # }" + case FunTy(lhs, rhs, eff) => + doc"(${lhs.showAsParams} ->${eff.fold(doc"")(e => doc"{${e.show}}")} ${rhs.show})" + case Forall(tvs, outer, body) => + val variables = tvs.map: tv => + tv.sym.showName + :: tv.lb.fold(doc"")(b => doc" :> ${b.show}") + :: tv.ub.fold(doc"")(b => doc" <: ${b.show}") + doc"forall ${variables.mkDocument(", ")}${outer.fold(doc"")(v => doc", outer ${v.showName}")}: ${body.show}" + case Constrained(constraints, body) => + val bounds = constraints.map(c => doc"${c.lhs.show} ${c.dir.showDbg} ${c.rhs.show}") + doc"[${bounds.mkDocument(", ")}] => ${body.show}" + case WildcardTy(in, out) => + doc"in ${in.fold(doc"βŠ₯")(_.show)} out ${out.fold(doc"⊀")(_.show)}" + case Rcd(mut, stats) => + (if mut then doc"mut " else doc"") :: braced: + doc" # " :: stats.map(_.show).mkDocument(doc", # ") + case RcdField(field, rhs, _) => doc"${field.show}: ${rhs.show}" + case RcdSpread(record) => doc"...${record.show}" + case Quoted(body) => doc"""code"${body.show}"""" + case Unquoted(body) => doc"$${${body.show}}" + case DynNew(cls, args) => doc"new! ${cls.show}${args.map(_.showAsParams).mkDocument()}" + case Asc(term, ty) => doc"(${term.show}: ${ty.show})" + case CompType(lhs, rhs, pol) => doc"(${lhs.show} ${if pol then "|" else "&"} ${rhs.show})" + case Neg(rhs) => doc"~(${rhs.show})" + case Region(name, body) => doc"region ${name.showName} in ${body.show}" + case RegRef(reg, value) => doc"(${reg.show}).ref ${value.show}" + case Assgn(lhs, rhs) => doc"${lhs.show} := ${rhs.show}" + case SetRef(ref, value) => doc"${ref.show} := ${value.show}" + case Drop(term) => doc"drop ${term.show}" + case Deref(ref) => doc"!(${ref.show})" + case Ret(result) => doc"return ${result.show}" + case Throw(result) => doc"throw ${result.show}" + case Label(label, _, body, _) => doc"do ${label.showName}: ${body.show}" + case Break(label, _, value) => doc"${label.showName}.break${value.fold(doc"")(v => doc" ${v.show}")}" + case Continue(label) => doc"${label.showName}.continue" + case Try(body, finallyDo) => doc"try ${body.show} finally ${finallyDo.show}" + case Annotated(annot, target) => doc"${annot.show} ${target.show}" + case Handle(lhs, rhs, args, _, defs, body) => + doc"handle ${lhs.showName} = ${rhs.show}${args.map(_.showAsParams).mkDocument()} with { #{ # ${ + defs.map(_.td.show).mkDocument(doc" # ")} #} # } in ${body.show}" + case td: TypeDef => + td.annotations.map(_.show :: " ").mkDocument() + :: doc"type ${td.bsym.showName}::${td.sym.showName}" + :: (if td.tparams.isEmpty then doc"" + else doc"[${td.tparams.map(_.sym.showName).mkDocument(", ")}]") + :: td.rhs.fold(doc"")(rhs => doc" = ${rhs.show}") + case SetConfig(_) => doc"#config(...)" + + this match + case t: Resolvable => t.expansion match + case S(S(exp)) => + val rhs = exp.show(using summon, summon[ShowCfg].copy(showExpansionMappings = false)) + if summon[ShowCfg].showExpansionMappings then + if exp === t then rhs + // ^ Some expansions only modify meta-data, such as types and symbols; + // we don't print them for conciseness + else res :: doc"{ ~> " :: rhs :: doc" }" + else exp.show + case _ => res + case _ => res + + end show + + def size: Int = this match + case Lit(Tree.StrLit(str)) => str.size / 4 + 1 + case _ => subTerms.iterator.map(_.size).sum + 1 + + def showDbg(using DebugPrinter): Str = this match + case r: Ref => r.sym.showAsPlain + case r: Resolved => + s"${r.showPlain}β€Ή${r.sym}β€Ί" + case trm: Term => + // s"$showPlainβ€Ή${trm.symbol.getOrElse("")}β€Ί" + s"$showPlain${trm.symbol.fold("")("β€Ή"+_+"β€Ί")}" + case _ => + showPlain + + def showAsParams(using Scope, ShowCfg, Raise): Document = this match + case tup: Tup => doc"(${tup.fields.map(_.show).mkDocument(", ")})" + case _ => doc"(...$show)" + + def showDbgAsParams(using DebugPrinter): Str = this match + case tup: Tup => s"(${tup.fields.map(_.showDbg).mkString(", ")})" + case _ => s"(...$showDbg)" + + def showPlain(using DebugPrinter): Str = this match + case Term.UnitVal() => "()" + case Term.DynTy() => "dyn" + case Lit(lit) => lit.idStr + case Resolved(t, sym) => t.showPlain + case r @ Ref(symbol) => symbol.showAsPlain + case r @ SimpleRef(symbol) => symbol.showAsPlain + case r @ MemberRef(symbol) => symbol.showAsPlain + case r @ SelfRef(sym) => sym.showAsPlain + case Capture(base, thru) => s"${base.showDbg}^${thru.showDbg}" + case App(lhs, rhs) => s"${lhs.showDbg}${rhs.showDbgAsParams}" + case RcdField(lhs, rhs, _) => s"${lhs.showDbg}: ${rhs.showDbg}" + case RcdSpread(bod) => s"...${bod.showDbg}" + case FunTy(lhs: Tup, rhs, eff) => + s"${lhs.fields.map(_.showDbg).mkString(", ")} ->${ + eff.map(e => s"{${e.showDbg}}").getOrElse("")} ${rhs.showDbg}" + case FunTy(lhs, rhs, eff) => + s"(...${lhs.showDbg}) ->${eff.map(e => s"{${e.showDbg}}").getOrElse("")} ${rhs.showDbg}" + case TyApp(lhs, targs) => s"${lhs.showDbg}[${targs.mkString(", ")}]" + case Forall(tvs, outer, body) => s"forall ${tvs.mkString(", ")}${outer.map(v => s", outer $v").mkString}: ${body.toString}" + case Constrained(constraints, body) => s"[${constraints.map(_.showDbg).mkString(", ")}] => ${body.showDbg}" + case WildcardTy(in, out) => s"in ${in.map(_.toString).getOrElse("βŠ₯")} out ${out.map(_.toString).getOrElse("⊀")}" + case Sel(pre, nme) => s"${pre.showDbg}.${nme.name}" + case SynthSel(pre, nme) => s"(${pre.showDbg}.)${nme.name}" + case Super(_, owner, id) => s"super[${owner.showDbg}].${id.name}" + case NewSel(pre, nme, cls) => s"${pre.showDbg}.${cls.fold("")(c => s"${c.showDbg}#")}${nme.name}" + case UnresolvedRef(_, id) => s"${id.name}β€Ήopenβ€Ί" + case DynSel(pre, fld, _, _) => s"${pre.showDbg}[${fld.showDbg}]" + case IfLike(kw, _, split) => s"${kw.name} { ${split.showDbg} }" + case SynthIf(split) => s"if { ${split.showDbg} }" + case SynthWhile(split) => s"while { ${split.showDbg} }" + case Lam(params, body) => s"Ξ»${params.showDbg}. ${body.showDbg}" + case Blk(stats, res) => + (stats.map(_.showDbg + "; ") :+ (res match { case Lit(Tree.UnitLit(false)) => "" case x => x.showDbg + " " })) + .mkString("{ ", "", "}") + case Rcd(mut, stats) => + (if mut then "mut " else "") + stats.map(_.showDbg + "; ").mkString("{ ", "", "}") + case Quoted(term) => s"""code"${term.showDbg}"""" + case Unquoted(term) => s"$${${term.showDbg}}" + case New(cls, args, rft) => + s"new ${cls.showDbg}${args.map(_.showDbgAsParams).mkString}${rft.fold("")(r => s"{ ${r._2.blk.showDbg} }")}" + case DynNew(cls, args) => + s"new! ${cls.showDbg}${args.map(_.showDbgAsParams).mkString}" + case SelProj(pre, cls, proj) => s"${pre.showDbg}.${cls.showDbg}#${proj.name}" + case Asc(term, ty) => s"${term.toString}: ${ty.toString}" + case LetDecl(sym, _) => s"let ${sym}" + case DefineVar(sym, rhs) => s"${sym} = ${rhs.showDbg}" + case Handle(lhs, rhs, args, derivedClsSym, defs, bod) => + s"handle ${lhs} = ${rhs}(${args.mkString(", ")}) ${defs} in ${bod}" + case Region(name, body) => s"region ${name.nme} in ${body.showDbg}" + case RegRef(reg, value) => s"(${reg.showDbg}).ref ${value.showDbg}" + case Assgn(lhs, rhs) => s"${lhs.showDbg} := ${rhs.showDbg}" + case SetRef(lhs, rhs) => s"${lhs.showDbg} := ${rhs.showDbg}" + case Drop(term) => s"drop $term" + case Deref(term) => s"!$term" + case Neg(ty) => s"~${ty.showDbg}" + case CompType(lhs, rhs, pol) => s"${lhs.showDbg} ${if pol then "|" else "&"} ${rhs.showDbg}" + case Error() => "" + case Tup(fields) => fields.map(_.showDbg).mkString("[", ", ", "]") + case Mut(und) => s"mut ${und.showDbg}" + case CtxTup(fields) => fields.map(_.showDbg).mkString("β€Ήusingβ€Ί[", ", ", "]") + case TermDefinition(k, sym, tsym, pss, tps, sign, body, flags, _, _, _) => + s"${flags.showDbg}${k.str} ${sym}${ + tps.map(_.map(_.showDbg)).mkStringOr(", ", "[", "]") + }${ + pss.map(_.showDbg).mkString("") + }${sign.fold("")(": "+_.showDbg)}${ + body match + case S(x) => " = " + x.showDbg + case N => "" + }" + case cls: ClassLikeDef => + s"${cls.kind} ${cls.sym.nme}${ + cls.tparams.map(_.showDbg).mkStringOr(", ", "[", "]")}${ + cls.paramsOpt.fold("")(_.toString)} ${cls.body}" + case Import(sym, str, file) => s"import $str from ${file}" + case Annotated(ann, target) => s"@${ann} ${target.showDbg}" + case Throw(res) => s"throw ${res.showDbg}" + case Label(label, _, body, _) => s"do ${label.nme}: ${body.showDbg}" + case Break(label, _, value) => s"${label.nme}.break${value.fold("")(v => s" ${v.showDbg}")}" + case Continue(label) => s"${label.nme}.continue" + case Try(body, finallyDo) => s"try ${body.showDbg} finally ${finallyDo.showDbg}" + case Ret(res) => s"return ${res.showDbg}" + case TypeDef(sym, _, tparams, rhs, _, _) => + s"type ${sym}${tparams.mkStringOr(", ", "[", "]")} = ${rhs.fold("")(x => x.showDbg)}" + case Missing => "missing" + case LeadingDotSel(nme) => s"_?_.${nme.name}" + case SetConfig(_) => "#config(...)" + +end StatementImpl + + diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/TypeFormula.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/TypeFormula.scala new file mode 100644 index 0000000000..6827c806ce --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/TypeFormula.scala @@ -0,0 +1,29 @@ +package hkmc2 +package semantics + +/** A union of intersections, with absorption. This is the free distributive + * lattice over A: no nominal subtyping, disjointness, or inference bounds are + * consulted. In particular, an atom can be a still-live inference reference. + * For a finite set of atoms there are finitely many formulas, even when recursive + * substitutions alternate union and intersection arbitrarily often. + */ +final case class TypeFormula[A] private (clauses: Set[Set[A]])(val orderedAtoms: Vector[A]): + // The second parameter list is not part of case-class equality. Preserve the + // first source order for observation and diagnostics without distinguishing + // equal formulas merely because their operands were written in another order. + def union(that: TypeFormula[A]): TypeFormula[A] = TypeFormula.normalized(clauses ++ that.clauses, orderedAtoms ++ that.orderedAtoms) + def intersection(that: TypeFormula[A]): TypeFormula[A] = + TypeFormula.normalized(for left <- clauses; right <- that.clauses yield left ++ right, orderedAtoms ++ that.orderedAtoms) + def map[B](f: A => B): TypeFormula[B] = + val mapped = orderedAtoms.map(atom => atom -> f(atom)).toMap + TypeFormula.normalized(clauses.map(_.map(mapped)), orderedAtoms.map(mapped)) + def atoms: Set[A] = clauses.flatten + +object TypeFormula: + def atom[A](value: A): TypeFormula[A] = TypeFormula(Set(Set(value)))(Vector(value)) + def top[A]: TypeFormula[A] = TypeFormula(Set(Set.empty))(Vector.empty) + def bottom[A]: TypeFormula[A] = TypeFormula(Set.empty)(Vector.empty) + private def normalized[A](clauses: Set[Set[A]], order: Vector[A]): TypeFormula[A] = + val result = clauses.filterNot(clause => clauses.exists(other => other != clause && other.subsetOf(clause))) + val atoms = result.flatten + TypeFormula(result)(order.distinct.filter(atoms)) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/TypeShape.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/TypeShape.scala new file mode 100644 index 0000000000..d03e02711f --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/TypeShape.scala @@ -0,0 +1,171 @@ +package hkmc2 +package semantics + +import hkmc2.utils.*, shorthands.* +import codegen.Erasure + +/** A type interpretation, separate from the value shapes of the same reference syntax. + * Compound types retain links to their operands so forward aliases need not be expanded eagerly. + */ +enum TypeShape: + case Nominal(defn: ClassLikeDef) + case Alias(symbol: TypeAliasSymbol, rhs: Opt[TypeResolution]) + case Union(left: TypeResolution, right: TypeResolution) + case Intersection(left: TypeResolution, right: TypeResolution) + // Negated interfaces remain opaque, but their dependencies must participate + // in source validation, including the rejection of unguarded alias cycles. + case Negation(base: TypeResolution) + // Substituted unions/intersections retain whole interpreted endpoints. Keeping + // their lattice normal form prevents repeated Boolean substitutions from + // nesting environments, without expanding bounds or changing endpoint marks. + case Combined(formula: TypeFormula[DeclaredType]) + case Tuple(fields: Ls[TypeResolution]) + case Record(source: Term.Rcd, fields: Ls[(RcdField, TypeResolution)]) + case Function(params: Term, result: TypeResolution) + case Polymorphic(params: Ls[TypeQuantifier], outer: Opt[VarSymbol], body: TypeResolution) + case Applied(base: TypeResolution, args: Ls[TypeResolution]) + case Wildcard(input: Opt[TypeResolution], output: Opt[TypeResolution]) + // Interpreted arguments retain both polarities in the argument's lexical environment. + case Argument(parts: TypeArgument) + // Substitution selects one part before the type is used in further constraints. + // The argument stays in its own lexical environment, including delayed parts. + case SelectedArgument(argument: DeclaredType, positive: Bool) + // A transported reference retains its context before any deferred component + // is observed. The resolver flattens these nodes and normalizes their marks. + case Contextual(reference: ContextualType) + // The same third-party symbol can have different inference in two exporters. + // Retain its originating host so importing a result needs no whole-state copy. + case Parameter(symbol: VarSymbol, host: Publisher.Data[ShapeEvent]) + // An omitted argument owns one source inference node. It is not a quantified + // binder and is never instantiated at a call site; marks distinguish its flows. + case Hole(host: Publisher.Data[TermShape]) + // Synthesized generic arguments retain inferred value shapes, for example + // the element union of the Array supertype of a tuple. Singleton annotations + // also retain their literal shape; no implementation is inspected. + case Inferred(value: TermShape) + // An open type expression reached through a reference. Unlike Contextual, + // its free binders still receive the observing declaration's substitution. + // Qualified references retain the receiver's bindings and lexical path too. + case Reference(base: TypeResolution, marks: Ls[Marks], bindings: Map[VarSymbol, DeclaredType], instances: TypeSubstitution) + case Unit + case Dynamic + case Abstract + case Top + case Bottom + +/** Candidates are published during elaboration. Erasure reads the resulting graph; it + * never performs lookup or resolves an alias by inspecting an elaborated definition. + */ +final class TypeResolution(val source: Term, report: Ls[(Message, Opt[Loc])] => Unit) extends Host[TypeShape]: + private var reported = false + def hasErrors: Bool = reported + def showDbg(using DebugPrinter): Str = s"type of ${source.showDbg}" + def publish(shape: TypeShape)(using NewResolverState): Unit = + if currentShapes.add(shape) then notifyShapeListeners(shape) + def listen(listener: ShapeListener[TypeShape])(using NewResolverState): Unit = + subscribeToShapes(listener) + def fail(messages: Ls[(Message, Opt[Loc])]): Unit = if !reported then + reported = true + report(messages) + def validate(seen: Set[TypeResolution])(using Erasure): Unit = if !seen(this) then + import TypeShape.* + import Message.MessageContext + val next = seen + this + val ambiguousReceiver = source.withoutCaptures match + case ref: Term.UnresolvedRef => ref.resolvedMembers.distinct.sizeCompare(1) > 0 + case sel: Term.NewSel => sel.hasAmbiguousClass + case _ => false + if shapes.isEmpty then fail(msg"This type has no resolved target" -> source.toLoc :: Nil) + else if ambiguousReceiver || shapes.sizeCompare(1) > 0 then + def declarations(resolution: TypeResolution, seen: Set[TypeResolution]): Ls[(Message, Opt[Loc])] = + if seen(resolution) then Nil else resolution.shapes.toList.flatMap: + case Nominal(defn) => msg"candidate: ${defn.sym.describeKind}" -> defn.toLoc :: Nil + case Alias(symbol, _) => msg"candidate: ${symbol.describeKind}" -> symbol.toLoc :: Nil + case Reference(base, _, _, _) => declarations(base, seen + resolution) + case _ => Nil + val candidates = source.withoutCaptures match + case ref: Term.UnresolvedRef => ref.resolvedMembers.distinct.map: (prefix, member) => + msg"candidate: ${member.describe}" -> member.toLoc + case _ => declarations(this, Set.empty).distinct + fail(msg"This type is ambiguous, as it has multiple resolved targets" -> source.toLoc :: candidates) + shapes.foreach: + case Alias(_, rhs) => rhs.foreach(_.validate(next)) + case Reference(base, _, _, _) => base.validate(next) + case Applied(base, args) => + base.validate(next) + args.foreach(_.validate(next)) + def checkArity(base: TypeResolution, seen: Set[TypeResolution]): Unit = if !seen(base) then + def check(name: Str, count: Int, loc: Opt[Loc]): Unit = if args.length > count then + fail(msg"Type '$name' accepts at most $count type ${"argument".pluralized(count)}, but got ${args.length}." -> + source.toLoc :: (msg"The type parameters are declared here." -> loc) :: Nil) + base.shapes.toList match + case Nominal(defn) :: Nil => check(defn.sym.nme, defn.tparams.length, defn.toLoc) + case Alias(symbol, _) :: Nil => symbol.defn.foreach(d => check(symbol.nme, d.tparams.length, d.toLoc)) + case Reference(inner, _, _, _) :: Nil => checkArity(inner, seen + base) + case _ => () + checkArity(base, Set.empty) + case Wildcard(input, output) => input.foreach(_.validate(next)); output.foreach(_.validate(next)) + case Argument(parts) => parts.input.resolution.validate(next); parts.output.resolution.validate(next) + case SelectedArgument(argument, _) => argument.resolution.validate(next) + case Contextual(reference) => reference.tpe.resolution.validate(next) + case Function(_, result) => result.validate(next) + case Polymorphic(params, _, body) => + params.foreach: param => + param.lower.foreach(_.validate(next)) + param.upper.foreach(_.validate(next)) + body.validate(next) + case Tuple(fields) => fields.foreach(_.validate(next)) + case Record(_, fields) => fields.foreach(_._2.validate(next)) + case Union(left, right) => left.validate(next); right.validate(next) + case Intersection(left, right) => left.validate(next); right.validate(next) + case Negation(base) => base.validate(next) + case Combined(formula) => formula.orderedAtoms.foreach(_.resolution.validate(next)) + case _ => () + +/** A type with its lexical type-parameter bindings. These bindings describe declared + * interfaces, not constructor arguments or value-flow capture paths. + * `positive` interprets substitutions in the source expression; it is not the + * direction of a constraint subsequently applied to the interpreted type. + */ +final case class DeclaredType(resolution: TypeResolution, bindings: Map[VarSymbol, DeclaredType], + instances: TypeSubstitution, positive: Bool)(val origin: Opt[TypeInterfaceReason]): + /** Diagnostic evidence is not part of type equality or cache keys. Retain one + * witness when normalizing/substituting the type; do not accumulate paths. + * A use's witness takes precedence over evidence in a shared cached reference. + */ + def withOrigin(witness: Opt[TypeInterfaceReason]): DeclaredType = + copy()(witness.orElse(origin)) + + /** Instantiation changes references to source binders, never expands their bounds. + * A reference already interpreted in another call retains that interpretation. + * The finite map contains original binders and canonical site instances only. + */ + def instantiate(substitution: TypeSubstitution): DeclaredType = + copy(instances = substitution.withOverrides(instances))(origin) + +/** A type reference observed from a common comparison scope. The marks belong + * to this endpoint: reversing a constraint swaps endpoints, not an expanded + * candidate's already-normalized path. + */ +final case class ContextualType(tpe: DeclaredType, marks: Ls[Marks]) + +/** An unqualified argument is interpreted separately in the two polarities; + * these parts coincide only when substitution gives the same type in both. + * Missing wildcard parts are Nothing on input and Any on output, never holes. + */ +final case class TypeArgument(input: DeclaredType, output: DeclaredType) + +/** A quantified binder belongs to the source scheme; its bounds retain graph links + * so mutually dependent bounds do not require expanding or copying their types. + */ +final case class TypeQuantifier(parameter: TypeShape.Parameter, + lower: Opt[TypeResolution], upper: Opt[TypeResolution]) + +/** A callable's binder and bounds interpreted in the surrounding lexical bindings. */ +final case class DeclaredTypeParameter(parameter: TypeShape.Parameter, + lower: Opt[DeclaredType], upper: Opt[DeclaredType]) + +/** The owner is the original declaration, or a quantified annotation for an + * anonymous callable. Applied and imported views retain this identity. + */ +final case class TypeScheme(owner: AnyDefinitionSymbol | TypeResolution, parameters: Ls[DeclaredTypeParameter]) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/TypeSubstitution.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/TypeSubstitution.scala new file mode 100644 index 0000000000..45a2f7a357 --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/TypeSubstitution.scala @@ -0,0 +1,21 @@ +package hkmc2 +package semantics + +import hkmc2.utils.*, shorthands.* + +/** A flat substitution from source binders to their canonical call-site instances. + * Keys are derived from instance origins; callers cannot choose a different key. + * Composition preserves existing instance identities, never instantiates an + * instance, and uses the right operand when both views interpret the same binder. + * The opaque map retains structural equality for inference-cache keys. + */ +opaque type TypeSubstitution <: Map[VarSymbol, TypeParameterInstance] = Map[VarSymbol, TypeParameterInstance] + +object TypeSubstitution: + val empty: TypeSubstitution = Map.empty + def apply(instances: IterableOnce[TypeParameterInstance]): TypeSubstitution = + instances.iterator.map(instance => instance.origin -> instance).toMap + + extension (self: TypeSubstitution) + def withOverrides(that: TypeSubstitution): TypeSubstitution = self ++ that + def without(parameters: IterableOnce[VarSymbol]): TypeSubstitution = self -- parameters diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala index 10d4c0db64..e24d6fdd38 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala @@ -15,6 +15,7 @@ import syntax.Tree import Elaborator.{State, Ctx, ctx} import Producer as P import Consumer as C +import hkmc2.semantics.ClassSymbol @@ -27,9 +28,33 @@ type ProdCtor = Producer.Ctor | Producer.Fun | Producer.Typ | Producer.Tup | Pro case class ConcreteProd(path: Path, ctor: ProdCtor) +enum AppTarget: + case ObjectMember(sym: ClassSymbol) + case Err(err: ErrorReport) + enum SelectionTarget: case ObjectMember(sym: MemberSymbol) case CompanionMember(comp: Term, sym: MemberSymbol) + case Err(err: ErrorReport) + + def describe: Str = this match + case ObjectMember(sym) => s"member ${sym.nme}" // TODO: more info (owner) + case CompanionMember(_, sym) => s"companion member ${sym.nme}" + case Err(err) => s"erroneous selection (${err.mainMsg})" + + def loc: Opt[Loc] = this match + case ObjectMember(sym) => sym.toLoc + case CompanionMember(_, sym) => sym.toLoc + case Err(err) => N + + import hkmc2.document.* + import hkmc2.document.Document.* + def show(using Scope, ShowCfg, Raise): Document = + this match + case ObjectMember(sym) => sym.showName + case CompanionMember(comp, sym) => doc"${comp.show}.${sym.showName}" + case Err(err) => err.mainMsg +end SelectionTarget /** This is a very sketchy exploration/proof of concept of flow analysis @@ -157,7 +182,7 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx): case sel @ AnySel(pre, nme, cls) => log(s"Selection ${sel.showDbg} ${sel.typ}") checkLDS(pre): pre_t => - sel.resolvedSym match + sel.legacyResolvedSym match case S(sym: BlockMemberSymbol) => log(s"RES ${sym.nme} in ${sel.showDbg}") getFlowSymOrType(sym) @@ -174,7 +199,7 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx): case nw @ New(cls, args, rft) => rft match case N => - cls.resolvedSym.flatMap(_.asCls) match + cls.legacyResolvedSym.flatMap(_.asCls) match case N => log(s"Unresolved or invalid class symbol in ${cls.showDbg}") P.Unknown(nw) @@ -239,6 +264,7 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx): def typeType(t: Term): Type = trace[Type](s"Typing consumer: ${t.showDbg}", post = res => s": ${res.showDbg}"): t match + case Capture(base, _) => typeType(base) case Ref(sym: VarSymbol) => Type.Ref(sym, Nil) // unparameterized type variable case Ref(cls: ClassSymbol) => Type.Ref(cls, Nil) case Ref(ts: BlockMemberSymbol) => @@ -283,6 +309,7 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx): :: targets.map: case ObjectMember(sym) => msg"object member ${sym.nme}" -> sym.toLoc case CompanionMember(_, sym) => msg"companion member ${sym.nme}" -> sym.toLoc + case target: SelectionTarget.Err => msg"${target.describe}" -> target.loc leadingDotSelsToExpand.foreach: sel => log(s"Resolved targets for ${sel.showDbg}: ${sel.resolvedTargets.mkString(", ")}") assert(sel.expansion.isEmpty) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/FlatPattern.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/FlatPattern.scala index db1baaa295..9d632df25a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/FlatPattern.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/FlatPattern.scala @@ -37,7 +37,7 @@ enum FlatPattern extends AutoLocated: case Record(entries: List[(Ident -> LocalVarSymbol)]) - def mkClone(using State): FlatPattern = this match + def mkClone(using State, codegen.Erasure): FlatPattern = this match case Lit(literal) => Lit(literal) case pattern @ ClassLike(constructor, symbol, arguments, refined) => ClassLike(constructor.mkClone, symbol, arguments, refined)(Tree.Dummy) @@ -57,6 +57,17 @@ enum FlatPattern extends AutoLocated: case (nme, als) => Vector(nme, als) .toVector + def show(using utils.Scope, ShowCfg, Raise): document.Document = + import document.*, Document.* + this match + case Lit(literal) => doc"${literal.idStr}" + case ClassLike(ctor, _, args, refined) => + (if refined then doc"refined " else doc"") :: ctor.show :: + args.fold(doc"")(as => doc"(${as.map(_._1.showName).mkDocument(", ")})") + case Tuple(size, inf) => doc"[]${if inf then ">=" else "="}$size" + case Record(entries) => + doc"{${entries.map((id, sym) => doc"${id.name}: ${sym.showName}").mkDocument(", ")}}" + def showDbg(using DebugPrinter): Str = (this match case Lit(literal) => literal.idStr diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/Normalization.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/Normalization.scala index 40c6223142..3ba594fddd 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/Normalization.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/Normalization.scala @@ -6,10 +6,11 @@ import hkmc2.utils.*, shorthands.* import syntax.{Literal, Tree, Keyword}, utils.* import Message.MessageContext import Elaborator.{Ctx, State, ctx} -import codegen.Lowering +import codegen.{Lowering, Erasure} -class Normalization(lowering: Lowering)(using tl: TL)(using Raise, Ctx, State, Config) extends TermSynthesizer: +class Normalization(lowering: Lowering)(using tl: TL)(using Raise, Ctx, State, Config, Erasure) extends TermSynthesizer: + import Normalization.*, Mode.* import tl.* @@ -639,14 +640,6 @@ object Normalization: !compareCasePattern(lhs, rhs) && !compareCasePattern(rhs, lhs) case _ => false - /** Get the parent class-like symbol from the extends clause of a class or module. */ - private def getParentClassLikeSymbol(sym: ClassSymbol | ModuleOrObjectSymbol) - : Opt[ClassSymbol | ModuleOrObjectSymbol] = - val ext: Opt[Term.New] = sym match - case cls: ClassSymbol => cls.defn.flatMap(_.ext) - case mod: ModuleOrObjectSymbol => mod.defn.flatMap(_.ext) - ext.flatMap(nw => nw.cls.resolvedSym.flatMap(_.asClsOrMod)) - /** Check if `child` is a subclass of `parent` by traversing the class hierarchy. * Uses a visited set to avoid infinite loops in case of cyclic inheritance. * TODO: Cache the subclasses set!! */ @@ -656,7 +649,7 @@ object Normalization: ): Bool = def go(sym: ClassSymbol | ModuleOrObjectSymbol, visited: Set[ClassSymbol | ModuleOrObjectSymbol]): Bool = - !visited.contains(sym) && (getParentClassLikeSymbol(sym) match + !visited.contains(sym) && (sym.irClassHeader.flatMap(_.parent).flatMap(_.asClsOrMod) match case S(parentSym) => (parentSym is parent) || go(parentSym, visited + sym) case N => false) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/TermSynthesizer.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/TermSynthesizer.scala index 3484de161d..6540a74c59 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/TermSynthesizer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/TermSynthesizer.scala @@ -27,7 +27,7 @@ trait TermSynthesizer(using State): protected final def app(l: Term, r: Term, s: FlowSymbol): Term.App = (Term.App(l, r)(App(Dummy, Dummy), N, s): Term.App).resolve protected final def `new`(cls: Term, args: Ls[Term], label: Str): Term.New = - Term.New(cls, args, N)(N) + Term.New(cls, args, N)(FlowSymbol.neww(), N) protected final def rcd(fields: RcdField*): Term.Rcd = Term.Rcd(false, fields.toList) protected final def splitLet(sym: LocalVarSymbol, term: Term)(inner: Split): Split = diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Compiler.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Compiler.scala index 8aecbdfbcb..dc812615a9 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Compiler.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Compiler.scala @@ -18,7 +18,7 @@ import collection.mutable.{Queue, Map as MutMap, LinkedHashMap}, collection.immu * a few matcher functions. Each matcher function matches a set of patterns * and returns a record that contains the results of each pattern. */ -class Compiler(using Context)(using tl: TL)(using Ctx, State, Raise) extends TermSynthesizer: +class Compiler(using Context)(using codegen.Erasure)(using tl: TL)(using Ctx, State, Raise) extends TermSynthesizer: import Compiler.*, tl.* import Pattern.* @@ -573,7 +573,7 @@ object Compiler: /** Clone a source-level constructor reference for insertion into synthesized * matcher code, preserving the path chosen by source resolution. */ - def preservedReference(term: Term)(using State): Term = + def preservedReference(term: Term)(using State, codegen.Erasure): Term = /** To make `Lowering` happy about the terms. */ def fillImplicitArgs(term: Term): Term = term match case ref: Ref => diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/FixedPointCompiler.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/FixedPointCompiler.scala index 1437171f44..a6fd31f653 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/FixedPointCompiler.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/FixedPointCompiler.scala @@ -154,7 +154,7 @@ object FixedPointCompiler: * machine compilation does not support get a warning and fall back to the * naive backtracking translation. */ -class FixedPointCompiler(using tl: TL)(using State, Ctx, Raise) extends TermSynthesizer: +class FixedPointCompiler(using codegen.Erasure)(using tl: TL)(using State, Ctx, Raise) extends TermSynthesizer: import FixedPointCompiler.*, tl.* /** Try to compile the given `@compile`-annotated pattern into a fixed-point diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Instantiator.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Instantiator.scala index 750c89553f..e757cb8695 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Instantiator.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Instantiator.scala @@ -14,7 +14,7 @@ import scala.collection.immutable.SeqMap object Instantiator: type Subst = Map[VarSymbol, Pat] -class Instantiator(using tl: TL)(using Ctx, State, Raise): +class Instantiator(using codegen.Erasure)(using tl: TL)(using Ctx, State, Raise): import tl.* /** The map contains all instantiated patterns. The key is a pattern symbol diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/SplitCompiler.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/SplitCompiler.scala index 6b44c7b977..5fecd56947 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/SplitCompiler.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/SplitCompiler.scala @@ -119,9 +119,11 @@ object SplitCompiler: import SplitCompiler.* /** This class compiles a pattern to a split that matches the pattern. */ -class SplitCompiler(using tl: TL)(using State, Ctx, Raise) extends TermSynthesizer: +class SplitCompiler(using codegen.Erasure)(using tl: TL)(using State, Ctx, Raise, Config) extends TermSynthesizer: import tl.*, SP.* + val newResolution: Bool = config.language.useNewResolution + private lazy val lteq = State.builtinOpsMap("<=") private lazy val lt = State.builtinOpsMap("<") private lazy val add = State.builtinOpsMap("+") @@ -600,6 +602,27 @@ class SplitCompiler(using tl: TL)(using State, Ctx, Raise) extends TermSynthesiz // Here we go! wrapPatternArgumentDefinitions(wrapUnapply(wrapDestruction(wrapExtractionArguments(consequent)))) + /** Validate recorded constructor candidates for both full and prefix matching. */ + private def resolvedConstructor(ctor: Constructor): Opt[Symbol] = + val target = ctor.target.withoutCaptures + val ambiguousReceiver = target match + case ref: Term.UnresolvedRef => ref.resolvedMembers.distinct.sizeCompare(1) > 0 + case _ => false + val erroneousTarget = target match + case ref: NewResolvable => ref.isErroneous + case Term.Error() => true + case _ => false + val targets = ctor.resolvedTargets.distinct + if ctor.isErroneous || erroneousTarget then N + else if ambiguousReceiver || targets.sizeCompare(1) > 0 then + error(msg"Constructor pattern is ambiguous." -> ctor.toLoc) + N + else targets match + case sym :: Nil => S(sym) + case _ => + error(msg"Cannot use this ${target.describe} as a pattern." -> ctor.toLoc) + N + /** Report errors when pattern parameters are used incorrectly. */ private def validatePatternParameter[A]( parameterTerm: Term, @@ -649,6 +672,17 @@ class SplitCompiler(using tl: TL)(using State, Ctx, Raise) extends TermSynthesiz */ def makeMatchSplit(scrutinee: Scrut, pattern: SP, outputNeeded: Bool): MakeSplit = pattern match + case ctor @ Constructor(target, arguments) if newResolution => + resolvedConstructor(ctor) match + case S(classSymbol: ClassSymbol) => + makeMatchClassSplit(scrutinee, target, classSymbol, arguments, outputNeeded) + case S(objectSymbol: ModuleOrObjectSymbol) => + makeMatchObjectSplit(pattern.toLoc, scrutinee, target, objectSymbol, arguments) + case S(patternSymbol: PatternSymbol) => + makeMatchPatternSplit(scrutinee, target, patternSymbol, arguments) + case S(symbol: VarSymbol) => + makeMatchPatternParameterSplit(scrutinee, target, symbol, arguments, pattern.toLoc) + case _ => RejectSplit case Constructor(target, arguments) => target.resolvedSym match case S(symbol: VarSymbol) => makeMatchPatternParameterSplit(scrutinee, target, symbol, arguments, pattern.toLoc) @@ -689,11 +723,21 @@ class SplitCompiler(using tl: TL)(using State, Ctx, Raise) extends TermSynthesiz // to be a function that takes a diagnostic information generation // function. val outputSymbol = new LazyScrut() - makeMatchSplit(scrutinee, pattern, false)( - (_output, _bindings) => alternative, // The output and bindings are discarded. - // The place where the diagnostic information should be stored. - outputSymbol.toLet(scrutinee(), makeConsequent(outputSymbol, SeqMap.empty) ~~: alternative) - ) + val success = outputSymbol.toLet(scrutinee(), makeConsequent(outputSymbol, SeqMap.empty) ~~: alternative) + if alternative.isFull then + makeMatchSplit(scrutinee, pattern, false)( + (_output, _bindings) => alternative, // The output and bindings are discarded. + success) + else + // An unfinished split means fallthrough, not a committed rejection. + // Swapping it into the inner pattern's consequent would let a match + // fall through to `success` (notably when a while loop should exit). + // Complete both inner outcomes before branching on the match result. + val test = Term.SynthIf(makeMatchSplit(scrutinee, pattern, false)( + (_output, _bindings) => Split.Else(Term.Lit(Tree.BoolLit(true))), + Split.Else(Term.Lit(Tree.BoolLit(false))))) + tempLet("negated", test): result => + Branch(result.safeRef, FlatPattern.Lit(Tree.BoolLit(false)), success) ~: alternative // Note that we might duplicate the alternative split here. case Wildcard() => (makeConsequent, alternative) => makeConsequent(scrutinee, SeqMap.empty) ~~: alternative case Literal(literal) => (makeConsequent, alternative) => @@ -916,42 +960,43 @@ class SplitCompiler(using tl: TL)(using State, Ctx, Raise) extends TermSynthesiz scrutinee: Scrut, pattern: SP, )(using Raise): MakePrefixSplit = pattern match - case Constructor(target, arguments) => target.resolvedSym match - // The case when the target refers to a pattern parameter. - case S(symbol: VarSymbol) => - makeMatchPrefixPatternParameterSplit(scrutinee, target, symbol, arguments, pattern.toLoc) - case symbolOption => symbolOption.flatMap(_.asClsLike) match - case S(symbol: PatternSymbol) => - makeMatchPrefixPatternSplit(scrutinee, target, symbol, arguments) - // We accept the string class as a valid string pattern as it literally - // means all strings. - case S(symbol: ClassSymbol) if symbol is ctx.builtins.Str => - arguments match - case S(args) if args.nonEmpty => - error( - msg"`${symbol.name}` does not take any arguments." -> target.toLoc, - msg"But the pattern has ${"sub-pattern" countBy args.size}." -> Loc(args) - ) - RejectPrefixSplit - case _ => (makeConsequent, alternative) => - val nonEmptySymbol = TempSymbol(N, erasedType = S(ErasedType.Bool), "nonEmpty") - val nonEmptyTerm = app( - this.lt.safeRef, - tup(fld(int(0)), fld(sel(scrutinee(), "length"))), - "string is not empty" - ) - val outputSymbol = TempSymbol(N, erasedType = S(ErasedType.Str), "stringHead") - val outputTerm = callStringGet(scrutinee(), 0, "head") - val remainsSymbol = TempSymbol(N, erasedType = S(ErasedType.Str), "stringTail") - val remainsTerm = callStringDrop(scrutinee(), 1, "tail") - Split.Let(nonEmptySymbol, nonEmptyTerm, - Branch(nonEmptySymbol.safeRef, - Split.Let(outputSymbol, outputTerm, - Split.Let(remainsSymbol, remainsTerm, - makeConsequent(outputSymbol.toScrut, remainsSymbol.toScrut, SeqMap.empty))) - ) ~: alternative) - case S(_: ModuleOrObjectSymbol) | S(_: ClassSymbol) | N => - RejectPrefixSplit + case ctor @ Constructor(target, arguments) => + (if newResolution then resolvedConstructor(ctor) else target.resolvedSym) match + // The case when the target refers to a pattern parameter. + case S(symbol: VarSymbol) => + makeMatchPrefixPatternParameterSplit(scrutinee, target, symbol, arguments, pattern.toLoc) + case symbolOption => symbolOption.flatMap(_.asClsLike) match + case S(symbol: PatternSymbol) => + makeMatchPrefixPatternSplit(scrutinee, target, symbol, arguments) + // We accept the string class as a valid string pattern as it literally + // means all strings. + case S(symbol: ClassSymbol) if symbol is ctx.builtins.Str => + arguments match + case S(args) if args.nonEmpty => + error( + msg"`${symbol.name}` does not take any arguments." -> target.toLoc, + msg"But the pattern has ${"sub-pattern" countBy args.size}." -> Loc(args) + ) + RejectPrefixSplit + case _ => (makeConsequent, alternative) => + val nonEmptySymbol = TempSymbol(N, erasedType = S(ErasedType.Bool), "nonEmpty") + val nonEmptyTerm = app( + this.lt.safeRef, + tup(fld(int(0)), fld(sel(scrutinee(), "length"))), + "string is not empty" + ) + val outputSymbol = TempSymbol(N, erasedType = S(ErasedType.Str), "stringHead") + val outputTerm = callStringGet(scrutinee(), 0, "head") + val remainsSymbol = TempSymbol(N, erasedType = S(ErasedType.Str), "stringTail") + val remainsTerm = callStringDrop(scrutinee(), 1, "tail") + Split.Let(nonEmptySymbol, nonEmptyTerm, + Branch(nonEmptySymbol.safeRef, + Split.Let(outputSymbol, outputTerm, + Split.Let(remainsSymbol, remainsTerm, + makeConsequent(outputSymbol.toScrut, remainsSymbol.toScrut, SeqMap.empty))) + ) ~: alternative) + case S(_: ModuleOrObjectSymbol) | S(_: ClassSymbol) | N => + RejectPrefixSplit case Composition(true, left, right) => val makeLeft = makeStringPrefixMatchSplit(scrutinee, left) val makeRight = makeStringPrefixMatchSplit(scrutinee, right) diff --git a/hkmc2/shared/src/main/scala/hkmc2/syntax/Keyword.scala b/hkmc2/shared/src/main/scala/hkmc2/syntax/Keyword.scala index 2c9c7f5038..b22cbaa0a7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/syntax/Keyword.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/syntax/Keyword.scala @@ -152,6 +152,7 @@ object Keyword: val `forall` = Keyword("forall", N, N) val `exists` = Keyword("exists", N, N) val `null` = Keyword("null", N, N) + val `dyn` = Keyword("dyn", N, N) val `undefined` = Keyword("undefined", N, N) val `abstract` = Keyword("abstract", N, N) val `constructor` = Keyword("constructor", N, N) @@ -224,5 +225,5 @@ object Keyword: type LetLike = `let`.type | `set`.type - type Modifier = `in`.type | `out`.type | `mut`.type | `abstract`.type | `declare`.type | `data`.type | `virtual`.type | `override`.type | + type Modifier = `in`.type | `out`.type | `mut`.type | `abstract`.type | `declare`.type | `data`.type | `virtual`.type | `override`.type | `open`.type | `public`.type | `private`.type | `staged`.type diff --git a/hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala b/hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala index aa459826ec..049feabe8e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala @@ -415,9 +415,11 @@ class ParseRules(using State): modified(`staged`), singleKw(`true`)(BoolLit(true)), singleKw(`false`)(BoolLit(false)), + singleKw(`dyn`)(Keywrd(`dyn`)), singleKw(`undefined`)(UnitLit(false)), singleKw(`null`)(UnitLit(true)), singleKw(`this`)(Ident("this")), + singleKw(`super`)(Ident("super")), singleKw(Keyword.__)(Under()), Kw(`#`)( ParseRule(s"'#' directive keyword")( diff --git a/hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala b/hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala index 8d06bd6187..8ce9358742 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala @@ -158,30 +158,37 @@ abstract class Parser( protected var indent = 0 private var _cur: Ls[TokLoc] = preprocessTokens(tokens) - private def preprocessTokens(tokens: Ls[TokLoc]): Ls[TokLoc] = tokens match - case (IDENT("new", false), l1) :: (IDENT("!", true), l2) :: rest => - (IDENT("new!", false), l1 ++ l2) :: preprocessTokens(rest) - case (IDENT("yield", false), l1) :: (IDENT("*", true), l2) :: rest => - (IDENT("yield*", false), l1 ++ l2) :: preprocessTokens(rest) - // * Remove empty indented sections - case (BRACKETS(Indent, toks), _) :: rest - if toks.forall: - case (NEWLINE | SPACE, _) => true - case _ => false - => - preprocessTokens(rest) - // * Expands end-of-line suspensions that introduce implied indentation, - // * skipping NOISE tokens between `...` and NEWLINE (eg `... // hello\n body`) - // * Note: using `NEWLINE_COMMA` instead of `NEWLINE` causes misparsing of things like `fun foo(..., ...)` - case (SUSPENSION(true), l0) :: NOISE((NEWLINE, l1) :: rest) => - val outerLoc = l0.left ++ rest.lastOption.map(_._2.right) - val innerLoc = l1.right ++ rest.lastOption.map(_._2.left) - BRACKETS(Indent, preprocessTokens(rest))(innerLoc) -> outerLoc :: Nil - case tl :: rest => - val rest2 = preprocessTokens(rest) - if rest2 is rest then tokens - else tl :: rest2 - case Nil => tokens + private def preprocessTokens(tokens: Ls[TokLoc]): Ls[TokLoc] = + // Flat token streams can exceed the JavaScript stack even without nested syntax. + // Keep nonempty suffixes in reverse order so rebuilding can share unchanged tails. + def rebuild(prefixes: Ls[Ls[TokLoc]], rest: Ls[TokLoc]): Ls[TokLoc] = + prefixes.foldLeft(rest): (rest, original) => + if rest is original.tail then original + else original.head :: rest + @tailrec + def loop(tokens: Ls[TokLoc], prefixes: Ls[Ls[TokLoc]]): Ls[TokLoc] = tokens match + case (IDENT("new", false), l1) :: (IDENT("!", true), l2) :: rest => + loop(rest, (IDENT("new!", false) -> (l1 ++ l2) :: Nil) :: prefixes) + case (IDENT("yield", false), l1) :: (IDENT("*", true), l2) :: rest => + loop(rest, (IDENT("yield*", false) -> (l1 ++ l2) :: Nil) :: prefixes) + // * Remove empty indented sections + case (BRACKETS(Indent, toks), _) :: rest + if toks.forall: + case (NEWLINE | SPACE, _) => true + case _ => false + => + loop(rest, prefixes) + // * Expands end-of-line suspensions that introduce implied indentation, + // * skipping NOISE tokens between `...` and NEWLINE (eg `... // hello\n body`) + // * Note: using `NEWLINE_COMMA` instead of `NEWLINE` causes misparsing of things like `fun foo(..., ...)` + case (SUSPENSION(true), l0) :: NOISE((NEWLINE, l1) :: rest) => + val outerLoc = l0.left ++ rest.lastOption.map(_._2.right) + val innerLoc = l1.right ++ rest.lastOption.map(_._2.left) + rebuild(prefixes, BRACKETS(Indent, preprocessTokens(rest))(innerLoc) -> outerLoc :: Nil) + case _ :: rest => + loop(rest, tokens :: prefixes) + case Nil => rebuild(prefixes, Nil) + loop(tokens, Nil) private def wrap[R](args: => Any)(using l: Line, n: Name)(mkRes: => R): R = printDbg(s"@ ${n.value}${args match { @@ -983,13 +990,13 @@ abstract class Parser( consume consume val inner = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.expr(0, allowNewlines = true)) - exprCont(DynAccess(acc, Bra(bk, inner)), prec, allowNewlines = allowNewlines) - // TODO: these should eventually no longer be treated as dynamic: + exprCont(DynAccess(acc, Bra(bk, inner), true), prec, allowNewlines = allowNewlines) + // `obj.[idx]` indexes an array; `obj.(fld)` should eventually no longer be dynamic either. case (PERIOD, l0) :: (br @ BRACKETS(bk @ (Round | Square), toks), l1) :: _ => consume consume val inner = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.expr(0, allowNewlines = true)) - exprCont(DynAccess(acc, Bra(bk, inner)).withLoc(S(l0 ++ l1)), prec, allowNewlines = allowNewlines) + exprCont(DynAccess(acc, Bra(bk, inner), bk is Round).withLoc(S(l0 ++ l1)), prec, allowNewlines = allowNewlines) case (PERIOD, l0) :: (br @ BRACKETS(Curly, toks), l1) :: _ => consume @@ -1000,7 +1007,7 @@ abstract class Parser( case (PERIOD, l0) :: (LITVAL(lit: (Tree & Literal)), l1) :: _ => consume consume - exprCont(DynAccess(acc, lit.withLoc(S(l1))).withLoc(S(l0 ++ l1)), prec, allowNewlines = allowNewlines) + exprCont(DynAccess(acc, lit.withLoc(S(l1)), true).withLoc(S(l0 ++ l1)), prec, allowNewlines = allowNewlines) /* case (PERIOD, l0) :: (br @ BRACKETS(Square, toks), l1) :: _ => @@ -1101,7 +1108,7 @@ abstract class Parser( case (SELECT(name, dyn), l0) :: _ if SelPrec >= prec => consume val tree = if dyn then - DynAccess(acc, new Ident(name).withLoc(S(l0))) + DynAccess(acc, new Ident(name).withLoc(S(l0)), true) else Sel(acc, new Ident(name).withLoc(S(l0))) exprCont(tree, prec, allowNewlines = allowNewlines) diff --git a/hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala b/hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala index 25a40deaec..f95a173c39 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala @@ -79,7 +79,8 @@ enum Tree extends AutoLocated: extends Tree with TypeDefImpl case Open(opened: Tree) case OpenIn(opened: Tree, body: Tree) - case DynAccess(obj: Tree, fld: Tree) + /** `dynamic` is false only for `obj.[idx]`, an array index checked during resolution. */ + case DynAccess(obj: Tree, fld: Tree, dynamic: Bool) case Modified(modifier: Keywrd[Keyword.Modifier], body: Tree) case Quoted(body: Tree) case Unquoted(body: Tree) @@ -160,7 +161,7 @@ enum Tree extends AutoLocated: case TyTup(tys) => tys.toVector case Sel(prefix, name) => Vector.double(prefix, name) case SynthSel(prefix, name) => Vector.single(prefix) - case DynAccess(prefix, fld) => Vector.double(prefix, fld) + case DynAccess(prefix, fld, _) => Vector.double(prefix, fld) case Open(bod) => Vector.single(bod) case OpenIn(opened, body) => Vector.double(opened, body) case Def(lhs, rhs) => Vector.double(lhs, rhs) @@ -203,7 +204,7 @@ enum Tree extends AutoLocated: case Reft(base, reft) => "refinement" case Sel(prefix, name) => "selection" case SynthSel(prefix, name) => "synthetic selection" - case DynAccess(prefix, name) => "dynamic field access" + case DynAccess(prefix, name, _) => "dynamic field access" case PrefixApp(kw, body) => s"prefix operator '${kw.name}'" case InfixApp(lhs, kw, rhs) => s"infix operator '${kw.name}'" case LexicalNew(body, _) => "new" @@ -304,7 +305,7 @@ enum Tree extends AutoLocated: case N => this case N => this case Sel(pre, nme) if nme.name.startsWith("'") => - DynAccess(pre.desugared, StrLit(nme.name.drop(1)).withLocOf(nme)).withLocOf(this) + DynAccess(pre.desugared, StrLit(nme.name.drop(1)).withLocOf(nme), true).withLocOf(this) case Pun(false, id) => InfixApp(id, Keywrd(Keyword.`:`), id) @@ -313,6 +314,9 @@ enum Tree extends AutoLocated: case LetLike(kw, und @ Under(), r, b) => LetLike(kw, Ident("_").withLocOf(und), r, b) + // `open` keeps its existing expression grammar; a declaration operand makes it a modifier. + case Open(body: (TypeOrTermDef | Modified | Annotated)) => + Annotated(Keywrd(Keyword.`open`).withLocOf(this), body.desugared) case PossiblyAnnotated(anns, m: Modified) => PossiblyAnnotated(anns, m match @@ -326,6 +330,8 @@ enum Tree extends AutoLocated: Annotated(kw, s.desugared) case Modified(kw @ Keywrd(Keyword.`virtual`), s) => Annotated(kw, s.desugared) + case Modified(kw @ Keywrd(Keyword.`override`), s) => + Annotated(kw, s.desugared) case Modified(kw @ Keywrd(Keyword.`public`), s) => Annotated(kw, s.desugared) case Modified(kw @ Keywrd(Keyword.`private`), s) => @@ -368,6 +374,11 @@ enum Tree extends AutoLocated: // fun f(a: A) case InfixApp(id: Ident, Keywrd(Keyword.`:`), sign) => R(ParamTree(flags, id, S(sign), N, modifiers)) + // The annotation describes the rest tuple, just as for an ordinary parameter. + case InfixApp(SpreadParam(id, spd), Keywrd(Keyword.`:`), sign) => + R(ParamTree(flags, id, S(sign), S(spd), modifiers)) + case Spread(kw, S(InfixApp(id: Ident, Keywrd(Keyword.`:`), sign))) => + R(ParamTree(flags, id, S(sign), S(SpreadKind.fromKw(kw)), modifiers)) // fun f(..a) | fun f(...a) case SpreadParam(id, spd) => R(ParamTree(flags, id, N, S(spd), modifiers)) @@ -504,6 +515,7 @@ sealed abstract class ValLike(str: Str, desc: Str)(using Line) extends TermDefKi sealed abstract class Val(str: Str, desc: Str)(using Line) extends ValLike(str, desc) case object ImmutVal extends Val("val", "value") case object MutVal extends Val("mut val", "mutable value") +case object RecordField extends ValLike("field", "record field") case object LetBind extends ValLike("let", "let binding") case object HandlerBind extends TermDefKind("handler", "handler binding") case object Fun extends TermDefKind("fun", "function") @@ -578,6 +590,12 @@ trait TypeOrTermDef extends Located: case Reft(base, body) => rec(base, symbName, annot, body :: refts) + // In `class C(...): R { ... }`, the parser attaches the brace body to R. + // Extract it as the class body, just as for a body following `extends`. + // Parenthesized result refinements remain part of the result annotation. + case InfixApp(tree, kw @ Keywrd(Keyword.`:`), Reft(ann, body)) if this.isInstanceOf[TypeDef] => + rec(InfixApp(tree, kw, ann), symbName, annot, body :: refts) + case InfixApp(tree, Keywrd(Keyword.`:`), ann) => rec(tree, symbName, S(ann), refts) @@ -656,10 +674,14 @@ trait TypeDefImpl(using State) extends TypeOrTermDef: rhs.getOrElse(Empty())) case Trt | Mxn => ??? + // Brace bodies and `with` bodies must expose the same members to builtin lookup + // and elaboration. Keep all candidates so elaboration can diagnose multiple bodies. + lazy val bodies: Ls[Tree] = reft ++ withPart + lazy val definedSymbols: Map[Str, BlockMemberSymbol] = // val fromParams = // val fromTypeParams = - withPart match + bodies.headOption match case S(blk: Block) => blk.definedSymbols.toMap case _ => @@ -672,7 +694,7 @@ trait TypeDefImpl(using State) extends TypeOrTermDef: pts.flatMap(_.desugared.asParam(inUsing = inUsing).toOption).collect: case pt @ ParamTree(ident = id, spd = N) => val k = if pt.flags.mut then MutVal else ImmutVal - TermSymbol(k, symbol.asClsLike, id, erasedType = N) + TermSymbol(k, symbol.asClsLike, id) .toList lazy val allSymbols = definedSymbols ++ diff --git a/hkmc2/shared/src/main/scala/hkmc2/utils/Publisher.scala b/hkmc2/shared/src/main/scala/hkmc2/utils/Publisher.scala new file mode 100644 index 0000000000..023e541c42 --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/utils/Publisher.scala @@ -0,0 +1,97 @@ +package hkmc2 +package utils + +import scala.collection.mutable.{ArrayBuffer, LinkedHashSet} + +import hkmc2.utils.*, shorthands.* +import hkmc2.semantics.{NewResolverState, Symbol} + + +type ShapeListener[-A] = A => NewResolverState ?=> Unit + +object Publisher: + private[hkmc2] final class Data[A]: + private[hkmc2] var owner: NewResolverState | Null = null + private[hkmc2] var completed = false + val listeners: ArrayBuffer[ShapeListener[A]] = ArrayBuffer.empty + val shapes: LinkedHashSet[A] = LinkedHashSet.empty + // Pass-local observers never become part of a copied inference graph. + private val observers: ArrayBuffer[ShapeListener[A]] = ArrayBuffer.empty + def observe(listener: ShapeListener[A])(using state: NewResolverState): () => Unit = + val data = state.local(this) + data.observers += listener + data.replay(listener) + () => { data.observers -= listener; () } + def copy(state: NewResolverState): Data[A] = + val result = new Data[A] + result.owner = state + // Preserve intermediate exporters' private graph nodes as well as the + // listener's original registration graph when this host is re-exported. + val origin = owner + listeners.foreach: listener => + result.listeners += (shape => (current: NewResolverState) ?=> + listener(shape)(using if origin == null then current else current.inGraph(origin))) + result.shapes ++= shapes + result + + def publish(shape: A)(using state: NewResolverState): Unit = + val data = state.local(this) + if data.shapes.add(shape) then data.notify(shape) + def notify(shape: A)(using NewResolverState): Unit = + // Appended listeners receive this candidate through replay. + val count = listeners.size + val observerCount = observers.size + var i = 0 + while i < count do + listeners(i)(shape) + i += 1 + i = 0 + while i < observerCount do + observers(i)(shape) + i += 1 + def addListener(listener: ShapeListener[A])(using origin: NewResolverState): Unit = + origin.local(this).listeners += + (shape => (current: NewResolverState) ?=> listener(shape)(using current.inGraph(origin))) + def replay(listener: ShapeListener[A])(using state: NewResolverState): Unit = + val candidates = state.local(this).shapes + // Apply the contextual listener, rather than discard a contextual thunk. + candidates.iterator.take(candidates.size).foreach[Unit](shape => listener(shape)) + def subscribe(listener: ShapeListener[A])(using NewResolverState): Unit = + addListener(listener) + replay(listener) + +trait Publisher[A]: + private[hkmc2] val originalData = new Publisher.Data[A] + // Syntax nodes do not all carry an elaborator owner. Bind their original host + // on first use; subsequent consumers only mutate copies in their own state. + private[hkmc2] def initialData(state: NewResolverState): Publisher.Data[A] = + val foreignSymbol = this match + case symbol: Symbol => !state.isOwnedSym(symbol) + case _ => false + if originalData.owner == null && !foreignSymbol then originalData.owner = state + originalData + + private[hkmc2] def isOwnedBy(state: NewResolverState): Bool = + originalData.owner eq state + + private[hkmc2] def shapeListeners: ArrayBuffer[ShapeListener[A]] = originalData.listeners + private[hkmc2] def inferenceHost(using state: NewResolverState): Publisher.Data[A] = state.data(this) + private[hkmc2] def addShapeListener(listener: ShapeListener[A])(using origin: NewResolverState): Unit = + inferenceHost.addListener(listener) + + private[hkmc2] def currentShapes(using state: NewResolverState): LinkedHashSet[A] = + state.data(this).shapes + private[hkmc2] def shapes: LinkedHashSet[A] = originalData.shapes + + private[hkmc2] def notifyShapeListeners(shape: A)(using NewResolverState): Unit = + inferenceHost.notify(shape) + +trait Host[A] extends Publisher[A]: + private[hkmc2] def subscribeToShapes(listener: ShapeListener[A])(using NewResolverState): Unit = + addShapeListener(listener) + replayShapes(listener) + private[hkmc2] def replayShapes(listener: ShapeListener[A])(using NewResolverState): Unit = + inferenceHost.replay(listener) + def showDbg(using DebugPrinter): Str + + diff --git a/hkmc2/shared/src/main/scala/hkmc2/utils/utils.scala b/hkmc2/shared/src/main/scala/hkmc2/utils/utils.scala index 8b96641683..52d6da3a07 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/utils/utils.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/utils/utils.scala @@ -70,9 +70,10 @@ class DebugPrinter: case xs: List[_] => "Ls of\n" + xs.iterator.map(aux(_)).mkString("\n").indent(" ") case xs: Vector[_] => "Vector of\n" + xs.iterator.map(aux(_)).mkString("\n").indent(" ") case s: Str => s.escaped - case TermDefFlags(isMethod) => + case TermDefFlags(isMethod, hasResultAnnotation) => val flags = Buffer.empty[Str] if isMethod then flags += "method" + if hasResultAnnotation then flags += "result annotation" flags.mkString("(", ", ", ")") case FldFlags(mut, spec, pat, value) => val flags = Buffer.empty[Str] diff --git a/hkmc2/shared/src/test/mlscript-compile/AsyncLibrary.mls b/hkmc2/shared/src/test/mlscript-compile/AsyncLibrary.mls new file mode 100644 index 0000000000..ce8f15c74b --- /dev/null +++ b/hkmc2/shared/src/test/mlscript-compile/AsyncLibrary.mls @@ -0,0 +1,15 @@ +#lang(0.3.x, strictResolution: true) + +module AsyncLibrary with + @annotations.async + fun inferred() = {answer: 53} + @annotations.async + fun typed(value: Int): {answer: Int} = {answer: value} + fun curried: Int -> Int -> Int + @annotations.async + fun curried(x)(y) = x + y + @annotations.async + fun getter: Int = 56 + class Methods with + @annotations.async + fun result(value: Int): Int = value diff --git a/hkmc2/shared/src/test/mlscript-compile/Benchmark.mls b/hkmc2/shared/src/test/mlscript-compile/Benchmark.mls index 972a341d68..2131c8d240 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Benchmark.mls +++ b/hkmc2/shared/src/test/mlscript-compile/Benchmark.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "benchmark" let bm = benchmark @@ -11,10 +13,10 @@ fun mkSuite() = new! mut bm.Suite() fun setPrefixPath(path) = set prefixPath = path -fun runSuite(suite) = +fun runSuite(suite: { run: Function }) = let settings = { async: false } suite.run(settings) -fun writeResults(suite, path) = +fun writeResults(suite: { map: Function }, path) = let jsonStr = JSON.stringify(suite.map((res, _, _) => res), null, 2) fs.writeFileSync(prefixPath + path, jsonStr) diff --git a/hkmc2/shared/src/test/mlscript-compile/Block.mls b/hkmc2/shared/src/test/mlscript-compile/Block.mls index ef323a67d6..67c91d5e84 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Block.mls +++ b/hkmc2/shared/src/test/mlscript-compile/Block.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./Predef.mls" import "./Option.mls" import "./StrOps.mls" @@ -19,10 +21,10 @@ type ParamList = Array[Symbol] class Symbol(val name: Str) // this is so that we're able to retrieve information about the class from the symbol -class ClassSymbol(val name: Str) extends Symbol(name) -class VirtualClassSymbol(val name: Str) extends ClassSymbol(name) -class ConcreteClassSymbol(val name: Str, val value: Class, val paramsOpt: Opt[ParamList], val auxParams: Array[ParamList]) extends ClassSymbol(name) -class ModuleSymbol(val name: Str, val value: Class) extends Symbol(name) +class ClassSymbol(name: Str) extends Symbol(name) +class VirtualClassSymbol(name: Str) extends ClassSymbol(name) +class ConcreteClassSymbol(name: Str, val value: Class, val paramsOpt: Opt[ParamList], val auxParams: Array[ParamList]) extends ClassSymbol(name) +class ModuleSymbol(name: Str, val value: Class) extends Symbol(name) class NoSymbol() extends Symbol("$no_symbol$") class Arm(val cse: Case, val body: Block) @@ -107,7 +109,7 @@ fun showSymbol(s: Symbol) = s.name.replaceAll("$", "_") fun showPath(p: Path): Str = if p is // avoids needing to import the runtime module - Select(ValueSimpleRef(Symbol("runtime")), ModuleSymbol("Unit", Runtime.Unit)) then "()" + Select(ValueSimpleRef(Symbol("runtime")), name) and name is ModuleSymbol(_, Runtime.Unit) and name.name is "Unit" then "()" Select(qual, name) then showPath(qual) + "." + showSymbol(name) DynSelect(qual, fld, false) then showPath(qual) + ".(" + showPath(fld) + ")" DynSelect(qual, fld, true) then showPath(qual) + ".[" + showPath(fld) + "]" @@ -121,7 +123,7 @@ fun showArg(arg: Arg) = showPath(arg.value) fun showArgs(args: Array[Arg]) = - args.map(showArg).join(", ") + args.map(pass1(showArg)).join(", ") fun showResult(r: Result): Str = if r is @@ -144,11 +146,11 @@ fun showCase(c) = Tup(len) then "[" + Array(len).fill("_").join(", ") + "]" _ then "" -fun showArm(a) = +fun showArm(a: Arm) = showCase(a.cse) + " then" + (if a.body is Return then " " else "\n ") + indent(showBlock(a.body)) fun showParams(p: ParamList) = - "(" + p.map(showSymbol(_)).join(", ") + ")" + "(" + p.map(pass1(showSymbol)).join(", ") + ")" fun showParamsOpt(p) = if p is @@ -156,7 +158,7 @@ fun showParamsOpt(p) = None then "" fun showParamList(ps: Array[ParamList]) = - ps.map(showParams).join("") + ps.map(pass1(showParams)).join("") fun showDefn(d: Defn): Str = if d is @@ -165,11 +167,11 @@ fun showDefn(d: Defn): Str = (if body is (Return | End) then " " else "\n ") + indent(showBlock(body)) ClsLikeDefn(sym, methods, _) then "class " + showSymbol(sym) + showParamsOpt(sym.paramsOpt) - + indent((if methods is [] then "" else " with\n") + methods.map(showDefn).join("\n")) + + indent((if methods is [] then "" else " with\n") + methods.map(pass1(showDefn)).join("\n")) // TODO: used to represent assignments in constructors, which still need some adjustments ValDefn(owner, sym, rhs) then "val " + showSymbol(sym) + " = " + showPath(rhs) - _ then "" + _ then "" fun showBlock(b) = if b is @@ -182,12 +184,12 @@ fun showBlock(b) = showResult(res) Match(scrut, arms, dflt, rest) then "if " + showPath(scrut) + " is" - + indent("\n" + arms.map(showArm).join("\n")) + + indent("\n" + arms.map(pass1(showArm)).join("\n")) + if dflt is Some(db) then indent("\nelse" + indent((if db is Return then " " else "\n") + showBlock(db))) else "" + showRestBlock(rest) Scoped(symbols, rest) then // initialize symbols - "let {" + symbols.map(showSymbol).join(", ") + "}" + showRestBlock(rest) + "let {" + symbols.map(pass1(showSymbol)).join(", ") + "}" + showRestBlock(rest) End() then "()" _ then "" + b diff --git a/hkmc2/shared/src/test/mlscript-compile/CachedHash.mls b/hkmc2/shared/src/test/mlscript-compile/CachedHash.mls index e218273866..be36ef4123 100644 --- a/hkmc2/shared/src/test/mlscript-compile/CachedHash.mls +++ b/hkmc2/shared/src/test/mlscript-compile/CachedHash.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + class CachedHash with let _hash = null fun hash() = if _hash !== null then _hash else diff --git a/hkmc2/shared/src/test/mlscript-compile/Char.mls b/hkmc2/shared/src/test/mlscript-compile/Char.mls index e2bb1ddc00..657fbc5db1 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Char.mls +++ b/hkmc2/shared/src/test/mlscript-compile/Char.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + module Char with pattern AnyChar = (Str as s where s.length is 1) => s diff --git a/hkmc2/shared/src/test/mlscript-compile/Example.mls b/hkmc2/shared/src/test/mlscript-compile/Example.mls index b1798ed94b..2aa1e4e726 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Example.mls +++ b/hkmc2/shared/src/test/mlscript-compile/Example.mls @@ -1,3 +1,4 @@ +#lang(0.3.x) import "./Predef.mls" open Predef @@ -12,7 +13,7 @@ module Example with ... fun pubFun = privFun -fun (/) funnySlash(f, arg) = f(arg) +fun (/) funnySlash[A, B](f: A -> B, arg: A) = f(arg) fun inc(x) = x + 1 diff --git a/hkmc2/shared/src/test/mlscript-compile/FilePrivateFunctions.mls b/hkmc2/shared/src/test/mlscript-compile/FilePrivateFunctions.mls index 4e51b387f9..1035316caa 100644 --- a/hkmc2/shared/src/test/mlscript-compile/FilePrivateFunctions.mls +++ b/hkmc2/shared/src/test/mlscript-compile/FilePrivateFunctions.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./Predef.mls" open annotations { noInline } diff --git a/hkmc2/shared/src/test/mlscript-compile/FingerTreeList.mls b/hkmc2/shared/src/test/mlscript-compile/FingerTreeList.mls index b9c84587af..2c03b39f0e 100644 --- a/hkmc2/shared/src/test/mlscript-compile/FingerTreeList.mls +++ b/hkmc2/shared/src/test/mlscript-compile/FingerTreeList.mls @@ -1,3 +1,7 @@ +// #lang(0.3.x) +// ^ Can't be used yet due to spreads; +// needs the perf. fixes at `LPTK/new-resolution-no-hashing` + import "./Option.mls" import "./Iter.mls" @@ -32,16 +36,16 @@ data class View[T](e1: T, rest: FingerTree[T]) class FingerTree[T] extends IterableBase with fun iterator() = let current = this - Iterator of () => if popFront(current) is + IteratorImpl of () => if popFront(current) is Nil then Result.Done View(head, tail) then set current = tail Result.Next of head - fun toString() = "[" + this joined(", ") + "]" - fun length = FingerTreeList.length(this) - fun at(i) = get(i)(this) - fun concat(other) = FingerTreeList.concat(this, other) - fun slice(beg, end) = + fun toString(): Str = "[" + this joined(", ") + "]" + fun length: Int = FingerTreeList.length(this) + fun at(i: Int): T = get(i)(this) + fun concat(other: FingerTree[T] | Array[T]): FingerTree[T] = FingerTreeList.concat(this, other) + fun slice(beg: Int, end: Int): FingerTree[T] = let len = this.length set beg = normIdxSlice(beg, len) set end = normIdxSlice(end, len) @@ -54,14 +58,14 @@ class FingerTree[T] extends IterableBase with FingerTreeList.dropLeftRight(beg, len - end)(this) object Empty extends FingerTree[Nothing] with - fun toString() = "[" + this joined(", ") + "]" + fun toString(): Str = "[" + this joined(", ") + "]" class Single[T](val e: T) extends FingerTree[T] with - fun toString() = "[" + this joined(", ") + "]" + fun toString(): Str = "[" + this joined(", ") + "]" // prefix and suffix are at most length 4 class Deep[T](val size: Int, val prefix: Array[T], val middle: FingerTree[Node[T]], val suffix: Array[T]) extends FingerTree[T] with - fun toString() = "[" + [...this].join(", ") + "]" + fun toString(): Str = "[" + [...this].join(", ") + "]" // amortized O(log n) fun concatMiddle(ft1, middle, ft2) = if [ft1, middle, ft2] is @@ -132,12 +136,12 @@ object SpliceMarker module FingerTreeList with... -fun toFingerTree(xs) = if xs is +fun toFingerTree[T](xs: FingerTree[T] | Array[T]): FingerTree[T] = if xs is FingerTree then xs else mk(...xs) // assume that the input is iterable if not a finger tree // amortized O(1) -fun (+:) cons(x, xs) = if toFingerTree(xs) is +fun (+:) cons[T](x: T, xs: FingerTree[T] | Array[T]): FingerTree[T] = if toFingerTree(xs) is Empty then Single(x) Single(y) then Deep(getNodeSize(y) + getNodeSize(x), [x], Empty, [y]) Deep(s, ax, dt, ay) then if (ax.length >= 4) @@ -147,7 +151,7 @@ fun (+:) cons(x, xs) = if toFingerTree(xs) is else Deep(s + getNodeSize(x), [x, ...ax], dt, ay) // amortized O(1) -fun (:+) snoc(xs, x) = if toFingerTree(xs) is +fun (:+) snoc[T](xs: FingerTree[T] | Array[T], x: T): FingerTree[T] = if toFingerTree(xs) is Empty then Single(x) Single(y) then Deep(getNodeSize(y) + getNodeSize(x), [y], Empty, [x]) Deep(s, ax, dt, ay) then if (ay.length >= 4) @@ -156,23 +160,23 @@ fun (:+) snoc(xs, x) = if toFingerTree(xs) is ay.0, ay.1, ay.2)), [ay.3, x]) else Deep(s + getNodeSize(x), ax, dt, [...ay, x]) -fun mk(...args) = args.reduce( +fun mk[T](...args: Array[T]): FingerTree[T] = args.reduce( (acc, x, ...) => snoc(acc, x), Empty) -fun isEmpty = case +fun isEmpty[T]: FingerTree[T] -> Bool = case Empty then true _ then false // O(1) -fun length = case +fun length[T]: (FingerTree[T] | Array[T]) -> Int = case Array as a then a.length Empty then 0 Single(x) then getNodeSize(x) Deep(s, _, _, _) then s // amortized O(1) -fun popFront(ft) = if toFingerTree(ft) is +fun popFront[T](ft: FingerTree[T] | Array[T]): Nil | View[T] = if toFingerTree(ft) is Empty then Nil Single(x) then View(x, Empty) Deep(s, [x], dt, ay) then if (popFront(dt)) is @@ -185,7 +189,7 @@ fun popFront(ft) = if toFingerTree(ft) is View(x, Deep(s - getNodeSize(x), ax, dt, ay)) // amortized O(1) -fun popBack(ft) = if toFingerTree(ft) is +fun popBack[T](ft: FingerTree[T] | Array[T]): Nil | View[T] = if toFingerTree(ft) is Empty then Nil Single(x) then View(x, Empty) Deep(s, ax, dt, [x]) then if popBack(dt) is @@ -198,10 +202,11 @@ fun popBack(ft) = if toFingerTree(ft) is View(y, Deep(s - getNodeSize(y), ax, dt, ay)) // amortized O(log n) -fun (++) concat(ft1, ft2) = concatMiddle(toFingerTree(ft1), [], toFingerTree(ft2)) +fun (++) concat[T](ft1: FingerTree[T] | Array[T], ft2: FingerTree[T] | Array[T]): FingerTree[T] = + concatMiddle(toFingerTree(ft1), [], toFingerTree(ft2)) // amortized O(log n) -fun ith(idx) = case +fun ith[T](idx: Int): FingerTree[T] -> Option[T] = case Empty then None Single(x) then ithNode(idx, x) Deep(s, ax, dt, ay) and @@ -211,27 +216,27 @@ fun ith(idx) = case else ithAffixElement(idx - getAffixSize(ax) - length(dt), ay) else None -fun get(idx) = case +fun get[T](idx: Int): (FingerTree[T] | Array[T]) -> T = case Array as a then a.at(idx) FingerTree as lft then if ith(normIdx(idx, length(lft)))(lft) is None then throw "get: index out of bounds" Some(x) then x -fun toArray(ft) = [...ft] +fun toArray[T](ft: FingerTree[T]): Array[T] = [...ft] /// amortized O(beg + end) -fun dropLeftRight(beg, end)(und) = if beg < 0 || end < 0 || beg + end > length(und) +fun dropLeftRight[T](beg: Int, end: Int)(und: FingerTree[T] | Array[T]): FingerTree[T] = if beg < 0 || end < 0 || beg + end > length(und) then throw "dropLeftRight: index out of bounds" else repeatPopFront(beg)(repeatPopBack(end)(toFingerTree(und))) -fun isFingerTree(xs) = if xs is +fun isFingerTree(xs: Any): Bool = if xs is FingerTree then true else false // The following functions are not meant for users; it's meant referred to by Runtime.mls // TODO: use `FingerTreeList.internals.concat` when this is implemented -fun __markerConcat(...arr) = +fun __markerConcat(...arr: Array[Any]): FingerTree[Any] = let acc = FingerTreeList.mk() let idx = 0 while idx < arr.length do @@ -244,4 +249,4 @@ fun __markerConcat(...arr) = set idx = idx + 1 acc -val __split = SpliceMarker +val __split: SpliceMarker = SpliceMarker diff --git a/hkmc2/shared/src/test/mlscript-compile/GenericLibrary.mls b/hkmc2/shared/src/test/mlscript-compile/GenericLibrary.mls new file mode 100644 index 0000000000..9290c96bdc --- /dev/null +++ b/hkmc2/shared/src/test/mlscript-compile/GenericLibrary.mls @@ -0,0 +1,16 @@ +#lang(0.3.x, strictResolution: true) + +module GenericLibrary with + class Known(val known: Int) + fun identity[A](x: A): A = x + class Methods with + fun identity[A](x: A): A = x + fun inferred[A](x: A) = x + val known = identity(Known(3)) + val mapped = [Known(4)].map((item, ...) => Known(5)) + fun inferred[A](x: A) = x + // Importers must observe a recursive getter's result without rebuilding its cycle. + fun recursiveIdentity[A](x: A) = + fun result = if true then x else result + result + fun recursiveGetter = if true then Known(7) else recursiveGetter diff --git a/hkmc2/shared/src/test/mlscript-compile/Iter.mls b/hkmc2/shared/src/test/mlscript-compile/Iter.mls index 78eb2ca8fd..5daa483a5a 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Iter.mls +++ b/hkmc2/shared/src/test/mlscript-compile/Iter.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + #config(liftDefns: Some(LiftDefns)) import "./Option.mls" @@ -9,24 +11,24 @@ open Stack { Nil, Cons } module Iter with ... -class IterableBase with +open class IterableBase with set this.(Symbol.iterator) = iterator - fun iterator() = throw new Error("Not implemented: iterator()") + open fun iterator() = throw new Error("Not implemented: iterator()") // require that subclasses are immutable - fun toArray() = [...this] + fun toArray(): Array = [...this] let array = null - fun asArray = + fun asArray: Array = if array is null then set array = toArray() Object.freeze() array else array // * Functions that create a fresh new result - fun at(idx) = asArray.at(idx) - fun concat(...xs) = asArray.concat(...xs) + open fun at(idx) = asArray.at(idx) + open fun concat(...xs) = asArray.concat(...xs) fun copyWithin(...args) = asArray.copyWithin(...args) fun entries() = asArray.entries() fun every(...args) = asArray.every(...args) @@ -46,13 +48,13 @@ class IterableBase with fun map(...args) = asArray.map(...args) fun reduce(...args) = asArray.reduce(...args) fun reduceRight(...args) = asArray.reduceRight(...args) - fun slice(...args) = asArray.slice(...args) + open fun slice(...args) = asArray.slice(...args) fun some(...args) = asArray.some(...args) fun toLocaleString(...args) = asArray.toLocaleString(...args) fun toReversed() = asArray.toReversed() fun toSorted(...args) = asArray.toSorted(...args) fun toSpliced(...args) = asArray.toSpliced(...args) - fun toString() = asArray.toString() + open fun toString() = asArray.toString() // `with` is an mls keyword fun id"with"(...args) = asArray.with(...args) @@ -66,17 +68,17 @@ class IterableBase with fun splice(...args) = toArray().splice(...args) fun unshift(...args) = toArray().unshift(...args) - fun length = asArray.length + open fun length = asArray.length fun values() = iterator() -// * Implements the `Iterable` protocol. +// * Implements the `IterableImpl` protocol. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol -class Iterable(mk) extends IterableBase with - fun iterator() = mk() +class IterableImpl(mk: () -> Iterator) extends IterableBase with + override fun iterator() = mk() // * Implements the `Iterator` protocol. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterator_protocol -class Iterator(val next) +class IteratorImpl(val next: Function) module Result with class Next(val value) with @@ -87,7 +89,7 @@ module Result with // A helper function to detect if `Symbol.iterator` is on an object, and if not, // indicate the object's class in the error message, which is useful for debugging. -fun getIterator(something) = +fun getIterator(something): Iterator = let test = something.(Symbol.iterator) if test is undefined then throw TypeError of "Not an iterable: " + if @@ -98,12 +100,12 @@ fun getIterator(something) = else prototype.constructor.name else test.call(something) -fun adaptIterable(iterable, makeNext) = Iterable of () => +fun adaptIterable(iterable, makeNext: Function) = IterableImpl of () => let iterator = getIterator of iterable - Iterator of makeNext of iterator + IteratorImpl of makeNext of iterator -fun mapping(xs, op) = - xs adaptIterable of iterator => () => +fun mapping(xs, op: Function) = + xs adaptIterable of (iterator: Iterator) => () => let next = iterator.next() if next.done then Result.Done @@ -113,14 +115,14 @@ fun mapping(xs, op) = fun withMap(op) = _ mapping(op) fun flattening(xss) = - Iterable of () => + IterableImpl of () => let iterableIterator = getIterator of xss - // Get the first `Iterator` from the first `Iterable`. + // Get the first `Iterator` from the first `IterableImpl`. let currentIterator = if let firstIterableResult = iterableIterator.next() firstIterableResult.done then None else Some of getIterator of firstIterableResult.value - // Recursively skip empty `Iterable`s and get the next `Iterator` and value. + // Recursively skip empty `IterableImpl`s and get the next `Iterator` and value. fun skipEmptyIterables() = if let nextIterableResult = iterableIterator.next() nextIterableResult.done then None @@ -128,7 +130,7 @@ fun flattening(xss) = let nextResult = nextIterator.next() nextResult.done then skipEmptyIterables() else Some of [nextIterator, nextResult.value] - Iterator of () => + IteratorImpl of () => if currentIterator is None then Result.Done // No more `Iterator`s means we're done. Some(iterator) and @@ -142,8 +144,8 @@ fun flattening(xss) = Result.Done else Result.Next of next.value -fun filtering(xs, op) = - xs adaptIterable of iterator => () => +fun filtering(xs, op: Function) = + xs adaptIterable of (iterator: Iterator) => () => let next = iterator.next() while next.done is false and op(next.value) is false do set next = iterator.next() @@ -174,7 +176,7 @@ fun zippingWithIndex(xs) = set i = i + 1 [x, j] -fun foldingImpl(iterator, acc, op) = +fun foldingImpl(iterator: Iterator, acc, op: Function) = let next = iterator.next() while next.done is false do set acc = op(acc, next.value) @@ -182,10 +184,10 @@ fun foldingImpl(iterator, acc, op) = acc fun appended(xs, ys) = - Iterable of () => + IterableImpl of () => let xsIterator = getIterator of xs let currentIterator = xsIterator - Iterator of () => if + IteratorImpl of () => if let next = currentIterator.next() next.done and currentIterator == xsIterator and @@ -198,18 +200,18 @@ fun appended(xs, ys) = fun withAppended(xs) = _ appended(xs) -fun reduced(xs, op) = +fun reduced(xs, op: Function) = let iterator = getIterator of xs let next = iterator.next() if next.done do throw new Error("Empty iterator") foldingImpl(iterator, next.value, op) -fun folded(xs, z, op) = +fun folded(xs, z, op: Function) = let iterator = getIterator of xs foldingImpl(iterator, z, op) -fun rightFolded(xs, z, op) = +fun rightFolded(xs, z, op: Function) = let iterator = getIterator of xs fun go() = if let next = iterator.next() @@ -229,7 +231,7 @@ fun joined(xs, sep) = String(next.value) (acc, x) => acc + sep + String(x) -fun firstDefined(xs, op) = +fun firstDefined(xs, op: Function) = let iterator = getIterator of xs let next = iterator.next() let result = None @@ -240,7 +242,7 @@ fun firstDefined(xs, op) = result // `exists` is a keyword -fun some(xs, op) = +fun some(xs, op: Function) = let iterator = getIterator of xs let next = iterator.next() while next.done is false do @@ -249,7 +251,7 @@ fun some(xs, op) = false // `forall` is a keyword -fun every(xs, op) = +fun every(xs, op: Function) = let iterator = getIterator of xs let next = iterator.next() while next.done is false do @@ -257,7 +259,7 @@ fun every(xs, op) = set next = iterator.next() true -fun each(xs, op) = +fun each(xs, op: Function) = let iterator = getIterator of xs let next = iterator.next() while next.done is false do @@ -266,9 +268,9 @@ fun each(xs, op) = fun toArray(view) = Array.from(view) -fun fromStack(stack) = Iterable of () => +fun fromStack(stack) = IterableImpl of () => let current = stack - Iterator of () => if current is + IteratorImpl of () => if current is Cons(head, tail) then set current = tail Result.Next of head diff --git a/hkmc2/shared/src/test/mlscript-compile/LazyArray.mls b/hkmc2/shared/src/test/mlscript-compile/LazyArray.mls index d7ac56fe3d..6350913f63 100644 --- a/hkmc2/shared/src/test/mlscript-compile/LazyArray.mls +++ b/hkmc2/shared/src/test/mlscript-compile/LazyArray.mls @@ -1,3 +1,4 @@ +#lang(0.3.x) import "./Iter.mls" open Iter @@ -10,9 +11,9 @@ fun normIdx(i, len) = fun enforceArray = case (LazyArr | Array | String | Str | TypedArray) as a then a - ow then throw Error("Expected an Array, got: " + ow.toString()) + ow then throw Error("Expected an Array, got: " + String(ow)) -fun concatHelper(a, l, ...args) = +fun concatHelper(a: Array, l: Int, ...args: Array[Indexable]) = let idx = 0 init_len = a.length @@ -34,7 +35,7 @@ fun normIdxSlice(i, len) = else 0 else i -fun sliceHelper(beg, fin, arr) = +fun sliceHelper(beg: Int, fin: Int, arr: Indexable) = let len = arr.length set beg = normIdxSlice(beg, len) set fin = normIdxSlice(fin, len) @@ -53,40 +54,40 @@ fun sliceHelper(beg, fin, arr) = // Marker base class for View and Splice class LazyArr extends IterableBase with - fun toString() = "[" + this joined(", ") + "]" - fun concat(...args) = concatHelper(mut [SpliceMarker, this], this.length, ...args) + open override fun toString() = "[" + this joined(", ") + "]" + open override fun concat(...args: Array[Indexable]) = concatHelper(mut [SpliceMarker, this], this.length, ...args) // Requires that the underlying iterable is constant-time indexable // Note: Ideally `underlying` should be typed as `IterableBase`, but `Array` currently does not extend it. -class View(val underlying, val start, val end) extends LazyArr with +class View(val underlying: Indexable, val start: Int, val end: Int) extends LazyArr with - fun iterator() = + override fun iterator() = let idx = start let until = underlying.length - end - Iterator of () => + IteratorImpl of () => if idx < until then let next = underlying.at(idx) set idx = idx + 1 Result.Next of next else Result.Done - fun materialize() = underlying.slice(start, underlying.length - end) + fun materialize(): Indexable = underlying.slice(start, underlying.length - end) - fun at(i) = + override fun at(i) = if i < -length || i >= length do throw RangeError("View.at: Index out of bounds") underlying.at(normIdx(i, length) + start) - fun slice(beg, fin) = sliceHelper(beg, fin, this) + override fun slice(beg, fin) = sliceHelper(beg, fin, this) val len = underlying.length - start - end - fun length = len - fun toString() = "[" + this joined(", ") + "]" + override fun length = len + override fun toString() = "[" + this joined(", ") + "]" object SpliceMarker -class Splice(val bits, val len) extends LazyArr with +class Splice(val bits: Array, val len: Int) extends LazyArr with let materialized = null - fun iterator() = + override fun iterator() = let idx = 0 let uitr = null @@ -108,20 +109,20 @@ class Splice(val bits, val len) extends LazyArr with set idx += 1 Result.Next of value else Result.Done - Iterator of nextf + IteratorImpl of nextf fun reify = let counter = 0 let arr = new mut Array(len) - let stack = mut [] + val stack: Array[[Int, Indexable]] = mut [] stack.push([0, bits]) while stack.length > 0 do let e = stack.pop() - let idx = e.[0] - let vals = e.[1] + let idx = e.0 + let vals = e.1 - if vals is Splice do - set vals = vals.bits + if vals is Splice as splice do + set vals = splice.bits while (idx < vals.length and (vals.at(idx) is SpliceMarker) is false) do @@ -134,35 +135,35 @@ class Splice(val bits, val len) extends LazyArr with stack.push([0, vals.at(idx + 1)]) arr - fun materialize() = + fun materialize(): Array = if materialized === null do set materialized = reify materialized - fun at(i) = materialize().at(i) - fun slice(beg, fin) = sliceHelper(beg, fin, this) - fun concat(...args) = + override fun at(i) = materialize().at(i) + override fun slice(beg, fin) = sliceHelper(beg, fin, this) + override fun concat(...args: Array[Indexable]) = if materialized === null then concatHelper(mut [SpliceMarker, this], len, ...args) else concatHelper(mut [SpliceMarker, materialized], len, ...args) - fun length = len - fun toString() = "[" + materialize() joined(", ") + "]" + override fun length = len + override fun toString() = "[" + materialize() joined(", ") + "]" module LazyArray with... fun mk(...args) = View of args, 0, 0 -fun concat(...args) = concatHelper(mut [], 0, ...args) -fun dropLeftRight(beg, fin)(xs) = +fun concat(...args: Array[Indexable]) = concatHelper(mut [], 0, ...args) +fun dropLeftRight(beg: Int, fin: Int)(xs: Indexable) = if beg < 0 || fin < 0 do throw RangeError("LazyArray.dropLeftRight: indices must be non-negative") if beg > xs.length || fin > xs.length do throw RangeError("LazyArray.dropLeftRight: indices out of bounds") sliceHelper(beg, xs.length - fin, xs) -fun equals(xs, ys) = +fun equals(xs: Indexable, ys: Indexable) = xs.length === ys.length && let idx = 0 while (idx < xs.length) do diff --git a/hkmc2/shared/src/test/mlscript-compile/LazyFingerTree.mls b/hkmc2/shared/src/test/mlscript-compile/LazyFingerTree.mls index daca2e5c83..837871d5dd 100644 --- a/hkmc2/shared/src/test/mlscript-compile/LazyFingerTree.mls +++ b/hkmc2/shared/src/test/mlscript-compile/LazyFingerTree.mls @@ -1,3 +1,4 @@ +#lang(0.3.x) import "./Iter.mls" import "./FingerTreeList.mls" @@ -9,7 +10,7 @@ fun normIdx(i, len) = if i < 0 then len + i else i -fun concatHelper(a, l, ...args) = +fun concatHelper(a: Array, l: Int, ...args: Array[Indexable]) = let idx = 0 let init_len = a.length set a.length = init_len + args.length * 2 @@ -30,7 +31,7 @@ fun normIdxSlice(i, len) = else 0 else i -fun sliceHelper(beg, fin, arr) = +fun sliceHelper(beg: Int, fin: Int, arr: Indexable) = let len = arr.length set beg = normIdxSlice(beg, len) set fin = normIdxSlice(fin, len) @@ -52,50 +53,50 @@ fun sliceHelper(beg, fin, arr) = // Marker base class for View and Splice class LazyFT extends IterableBase with - fun toString() = "[" + this joined(", ") + "]" - fun concat(...args) = concatHelper(mut [SpliceMarker, this], this.length, ...args) + open override fun toString() = "[" + this joined(", ") + "]" + open override fun concat(...args: Array[Indexable]) = concatHelper(mut [SpliceMarker, this], this.length, ...args) -class View(val underlying, val start, val end) extends LazyFT with +class View(val underlying: Indexable, val start: Int, val end: Int) extends LazyFT with - fun iterator() = + override fun iterator() = let idx = start let until = underlying.length - end - Iterator of () => + IteratorImpl of () => if idx < until then let next = underlying.at(idx) set idx = idx + 1 Result.Next of next else Result.Done - fun materialize() = underlying.slice(start, underlying.length - end) + fun materialize(): Indexable = underlying.slice(start, underlying.length - end) - fun at(i) = + override fun at(i) = if i < -length || i >= length do throw RangeError("View.at: Index out of bounds") underlying.at(normIdx(i, length) + start) - fun slice(beg, fin) = sliceHelper(beg, fin, this) + override fun slice(beg, fin) = sliceHelper(beg, fin, this) val len = underlying.length - start - end - fun length = len - fun toString() = "[" + this joined(", ") + "]" + override fun length = len + override fun toString() = "[" + this joined(", ") + "]" object SpliceMarker -class Splice(val bits, val len) extends LazyFT with +class Splice(val bits: Array, val len: Int) extends LazyFT with let materialized = null - fun iterator() = materialize().iterator() + override fun iterator() = materialize().iterator() fun reify = let acc = FingerTreeList.mk() - let stack = mut [] + val stack: Array[[Int, Indexable]] = mut [] stack.push([0, bits]) while stack.length > 0 do let e = stack.pop() - let idx = e.[0] - let vals = e.[1] + let idx = e.0 + let vals = e.1 - if vals is Splice do - set vals = vals.bits + if vals is Splice as splice do + set vals = splice.bits while (idx < vals.length and (vals.at(idx) is SpliceMarker) is false) do @@ -107,39 +108,40 @@ class Splice(val bits, val len) extends LazyFT with let next = vals.at(idx + 1) if FingerTreeList.isFingerTree(next) then set acc = acc ++ next + // SpliceMarker is always followed by an indexable sequence. else stack.push([0, next]) acc - fun materialize() = + fun materialize(): IterableIndexable = if materialized == null do set materialized = reify materialized - fun at(i) = materialize().at(i) + override fun at(i) = materialize().at(i) // called on eager spreads in matches - fun slice(beg, fin) = sliceHelper(beg, fin, this) - fun concat(...args) = + override fun slice(beg, fin) = sliceHelper(beg, fin, this) + override fun concat(...args: Array[Indexable]) = if materialized === null then concatHelper(mut [SpliceMarker, this], len, ...args) else concatHelper(mut [SpliceMarker, materialized], len, ...args) - fun length = len - fun toString() = "[" + materialize() joined(", ") + "]" + override fun length = len + override fun toString() = "[" + materialize() joined(", ") + "]" module LazyFingerTree with... fun mk(...args) = View of FingerTreeList.mk(...args), 0, 0 -fun concat(...args) = concatHelper(mut [], 0, ...args) -fun dropLeftRight(beg, fin)(xs) = +fun concat(...args: Array[Indexable]) = concatHelper(mut [], 0, ...args) +fun dropLeftRight(beg: Int, fin: Int)(xs: Indexable) = if beg < 0 || fin < 0 do throw RangeError("LazyFingerTree.dropLeftRight: indices must be non-negative") if beg > xs.length || fin > xs.length do throw RangeError("LazyFingerTree.dropLeftRight: indices out of bounds") sliceHelper(beg, xs.length - fin, xs) -fun equals(xs, ys) = +fun equals(xs: Indexable, ys: Indexable) = if xs is [x, ...xs] then if ys is @@ -155,7 +157,7 @@ fun __concat(...args) = let idx = 0 while (idx < args.length) do if args.[idx] is SpliceMarker then - set len += args.[idx + 1].length + set len += (args.[idx + 1] as Indexable).length set idx += 2 else set len += 1 diff --git a/hkmc2/shared/src/test/mlscript-compile/LegacyGenericLibrary.mls b/hkmc2/shared/src/test/mlscript-compile/LegacyGenericLibrary.mls new file mode 100644 index 0000000000..0d3cce85ec --- /dev/null +++ b/hkmc2/shared/src/test/mlscript-compile/LegacyGenericLibrary.mls @@ -0,0 +1,5 @@ +// Kept in legacy mode to exercise signatures imported by new resolution. +module LegacyGenericLibrary with + class Cell[A](val value: A) with + fun get: A = value + fun getAt(index: Int): A = value diff --git a/hkmc2/shared/src/test/mlscript-compile/LenientSelections.mls b/hkmc2/shared/src/test/mlscript-compile/LenientSelections.mls new file mode 100644 index 0000000000..dbab3d42a5 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript-compile/LenientSelections.mls @@ -0,0 +1,9 @@ +#lang(0.3.x) + +// The declared union supplies a target while admitting another known shape +// without that member. Importers preserve the target and its runtime access. +module LenientSelections with + class WithValue(val value: Int) + class WithoutValue + fun read(x: WithValue | WithoutValue, enabled: Bool) = if enabled then x.value else 0 + val observed = read(WithValue(7), true) diff --git a/hkmc2/shared/src/test/mlscript-compile/MutMap.mls b/hkmc2/shared/src/test/mlscript-compile/MutMap.mls index 34ebbbf402..b9c63688e9 100644 --- a/hkmc2/shared/src/test/mlscript-compile/MutMap.mls +++ b/hkmc2/shared/src/test/mlscript-compile/MutMap.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./Option.mls" import "./Predef.mls" @@ -7,36 +9,37 @@ open Option { Some, None } module MutMap with ... -class MutMap(val underlying) with +class MutMap[A, B](val underlying: Map[A, B]) with set this.(Symbol.iterator) = () => underlying.(Symbol.iterator)() -fun get(key)(m) = +fun get[A, B](key: A)(m: MutMap[A, B]): Option[B] = + // Map.get also admits undefined for absent keys; has excludes that case. if m.underlying.has(key) then Some of m.underlying.get(key) else None -fun insert(key, value)(m) = +fun insert[A, B](key: A, value: B)(m: MutMap[A, B]) = m.underlying.set(key, value) () -fun delete(key)(m) = +fun delete(key)(m: MutMap) = m.underlying.delete(key) () -fun updateWith(key)(op)(m) = +fun updateWith(key)(op: Function)(m: MutMap) = if op(m |> get(key)) is Some(value) then m.underlying.set(key, value) None then m.underlying.delete(key) -fun keysIterator(m) = m.underlying.keys() +fun keysIterator(m: MutMap) = m.underlying.keys() -fun valuesIterator(m) = m.underlying.values() +fun valuesIterator(m: MutMap) = m.underlying.values() -fun values(m) = Array.from of m.underlying.values() +fun values(m: MutMap) = Array.from of m.underlying.values() -fun toMap(entries) = +fun toMap(entries: Array) = let m = empty i = 0 @@ -46,6 +49,6 @@ fun toMap(entries) = set i += 1 m -fun empty = new MutMap(new Map()) +fun empty[A, B]: MutMap[A, B] = new MutMap(new Map()) diff --git a/hkmc2/shared/src/test/mlscript-compile/NamedFieldLibrary.mls b/hkmc2/shared/src/test/mlscript-compile/NamedFieldLibrary.mls new file mode 100644 index 0000000000..b3dc3b495b --- /dev/null +++ b/hkmc2/shared/src/test/mlscript-compile/NamedFieldLibrary.mls @@ -0,0 +1,9 @@ +#lang(0.3.x) + +module NamedFieldLibrary with ... + +fun pack(x) = [value: x] +fun select(fields: {value: Int}) = fields.value + +val fields = [value: 36] +val observed = fields.0.value diff --git a/hkmc2/shared/src/test/mlscript-compile/NestedFunctionsNoLift.mls b/hkmc2/shared/src/test/mlscript-compile/NestedFunctionsNoLift.mls index a6c89eb4c0..b7baadfd99 100644 --- a/hkmc2/shared/src/test/mlscript-compile/NestedFunctionsNoLift.mls +++ b/hkmc2/shared/src/test/mlscript-compile/NestedFunctionsNoLift.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + #config(liftDefns: None) import "./Predef.mls" diff --git a/hkmc2/shared/src/test/mlscript-compile/NoFreeze.mjs b/hkmc2/shared/src/test/mlscript-compile/NoFreeze.mjs index 7ceb3b71f5..d15852c63e 100644 --- a/hkmc2/shared/src/test/mlscript-compile/NoFreeze.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/NoFreeze.mjs @@ -31,4 +31,4 @@ let NoFreeze1; static [definitionMetadata] = ["class", "NoFreeze"]; }); export { NoFreeze1 as _$_modulePrivate_$_NoFreeze }; -let NoFreeze = NoFreeze1; export default NoFreeze; +export default NoFreeze1; diff --git a/hkmc2/shared/src/test/mlscript-compile/NoFreeze.mls b/hkmc2/shared/src/test/mlscript-compile/NoFreeze.mls index 9e73654ed6..7a4b4ceaad 100644 --- a/hkmc2/shared/src/test/mlscript-compile/NoFreeze.mls +++ b/hkmc2/shared/src/test/mlscript-compile/NoFreeze.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + #config(noFreeze: true) #config(noModuleCheck: true) diff --git a/hkmc2/shared/src/test/mlscript-compile/ObjectBuffer.mls b/hkmc2/shared/src/test/mlscript-compile/ObjectBuffer.mls index 7941a9d780..111802c2fd 100644 --- a/hkmc2/shared/src/test/mlscript-compile/ObjectBuffer.mls +++ b/hkmc2/shared/src/test/mlscript-compile/ObjectBuffer.mls @@ -1,11 +1,13 @@ +#lang(0.3.x) + fun max(a, b) = if a < b then b else a module ObjectBuffer with abstract class ObjectBuffer with val buf - fun mkNew(cls) - fun del(cls, inst) + fun mkNew[A](cls: { size: Int, ctor: (ObjectBuffer, Int) -> A }): A + fun del(cls: { size: Int }, inst) fun read(idx) fun write(idx, v) @@ -17,12 +19,11 @@ module ObjectBuffer with buf.[0] = buf.length buf.[1] = -1 - fun mkNew(cls) = - if cls.size === 0 do return 0 - val idx = _alloc(getBlockSize(cls.size)) + fun mkNew[A](cls: { size: Int, ctor: (ObjectBuffer, Int) -> A }): A = + val idx = if cls.size === 0 then 0 else _alloc(getBlockSize(cls.size)) cls.ctor(this, idx) - fun del(cls, inst) = + fun del(cls: { size: Int }, inst) = if cls.size !== 0 do _free(inst, getBlockSize(cls.size)) diff --git a/hkmc2/shared/src/test/mlscript-compile/Option.mls b/hkmc2/shared/src/test/mlscript-compile/Option.mls index e6a48900fc..1d2320f944 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Option.mls +++ b/hkmc2/shared/src/test/mlscript-compile/Option.mls @@ -1,30 +1,32 @@ -type Option[A] = Option.{ Some(A) | None } +#lang(0.3.x) + +type Option[A] = Option.{ Some[A] | None } module Option with ... -data class Some[T](value) +data class Some[T](value: T) object None -fun isDefined(x) = if x is +fun isDefined[A](x: Option[A]): Bool = if x is Some then true None then false data class Both(fst, snd) -fun getOrElse(opt, default) = if opt is +fun getOrElse[A](opt: Option[A], default: A): A = if opt is Some(value) then value None then default -fun flatMap(opt, f) = if opt is +fun flatMap[A, B](opt: Option[A], f: A -> Option[B]): Option[B] = if opt is Some(value) then f(value) None then None module unsafe with - fun get(opt) = if opt is + fun get[A](opt: Option[A]): A = if opt is Some(value) then value None then throw Error("None.get") diff --git a/hkmc2/shared/src/test/mlscript-compile/OverloadedClassLibrary.mls b/hkmc2/shared/src/test/mlscript-compile/OverloadedClassLibrary.mls new file mode 100644 index 0000000000..db628334df --- /dev/null +++ b/hkmc2/shared/src/test/mlscript-compile/OverloadedClassLibrary.mls @@ -0,0 +1,15 @@ +#lang(0.3.x, strictResolution: true) + +class Hidden { + fun answer() = 51 +} +fun Hidden() = 15 + +module OverloadedClassLibrary { + fun fresh() = new Hidden + fun value() = Hidden() + class Item { + fun answer() = 52 + } + fun Item() = 16 +} diff --git a/hkmc2/shared/src/test/mlscript-compile/OverloadedDefault.mls b/hkmc2/shared/src/test/mlscript-compile/OverloadedDefault.mls new file mode 100644 index 0000000000..9d807e9351 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript-compile/OverloadedDefault.mls @@ -0,0 +1,6 @@ +#lang(0.3.x, strictResolution: true) + +class OverloadedDefault { + fun answer() = 54 +} +fun OverloadedDefault() = 19 diff --git a/hkmc2/shared/src/test/mlscript-compile/Predef.mjs b/hkmc2/shared/src/test/mlscript-compile/Predef.mjs index 7b74f1e082..2abe09dede 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Predef.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Predef.mjs @@ -24,10 +24,8 @@ lambda2 = (undefined, function (Predef2, acc, x) { return acc + x }); lambda$1 = (undefined, function (Predef2, b) { - return (a, i) => { - let tmp; - tmp = runtime.safeCall(b.at(i)); - return Predef2.equals(a, tmp) + return (a, i, ..._) => { + return lambda1(Predef2, b, a, i, ..._) } }); lambda$ = (undefined, function (Predef2, a, b) { @@ -35,7 +33,7 @@ lambda$ = (undefined, function (Predef2, a, b) { return lambda(Predef2, a, b, field) } }); -lambda1 = (undefined, function (Predef2, b, a, i) { +lambda1 = (undefined, function (Predef2, b, a, i, ..._) { let tmp; tmp = runtime.safeCall(b.at(i)); return Predef2.equals(a, tmp) @@ -189,7 +187,7 @@ lambda = (undefined, function (Predef2, a, b, field) { } } static equals(a, b) { - let scrut, scrut1, scrut2, ac, scrut3, md, scrut4, scrut5, scrut6, scrut7, scrut8, scrut9, lambda$here, lambda$here1; + let scrut, scrut1, scrut2, ac, scrut3, md, scrut4, scrut5, scrut6, scrut7, scrut8, scrut9, lambda$here; scrut = a === b; if (scrut === true) { return true @@ -198,8 +196,7 @@ lambda = (undefined, function (Predef2, a, b, field) { if (b instanceof globalThis.Array) { scrut1 = a.length === b.length; if (scrut1 === true) { - lambda$here = lambda$1(Predef, b); - return runtime.safeCall(a.every(lambda$here)) + return runtime.safeCall(a.every(lambda$1(Predef, b))) } } } @@ -211,16 +208,16 @@ lambda = (undefined, function (Predef2, a, b, field) { if (scrut8 === true) { scrut7 = b !== null; if (scrut7 === true) { - ac = a.constructor; + ac = a["constructor"]; scrut3 = ac !== undefined; if (scrut3 === true) { - scrut6 = ac === b.constructor; + scrut6 = ac === b["constructor"]; if (scrut6 === true) { md = ac[Predef.Symbols.definitionMetadata]; scrut4 = md !== undefined; if (scrut4 === true) { - lambda$here1 = lambda$(Predef, a, b); - scrut5 = runtime.safeCall(md[2].every(lambda$here1)); + lambda$here = lambda$(Predef, a, b); + scrut5 = runtime.safeCall(md[2].every(lambda$here)); if (scrut5 === true) { return true } @@ -349,4 +346,4 @@ export { lambda2 as _$_modulePrivate_$_lambda2 }; export { lambda$ as _$_modulePrivate_$_lambda$ }; export { lambda$1 as _$_modulePrivate_$_lambda$1 }; export { lambda$2 as _$_modulePrivate_$_lambda$2 }; -let Predef = Predef1; export default Predef; +export default Predef1; diff --git a/hkmc2/shared/src/test/mlscript-compile/Predef.mls b/hkmc2/shared/src/test/mlscript-compile/Predef.mls index b02ae78268..c4f109f7dd 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Predef.mls +++ b/hkmc2/shared/src/test/mlscript-compile/Predef.mls @@ -1,3 +1,4 @@ +#lang(0.3.x) import "./RuntimeJS.mjs" @@ -23,31 +24,33 @@ fun id(x) = x fun maybe = hide(true) -fun (@) apply(f, ...args) = f(...args) +// Arbitrary variadic forwarding has no fixed callback domain. Keep this +// reflective calling convention explicit; the fixed-arity helpers use arrows. +fun (@) apply(f: dyn, ...args) = f(...args) -fun (|>) pipeInto(x, f) = f(x) -fun (<|) pipeFrom(f, x) = f(x) +fun (|>) pipeInto[A, B](x: A, f: A -> B): B = f(x) +fun (<|) pipeFrom[A, B](f: A -> B, x: A): B = f(x) // Same as above but with high precedence -fun (.>) pipeIntoHi(x, f) = f(x) -fun (<.) pipeFromHi(f, x) = f(x) +fun (.>) pipeIntoHi[A, B](x: A, f: A -> B): B = f(x) +fun (<.) pipeFromHi[A, B](f: A -> B, x: A): B = f(x) -fun (!>) tap(x, f) = f(x); x -fun () tap[A, B](x: A, f: A -> B): A = f(x); x +fun ( B, x: A): A = f(x); x fun (|!) alsoDo(x, eff) = x -fun (>>) andThen(f, g)(x) = g(f(x)) -fun (<<) compose(f, g)(x) = f(g(x)) +fun (>>) andThen[A, B, C](f: A -> B, g: B -> C)(x: A): C = g(f(x)) +fun (<<) compose[A, B, C](f: B -> C, g: A -> B)(x: A): C = f(g(x)) // x \foo(a, b, c) == foo(x, a, b, c) -fun (\) passTo(receiver, f)(...args) = f(receiver, ...args) +fun (\) passTo(receiver, f: dyn)(...args) = f(receiver, ...args) // Same as above but with low precedence -fun (|.) passToLo(receiver, f)(...args) = f(receiver, ...args) +fun (|.) passToLo(receiver, f: dyn)(...args) = f(receiver, ...args) // x |>. foo(a, b, c) == foo.call(x, a, b, c) -fun (|>.) call(receiver, f)(...args) = f.call(receiver, ...args) +fun (|>.) call(receiver, f: dyn)(...args) = f.call(receiver, ...args) object Symbols with @@ -70,17 +73,17 @@ type (>:>) Sup[A, B] = Sub[B, A] // * - arrays of values that are pairwise recursively equal; // * - instances of the same parameterized MLscript class when all fields are public and recursively equal. // TODO: compare lazy arrays and other sequences and collections -fun (==) equals(a, b) = if +fun (==) equals(a: Any, b: Any): Bool = if a === b then true - a is Array and b is Array and a.length === b.length then a.every((a, i) => a == b.at(i)) + a is Array as a and b is Array as b and a.length === b.length then a.every((a, i, ...) => a == b.at(i)) else a !== undefined and a !== null and b !== undefined and b !== null and - let ac = a.constructor - ac !== undefined and ac === b.constructor and - let md = ac.(Symbols.definitionMetadata) + let ac = a!constructor + ac !== undefined and ac === b!constructor and + let md = ac!(Symbols.definitionMetadata) md !== undefined and - md.2.every(field => field !== null and a.(field) == b.(field)) + md.2.every(field => field !== null and a!(field) == b!(field)) -fun (!=) nequals(a, b) = not a == b +fun (!=) nequals(a: Any, b: Any): Bool = not a == b fun pass1 = Rendering.pass1 @@ -113,7 +116,7 @@ fun mkSet(...xs) = new Set(xs) val foldl = fold // fun foldr(f)(...rest, init) = // TODO allow this syntax -fun foldr(f)(first, ...rest) = +fun foldr[A](f: (A, A) -> A)(first: A, ...rest: Array[A]): A = let len = rest.length if len === 0 then first else... let diff --git a/hkmc2/shared/src/test/mlscript-compile/QuoteExample.mls b/hkmc2/shared/src/test/mlscript-compile/QuoteExample.mls index 9013f3ea45..6efa69c1c5 100644 --- a/hkmc2/shared/src/test/mlscript-compile/QuoteExample.mls +++ b/hkmc2/shared/src/test/mlscript-compile/QuoteExample.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./Predef.mls" module QuoteExample with @@ -6,7 +8,7 @@ module QuoteExample with fun power(x) = case 0 then `1.0 n then x `*. power(x)(n - 1) - fun bind(rhs, k) = `let x = rhs `in k(x) + fun bind(rhs, k: Function) = `let x = rhs `in k(x) fun body(x, y) = case 0 then x 1 then y diff --git a/hkmc2/shared/src/test/mlscript-compile/QuoteExample2.mls b/hkmc2/shared/src/test/mlscript-compile/QuoteExample2.mls index 194eb24237..f95b092d17 100644 --- a/hkmc2/shared/src/test/mlscript-compile/QuoteExample2.mls +++ b/hkmc2/shared/src/test/mlscript-compile/QuoteExample2.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./Term.mls" import "./QuoteExample.mls" import "./QuoteExample1.mls" diff --git a/hkmc2/shared/src/test/mlscript-compile/Record.mls b/hkmc2/shared/src/test/mlscript-compile/Record.mls index c403501770..dc27aa273a 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Record.mls +++ b/hkmc2/shared/src/test/mlscript-compile/Record.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./Predef.mls" open Predef diff --git a/hkmc2/shared/src/test/mlscript-compile/RecordProducers.mls b/hkmc2/shared/src/test/mlscript-compile/RecordProducers.mls new file mode 100644 index 0000000000..846adda739 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript-compile/RecordProducers.mls @@ -0,0 +1,8 @@ +#lang(0.3.x, strictResolution: true) + +module RecordProducers { + val fields = {...{value: 36}} + // Resolving this selection records the `value` field in the shape of `fields`, + // before another file imports this module. + val observed = fields.value +} diff --git a/hkmc2/shared/src/test/mlscript-compile/Rendering.mls b/hkmc2/shared/src/test/mlscript-compile/Rendering.mls index 122d682765..058fac6ee7 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Rendering.mls +++ b/hkmc2/shared/src/test/mlscript-compile/Rendering.mls @@ -1,3 +1,4 @@ +#lang(0.3.x) import "./RuntimeJS.mjs" @@ -7,15 +8,15 @@ module Rendering with ... let definitionMetadataSymbol = RuntimeJS.symbols.definitionMetadata let prettyPrintSymbol = RuntimeJS.symbols.prettyPrint -fun pass1(f)(...xs) = f(xs.0) -fun pass2(f)(...xs) = f(xs.0, xs.1) -fun pass3(f)(...xs) = f(xs.0, xs.1, xs.2) +fun pass1[A, B](f: A -> B)(...xs): B = f(xs.0) +fun pass2[A, B, C](f: (A, B) -> C)(...xs): C = f(xs.0, xs.1) +fun pass3[A, B, C, D](f: (A, B, C) -> D)(...xs): D = f(xs.0, xs.1, xs.2) -fun passing(f, ...args) = f.bind(null, ...args) +fun passing(f: Function, ...args) = f.bind(null, ...args) -fun map(f)(...xs) = xs.map(pass1(f)) +fun map[A, B](f: A -> B)(...xs: Array[A]): Array[B] = xs.map(pass1(f)) -fun fold(f)(init, ...rest) = +fun fold[A, B](f: (A, B) -> A)(init: A, ...rest: Array[B]): A = let i = 0 len = rest.length @@ -84,7 +85,7 @@ private fun symbolsForMap(n: Int) = start: headline + " {", end: "}", entrySeparator: ",", keyValueSeparator: " => ", empty: headline + " {}", padding: true -fun render(target, ...args) = +fun render(target, ...args): Str = let options = if args is [options, ...] then options else emptyOptions indent = if @@ -118,7 +119,7 @@ fun render(target, ...args) = let prefix = if index === 0 then "" else indents prefix + line + postfix .join("\n") - fun renderArray(array, level, keyLength, startPos) = + fun renderArray(array: Array, level, keyLength, startPos) = let i = 0 length = array.length @@ -153,9 +154,9 @@ fun render(target, ...args) = valueStr else throw new Error("All items in this array has been rendered.") renderSequence of done, next, level, keyLength, startPos, symbolsForArray - fun renderSet(theSet, level, keyLength, startPos) = + fun renderSet(theSet: Set, level, keyLength, startPos) = let - iterator = theSet.(Symbol.iterator)() + iterator = theSet.values() peek = iterator.next() // The length of indentations that should be prepended to items in this // array if this array is displayed in multi-lines. @@ -176,7 +177,7 @@ fun render(target, ...args) = renderSequence of done, next, level, keyLength, startPos, symbolsForSet(theSet.size) // Render instances of MLscript classes. - fun renderClassInstance(constructorName, fieldNames, instance, level, keyLength, startPos) = + fun renderClassInstance(constructorName: Str, fieldNames: Array[Str | null], instance: Any, level, keyLength, startPos) = let length = fieldNames.length i = 0 @@ -193,7 +194,7 @@ fun render(target, ...args) = then itemIndentationLength else prefixLengthIfSameLine renderValue(value, level + 1, 0, valueStartLength) renderSequence of done, next, level, keyLength, startPos, symbolsForClass(constructorName) - fun renderSequence(done, next, level: Int, keyLength: Int, startPos: Int, symbols) = + fun renderSequence(done: () -> Bool, next: (Bool, Int) -> Str, level: Int, keyLength: Int, startPos: Int, symbols: {start: Str, end: Str, separator: Str, empty: Str, padding: Bool}) = let items = mut [] hasMultiline = false @@ -241,7 +242,7 @@ fun render(target, ...args) = itemIndentationLength = if indent is null then 0 else (level + 1) * indent.length let skipNonEnumerable() = - while i < length and entries.[i].1.enumerable is false do set i += 1 + while i < length and entries.[i].1!enumerable is false do set i += 1 let done() = i >= length let next(isFirst: Bool, prefixLength: Int) = let @@ -270,9 +271,9 @@ fun render(target, ...args) = skipNonEnumerable() renderRecordLike of done, next, level, keyLength, startPos, symbolsForObject, isRefinement - fun renderMap(map, level, keyLength, startPos) = + fun renderMap(map: Map, level, keyLength, startPos) = let - iterator = map.(Symbol.iterator)() + iterator = map.entries() peek = iterator.next() // The length of indentations that should be prepended to items in this // array if this array is displayed in multi-lines. @@ -301,7 +302,7 @@ fun render(target, ...args) = [keyStr, valueStr] renderRecordLike of done, next, level, keyLength, startPos, symbolsForMap(map.size), false - fun renderRecordLike(done, next, level, keyLength, startPos, symbols, isRefinement) = + fun renderRecordLike(done: () -> Bool, next: (Bool, Int) -> [Str, Str], level, keyLength, startPos, symbols: {start: Str, end: Str, entrySeparator: Str, keyValueSeparator: Str, empty: Str, padding: Bool}, isRefinement) = let startPadding = if isRefinement then " " @@ -347,17 +348,17 @@ fun render(target, ...args) = let lengthIfBreakParent = parentIndent.length + (singleLineLength - startPos + keyLength) lengthIfBreakParent <= breakLength then layoutSingleLine() else layoutMultiLine() - fun renderValue(arg, level, keyLength, startPos) = if + fun renderValue(arg: Any, level: Int, keyLength: Int, startPos: Int): Str = if arg is // Primitive values undefined then "undefined" null then "null" Str then JSON.stringify(arg) - BigInt then arg.toString() + "n" - Symbol and + BigInt as arg then arg.toString() + "n" + Symbol as arg and arg.description is undefined then "Symbol()" else "Symbol(\"" + arg.description + "\")" - Num then arg.toString() - RegExp then arg.toString() + Num as arg then arg.toString() + RegExp as arg then arg.toString() true then "true" false then "false" arg === () then "()" @@ -383,15 +384,15 @@ fun render(target, ...args) = Map then renderMap of arg, level, keyLength, startPos WeakSet then "WeakSet { }" WeakMap then "WeakMap { }" - Error then arg.name + ": " + arg.message - Function then + Error as arg then arg.name + ": " + arg.message + Function as arg then let desc = Object.getOwnPropertyDescriptor(arg, "prototype") let head = if // ES 2015 classes: `desc.writable` is `false`. - desc is Object and desc.writable is false and + desc is Object and desc!writable is false and // If it is from MLscript, use the kind written in metadata. - desc.value.constructor.hasOwnProperty(definitionMetadataSymbol) and - desc.value.constructor.(definitionMetadataSymbol) is [kind, name, ...] then + desc!value!constructor!hasOwnProperty(definitionMetadataSymbol) and + desc!value!constructor!(definitionMetadataSymbol) is [kind, name, ...] then kind + " " + name arg.name is "" then "class" // Show "class" if there is no name. else "class " + arg.name // Or, we can use the name. @@ -426,19 +427,19 @@ fun render(target, ...args) = // patterns, or parameter-less classes), use the constructor name. definitionMetadata is [kind, name] then // For class instances and objects, do not show the kind. - let head = if kind is + let head = (if kind is "class" | "object" then name - else kind + " " + name + else kind + " " + name) as Str let body = renderObject of arg, level, keyLength, startPos + head.length, true head + (if body is "{}" then "" else " " + body) // Otherwise, we print the name of the constructor and the properties. - let head = proto.constructor.name + " " + let head = (proto.constructor.name + " ") as Str else head + renderObject of arg, level, keyLength, startPos + head.length, true // Otherwise, the prototype is not a function. // If the prototype has its own implementation of `toString`, call it. - proto.hasOwnProperty("toString") then arg.toString() indentText(level) + proto.hasOwnProperty("toString") then arg!toString() indentText(level) // Otherwise, treat the input as a normal object. else renderObject(arg, level, keyLength, startPos, false) // We have finished visiting this object. diff --git a/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs b/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs index 7d6625ea4b..539d071b2c 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs @@ -44,11 +44,11 @@ lambda3 = (undefined, function (Runtime2, promise, resume) { static [definitionMetadata] = ["class", "Capture$scope32"]; }); lambda$3 = (undefined, function (scope32$cap, cont) { - return (m, marker) => { - return lambda2(scope32$cap, cont, m, marker) + return (m, marker, ..._) => { + return lambda2(scope32$cap, cont, m, marker, ..._) } }); -lambda2 = (undefined, function (scope32$cap, cont, m, marker) { +lambda2 = (undefined, function (scope32$cap, cont, m, marker, ..._) { let scrut, tmp, tmp1; scrut = runtime.safeCall(m.has(cont)); if (scrut === true) { @@ -70,11 +70,11 @@ lambda2 = (undefined, function (scope32$cap, cont, m, marker) { static [definitionMetadata] = ["class", "Capture$scope30"]; }); lambda$2 = (undefined, function (scope30$cap, cont) { - return (m, marker) => { - return lambda1(scope30$cap, cont, m, marker) + return (m, marker, ..._) => { + return lambda1(scope30$cap, cont, m, marker, ..._) } }); -lambda1 = (undefined, function (scope30$cap, cont, m, marker) { +lambda1 = (undefined, function (scope30$cap, cont, m, marker, ..._) { let scrut, tmp, tmp1; scrut = runtime.safeCall(m.has(cont)); if (scrut === true) { @@ -85,7 +85,7 @@ lambda1 = (undefined, function (scope30$cap, cont, m, marker) { } return runtime.Unit; }); -lambda = (undefined, function (l) { +lambda = (undefined, function (l, ..._) { let tmp, tmp1; tmp = l.localName + "="; tmp1 = runtime.safeCall(Rendering.render(l.value)); @@ -206,9 +206,7 @@ lambda4 = (undefined, function (Runtime2, EffectHandle1, value) { } #_reified; resumeWith(value) { - let lambda$here; - lambda$here = lambda$(Runtime, this, value); - return Runtime._try(lambda$here) + return Runtime._try(lambda$(Runtime, this, value)) } raise() { Runtime.curEffect = this.reified; @@ -283,9 +281,9 @@ lambda4 = (undefined, function (Runtime2, EffectHandle1, value) { toString() { return runtime.render(this); } static [definitionMetadata] = ["class", "Tuple"]; }); - (class Str { + (class StrOps { static { - Runtime.Str = this + Runtime.StrOps = this } static startsWith(string, prefix) { return runtime.safeCall(string.startsWith(prefix)) @@ -305,7 +303,7 @@ lambda4 = (undefined, function (Runtime2, EffectHandle1, value) { return runtime.safeCall(string.slice(n)) } toString() { return runtime.render(this); } - static [definitionMetadata] = ["class", "Str"]; + static [definitionMetadata] = ["class", "StrOps"]; }); Runtime.render = Rendering.render; (class TraceLogger { @@ -795,7 +793,7 @@ lambda4 = (undefined, function (Runtime2, EffectHandle1, value) { } if (tr instanceof Runtime.EffectSig.class) { Runtime.curEffect = null; - tmp = "Error: Unhandled effect " + tr.handler.constructor.name; + tmp = "Error: Unhandled effect " + tr.handler["constructor"]["name"]; tmp1 = Runtime.showStackTrace(tmp, tr, debug, false); throw Runtime.CustomStackError(tmp1) } @@ -805,7 +803,7 @@ lambda4 = (undefined, function (Runtime2, EffectHandle1, value) { let tmp, tmp1, tmp2, tmp3, tmp4; tmp = Runtime.curEffect; Runtime.curEffect = null; - tmp1 = "Error: Effect " + tmp.handler.constructor.name; + tmp1 = "Error: Effect " + tmp.handler["constructor"]["name"]; tmp2 = tmp1 + " is raised "; tmp3 = tmp2 + position; tmp4 = Runtime.showStackTrace(tmp3, tmp, false, false); @@ -856,7 +854,7 @@ lambda4 = (undefined, function (Runtime2, EffectHandle1, value) { curHandler = curHandler.nextHandler; scrut1 = curHandler !== null; if (scrut1 === true) { - tmp = "\n\twith handler " + curHandler.handler.constructor.name; + tmp = "\n\twith handler " + curHandler.handler["constructor"]["name"]; tmp1 = msg + tmp; msg = tmp1; atTail = false; @@ -874,18 +872,17 @@ lambda4 = (undefined, function (Runtime2, EffectHandle1, value) { return header; } static showFunctionContChain(cont, hl, vis, reps) { - let scrut, scrut1, scrut2, tmp, tmp1, tmp2, tmp3, tmp4, scope30$cap, lambda$here; + let scrut, scrut1, scrut2, tmp, tmp1, tmp2, tmp3, tmp4, scope30$cap; scope30$cap = new Capture$scope301(undefined); if (cont instanceof Runtime.FunctionContFrame.class) { - tmp = cont.constructor.name + "(pc="; + tmp = cont["constructor"]["name"] + "(pc="; scope30$cap.result$0 = tmp + cont.saved.at(1); - lambda$here = lambda$2(scope30$cap, cont); - runtime.safeCall(hl.forEach(lambda$here)); + runtime.safeCall(hl.forEach(lambda$2(scope30$cap, cont))); scrut = runtime.safeCall(vis.has(cont)); if (scrut === true) { tmp1 = reps + 1; reps = tmp1; - scrut1 = tmp1 > 10; + scrut1 = reps > 10; if (scrut1 === true) { throw runtime.safeCall(globalThis.Error("10 repeated continuation frame (loop?)")) } @@ -905,17 +902,16 @@ lambda4 = (undefined, function (Runtime2, EffectHandle1, value) { return "(NOT CONT)"; } static showHandlerContChain(cont, hl, vis, reps) { - let scrut, scrut1, scrut2, tmp, tmp1, tmp2, tmp3, scope32$cap, lambda$here; + let scrut, scrut1, scrut2, tmp, tmp1, tmp2, tmp3, scope32$cap; scope32$cap = new Capture$scope321(undefined); if (cont instanceof Runtime.HandlerContFrame.class) { - scope32$cap.result$0 = cont.handler.constructor.name; - lambda$here = lambda$3(scope32$cap, cont); - runtime.safeCall(hl.forEach(lambda$here)); + scope32$cap.result$0 = cont.handler["constructor"]["name"]; + runtime.safeCall(hl.forEach(lambda$3(scope32$cap, cont))); scrut = runtime.safeCall(vis.has(cont)); if (scrut === true) { tmp = reps + 1; reps = tmp; - scrut1 = tmp > 10; + scrut1 = reps > 10; if (scrut1 === true) { throw runtime.safeCall(globalThis.Error("10 repeated continuation frame (loop?)")) } @@ -994,7 +990,7 @@ lambda4 = (undefined, function (Runtime2, EffectHandle1, value) { static debugEff(eff) { if (eff instanceof Runtime.EffectSig.class) { runtime.safeCall(globalThis.console.log("Debug EffectSig:")); - runtime.safeCall(globalThis.console.log("handler: ", eff.handler.constructor.name)); + runtime.safeCall(globalThis.console.log("handler: ", eff.handler["constructor"]["name"])); runtime.safeCall(globalThis.console.log("handlerFun: ", eff.handlerFun)); return Runtime.debugContTrace(eff.contTrace) } @@ -1139,7 +1135,7 @@ lambda4 = (undefined, function (Runtime2, EffectHandle1, value) { try { tmp1 = Runtime.stackDepth + 3; Runtime.stackDepth = tmp1; - tmp2 = runtime.safeCall(cont.resume(value)); + tmp2 = cont.resume(value); tmp = tmp2; } finally { Runtime.stackDepth = old; @@ -1195,7 +1191,7 @@ lambda4 = (undefined, function (Runtime2, EffectHandle1, value) { if (tmp === true) { tmp1 = Runtime.stackHandler !== null; if (tmp1 === true) { - return runtime.safeCall(Runtime.stackHandler.delay()) + return Runtime.stackHandler.delay() } return runtime.Unit; } @@ -1276,4 +1272,4 @@ export { Capture$scope321 as _$_modulePrivate_$_Capture$scope32 }; export { lambda$3 as _$_modulePrivate_$_lambda$3 }; export { lambda$4 as _$_modulePrivate_$_lambda$4 }; export { continuation$ as _$_modulePrivate_$_continuation$ }; -let Runtime = Runtime1; export default Runtime; +export default Runtime1; diff --git a/hkmc2/shared/src/test/mlscript-compile/Runtime.mls b/hkmc2/shared/src/test/mlscript-compile/Runtime.mls index 82d5904012..5294741092 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Runtime.mls +++ b/hkmc2/shared/src/test/mlscript-compile/Runtime.mls @@ -1,3 +1,4 @@ +#lang(0.3.x) import "./RuntimeJS.mjs" import "./Rendering.mjs" import "./LazyArray.mjs" @@ -25,10 +26,10 @@ val short_or = RuntimeJS.short_or fun unreachable = throw Error("unreachable") -fun assertFail(file, line) = throw Error("Assertion failed (" + file + ":" + line + ")") +fun assertFail(file: Str, line: Str) = throw Error("Assertion failed (" + file + ":" + line + ")") -fun checkArgs(functionName, expected, isUB, got) = +fun checkArgs(functionName: Str, expected: Int, isUB: Bool, got: Int) = if got < expected || isUB && got > expected do let name = if functionName.length > 0 then " '" + functionName + "'" else "" // throw globalThis.Error("Function" + name + " expected " @@ -67,14 +68,14 @@ val bitor = RuntimeJS.bitor val shl = RuntimeJS.shl val try_catch = RuntimeJS.try_catch -class EffectHandle(_reified) with +class EffectHandle(_reified: EffectSig) with val reified = _reified fun resumeWith(value) = _try(() => resume(reified.contTrace)(value)) fun raise() = set curEffect = reified -fun _try(f) = +fun _try(f: () -> Any) = let res = f() if curEffect !== null then let tmp = curEffect @@ -84,48 +85,53 @@ fun _try(f) = // For `pattern` definitions -data class MatchSuccess(output, bindings) +data class MatchSuccess[out A, out B](output: A, bindings: B) data class MatchFailure(errors) +// Runtime pattern objects expose their transformed output through MatchSuccess. +type MatchResult[A] = MatchSuccess[A, Any] | MatchFailure +type Pattern[A, B] = { unapply: A -> MatchResult[B] } + // For pattern matching on tuples module Tuple with @pure - fun slice(xs, i, j) = xs.slice(i, xs.length - j) + fun slice(xs: Indexable, i: Int, j: Int) = xs.slice(i, xs.length - j) @pure - fun lazySlice(xs, i, j) = LazyArray.dropLeftRight(i, j)(xs) + fun lazySlice(xs: Indexable, i: Int, j: Int) = LazyArray.dropLeftRight(i, j)(xs) @pure fun lazyConcat(...args) = LazyArray.__concat(...args) @pure - fun get(xs, i) = + fun get(xs: Indexable, i: Int) = if i >= xs.length do throw RangeError("Tuple.get: index out of bounds") if i < -xs.length do throw RangeError("Tuple.get: negative index out of bounds") - xs.[i] + // Raw indexing preserves undefined elements; a foreign method call would normalize them to Unit. + xs![i] @pure fun isArrayLike(xs) = Iter.isArrayLike(xs) val split = LazyArray.__split -module Str with +module StrOps with @pure - fun startsWith(string, prefix) = string.startsWith(prefix) + fun startsWith(string: Str, prefix: Str) = string.startsWith(prefix) @pure - fun get(string, i) = + fun get(string: Str, i: Int) = if i >= string.length then throw RangeError("Str.get: index out of bounds") else string.at(i) @pure - fun take(string, n) = string.slice(0, n) + fun take(string: Str, n: Int) = string.slice(0, n) @pure - fun leaveOut(string, n) = string.slice(n) + fun leaveOut(string: Str, n: Int) = string.slice(n) // Re-export rendering functions val render = Rendering.render @@ -143,11 +149,11 @@ module TraceLogger with set indentLvl = prev + 1 prev else () - fun resetIndent(n) = + fun resetIndent(n: Int) = if enabled then set indentLvl = n else () - fun log(msg) = + fun log(msg: Str) = if enabled then console.log("| ".repeat(indentLvl) + msg.replaceAll("\n", "\n" + " ".repeat(indentLvl))) else () @@ -157,8 +163,10 @@ module TraceLogger with // layout: // | f | pc | pcStr | debugInfo | this | argLists... (*) | localCnt | locals... | // (*) argLists is length encoded nested array +// The packed array mixes functions, counters, debug tables, and arbitrary saved values. +// Casts below decode slots whose types are fixed by this layout. -mut val curEffect = null +mut val curEffect: EffectSig | null = null mut val resumeValue = null mut val resumeArr = null mut val resumeIdx = null @@ -167,11 +175,11 @@ mut val resumePc = -1 object FatalEffect object PrintStackEffect -data class FunctionContFrame(next, saved) with +data class FunctionContFrame(next: FunctionContFrame | null, saved: Array) with fun resume(value) = let i = 0 - f = saved.[0] + f = saved.[0] as Function argListsLength = saved.[5] currentArgList = 6 set @@ -183,15 +191,15 @@ data class FunctionContFrame(next, saved) with while i < argListsLength - 1 do let argListLength = saved.[currentArgList] set - f = f.apply(saved.[4], saved.slice(currentArgList + 1, currentArgList + 1 + argListLength)) + f = f.apply(saved.[4], saved.slice(currentArgList + 1, currentArgList + 1 + argListLength)) as Function currentArgList += argListLength + 1 i += 1 let argListLength = saved.[currentArgList] set resumeIdx = currentArgList + argListLength + 2 f.apply(saved.[4], saved.slice(currentArgList + 1, currentArgList + 1 + argListLength)) - fun getLocals = - let debugInfo = saved.[3] + fun getLocals: Array[LocalVarInfo] = + let debugInfo = saved.[3] as Array let i = 0 let cur = 6 while i < saved.[5] do @@ -204,24 +212,31 @@ data class FunctionContFrame(next, saved) with res.push(new LocalVarInfo(debugInfo.[i + 1], saved.[cur + 1 + debugInfo.[i]])) set i += 2 res - fun getNme = saved.[3].[0] + fun getNme = (saved.[3] as Array).[0] fun getLoc = let loc = saved.[2] if loc is null then "pc=" + saved.[1] else loc -data class HandlerContFrame(next, nextHandler, handler) +data class HandlerContFrame(next: FunctionContFrame | null, nextHandler: HandlerContFrame | null, handler: Any) -data class ContTrace(next, last, nextHandler, lastHandler, resumed) -data class EffectSig(contTrace, handler, handlerFun) +data class ContTrace( + next: FunctionContFrame | null, + last: ContTrace | HandlerContFrame | FunctionContFrame | null, + nextHandler: HandlerContFrame | null, + lastHandler: ContTrace | HandlerContFrame | null, + resumed: Bool +) +// PrintStackEffect stores the showLocals flag; FatalEffect has no handler function. +data class EffectSig(contTrace: ContTrace, handler: Any, handlerFun: Function | Bool | null) -class NonLocalReturn with +abstract class NonLocalReturn with fun ret(value) -data class FnLocalsInfo(fnName, locals) -data class LocalVarInfo(localName, value) -data class CustomStackError(stack) with +data class FnLocalsInfo(fnName: Str, locals: Array[LocalVarInfo]) +data class LocalVarInfo(localName: Str, value: Any) +data class CustomStackError(stack: Str) with fun toString() = stack @@ -244,16 +259,16 @@ fun topLevelEffect(debug) = tr = curEffect if tr is EffectSig then set curEffect = null - throw CustomStackError(showStackTrace("Error: Unhandled effect " + tr.handler.constructor.name, tr, debug, false)) + throw CustomStackError(showStackTrace("Error: Unhandled effect " + tr.handler!constructor!name, tr, debug, false)) else v fun illegalEffect(position) = let tmp = curEffect set curEffect = null - throw CustomStackError(showStackTrace("Error: Effect " + tmp.handler.constructor.name + " is raised " + position, tmp, false, false)) + throw CustomStackError(showStackTrace("Error: Effect " + tmp.handler!constructor!name + " is raised " + position, tmp, false, false)) -fun showStackTrace(header, tr, debug, showLocals) = +fun showStackTrace(header: Str, tr: EffectSig, debug: Bool, showLocals: Bool) = let msg = header curHandler = tr.contTrace @@ -265,7 +280,7 @@ fun showStackTrace(header, tr, debug, showLocals) = let curLocals = cur.getLocals let loc = cur.getLoc let localsMsg = if showLocals and curLocals.length > 0 - then " with locals: " + curLocals.map(l => l.localName + "=" + Rendering.render(l.value)).join(", ") + then " with locals: " + curLocals.map((l, ...) => l.localName + "=" + Rendering.render(l.value)).join(", ") else "" set msg += "\n\tat " + cur.getNme + " (" + loc + ")" @@ -275,16 +290,16 @@ fun showStackTrace(header, tr, debug, showLocals) = set curHandler = curHandler.nextHandler if curHandler !== null do set - msg += "\n\twith handler " + curHandler.handler.constructor.name + msg += "\n\twith handler " + curHandler.handler!constructor!name atTail = false if atTail do set msg += "\n\tat tail position" msg -fun showFunctionContChain(cont, hl, vis, reps) = - if cont is FunctionContFrame then - let result = cont.constructor.name + "(pc=" + cont.saved.[1] - hl.forEach((m, marker) => if m.has(cont) do set result += ", " + marker) +fun showFunctionContChain(cont: Any, hl: Map[Str, Set], vis: Set, reps: Int): Str = + if cont is FunctionContFrame as frame then + let result = frame!constructor!name + "(pc=" + frame.saved.[1] + hl.forEach((m, marker, ...) => if m.has(cont) do set result += ", " + marker) if vis.has(cont) then set reps = reps + 1 if reps > 10 do @@ -292,16 +307,16 @@ fun showFunctionContChain(cont, hl, vis, reps) = set result += ", REPEAT" else vis.add(cont) - result + ") -> " + showFunctionContChain(cont.next, hl, vis, reps) + result + ") -> " + showFunctionContChain(frame.next, hl, vis, reps) else if cont === null then "(null)" else "(NOT CONT)" -fun showHandlerContChain(cont, hl, vis, reps) = - if cont is HandlerContFrame then - let result = cont.handler.constructor.name - hl.forEach((m, marker) => if m.has(cont) do set result += ", " + marker) +fun showHandlerContChain(cont: Any, hl: Map[Str, Set], vis: Set, reps: Int): Str = + if cont is HandlerContFrame as frame then + let result = frame.handler!constructor!name + hl.forEach((m, marker, ...) => if m.has(cont) do set result += ", " + marker) if vis.has(cont) then set reps = reps + 1 if reps > 10 do @@ -309,7 +324,7 @@ fun showHandlerContChain(cont, hl, vis, reps) = set result += ", REPEAT" else vis.add(cont) - result + " -> " + showFunctionContChain(cont.next, hl, vis, reps) + result + " -> " + showFunctionContChain(frame.next, hl, vis, reps) else if cont === null then "(null)" else @@ -319,7 +334,7 @@ fun debugCont(cont) = console.log(showFunctionContChain(cont, new Map(), new Set fun debugHandler(cont) = console.log(showHandlerContChain(cont, new Map(), new Set(), 0)) fun debugContTrace(contTrace) = - if contTrace is ContTrace then + if contTrace is ContTrace as contTrace then console.log("resumed: ", contTrace.resumed) if contTrace.last === contTrace do console.log("") @@ -340,9 +355,9 @@ fun debugContTrace(contTrace) = console.log(contTrace) fun debugEff(eff) = - if eff is EffectSig then + if eff is EffectSig as eff then console.log("Debug EffectSig:") - console.log("handler: ", eff.handler.constructor.name) + console.log("handler: ", eff.handler!constructor!name) console.log("handlerFun: ", eff.handlerFun) debugContTrace(eff.contTrace) else @@ -355,14 +370,14 @@ fun unwind(...saved) = curEffect.contTrace.last.next = new mut FunctionContFrame(null, saved) curEffect.contTrace.last = curEffect.contTrace.last.next -fun mkEffect(handler, handlerFun) = +fun mkEffect(handler: Any, handlerFun: Function | Bool | null) = let res = new mut EffectSig(new mut ContTrace(null, null, null, null, false), handler, handlerFun) set res.contTrace.last = res.contTrace res.contTrace.lastHandler = res.contTrace curEffect = res -fun handleBlockImpl(cur, handler) = +fun handleBlockImpl(cur: EffectSig, handler: Any) = let handlerFrame = new mut HandlerContFrame(null, null, handler) set cur.contTrace.lastHandler.nextHandler = handlerFrame @@ -370,17 +385,17 @@ fun handleBlockImpl(cur, handler) = cur.contTrace.last = handlerFrame handleEffects(cur) -fun enterHandleBlock(handler, body) = +fun enterHandleBlock(handler: Any, body: () -> Any) = let tmp = body() if curEffect === null then tmp else handleBlockImpl(curEffect, handler) -fun handleEffects(cur) = +fun handleEffects(cur: Any): Any = while cur is - EffectSig then - let nxt = handleEffect(cur) + EffectSig as effect then + let nxt = handleEffect(effect) if cur === nxt then set curEffect = cur return null @@ -390,7 +405,7 @@ fun handleEffects(cur) = return cur // return either new effect, final result or the same continuation if there is no handler -fun handleEffect(cur) = +fun handleEffect(cur: EffectSig): Any = // debugEff(cur) // find the handle block corresponding to the current effect let prevHandlerFrame = cur.contTrace @@ -424,7 +439,7 @@ fun handleEffect(cur) = // handle the effect set curEffect = null - let tmp = set stackDepth += 2 in cur.handlerFun(resume(cur.contTrace)) + let tmp = set stackDepth += 2 in (cur.handlerFun as Function)(resume(cur.contTrace)) if curEffect !== null then set cur = curEffect // relink the saved frames @@ -441,13 +456,13 @@ fun handleEffect(cur) = // resume the unlinked handle blocks resumeContTrace(saved, tmp) -fun resume(contTrace)(value) = +fun resume(contTrace: ContTrace)(value: Any): Any = if contTrace.resumed do throw Error("Multiple resumption") set contTrace.resumed = true handleEffects(resumeContTrace(contTrace, value)) -fun resumeContTrace(contTrace, value) = +fun resumeContTrace(contTrace: ContTrace, value: Any): Any = let cont = contTrace.next let handlerCont = contTrace.nextHandler while @@ -456,14 +471,14 @@ fun resumeContTrace(contTrace, value) = set value = set stackDepth += 3 in cont.resume(value) if curEffect !== null do set value = curEffect - if value is EffectSig then + if value is EffectSig as effect then set - value.contTrace.last.next = cont.next - value.contTrace.lastHandler.nextHandler = handlerCont + effect.contTrace.last.next = cont.next + effect.contTrace.lastHandler.nextHandler = handlerCont if contTrace.last !== cont do - set value.contTrace.last = contTrace.last + set effect.contTrace.last = contTrace.last if handlerCont !== null do - set value.contTrace.lastHandler = contTrace.lastHandler + set effect.contTrace.lastHandler = contTrace.lastHandler return value else set cont = cont.next @@ -476,8 +491,8 @@ fun resumeContTrace(contTrace, value) = // js async object AsyncEffectMarker -fun await(promise) = - mkEffect(AsyncEffectMarker, resume => +fun await(promise: Promise) = + mkEffect(AsyncEffectMarker, (resume: Any -> Any) => fun continuation(value) = let r = resume(value) if curEffect !== null do @@ -486,7 +501,7 @@ fun await(promise) = promise.then(continuation) ) -fun toJsAsync(thunk) = +fun toJsAsync(thunk: () -> Any) = let r = enterHandleBlock(AsyncEffectMarker, thunk) if curEffect !== null do illegalEffect("in exported async function") @@ -505,11 +520,11 @@ object StackDelayHandler with fun checkDepth() = if stackDepth >= stackLimit && stackHandler !== null then // this is a tail call to effectful function - stackHandler.delay() + (stackHandler as StackDelayHandler).delay() else () -fun runStackSafe(limit, f) = +fun runStackSafe(limit: Int, f: () -> Any) = set stackLimit = limit in... set stackDepth = 1 in... set stackHandler = StackDelayHandler in... @@ -517,7 +532,7 @@ fun runStackSafe(limit, f) = if curEffect !== null do throw new Error("Effect crossed through stack safe boundary") while stackResume !== null do - let saved = stackResume + let saved = stackResume as Function set stackResume = null stackDepth = 1 @@ -526,7 +541,7 @@ fun runStackSafe(limit, f) = throw new Error("Effect crossed through stack safe boundary") result -class Int31(v) with +class Int31(v: Int) with fun zext() = bitand(v, bitnot(shl(1, 31))) as Int fun sext() = bitor(v, shl(1, 31)) as Int diff --git a/hkmc2/shared/src/test/mlscript-compile/SealedClasses.mls b/hkmc2/shared/src/test/mlscript-compile/SealedClasses.mls new file mode 100644 index 0000000000..4e4d72e388 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript-compile/SealedClasses.mls @@ -0,0 +1,14 @@ +#lang(0.3.x, strictResolution: true) + +module SealedClasses with + class Closed { fun value() = 1 } + open class Open { fun value() = 2 } + abstract class Abstract { fun value() } + // Sealing restricts direct subclasses; a local subclass may itself be open. + open class OpenChild extends Closed + class ClosedChild extends Open + val alias = Closed + module Nested with + class Closed + class Parameterized[A](val item: A) + open class OpenParameterized[A](val item: A) diff --git a/hkmc2/shared/src/test/mlscript-compile/Shape.mls b/hkmc2/shared/src/test/mlscript-compile/Shape.mls index d8a3dcca30..1b0a83f50b 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Shape.mls +++ b/hkmc2/shared/src/test/mlscript-compile/Shape.mls @@ -1,7 +1,11 @@ +#lang(0.3.x) import "./Block.mls" import "./Option.mls" +import "./Predef.mls" -open Block { Literal, ClassSymbol, showSymbol, isPrimitiveType, isPrimitiveTypeOf } +open Predef { pass1 } + +open Block { Literal, ClassSymbol, ConcreteClassSymbol, showSymbol, isPrimitiveType, isPrimitiveTypeOf } open Option type Shape = Shape.Shape @@ -19,16 +23,14 @@ fun show(s: Shape) = if s is Dyn then "Dyn" Lit(lit) then "Lit(" + Block.showLiteral(lit) + ")" - Arr(shapes) then "Arr(" + shapes.map(show).join(", ") + ")" - Class(sym, params) then "Class(" + showSymbol(sym) + ", [" + params.map(show).join(", ") + "])" + Arr(shapes) then "Arr(" + shapes.map(pass1(show)).join(", ") + ")" + Class(sym, params) then "Class(" + showSymbol(sym) + ", [" + params.map(pass1(show)).join(", ") + "])" fun sel(s1: Shape, s2: Shape): Array[Shape] = if [s1, s2] is - [Class(sym, params), Lit(n)] and n is Str - and sym.args is Some(args) - and args.find(_ == n) - == () then [] - is i then [params.(i)] + [Class(ConcreteClassSymbol(_, _, Some(args), _), params), Lit(n)] and n is Str then + let i = args.findIndex(pass1((arg: Block.Symbol) => arg.name == n)) + if i >= 0 then [params.(i)] else [] [Dyn, Lit(n)] and n is Str then [Dyn()] [Arr(shapes), Lit(n)] and n is Int @@ -44,18 +46,18 @@ fun sel(s1: Shape, s2: Shape): Array[Shape] = fun static(s: Shape) = if s is Dyn then false - Lit(l) then not (l is Str and isPrimitiveType(l)) // redundant bracket? - Class(_, params) then params.every(static) - Arr(shapes) then shapes.every(static) + Lit(l) then not (l is Str and isPrimitiveType(Block.Symbol(l))) // redundant bracket? + Class(_, params) then params.every(pass1(static)) + Arr(shapes) then shapes.every(pass1(static)) open Block { Case } fun silh(p: Case): Shape = if p is Block.Lit(l) then Lit(l) Block.Cls(sym, path) then - val size = if sym.args is Some(i) then i else 0 - Class(sym, Array(size).fill(Dyn)) - Block.Tup(n) then Arr(Array(n).fill(Dyn)) + val size = if sym is ConcreteClassSymbol(_, _, Some(params), _) then params.length else 0 + Class(sym, Array(size).fill(Dyn())) + Block.Tup(n) then Arr(Array(n).fill(Dyn())) // TODO: use Option instead, since all of them return at most one shape fun filter(s: Shape, p: Case): Array[Shape] = diff --git a/hkmc2/shared/src/test/mlscript-compile/Stack.mls b/hkmc2/shared/src/test/mlscript-compile/Stack.mls index 0865d2a1c5..646711a743 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Stack.mls +++ b/hkmc2/shared/src/test/mlscript-compile/Stack.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + type Stack[A] = Stack.{ Cons[A] | Nil } @@ -17,7 +19,7 @@ fun reverseAndAppend(xs, tail) = if xs is fun reverse(xs) = reverseAndAppend(xs, Nil) -fun fromArray(arr) = +fun fromArray(arr: Array) = let ls = Nil i = arr.length - 1 @@ -70,7 +72,7 @@ fun (:::) concat(xs, ys) = if fun (:+) append(xs, y) = xs ::: y :: Nil -fun filter(xs, f) = if xs is +fun filter(xs, f: Function) = if xs is head :: tail and f(head) then head :: filter(tail, f) else filter(tail, f) diff --git a/hkmc2/shared/src/test/mlscript-compile/StrOps.mls b/hkmc2/shared/src/test/mlscript-compile/StrOps.mls index 2215b041ff..cc0e948a32 100644 --- a/hkmc2/shared/src/test/mlscript-compile/StrOps.mls +++ b/hkmc2/shared/src/test/mlscript-compile/StrOps.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + module StrOps with ... diff --git a/hkmc2/shared/src/test/mlscript-compile/Term.mls b/hkmc2/shared/src/test/mlscript-compile/Term.mls index 6f71a4541c..0b1225835a 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Term.mls +++ b/hkmc2/shared/src/test/mlscript-compile/Term.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "fs" import "process" import "path" @@ -71,17 +73,17 @@ class Term with class Context(val names: Map[Str, Int], val bindings: Map[Symbol, Str], val dependencies: Set[Str], val printOnly: Bool) with - fun get(sym) = - if bindings.has(sym) then bindings.get(sym) + fun get(sym: Symbol): Str = + if bindings.get(sym) is ~undefined as name then name else if printOnly then sym.name else throw Error(StrOps.concat("Invalid binding name ", sym.name)) - fun nest = Context(names, new Map(bindings), dependencies, printOnly) - fun add(sym) = + fun nest: Context = Context(names, new Map(bindings), dependencies, printOnly) + fun add(sym: Symbol): Str = let fn = freshName(sym.name) bindings.set(sym, fn) fn - fun depends(d) = dependencies.add(d) - fun freshName(name) = + fun depends(d: Str) = dependencies.add(d) + fun freshName(name: Str): Str = if not names.has(name) do names.set(name, 0) let i = names.get(name) @@ -89,12 +91,12 @@ class Context(val names: Map[Str, Int], val bindings: Map[Symbol, Str], val depe StrOps.concat of name, "_", i.toString() -fun indent(str, ind, keepLeading) = - let res = str.split("\n").map(s => StrOps.concat(ind, s)).join("\n") +fun indent(str: Str, ind: Str, keepLeading: Bool): Str = + let res = str.split("\n").map((s, ...rest) => StrOps.concat(ind, s)).join("\n") if keepLeading then res else res.substring(ind.length) -fun showStmt(s, ctx) = +fun showStmt(s: Statement, ctx: Context): Str = if s is LetDecl(sym) then let freshName = ctx.add(sym) @@ -102,18 +104,18 @@ fun showStmt(s, ctx) = DefineVar(sym, value) then StrOps.concat of ctx.get(sym), " = ", show(value, ctx) -fun showPattern(p, ctx) = +fun showPattern(p: Pattern, ctx: Context): Str = if p is - LitPattern(lit) then lit.toString() + LitPattern(lit) then String(lit) // TODO: other patterns -fun paren(t, ctx) = +fun paren(t: Term, ctx: Context): Str = if (t is Ref) || (t is CSRef) || (t is Lit) || (t is Sel) then show(t, ctx) else StrOps.concat of "(", show(t, ctx), ")" -fun showSplit(s, ctx, isCont) = +fun showSplit(s: Split, ctx: Context, isCont: Bool): Str = if s is Cons(Branch(scrut, ptrn, cont), tail) then StrOps.concat of show(scrut, ctx), " is ", showPattern(ptrn, ctx), " then ", showSplit(cont, ctx, true), "\n", showSplit(tail, ctx, false) @@ -132,48 +134,48 @@ fun showSplit(s, ctx, isCont) = UseSplit(sym) then StrOps.concat of "$", ctx.get(sym) -fun show(t, ctx) = +fun show(t: Term, ctx: Context): Str = if t is Ref(sym) then ctx.get(sym) CSRef(Symbol(name), baseFile, file) then if file is undefined then ctx.depends(baseFile) else ctx.depends(path.join of path.dirname(baseFile), file) name - Lit(lit) then lit.toString() + Lit(lit) then String(lit) Builtin(name) then name Sel(prefix, name) then StrOps.concat of paren(prefix, ctx), ".", name App(lhs, rhs) then StrOps.concat of paren(lhs, ctx), show(rhs, ctx) - Tup(fields) then StrOps.concat of "(", fields.map(t => show(t, ctx)).join(", "), ")" + Tup(fields) then StrOps.concat of "(", fields.map((t, ...rest) => show(t, ctx)).join(", "), ")" Lam(params, body) then let nest = ctx.nest - let freshParams = params.map(s => nest.add(s)) + let freshParams = params.map((s, ...rest) => nest.add(s)) StrOps.concat of "(", freshParams.join(", "), ") =>\n", indent(show(body, nest), " ", true) Blk(stats, res) then let nest = ctx.nest - StrOps.concat of stats.map(s => showStmt(s, nest)).join("\n"), "\n", show(res, nest) + StrOps.concat of stats.map((s, ...rest) => showStmt(s, nest)).join("\n"), "\n", show(res, nest) IfLike(Keyword.If, split) then StrOps.concat of "if \n", indent(showSplit(split, ctx, false), " ", true) // TODO: other terms -fun print(t) = +fun print(t: Term) = let ctx = Context(new Map(), new Map(), new Set(), true) console.log(show(t, ctx)) -fun genImport(base, p) = +fun genImport(base: Str, p: Str): Str = StrOps.concat of "import \"./", path.relative(base, url.fileURLToPath(p)).slice(0, -4), ".mls\"" -fun codegen(t, file) = +fun codegen(t: Term, file: Str) = let ctx = Context(new Map(), new Map(), new Set(), false) let moduleName = path.parse(file).name let fullpath = path.join of process.cwd(), file let code = StrOps.concat of "module ", moduleName, " with ...\nfun res =\n", indent(show(t, ctx), " ", true), "\n" let dependencies = - Array.from(ctx.dependencies).map(s => genImport(path.dirname(fullpath), s)) + Array.from(ctx.dependencies).map((s, ...rest) => genImport(path.dirname(fullpath), s)) if not fs.existsSync(file) do fs.writeFileSync(file, "", "utf8") let originData = fs.readFileSync(file, "utf8") - let newData = StrOps.concat of dependencies.join("\n"), "\n", code + let newData = StrOps.concat of "#lang(0.3.x)\n\n", dependencies.join("\n"), "\n", code if newData != originData do fs.writeFileSync(file, newData, "utf8") diff --git a/hkmc2/shared/src/test/mlscript-compile/TreeTracer.mls b/hkmc2/shared/src/test/mlscript-compile/TreeTracer.mls index 3d50c7675c..d620ea5248 100644 --- a/hkmc2/shared/src/test/mlscript-compile/TreeTracer.mls +++ b/hkmc2/shared/src/test/mlscript-compile/TreeTracer.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./Predef.mls" import "./StrOps.mls" @@ -8,7 +10,7 @@ module TreeTracer with ... class TreeTracer with mut val steps = 0 mut val enabled = false - fun output(outie, innie, innieAlt, message) = if enabled do + fun output(outie, innie, innieAlt, message: Str) = if enabled do let lines = message.split("\n") lines.forEach of (line, i, xs) => if let postfix = if lines.length > 1 then " ↡" else "" diff --git a/hkmc2/shared/src/test/mlscript-compile/XML.mls b/hkmc2/shared/src/test/mlscript-compile/XML.mls index 606fbfdd21..47cef5204a 100644 --- a/hkmc2/shared/src/test/mlscript-compile/XML.mls +++ b/hkmc2/shared/src/test/mlscript-compile/XML.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./Predef.mls" import "./Iter.mls" @@ -14,8 +16,10 @@ module XML with ... fun serializeValue(value) = if value is Str then JSON.stringify(value) - StyleAttributeValue then value.toValue() - + // * TODO: Allow just `StyleAttributeValue then value.toValue()` + // * Currently, scrutinees are not refined implicitly for resolution. + StyleAttributeValue as value then value.toValue() + fun joinAttributes(attributes) = attributes mapping of case [name, value] then name + "=" + serializeValue(value) diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mls b/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mls index 0d3035c3aa..9267e1706f 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mls @@ -1,26 +1,22 @@ +#lang(0.3.x) import "fs" import "../StrOps.mls" open StrOps +open StrOps { concat2, ~ } import "../Predef.mls" open Predef -declare class Num -declare class Bool - -declare val String - - data class Project(val num: Str) data class Accounting with ... -fun display(amt): Str = (amt / 1000).toFixed(1) +fun display(amt: Num): Str = ((amt / 1000) as Num).toFixed(1) val warnings = mut [] @@ -55,27 +51,27 @@ data class Report(fileName) with fun init() = wln of "" - wln of "|" ~ "Year" ~ "|" ~ lines.map(x => x.name).join("|") ~ "|" - wln of "|" ~ "---" ~ "|" ~ lines.map(x => "--:").join("|") ~ "|" + wln of "|" ~ "Year" ~ "|" ~ lines.map(pass1((x: Line) => x.name)).join("|") ~ "|" + wln of "|" ~ "---" ~ "|" ~ lines.map(pass1(x => "--:")).join("|") ~ "|" fun snapShot(label) = - wln of "|" ~ String(label) ~ "|" ~ lines.map(x => display(x.balance)).join("|") ~ "|" + wln of "|" ~ String(label) ~ "|" ~ lines.map(pass1((x: Line) => display(x.balance))).join("|") ~ "|" fun wrapUp() = wln of "" - warnings.forEach of x => (wln(x), wln("")) + warnings.forEach of pass1(x => (wln(x), wln(""))) wln of "### Remaining Available Funds" wln of "" wln of "|" ~ "Summary" ~ "| |" wln of "|" ~ "---" ~ "|--:|" wln of "|" ~ "Matchable" ~ "|" ~ display( - lines.filter(x => x.isMatchable).map(x => x.balance).reduce((a, b) => a +. b, 0) + lines.filter(pass1((x: Line) => x.isMatchable)).map(pass1((x: Line) => x.balance)).reduce(pass2((a, b) => a +. b), 0) ) ~ "|" wln of "|" ~ "Non-matchable" ~ "|" ~ display( - lines.filter(x => not x.isMatchable).map(x => x.balance).reduce((a, b) => a +. b, 0) + lines.filter(pass1((x: Line) => not x.isMatchable)).map(pass1((x: Line) => x.balance)).reduce(pass2((a, b) => a +. b), 0) ) ~ "|" -fun process(filename, k) = +fun process(filename: Str, k: Report -> ()) = let report = Report(filename) report.init() k(report) diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/CSV.mls b/hkmc2/shared/src/test/mlscript-compile/apps/CSV.mls index 00a1f50b37..30d6e68b0e 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/CSV.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/CSV.mls @@ -1,3 +1,4 @@ +#lang(0.3.x) // Adapted from https://www.bennadel.com/blog/1504-ask-ben-parsing-csv-strings-with-javascript-exec-regular-expression-command.htm // (JS code commented at bottom) @@ -23,7 +24,7 @@ val objPattern = new mut RegExp of "gi" // This will parse a delimited string into an array of arrays. -fun toArrays(strData) = +fun toArrays(strData: Str) = // Create an array to hold our data. Give the array a default empty first row. let arrData = mut [mut []] diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/parsing-web-demo/Examples.mls b/hkmc2/shared/src/test/mlscript-compile/apps/parsing-web-demo/Examples.mls index 486f942724..5157544757 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/parsing-web-demo/Examples.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/parsing-web-demo/Examples.mls @@ -1,10 +1,13 @@ +#lang(0.3.x) + import "../../MutMap.mls" import "../../Predef.mls" open Predef module Examples with... -val examples = MutMap.empty +type Example = {name: Str, source: Str} +val examples: MutMap.MutMap[Str, Example] = MutMap.empty examples |> MutMap.insert of "hanoi" diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/parsing-web-demo/main.mls b/hkmc2/shared/src/test/mlscript-compile/apps/parsing-web-demo/main.mls index c3fa60c204..403b32cf58 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/parsing-web-demo/main.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/parsing-web-demo/main.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + // Note: This file does not have a corresponding test file. It is recommended to // test it through the following steps. // @@ -17,6 +19,7 @@ import "../../StrOps.mls" import "../../Iter.mls" import "../../XML.mls" import "../../Option.mls" +import "../../MutMap.mls" import "../../Runtime.mls" import "../../Predef.mls" import "../parsing/TreeHelpers.mls" @@ -33,13 +36,17 @@ open Examples module Main with ... +type Example = {name: Str, source: Str} + let query = document.querySelector.bind(document) let editor = query("#editor") let selector = query("select#example") let parseButton = query("button#parse") let outputPanel = query("#output") -examples Iter.each of case [key, example] then +examples Iter.each of (entry: [Str, Example]) => + let key = entry.0 + let example = entry.1 let option = document.createElement of "option" set option.value = key set option.textContent = example.name @@ -49,7 +56,7 @@ examples Iter.each of case [key, example] then set editor.value = example.source // Make pressing Tab add two spaces at the caret's position. -editor.addEventListener of "keydown", event => +editor.addEventListener of "keydown", (event: dyn) => if event.key is "Tab" do // Prevent the default tab behavior (focusing the next element). event.preventDefault() @@ -59,12 +66,12 @@ editor.addEventListener of "keydown", event => set editor.selectionEnd = start + 2 set editor.selectionStart = editor.selectionEnd -selector.addEventListener of "change", event => - if examples.get(selector.value) is +selector.addEventListener of "change", (event: dyn) => + if (examples |> MutMap.get(selector.value)) is Some(example) do set editor.value = example.source None do throw new Error of "Example \"" + selector.value + "\" not found" -parseButton.addEventListener of "click", event => +parseButton.addEventListener of "click", (event: dyn) => let tokens = Lexer.lex(editor.value, noWhitespace: true) set outputPanel.innerHTML = "" Runtime.try_catch of @@ -83,17 +90,19 @@ parseButton.addEventListener of "click", event => let indentRegex = new RegExp("""^(\s*)""") -fun parseIndentedText(text) = - let root = (text: "", children: []) - let stack = [(node: root, indent: -1)] +type IndentedNode = {text: Str, children: Array[IndentedNode]} + +fun parseIndentedText(text: Str) = + let root = (text: "", children: mut []) + let stack = mut [(node: root, indent: -1)] text.split("\n") - Iter.filtering(line => line.trim().length > 0) - Iter.each of line => - let indent = line.match(indentRegex).[1].length + Iter.filtering((line: Str) => line.trim().length > 0) + Iter.each of (line: Str) => + let indent = indentRegex.exec(line).1.length let text = line.substring(indent) while indent <= stack.[stack.length - 1].indent do stack.pop() - let newNode = (text: text, children: []) + let newNode = (text: text, children: mut []) stack.[stack.length - 1].node.children.push of newNode stack.push of node: newNode, indent: indent root.children @@ -132,9 +141,9 @@ class CollapsibleTree extends HTMLElement with let treeElement = createDetailsTree(treeData) this.appendChild(treeElement) - fun createDetailsTree(nodes) = + fun createDetailsTree(nodes: Array[IndentedNode]) = let fragment = document.createDocumentFragment() - nodes Iter.each of node => + nodes Iter.each of (node: IndentedNode) => let details = document.createElement("details") details.setAttribute("open", "") let summary = document.createElement("summary") @@ -159,20 +168,20 @@ class ErrorDisplay extends HTMLElement with fun connectedCallback() = this.render() - fun setError(value) = + fun setError(value: Error) = set _error = Some(value) this.render() fun render() = if _error is Some(error) do let stackLines = error.stack.split("\n") - if stackLines.[0].startsWith(error.name) do + if stackLines.length > 0 and stackLines.[0].startsWith(error.name) do stackLines.shift() set this.shadowRoot.innerHTML = elem("div", "class": "error-container") of elem("h3", "class": "error-message") of error.name + ": " + error.message elem("ul", "class": "stack-trace") of stackLines - Iter.mapping of line => elem("li") of line.trim() + Iter.mapping of (line: Str) => elem("li") of line.trim() Iter.joined("") elem("style") of errorDisplayStyle @@ -184,7 +193,7 @@ fun makeFigures(entries) = entries Iter.joined("") fun displayRules() = - open ParseRuleVisualizer + open ParseRuleVisualizer {render, reset} open Rules reset() set query("#syntax-diagrams>main").innerHTML = StrOps.concat of diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/BasicExpr.mls b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/BasicExpr.mls index df342e0e2e..53cec827e8 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/BasicExpr.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/BasicExpr.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "../../StrOps.mls" import "../../Option.mls" import "../../Predef.mls" diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Expr.mls b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Expr.mls index 0b8bfeddca..046cb30188 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Expr.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Expr.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "../../StrOps.mls" import "../../Option.mls" import "../../Predef.mls" diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Extension.mls b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Extension.mls index 345201a51f..7e1f8389a7 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Extension.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Extension.mls @@ -1,3 +1,4 @@ +#lang(0.3.x) import "./Keywords.mls" import "./Rules.mls" import "./Token.mls" @@ -74,7 +75,7 @@ fun extendCategory(choiceBodyTree: Tree) = print of "Invalid syntax description: " + choiceBodyTree Tree.summary() fun parseChoiceTree(tree: Tree) = - fun go(trees: Stack[Tree]): Choice[Tree] = + fun go(trees: Stack[Tree]): Choice[Stack[Tree]] = let res = if trees is Tree.App(Tree.Ident("keyword", _), Tree.Literal(LiteralKind.String, name)) :: rest and Keywords.all |> MutMap.get(name) is Some(keyword) then @@ -88,7 +89,7 @@ fun parseChoiceTree(tree: Tree) = Tree.Tuple(categoryIdent :: choiceTree :: funcIdent :: Nil) and categoryIdent is Tree.Literal(LiteralKind.String, categoryName) and funcIdent is Tree.Ident and - let op = (trees) => trees + let op = (trees: Stack[Tree]) => trees Iter.fromStack() Iter.folded of funcIdent, (f, x) => Tree.App(f, x) choiceTree is diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Keywords.mls b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Keywords.mls index a13ea96d87..56676cfb29 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Keywords.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Keywords.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "../../Option.mls" import "../../Iter.mls" import "../../Predef.mls" @@ -13,7 +15,7 @@ module Keywords with ... val INT_MIN = -2147483648 val INT_MAX = 2147483647 -class Keyword(val name: Str, val leftPrec, val rightPrec) with +class Keyword(val name: Str, val leftPrec: Some[Int] | None, val rightPrec: Some[Int] | None) with fun leftPrecOrMin = if leftPrec is Some(prec) then prec else INT_MIN fun rightPrecOrMin = if rightPrec is Some(prec) then prec else INT_MIN fun leftPrecOrMax = if leftPrec is Some(prec) then prec else INT_MAX @@ -24,7 +26,7 @@ class Keyword(val name: Str, val leftPrec, val rightPrec) with (if leftPrec is Some(prec) then prec.toString() else "N/A"), ", ", (if rightPrec is Some(prec) then prec.toString() else "N/A"), ")" -fun makePrecMap(startPrec: Int, ...ops) = +fun makePrecMap(startPrec: Int, ...ops: Array[Str]) = let m = MutMap.empty i = 0 @@ -35,8 +37,8 @@ fun makePrecMap(startPrec: Int, ...ops) = set i += 1 m -val all = MutMap.empty -fun keyword(name, leftPrec, rightPrec) = +val all: MutMap.MutMap[Str, Keyword] = MutMap.empty +fun keyword(name: Str, leftPrec: Some[Int] | None, rightPrec: Some[Int] | None) = let result = Keyword(name, leftPrec, rightPrec) all |> MutMap.insert(name, result) result @@ -112,7 +114,7 @@ val periodPrec = precMap |> MutMap.get(".") |> Option.unsafe.get val _period = keyword of ".", Some(periodPrec), Some(periodPrec) -val maxOperatorPrec = periodPrec +val maxOperatorPrec: Int = periodPrec val appPrec = maxOperatorPrec - 1 val prefixPrec = appPrec - 1 @@ -135,7 +137,7 @@ val _rightCurly = keyword of "}", basePrec, None val _begin = keyword of "begin", bracketPrec, basePrec val _end = keyword of "end", basePrec, None -let builtinKeywords = new Set(all |> MutMap.keysIterator) +private val builtinKeywords = new Set(all |> MutMap.keysIterator) fun extended = all Iter.filtering of case [k, _] then builtinKeywords.has(k) is false Iter.toArray() @@ -149,7 +151,7 @@ pattern FloatOperator = "+." | "-." | "*." | "/." pattern RightAssociative = "@" | "/" | "," | ":" -fun opPrec(opStr) = if +fun opPrec(opStr: Str) = if opStr is FloatOperator then tuple of Keywords.charPrec of opStr.at(0) Keywords.charPrec of opStr.at(0) @@ -163,7 +165,7 @@ fun opPrec(opStr) = if (Keywords.charPrec of lastChar) + if lastChar is RightAssociative then -1 else 0 -fun opPrecOpt(opStr) = if +fun opPrecOpt(opStr: Str) = if opStr is "" then None opStr is FloatOperator then Some of tuple of Keywords.charPrec of opStr.at(0) diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Lexer.mls b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Lexer.mls index 769521dab5..b4d32a77ea 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Lexer.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Lexer.mls @@ -1,4 +1,3 @@ -#config(liftDefns: Some(LiftDefns)) import "../../Predef.mls" @@ -13,7 +12,7 @@ import "../../Iter.mls" import "./Token.mls" open Stack -open StrOps +open StrOps { concat2, ~ } open Option { Some, None} open Token { LiteralKind, LineLookupTable } @@ -56,7 +55,7 @@ fun makeLineLookupTable(text: Str): LineLookupTable = ns.push(i') LineLookupTable(ns) -fun lex(str: Str, options) = +fun lex(str: Str, options: {noWhitespace: Bool}) = using LineLookupTable = makeLineLookupTable(str) fun char(idx: Int) = if idx < str.length diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/ParseRule.mls b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/ParseRule.mls index dc227a8eed..649dfa15fb 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/ParseRule.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/ParseRule.mls @@ -51,7 +51,7 @@ module Choice with check(item is Choice, name + ": element [" + index + "] is not Choice") // Shorthands for constructing rule choices. - fun keyword(keyword)(...choices) = + fun keyword(keyword: Keywords.Keyword)(...choices) = ensureChoices(choices, "Choice.keyword") Keyword(keyword, rule("`" + keyword.name + "` keyword", ...choices)) @@ -152,14 +152,15 @@ fun lazy(init) = new Lazy of init data class ParseRule[A](name: Str, mut val choices: Stack[Choice[A]]) with open Choice { Keyword, Ref, End, Siding } - fun map[B](op: A -> B) = + fun map[B](op: A -> B): ParseRule[B] = new mut ParseRule of name, choices Iter.fromStack() Iter.mapping of choice => choice Choice.map(op) Iter.toStack() - fun andThen[B, C](rest: ParseRule[B], process: (A, B) -> C) = - fun go(rule: ParseRule[B]) = new ParseRule of rule.name, rule.choices + fun andThen[B, C](rest: ParseRule[B], process: (A, B) -> C): ParseRule[C] = + // Each choice may have a different intermediate result type; append B to that result. + fun go[X](rule: ParseRule[X]): ParseRule[[X, B]] = new ParseRule of rule.name, rule.choices Iter.fromStack() Iter.mapping of case Keyword(keyword, rest') then [Keyword(keyword, go(rest'))] @@ -216,7 +217,7 @@ data class ParseRule[A](name: Str, mut val choices: Stack[Choice[A]]) with MutMap.toMap() // Collect all keyword choices in this rule. - fun keywordChoices = _keywordChoices.get() + fun keywordChoices: MutMap.MutMap[Str, ParseRule[A]] = _keywordChoices.get() let _refChoice = lazy of () => choices Iter.fromStack() @@ -245,7 +246,7 @@ data class ParseRule[A](name: Str, mut val choices: Stack[Choice[A]]) with this // Display parse rules as a tree in a BNF-like format. - fun display = + fun display: Str = /// Display a single `Choice`. fun displayChoice[A](choice: Choice[A]) = if choice is Choice.Keyword(keyword, rest) then diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/ParseRuleVisualizer.mls b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/ParseRuleVisualizer.mls index e5293b9a8b..7e61966b03 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/ParseRuleVisualizer.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/ParseRuleVisualizer.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "../../Predef.mls" import "../../Stack.mls" import "../../Iter.mls" @@ -24,13 +26,13 @@ val tracer = new TreeTracer.TreeTracer() let defaultKinds = tuple of "type", "term", "typevar", "ident" -let renderedKinds = new Set of defaultKinds +mut val renderedKinds: Set[Str] = new Set of defaultKinds fun reset() = set renderedKinds = new Set of defaultKinds // * `rr` is the railroad library. -fun render[T](rr, title: Str, rule: ParseRule.ParseRule[T]) = +fun render[T](rr: dyn, title: Str, rule: ParseRule.ParseRule[T]) = let helperRules = mut [] let referencedKinds = new Set() let renderCache = new Map() @@ -40,7 +42,7 @@ fun render[T](rr, title: Str, rule: ParseRule.ParseRule[T]) = None then lhs fun diagram(choicesOpt) = rr.Diagram of if choicesOpt is Some(choices) then choices else [] - fun renderChoice(parentRule, choice) = if choice is + fun renderChoice(parentRule, choice: Choice) = if choice is Choice.End then tracer.print of "found Choice.End" None @@ -85,7 +87,7 @@ fun render[T](rr, title: Str, rule: ParseRule.ParseRule[T]) = while referencedKinds.size > 0 do let currentKinds = referencedKinds set renderedKinds = renderedKinds.union of currentKinds - referencedKinds = new Set() + set referencedKinds = new Set() diagrams.push of ...currentKinds Iter.mapping of kind => let theRule = syntaxKinds |> MutMap.get(kind) |> Option.unsafe.get diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Parser.mls b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Parser.mls index 1b9244f435..6c591c487a 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Parser.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Parser.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "../../Predef.mls" import "../../Option.mls" import "../../Stack.mls" @@ -24,7 +26,12 @@ open Rules { termRule, typeRule, declRule, syntaxKinds } module Parser with ... -val tracer = new TreeTracer.TreeTracer +val tracer: TreeTracer.TreeTracer = new TreeTracer.TreeTracer + +type ExprOptions = { + kind: Str, rule: ParseRule.ParseRule[Tree], + allowOperators: Bool, allowLiterals: Bool +} let termOptions = kind: "term", rule: termRule @@ -33,7 +40,7 @@ let typeOptions = kind: "type", rule: typeRule allowOperators: false, allowLiterals: true, -fun parse(tokens) = +fun parse(tokens: Stack[Token.Token]) = let counter = 0 fun consume = @@ -76,7 +83,7 @@ fun parse(tokens) = tree else throw Error("Unknown syntax kind: \"" + kind + "\"") - fun parseRule(prec: Int, rule) = tracer.trace of + fun parseRule(prec: Int, rule: ParseRule.ParseRule[Tree]): Tree = tracer.trace of "parsing rule \"" + rule.name + "\" with precedence " + prec result => "parsed rule \"" + rule.name + "\": " + result Tree.summary() () => if tokens is... @@ -131,7 +138,7 @@ fun parse(tokens) = tracer.print of "no end choice but found the end of input", source.line Tree.error("unexpected end of input") - fun expr(prec: Int, options): Tree = tracer.trace of + fun expr(prec: Int, options: ExprOptions): Tree = tracer.trace of options.kind + " <<< " + prec + " " + TokenHelpers.preview(tokens) result => options.kind + " >>> " + result Tree.summary() () => if tokens is ... @@ -163,17 +170,21 @@ fun parse(tokens) = token :: _ then Tree.error("unrecognized token: " + token) Nil then Tree.error("unexpected end of input") - fun exprCont(acc: Tree, prec: Int, options) = if tokens is - let infix = options.rule.refChoice Option.flatMap of case + fun infixRule(options: ExprOptions): Option[{process: (Tree, Tree) -> Tree, rule: ParseRule.ParseRule[Tree]}] = + // A category may have no continuation rule (for example, an atomic kind). + options.rule.refChoice Option.flatMap of case Ref(kind, process, None, None, rest) and kind == options.kind then - { process: process, rule: rest } + Some of { process: process, rule: rest } else throw Error("Kind " + options.kind + " does not have infix rules") + + fun exprCont(acc: Tree, prec: Int, options: ExprOptions) = if tokens is + let infix = infixRule(options) do tracer.print of ">>> " + options.kind + "Cont " + prec + " " + acc Tree.summary() + " <<<", source.line // * the case of infix keyword rules do tracer.print of "check keyword " + TokenHelpers.preview(tokens), source.line Token.Identifier(name, _) :: _ and Keywords.all |> MutMap.get(name) is Some(keyword) and do tracer.print of "found a keyword: " + name, source.line - infix.rule.keywordChoices |> MutMap.get(name) is Some(rule) and + infix is Some(infix) and infix.rule.keywordChoices |> MutMap.get(name) is Some(rule) and do tracer.print of "keyword `" + name + "` is found in infix rules", source.line keyword.leftPrecOrMin > prec and rule.refChoice is Some(Ref(kind, process, outerPrec, innerPrec, rest)) and @@ -201,7 +212,7 @@ fun parse(tokens) = acc // * the case of application do tracer.print of "not a keyword", source.line - token :: _ and infix.rule.refChoice is + token :: _ and infix is Some(infix) and infix.rule.refChoice is Some(Ref(kind, process, outerPrec, innerPrec, rest)) and do tracer.print of "found reference to " + kind + " with outerPrec = " + outerPrec, source.line let outerPrec' = outerPrec Option.getOrElse(Keywords.maxOperatorPrec) @@ -223,7 +234,7 @@ fun parse(tokens) = None then tracer.print of "cannot consume " + token, source.line acc - Nil then acc + _ then acc fun handleDirective(tree: Tree, acc: Stack[Tree]) = if tree is // Call the corresponding handler for the directive. @@ -245,7 +256,7 @@ fun parse(tokens) = do tracer.print of ">>>>>> mod <<<<<<", source.line Token.Identifier(";;", _) :: _ then consume - mod + mod(acc) Token.Identifier(name, _) :: _ and Keywords.all |> MutMap.get(name) is Some(keyword) and // First, try if the keyword is the prefix of the term. diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/PrattParsing.mls b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/PrattParsing.mls index aab7edb932..27e0866d1a 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/PrattParsing.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/PrattParsing.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "../../Predef.mls" import "../../Stack.mls" import "../../Option.mls" diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/RecursiveDescent.mls b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/RecursiveDescent.mls index b3b89009a8..32a6c5dc95 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/RecursiveDescent.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/RecursiveDescent.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "../../Predef.mls" import "../../Stack.mls" import "../../Option.mls" diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Rules.mls b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Rules.mls index 53bdec6664..86ac2b4aa6 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Rules.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Rules.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "../../Predef.mls" import "../../Option.mls" import "../../Stack.mls" @@ -20,21 +22,21 @@ type Opt[A] = Some[A] | None module Rules with ... -val syntaxKinds = MutMap.empty +val syntaxKinds: MutMap.MutMap[Str, ParseRule.ParseRule] = MutMap.empty val extendedKinds = new Set() -fun getRuleByKind(kind: Str) = syntaxKinds |> MutMap.get(kind) |> Option.unsafe.get +fun getRuleByKind(kind: Str): ParseRule.ParseRule[out Anything] = syntaxKinds |> MutMap.get(kind) |> Option.unsafe.get fun define(name: Str)(...choices) = syntaxKinds |> MutMap.updateWith(name) of case None then Some(rule(name, ...choices)) - Some(rule) then Some(rule.extendChoices(choices Iter.toStack())) + Some(rule) then Some((rule as ParseRule.ParseRule).extendChoices(choices Iter.toStack())) fun idFirst(value, _) = value fun idSecond(_, value) = value fun someFirst(value, _) = Some(value) fun listFirst(value, _) = value :: Nil -fun listLike(fields) = +fun listLike(fields: {head: Str, tail: Str, name: Str, sep: Keywords.Keyword | undefined}) = let mkTail = if fields.("sep") is undefined then id else keyword(fields.("sep")) reference(fields.head) of process: Cons, name: "the first " + fields.name @@ -103,7 +105,9 @@ fun makeInfixChoice(kw: Keywords.Keyword, rhsKind: Str, compose: (Tree, Tree) -> process: (rhs, _) => lhs => compose(lhs, rhs) name: "operator `" + kw.name + "` right-hand side" -fun makeBracketRule(fields) = // opening, closing, kind, wrapContent +fun makeBracketRule(fields: { + opening: Keywords.Keyword, closing: Keywords.Keyword, kind: Str, wrapContent: Tree -> Tree +}) = // Pass the error message of closing bracket to the content. keyword(fields.opening) of reference(fields.kind) of process: (tree: Tree, next: Tree) => if next is @@ -113,7 +117,7 @@ fun makeBracketRule(fields) = // opening, closing, kind, wrapContent choices: tuple of keyword(fields.closing) of end of Tree.empty // Prefix rules and infix rules for expressions. -val termRule = rule of +val termRule: ParseRule.ParseRule[Tree] = rule of "prefix rules for expressions" letExpression // `fun` term @@ -228,7 +232,7 @@ val termRule = rule of outerPrec: Keywords.appPrec // Prefix rules and infix rules for types. -val typeRule = rule of +val typeRule: ParseRule.ParseRule[Tree] = rule of "rules for types" // "(" { "," } ")" [ ] keyword(Keywords._leftRound) of typeExpr of @@ -347,7 +351,7 @@ define("type-params-tail") of keyword(Keywords._comma) of typeVar of choices: tuple of reference("type-params-tail") of process: idFirst, name: "more type parameters" -val declRule = rule of +val declRule: ParseRule.ParseRule[Tree] = rule of "prefix rules for module items" makeLetBindings(false) // let definition keyword(Keywords._type) of diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Test.mls b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Test.mls index e52fa72200..ad1c063e05 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Test.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Test.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./Lexer.mls" import "./Keywords.mls" import "./Token.mls" diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Token.mls b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Token.mls index e67cb45c0f..1b7baa5882 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Token.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Token.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "../../Option.mls" import "../../Predef.mls" @@ -22,9 +24,13 @@ module LiteralKind with String Boolean +type LiteralKind = LiteralKind.{Integer | Decimal | String | Boolean} + +type Location = {start: Int, end: Int, lookupTable: LineLookupTable} + abstract class Token with - let _location = None - fun withLocation(start, end, lookupTable) = + private mut val _location: Some[Location] | None = None + fun withLocation(start: Int, end: Int, lookupTable: LineLookupTable) = set _location = Some of start: start, end: end, lookupTable: lookupTable this fun location = _location @@ -63,7 +69,7 @@ data Error() extends Token Comment(content: Str) extends Token Identifier(name: Str, symbolic: Bool) extends Token - Literal(kind, literal: Str) extends Token + Literal(kind: LiteralKind, literal: Str) extends Token fun same(a, b) = if a is Space and b is Space then true @@ -72,28 +78,28 @@ fun same(a, b) = if a is Literal(k, l) and b is Literal(k', l') then k == k' and l == l' else false -fun integer(literal, endIndex)(using llt: LineLookupTable) = +fun integer(literal: Str, endIndex: Int)(using llt: LineLookupTable) = Literal(LiteralKind.Integer, literal).withLocation of endIndex - literal.length, endIndex, llt -fun decimal(literal, endIndex)(using llt: LineLookupTable) = +fun decimal(literal: Str, endIndex: Int)(using llt: LineLookupTable) = Literal(LiteralKind.Decimal, literal).withLocation of endIndex - literal.length, endIndex, llt -fun string(literal, startIndex, endIndex)(using llt: LineLookupTable) = +fun string(literal: Str, startIndex: Int, endIndex: Int)(using llt: LineLookupTable) = Literal(LiteralKind.String, literal).withLocation(startIndex, endIndex, llt) -fun boolean(literal, endIndex)(using llt: LineLookupTable) = +fun boolean(literal: Str, endIndex: Int)(using llt: LineLookupTable) = Literal(LiteralKind.Boolean, literal).withLocation of endIndex - literal.length, endIndex, llt -fun identifier(name, endIndex)(using llt: LineLookupTable) = +fun identifier(name: Str, endIndex: Int)(using llt: LineLookupTable) = Identifier(name, false).withLocation of endIndex - name.length, endIndex, llt -fun symbol(name, endIndex)(using llt: LineLookupTable) = +fun symbol(name: Str, endIndex: Int)(using llt: LineLookupTable) = Identifier(name, true).withLocation of endIndex - name.length, endIndex, llt -fun comment(content, startIndex, endIndex)(using llt: LineLookupTable) = +fun comment(content: Str, startIndex: Int, endIndex: Int)(using llt: LineLookupTable) = Comment(content).withLocation(startIndex, endIndex, llt) -fun error(startIndex, endIndex)(using llt: LineLookupTable) = +fun error(startIndex: Int, endIndex: Int)(using llt: LineLookupTable) = Error().withLocation(startIndex, endIndex, llt) -fun space(startIndex, endIndex)(using llt: LineLookupTable) = +fun space(startIndex: Int, endIndex: Int)(using llt: LineLookupTable) = Space().withLocation(startIndex, endIndex, llt) fun summary(token) = if token is @@ -103,7 +109,7 @@ fun summary(token) = if token is Identifier(name, _) then name Literal(_, literal) then literal -fun display(token) = (if token is +fun display(token: Token) = (if token is Space then "space" Error then "error" Comment then "comment" diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/TokenHelpers.mls b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/TokenHelpers.mls index 17b4839288..14c7127ef4 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/TokenHelpers.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/TokenHelpers.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./Token.mls" import "../../Stack.mls" import "../../Predef.mls" diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Tree.mls b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Tree.mls index 5d63267497..bb9b269e9f 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Tree.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/Tree.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "../../Iter.mls" import "../../Predef.mls" import "../../Stack.mls" @@ -43,7 +45,7 @@ module Tree with Modified(modifier, subject) extends Tree Tuple(trees: Stack[Tree]) extends Tree Sequence(trees: Stack[Tree]) extends Tree - Literal(kind, value) extends Tree + Literal(kind: Token.LiteralKind, value: Str) extends Tree Match(scrutinee: Tree, branches: Stack[Tree]) extends Tree Lambda(params: Stack[Tree], body: Tree) extends Tree App(callee: Tree, argument: Tree) extends Tree @@ -100,7 +102,7 @@ module Tree with Tuple(trees) then "(" + trees Iter.fromStack() Iter.mapping(go) Iter.joined(", ") + ")" Sequence(trees) then trees Iter.fromStack() Iter.mapping(go) Iter.joined("; ") Literal(LiteralKind.String, value) and - value.length > 5 then JSON.stringify(value.slice(0, 5)).slice(0, -1) + "…\"" + value.length > 5 then (JSON.stringify(value.slice(0, 5)) as Str).slice(0, -1) + "…\"" else JSON.stringify(value) Literal(_, value) then value Match(scrutinee, branches) then mkStr of diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/TreeHelpers.mls b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/TreeHelpers.mls index c0ae10f278..a5eaaad969 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/parsing/TreeHelpers.mls +++ b/hkmc2/shared/src/test/mlscript-compile/apps/parsing/TreeHelpers.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "../../Option.mls" import "../../Predef.mls" import "../../Stack.mls" @@ -16,7 +18,7 @@ fun first(array) = if array is [first, ...] then first fun second(array) = if array is [_, second, ...] then second -fun indented(text) = text.split("\n").join("\n ") +fun indented(text: Str) = text.split("\n").join("\n ") fun showAsTree(thing) = fun itemize(something) = if something is @@ -33,7 +35,7 @@ fun showAsTree(thing) = tuple of ("Stack of \n" + " " + indented of items.join("\n")), [] Nil then ["Nil", []] Str then [JSON.stringify(something), []] // TODO: This doesn't work. - Int then [something.toString(), []] + Int as number then [number.toString(), []] Tree.Empty then ["Empty", []] Tree.Error(Tree.Empty, m) then tuple of "Error", [["message", go(m)]] Tree.Error(t, m) then tuple of "Error", [["tree", go(t)], ["message", go(m)]] diff --git a/hkmc2/shared/src/test/mlscript-compile/cpp/Makefile b/hkmc2/shared/src/test/mlscript-compile/cpp/Makefile index bba9ca31e9..3d255e68d0 100644 --- a/hkmc2/shared/src/test/mlscript-compile/cpp/Makefile +++ b/hkmc2/shared/src/test/mlscript-compile/cpp/Makefile @@ -1,6 +1,5 @@ CXX := g++ -CFLAGS += -O3 -Wall -Wextra -std=c++20 -I. -Wno-inconsistent-missing-override -I/opt/homebrew/include -LDFLAGS += -L/opt/homebrew/lib +CFLAGS += -O3 -Wall -Wextra -std=c++20 -I. -Wno-inconsistent-missing-override LDLIBS := -lmimalloc -lgmp SRC := INCLUDES := mlsprelude.h diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/NofibPrelude.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/NofibPrelude.mls index 944717bcd6..c56a0d199a 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/NofibPrelude.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/NofibPrelude.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open Predef @@ -33,7 +35,7 @@ fun _internal_cons_to_str(ls) = if ls is Nil then "" h :: Nil then render(h) h :: t then render(h) + "," + _internal_cons_to_str(t) -fun ltList(xs, ys, lt, gt) = if xs is +fun ltList(xs, ys, lt: Function, gt: Function) = if xs is Nil and ys is Nil then false else true @@ -52,20 +54,20 @@ abstract class LzList[out T]: LzCons[T] | LzNil data class LzCons[out T](head: T, tail: LazyList[T]) extends LzList[T] object LzNil extends LzList -fun ltTup2(t1, t2, lt1, gt1, lt2) = if t1 is [a, b] and t2 is [c, d] and +fun ltTup2(t1, t2, lt1: Function, gt1: Function, lt2: Function) = if t1 is [a, b] and t2 is [c, d] and lt1(a, c) then true gt1(a, c) then false else lt2(b, d) fun eqTup2(t1, t2) = if t1 is [a, b] and t2 is [c, d] then a == c and b == d -fun compose(f, g) = x => f(g(x)) +fun compose(f: Function, g: Function) = x => f(g(x)) fun snd(x) = if x is [f, s] then s fun fst(x) = if x is [f, s] then f -fun until(p, f, i) = if p(i) then i else until(p, f, f(i)) +fun until(p: Function, f: Function, i) = if p(i) then i else until(p, f, f(i)) -fun flip(f, x, y) = f(y)(x) +fun flip[A, B, C](f: B -> A -> C, x: A, y: B): C = f(y)(x) fun power(a, n) = globalThis.Math.pow(a, n) @@ -86,13 +88,13 @@ fun abs(x) = globalThis.Math.abs(x) fun head(l) = if l is h :: t then h fun tail(l) = if l is h :: t then t -fun while_(p, f, x) = if p(x) then while_(p, f, f(x)) else x +fun while_(p: Function, f: Function, x) = if p(x) then while_(p, f, f(x)) else x fun reverse(l) = fun r(l', l) = if l is x :: xs then r(x :: l', xs) else l' r(Nil, l) -fun map(f, xs) = if xs is +fun map[A, B](f: A -> B, xs: List[A]): List[B] = if xs is x :: xs then f(x) :: map(f, xs) Nil then Nil @@ -107,7 +109,7 @@ fun listEq(xs, ys) = if xs is hx :: tx and ys is hy :: ty and (hx == hy) then listEq(tx, ty) else false -fun listEqBy(f, a, b) = if a is +fun listEqBy(f: Function, a, b) = if a is Nil and b is Nil then true x :: xs and b is y :: ys then f(x, y) && listEqBy(f, xs, ys) else false @@ -155,13 +157,13 @@ fun concat(ls) = if ls is Nil then Nil x :: xs then append(x, concat(xs)) -fun filter(f, ls) = if ls is +fun filter(f: Function, ls) = if ls is Nil then Nil h :: t and f(h) then h :: filter(f, t) else filter(f, t) -fun all(p, ls) = if ls is +fun all(p: Function, ls) = if ls is Nil then true h :: t and p(h) then all(p, t) @@ -173,46 +175,46 @@ fun orList(ls) = if ls is h then true else orList(t) -fun leaveWhile(f, ls) = if ls is +fun leaveWhile(f: Function, ls) = if ls is Nil then Nil h :: t and f(h) then leaveWhile(f, t) else h :: t -fun foldl(f, a, xs) = if xs is +fun foldl[A, B](f: (B, A) -> B, a: B, xs: List[A]): B = if xs is Nil then a h :: t then foldl(f, f(a, h), t) -fun scanl(f, q, ls) = if ls is +fun scanl(f: Function, q, ls) = if ls is Nil then q :: Nil x :: xs then q :: scanl(f, f(q, x), xs) -fun scanr(f, q, ls) = if ls is +fun scanr(f: Function, q, ls) = if ls is Nil then q :: Nil x :: xs and scanr(f, q, xs) is q :: t then f(x, q) :: q :: t -fun foldr(f, z, xs) = if xs is +fun foldr[A, B](f: (A, B) -> B, z: B, xs: List[A]): B = if xs is Nil then z h :: t then f(h, foldr(f, z, t)) fun foldl1(f, ls) = if ls is x :: xs then foldl(f, x, xs) -fun foldr1(f, ls) = if ls is +fun foldr1[A](f: (A, A) -> A, ls: List[A]): A = if ls is x :: Nil then x x :: xs then f(x, foldr1(f, xs)) fun maximum(xs) = foldl1((x, y) => if x > y then x else y, xs) -fun nubBy(eq, ls) = if ls is +fun nubBy(eq: Function, ls) = if ls is Nil then Nil h :: t then h :: nubBy(eq, filter(y => not(eq(h, y)), t)) -fun zipWith(f, xss, yss) = if +fun zipWith(f: Function, xss, yss) = if xss is x :: xs and yss is y :: ys then f(x, y) :: zipWith(f, xs, ys) else Nil -fun deleteBy(eq, x, ys) = if ys is +fun deleteBy(eq: Function, x, ys) = if ys is Nil then Nil y :: ys and eq(x, y) then ys @@ -261,31 +263,31 @@ fun transpose(xss) = Nil :: xss then transpose(xss) (x :: xs) :: xss and unzip(lscomp(xss)) is [hds, tls] then combine(x, hds, xs, tls) -fun break_(p, ls) = if ls is +fun break_(p: Function, ls) = if ls is Nil then [Nil, Nil] x :: xs and p(x) then [Nil, x :: xs] break_(p, xs) is [ys, zs] then [x :: ys, zs] -fun flatMap(f, ls) = if ls is +fun flatMap(f: Function, ls) = if ls is Nil then Nil h :: t then append(f(h), flatMap(f, t)) // ===================== -fun map_lz(f, ls) = lazy of () => +fun map_lz(f: Function, ls) = lazy of () => if force(ls) is LzNil then LzNil LzCons(h, t) then LzCons(f(h), map_lz(f, t)) -fun filter_lz(p, ls) = lazy of () => +fun filter_lz(p: Function, ls) = lazy of () => if force(ls) is LzNil then LzNil LzCons(h, t) and p(h) then LzCons(h, filter_lz(p, t)) else force(filter_lz(p, t)) -fun nubBy_lz(eq, ls) = lazy of () => +fun nubBy_lz(eq: Function, ls) = lazy of () => if force(ls) is LzNil then LzNil LzCons(h, t) then LzCons(h, nubBy_lz(eq, filter_lz(y => not(eq(h, y)), t))) @@ -320,15 +322,15 @@ fun zip_lz_lz(xs, ys) = if force(xs) is LzCons(x, xs) and force(ys) is LzCons(y, ys) then lazy of () => LzCons([x, y], zip_lz_lz(xs, ys)) else lazy of () => LzNil -fun zipWith_lz_lz(f, xss, yss) = lazy of () => if +fun zipWith_lz_lz(f: Function, xss, yss) = lazy of () => if force(xss) is LzCons(x, xs) and (force(yss)) is LzCons(y, ys) then LzCons(f(x, y), zipWith_lz_lz(f, xs, ys)) else LzNil -fun zipWith_lz_nl(f, xss, yss) = if +fun zipWith_lz_nl(f: Function, xss, yss) = if force(xss) is LzCons(x, xs) and yss is y :: ys then f(x, y) :: zipWith_lz_nl(f, xs, ys) else Nil -fun iterate(f, x) = lazy of () => LzCons(x, iterate(f, f(x))) +fun iterate(f: Function, x) = lazy of () => LzCons(x, iterate(f, f(x))) fun append_nl_lz(xs, ys) = if xs is Nil then ys @@ -359,8 +361,8 @@ fun tan(x) = globalThis.Math.tan(x) fun sin(x) = globalThis.Math.sin(x) fun cos(x) = globalThis.Math.cos(x) fun round(x) = globalThis.Math.round(x) -fun int_of_char(x) = x.charCodeAt(0) -fun nofibStringToList(s) = +fun int_of_char(x: Str) = x.charCodeAt(0) +fun nofibStringToList(s: Str) = fun go(i) = if i < s.length then s.charAt(i) :: go(i + 1) else Nil go(0) fun nofibListToString(ls) = if ls is diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/ansi.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/ansi.mls index 04bf8af28c..c279d71f72 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/ansi.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/ansi.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" open NofibPrelude @@ -15,21 +17,21 @@ fun highlight(s) = nofibStringToList("ESC[7m") +: s +: nofibStringToList("ESC[0m fun end(xs) = nofibStringToList("") -fun readChar(eof, consume, cs) = if cs is +fun readChar(eof: Function, consume: Function, cs) = if cs is Nil then eof(Nil) c :: cs then consume(c, cs) -fun peekChar(eof, consume, cs) = if cs is +fun peekChar(eof: Function, consume: Function, cs) = if cs is Nil then eof(Nil) c :: cs then consume(c, c :: cs) -fun pressAnyKey(prog, x) = readChar(prog, (c, x) => prog(x), x) +fun pressAnyKey(prog: Function, x) = readChar(prog, (c, x) => prog(x), x) -fun unreadChar(c, prog, cs) = prog(c :: cs) +fun unreadChar(c, prog: Function, cs) = prog(c :: cs) -fun writeChar(c, prog, cs) = c :: prog(cs) +fun writeChar(c, prog: Function, cs) = c :: prog(cs) -fun writeString(s, prog, cs) = s +: prog(cs) +fun writeString(s, prog: Function, cs) = s +: prog(cs) fun writes(ss, a, b) = writeString(concat(ss), a, b) @@ -41,7 +43,7 @@ fun writeAt(x_y, s, a) = if x_y is [x, y] then p => writeString(goto(x, y) +: s, fun moveTo(x_y, a) = if x_y is [x, y] then p => writeString(goto(x, y), a, p) -fun returnn(s, consume) = consume(reverse(s)) +fun returnn(s, consume: Any -> Function) = consume(reverse(s)) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” fun deletee(n, s, l, consume, d) = if n > 0 then writeString(nofibStringToList("BS_BS"), loop(n - 1, tail(s), l, consume), d) else ringBell(loop(0, nofibStringToList(""), l, consume), d) diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/atom.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/atom.mls index f1ba8582fa..56ce93a2be 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/atom.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/atom.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" open NofibPrelude @@ -34,7 +36,7 @@ fun show(s) = fun propagate(dt, aforce, state) = if state is State(pos, vel) then State(dotPlus(pos, scalarMut(dt, vel)), dotPlus(vel, scalarMut(dt, aforce))) -fun runExperiment(law, dt, param, init) = lazy of () => +fun runExperiment(law: Function, dt, param, init) = lazy of () => let stream = runExperiment(law, dt, param, init) LzCons(init, zipWith_lz_lz((x, y) => propagate(dt, x, y), law(param, stream), stream)) diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/awards.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/awards.mls index 85f77c3a66..e8b809b25b 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/awards.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/awards.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -17,7 +19,7 @@ fun qsort(le, ls, r) = if ls is x :: Nil then x :: r x :: xs then qpart(le, x, xs, Nil, Nil, r) -fun qpart(le, x, ys, rlt, rge, r) = if ys is +fun qpart(le: Function, x, ys, rlt, rge, r) = if ys is Nil then rqsort(le, rlt, x :: rqsort(le, rge, r)) y :: ys and le(x, y) then qpart(le, x, ys, rlt, y :: rge, r) @@ -28,7 +30,7 @@ fun rqsort(le, ls, r) = if ls is x :: Nil then x :: r x :: xs then rqpart(le, x, xs, Nil, Nil, r) -fun rqpart(le, x, yss, rle, rgt, r) = if yss is +fun rqpart(le: Function, x, yss, rle, rgt, r) = if yss is Nil then qsort(le, rle, x :: qsort(le, rgt, r)) y :: ys and le(y, x) then rqpart(le, x, ys, y :: rle, rgt, r) diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/banner.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/banner.mls index b3f02e72a6..3f806dc9cd 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/banner.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/banner.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" open NofibPrelude diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/boyer.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/boyer.mls index 780b1f7776..f0909ca8e2 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/boyer.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/boyer.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" open NofibPrelude diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/boyer2.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/boyer2.mls index f82474b5f1..8443d63134 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/boyer2.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/boyer2.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/calendar.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/calendar.mls index f4f3c881f7..1dd0a445cb 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/calendar.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/calendar.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/cichelli.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/cichelli.mls index 07e3036bab..b5c3dcae08 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/cichelli.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/cichelli.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -70,7 +72,7 @@ fun maxm(x, y) = if x is fun hash(cvs, k) = if k is K(_, a, z, n) then n + assoc(a, cvs) + assoc(z, cvs) -fun select(p, x, ts_fs) = if ts_fs is [ts, fs] and +fun select(p: Function, x, ts_fs) = if ts_fs is [ts, fs] and p(x) then [x :: ts, fs] else [ts, x :: fs] diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/circsim.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/circsim.mls index c4d20e5ac9..bff4649203 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/circsim.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/circsim.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -30,15 +32,15 @@ object Unit data class PS[T](pid: Int, compType: Componenet, pathDepth: Int, inports: List[[Int, Int, T]], outports: List[[Int, T, Bool, Int, Bool, Int]]) -fun pid(p) = p.pid +fun pid(p: PS) = p.pid -fun compType(p) = p.compType +fun compType(p: PS) = p.compType -fun pathDepth(p) = p.pathDepth +fun pathDepth(p: PS) = p.pathDepth -fun inports(p) = p.inports +fun inports(p: PS) = p.inports -fun outports(p) = p.outports +fun outports(p: PS) = p.outports fun updateOutports(p, noutports) = PS(pid(p), compType(p), pathDepth(p), inports(p), noutports) @@ -58,12 +60,12 @@ fun get(t) = if t is Cell(x) then x :: Nil Node(_, l, r) then get(l) +: get(r) -fun upsweep(f, t) = if t is +fun upsweep(f: Function, t) = if t is Cell(a) then [a, Cell(a)] Node(x, l, r) and upsweep(f, l) is [lv, l_] and upsweep(f, r) is [rv, r_] then [f(lv, rv), Node([lv, rv], l_, r_)] -fun downsweep(g, d, t) = if t is +fun downsweep(g: Function, d, t) = if t is Cell(x) then Cell(d) Node([lv, rv], l, r) and g(lv, rv, d) is [dl, dr] then Node(Unit, downsweep(g, dl, l), downsweep(g, dr, r)) @@ -71,15 +73,15 @@ fun downsweep(g, d, t) = if t is fun sweep_ud(up, down, u, t) = if upsweep(up, t) is [ans, t_] then [ans, downsweep(down, u, t_)] -fun scanL(f, u, xs) = +fun scanL(f: Function, u, xs) = fun down1(l, r, x) = [x, f(x, l)] if sweep_ud(f, down1, u, put(xs)) is [up_ans, t_] then [up_ans, get(t_)] -fun scanR(f, u, xs) = +fun scanR(f: Function, u, xs) = fun down2(l, r, x) = [f(r, x), x] if sweep_ud(f, down2, u, put(xs)) is [up_ans, t_] then [up_ans, get(t_)] -fun scanlr(f, g, lu, ru, xs) = +fun scanlr(f: Function, g: Function, lu, ru, xs) = fun up(f, g, lxly, rxry) = if lxly is [lx, ly] and rxry is [rx, ry] then [f(lx, rx), g(ly, ry)] fun down3(f, g, lxly, rxry, ab) = if lxly is [lx, ly] and rxry is [rx, ry] and ab is [a, b] then diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/clausify.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/clausify.mls index c049c89741..a555bac873 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/clausify.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/clausify.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/constraints.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/constraints.mls index a9ee12304a..05f369b48e 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/constraints.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/constraints.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -17,7 +19,7 @@ fun qsort(le, ls, r) = if ls is x :: Nil then x :: r x :: xs then qpart(le, x, xs, Nil, Nil, r) -fun qpart(le, x, ls, rlt, rge, r) = if ls is +fun qpart(le: Function, x, ls, rlt, rge, r) = if ls is Nil then rqsort(le, rlt, x :: rqsort(le, rge, r)) y :: ys and le(x, y) then qpart(le, x, ys, rlt, y :: rge, r) @@ -28,7 +30,7 @@ fun rqsort(le, ls, r) = if ls is x :: Nil then x :: r x :: xs then rqpart(le, x, xs, Nil, Nil, r) -fun rqpart(le, x, ls, rle, rgt, r) = if ls is +fun rqpart(le: Function, x, ls, rle, rgt, r) = if ls is Nil then rqsort(le, rle, x :: qsort(le, rgt, r)) y :: ys and le(y, x) then rqpart(le, x, ys, y :: rle, rgt, r) @@ -54,21 +56,21 @@ data class Node[out T](lab: T, children: List[Node[T]]) fun label(n) = if n is Node(l, _) then l -fun mapTree(f, n) = if n is Node(l, c) then Node(f(l), map((x => mapTree(f, x)), c)) +fun mapTree(f: Function, n) = if n is Node(l, c) then Node(f(l), map((x => mapTree(f, x)), c)) -fun foldTree(f, n) = if n is Node(l, c) then f(l, map((x => foldTree(f, x)), c)) +fun foldTree(f: Function, n) = if n is Node(l, c) then f(l, map((x => foldTree(f, x)), c)) -fun filterTree(p, t) = +fun filterTree(p: Function, t) = fun f1(a, cs) = Node(a, filter(x => p(label(x)), cs)) foldTree(f1, t) -fun prune(p, t) = filterTree(x => not(p(x)), t) +fun prune(p: Function, t) = filterTree(x => not(p(x)), t) fun leaves(t) = if t is Node(leaf, Nil) then leaf :: Nil Node(_, cs) then concat(map(leaves, cs)) -fun initTree(f, x) = Node(x, map(y => initTree(f, y), f(x))) +fun initTree(f: Function, x) = Node(x, map(y => initTree(f, y), f(x))) // -- Figure 3. Simple backtracking solver for CSPs. fun mkTree(csp) = if csp is CSP(vars, vals, rel) then @@ -122,7 +124,7 @@ fun knownSolution(c) = if c is fun checkComplete(csp, s) = if complete(csp, s) then Known(Nil) else Unknown -fun search(labeler, csp) = +fun search(labeler: Function, csp) = map of fst filter of diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/cryptarithm1.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/cryptarithm1.mls index d033310a6b..501f70ff40 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/cryptarithm1.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/cryptarithm1.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" open NofibPrelude diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/cryptarithm2.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/cryptarithm2.mls index 47915e74fa..4ce36acb73 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/cryptarithm2.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/cryptarithm2.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -17,17 +19,17 @@ fun delete_(xs, e) = deleteBy((x, y) => x === y, e, xs) fun listDiff(a, ls) = foldl(delete_, a, ls) -data class StateT[S, M, A](run) +data class StateT[S, M, A](run: Function) fun runStateT(m, s) = if m is StateT(run) then run(s) -fun bind(m, f) = +fun bind(m, f: Function) = StateT of s => concat of map(case { [a, ss] then runStateT(f(a), ss) }, runStateT(m, s)) fun return_(a) = StateT(s => [a, s] :: Nil) -fun mapM(f, ls) = foldr((a, r) => bind(f(a), x => bind(r, xs => return_(x :: xs))), return_(Nil), ls) +fun mapM(f: Function, ls) = foldr((a, r) => bind(f(a), x => bind(r, xs => return_(x :: xs))), return_(Nil), ls) fun lift(ls) = StateT(s => concat(map(x => [x, s] :: Nil, ls))) diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/cse.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/cse.mls index 8f5acc95b1..15d761943f 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/cse.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/cse.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -9,13 +11,13 @@ module cse with ... fun retURN(a) = s => [s, a] -fun bind(m, f) = s => if m(s) is [s_, a] then f(a)(s_) +fun bind(m: Function, f: Any -> Function) = s => if m(s) is [s_, a] then f(a)(s_) -fun join(m) = s => if m(s) is [s_, ma] then ma(s_) +fun join(m: Any -> [Any, Function]) = s => if m(s) is [s_, ma] then ma(s_) -fun mmap(f, m) = s => if m(s) is [s_, a] then [s_, f(a)] +fun mmap(f: Function, m: Function) = s => if m(s) is [s_, a] then [s_, f(a)] -fun mmapl(f, aas) = if aas is +fun mmapl(f: Function, aas) = if aas is Nil then retURN(Nil) a :: as_ then bind of @@ -24,7 +26,7 @@ fun mmapl(f, aas) = if aas is mmapl(f, as_) bs => retURN(b :: bs) -fun mmapr(f, xs) = if xs is +fun mmapr(f: Function, xs) = if xs is Nil then retURN(Nil) x :: xs then bind of @@ -33,25 +35,25 @@ fun mmapr(f, xs) = if xs is f(x) y => retURN(y :: ys) -fun mfoldl(f, a, xs) = if xs is +fun mfoldl(f: Function, a, xs) = if xs is Nil then retURN(a) x :: xs then bind(f(a, x), fax => mfoldl(f, fax, xs)) -fun mfoldr(f, a, xs) = if xs is +fun mfoldr(f: Function, a, xs) = if xs is Nil then retURN(a) x :: xs then bind(mfoldr(f, a, xs), y => f(x, y)) fun mif(c, t, f) = bind(c, cond => if cond then t else f) -fun startingWith(m, v) = if m(v) is [final, answer] then answer +fun startingWith(m: Function, v) = if m(v) is [final, answer] then answer fun fetch(s) = [s, s] -fun fetchWith(f) = s => [s, f(s)] +fun fetchWith(f: Function) = s => [s, f(s)] -fun update(f) = s => [f(s), s] +fun update(f: Function) = s => [f(s), s] fun set_(s_) = s => [s_, s] @@ -78,9 +80,9 @@ fun visited(n) = bind of fetch us => if inList(n, us) then retURN(true) else bind(set_(n :: us), _p => retURN(false)) -fun newlyDefined(x, fx, f, y) = if x === y then fx else f(y) +fun newlyDefined(x, fx, f: Function, y) = if x === y then fx else f(y) -fun findCommon(ls) = +fun findCommon[A](ls: List[[Int, A, List[Int]]]) = fun sim(n_s_cs, r_lg) = if n_s_cs is [n, s, cs] and r_lg is [r, lg] then let rcs = map(r, cs) fun lscomp(ls) = if ls is diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/eliza.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/eliza.mls index 3d2106539b..fe7c40943c 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/eliza.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/eliza.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -7,9 +9,9 @@ module eliza with ... -fun toUpper(c) = c.toUpperCase() +fun toUpper(c: Str) = c.toUpperCase() -fun lz_map(f, ls) = lazy of () => +fun lz_map(f: Function, ls) = lazy of () => if ls is Nil then LzNil h :: t then LzCons(f(h), lz_map(f, t)) diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/fish.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/fish.mls index ebf4c61882..38d1f3bb8f 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/fish.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/fish.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" open NofibPrelude @@ -62,13 +64,13 @@ fun grid(m, n, segments, a, b, c) = lscomp(t) lscomp(segments) -fun rot(p, a, b, c) = p(vec_add(a, b), c, vec_sub([0, 0], b)) +fun rot(p: Function, a, b, c) = p(vec_add(a, b), c, vec_sub([0, 0], b)) -fun beside(m, n, p, q, a, b, c) = +fun beside(m, n, p: Function, q: Function, a, b, c) = p(a, scale_vec2(b, m, m + n), c) +: q(vec_add(a, scale_vec2(b, m, m + n)), scale_vec2(b, n, n + m), c) -fun above(m, n, p, q, a, b, c) = +fun above(m, n, p: Function, q: Function, a, b, c) = p(vec_add(a, scale_vec2(c, n, m + n)), b, scale_vec2(c, m, n + m)) +: q(a, b, scale_vec2(c, n, m + n)) diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/gcd.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/gcd.mls index b3a02cd918..eeb5a50da8 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/gcd.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/gcd.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" open NofibPrelude diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/integer.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/integer.mls index ad8d823795..97c962a944 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/integer.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/integer.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -6,7 +8,7 @@ module integer with ... -fun integerbench(op, astart, astep, alim, bstart, bstep, blim) = +fun integerbench(op: Function, astart, astep, alim, bstart, bstep, blim) = fun lscomp1(ls) = if ls is Nil then Nil a :: t1 then @@ -17,7 +19,7 @@ fun integerbench(op, astart, astep, alim, bstart, bstep, blim) = lscomp1(enumFromThenTo(astart, astart + astep, alim)) -fun intbench(op, astart, astep, alim, bstart, bstep, blim) = +fun intbench(op: Function, astart, astep, alim, bstart, bstep, blim) = fun lscomp1(ls) = if ls is Nil then Nil a :: t1 then @@ -55,7 +57,7 @@ fun runalltests(astart, astep, alim, bstart, bstep, blim) = runbench((a, b) => z_gt(a, b), (a, b) => a > b, "(>)", astart, astep, alim, astart, astep, alim); runbench((a, b) => z_geq(a, b), (a, b) => a >= b, "(>=)", astart, astep, alim, astart, astep, alim) -fun testInteger_nofib(n) = runalltests(-2100000000, n, 2100000000, -2100000000, n, -2100000000) +fun testInteger_nofib(n): List[Bool] = runalltests(-2100000000, n, 2100000000, -2100000000, n, -2100000000) // NOTE: original input -2100000000 14000001 2100000000 diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/knights.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/knights.mls index 149f9c9ddd..3748abd52f 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/knights.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/knights.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -5,7 +7,7 @@ open NofibPrelude module knights with ... -fun myIsDigit(c) = +fun myIsDigit(c: Str) = c.codePointAt(0) >= 48 && c.codePointAt(0) <= 57 fun intintComp(a_b, c_d) = if a_b is [a, b] and c_d is [c, d] then (a < c) || ((a === c) && (b < d)) @@ -247,16 +249,16 @@ fun inquireFront_lz(h_t) = if force(h_t) is LzCons(h, t) then h fun addAllFront_lz(list, q) = append_lz_lz(list, q) -fun depthSearch(q, growFn, finFn) = +fun depthSearch(q, growFn: Function, finFn: Function) = if emptyQueue_lz(q) then lazy of () => LzNil finFn(inquireFront_lz(q)) then lazy of () => LzCons(inquireFront_lz(q), depthSearch(removeFront_lz(q), growFn, finFn)) else depthSearch(addAllFront_lz(growFn(inquireFront_lz(q)), removeFront_lz(q)), growFn, finFn) fun printTour(ss) = - fun strToInt(y, xs) = if xs is + fun strToInt(y, xs: List[Str]) = if xs is Nil then y - x :: xs then strToInt((10 * y) + (x.codePointAt(0) - 48), xs) + x :: xs then strToInt((10 * y) + ((x as Str).codePointAt(0) - 48), xs) fun pp(xs) = if xs is Nil then Nil [x, y] :: xs then diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/lambda.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/lambda.mls index f15888b431..b0671c6a69 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/lambda.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/lambda.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -14,7 +16,7 @@ data class MyState[S, A](r: S -> [S, A]) fun myRunState(m, s) = if m is MyState(f) then f(s) -fun myBind(m, f) = MyState(s => if myRunState(m, s) is [s_, a] then myRunState(f(a), s_)) +fun myBind(m, f: Function) = MyState(s => if myRunState(m, s) is [s_, a] then myRunState(f(a), s_)) fun myReturn(a) = MyState(s => [s, a]) @@ -60,7 +62,7 @@ fun eqTerm(a, b) = if a is else false //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -fun myMaybe(d, f, x) = if x is Some(x) then f(x) +fun myMaybe(d, f: Function, x) = if x is Some(x) then f(x) val incr = myReturn(Unit) //β”‚ incr = MyState([function]) diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/lastpiece.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/lastpiece.mls index bc6b506d43..b521f01858 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/lastpiece.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/lastpiece.mls @@ -1,3 +1,7 @@ +// #lang(0.3.x) +// ^ Too slow for the CI at the moment, making it flaky; +// needs the perf. fixes at `LPTK/new-resolution-no-hashing` + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -8,7 +12,7 @@ module lastpiece with ... fun isSome(x) = x is Some -fun mapMaybe(f, ls) = if ls is +fun mapMaybe(f: Function, ls) = if ls is Nil then Nil h :: t and f(h) is None then mapMaybe(f, t) @@ -360,7 +364,7 @@ fun displayBoard(bd) = fun row(n) = hcat(map(col => sq(n, col), enumFromTo(1, maxCol))) above_(vcat(map(row, enumFromTo(1, maxCol))), false, text(Nil)) -fun eliminateEmpty(cons, p, g, q) = if p is +fun eliminateEmpty(cons: Function, p, g, q) = if p is Empty then q else [ NotEmpty, @@ -462,7 +466,7 @@ fun printDoc(d) = let done = "\n" :: Nil fullRender(ZigZagMode, 200, 1.5, put, done, d) -fun fullRender(m, l, r, txt, a, b) = +fun fullRender(m, l, r, txt: Function, a, b) = fun annTxt(p, x) = if p is NoAnnot(s, _) then txt(s, x) _ then x @@ -470,7 +474,7 @@ fun fullRender(m, l, r, txt, a, b) = fun ceiling(x) = globalThis.Math.ceil(x) -fun fullRenderAnn(m, lineLen, ribbons, txt, rest, doc) = if m is +fun fullRenderAnn(m, lineLen, ribbons, txt: Function, rest, doc) = if m is OneLineMode then easyDisplay(spaceText, (a, b) => b, txt, rest, reduceDoc(doc)) LeftMode then easyDisplay(nlText, first, txt, rest, reduceDoc(doc)) else @@ -479,7 +483,7 @@ fun fullRenderAnn(m, lineLen, ribbons, txt, rest, doc) = if m is let doc1 = best(bestLineLen, ribbonLen, reduceDoc(doc)) displayDoc(m, lineLen, ribbonLen, txt, rest, doc1) -fun easyDisplay(nlSpaceText, choose, txt, end, x) = +fun easyDisplay(nlSpaceText, choose: Function, txt: Function, end, x) = fun lay(x) = if x is NoDoc then throw Error("easyDisplay: NoDoc") Union(p, q) then lay(choose(p, q)) @@ -491,7 +495,7 @@ fun easyDisplay(nlSpaceText, choose, txt, end, x) = Beside(_, _, _) then throw Error("easyDisplay Beside") lay(x) -fun displayDoc(m, pageWidth, ribbonWidth, txt, end, doc) = +fun displayDoc(m, pageWidth, ribbonWidth, txt: Function, end, doc) = let gapWidth = pageWidth - ribbonWidth let shift = intDiv(gapWidth, 2) fun lay(k, docc) = diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/lcss.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/lcss.mls index 42c9ae58f1..ab17562cf6 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/lcss.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/lcss.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" open NofibPrelude @@ -50,7 +52,7 @@ fun lcss(xs, ys) = algc(listLen(xs), listLen(ys), xs, ys)(Nil) fun lcssMain(a, b ,c, d, e, f) = lcss(enumFromThenTo(a,b,c), enumFromThenTo(d,e,f)) -fun testLCSS_nofib(d) = lcssMain(1,2,60,30,31,90) +fun testLCSS_nofib(d): List[Int] = lcssMain(1,2,60,30,31,90) diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/life.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/life.mls index b7da7faf38..33ba9fd762 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/life.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/life.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -25,7 +27,7 @@ fun init(ls) = if ls is a :: t then a :: init(t) else throw Error(ls) -fun zipWith3(f, xs, ys, zs) = if +fun zipWith3(f: Function, xs, ys, zs) = if xs is hx :: tx and ys is hy :: ty and zs is hz :: tz then f(hx, hy, hz) :: zipWith3(f, tx, ty, tz) else Nil diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/mandel.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/mandel.mls index fdcfc337b2..98a7119950 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/mandel.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/mandel.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/mandel2.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/mandel2.mls index dc5ee1620c..20e40eb503 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/mandel2.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/mandel2.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/mate.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/mate.mls index b4460f6159..b9dc8671ff 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/mate.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/mate.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -7,7 +9,7 @@ module mate with ... //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -fun rqpart(le, x, ys, rle, rgt, r) = if ys is +fun rqpart(le: Function, x, ys, rle, rgt, r) = if ys is Nil then qsort(le, rle, x :: qsort(le, rgt, r)) y :: ys and le(y, x) then rqpart(le, x, ys, y :: rle, rgt, r) @@ -18,7 +20,7 @@ fun rqsort(le, xs, r) = if xs is x :: Nil then x :: r x :: xs then rqpart(le, x, xs, Nil, Nil, r) -fun qpart(le, x, ys, rlt, rge, r) = if ys is +fun qpart(le: Function, x, ys, rlt, rge, r) = if ys is Nil then rqsort(le, rlt, x :: rqsort(le, rge, r)) y :: ys and le(x, y) then qpart(le, x, ys, rlt, y :: rge, r) @@ -96,15 +98,15 @@ fun moveInFullCompare(mif1, mif2) = if sc === 0 then moveCompare(m1, m2) else sc else pc -fun maybe(d, f, x) = if x is +fun maybe[A, B](d: B, f: A -> B, x: Option[A]): B = if x is None then d Some(x) then f(x) -fun isUpper(c) = let x = c.charCodeAt(0) in (x >= 65 and x <= 90) +fun isUpper(c: Str) = let x = c.charCodeAt(0) in (x >= 65 and x <= 90) -fun isLower(c) = let x = c.charCodeAt(0) in (x >= 97 and x <= 122) +fun isLower(c: Str) = let x = c.charCodeAt(0) in (x >= 97 and x <= 122) -fun toLower(c) = if isUpper(c) then String.fromCharCode(c.charCodeAt(0) + 32) else c +fun toLower(c: Str) = if isUpper(c) then String.fromCharCode(c.charCodeAt(0) + 32) else c fun words(s) = if leaveWhile(x => x === " ", s) is Nil then Nil @@ -120,7 +122,7 @@ fun lines(s) = if break_(x => x === "\n", s) is [l, s_] then Nil then Nil Cons(_, s__) then lines(s__) -fun any(p, ls) = if ls is +fun any(p: Function, ls) = if ls is Nil then false x :: xs then p(x) || any(p, xs) @@ -170,7 +172,7 @@ fun showSquare(c, x_y) = if x_y is [x, y] then ) +: nofibStringToList(stringOfInt(if c is Black then 9 - y else y)) -fun emptyAtAll(bd, e) = if bd is Board(wkss, bkss) then +fun emptyAtAll(bd, e: Function) = if bd is Board(wkss, bkss) then fun emptyAtAllAnd(b, ls) = if ls is Nil then b [_, s] :: xs then (not(e(s)) and emptyAtAllAnd(b, xs)) @@ -251,7 +253,7 @@ fun sift(c, bd, ms, sqs) = if sqs is else sift(c, bd, Move(sq, Some(p_), None) :: ms, sqs) else sift(c, bd, ms, sqs) -fun moveLine(bd, c, sq, inc, cont) = +fun moveLine(bd, c, sq, inc: Function, cont: Function) = fun ml(sq, ms) = let sq_ = inc(sq) if onboard(sq_) and pieceAt(bd, sq_) is @@ -473,7 +475,7 @@ fun parseProblem(s) = fun readProblem(s) = parseProblem(lines(s)) -fun foldr_lz(f, a, x) = if x is +fun foldr_lz(f: Function, a, x) = if x is h :: t then f(h, lazy of () => foldr_lz(f, a, t)) Nil then a diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/minimax.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/minimax.mls index 83e664d4f0..fc3b10979d 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/minimax.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/minimax.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -171,7 +173,7 @@ fun scorePiece(p, score) = if p is Empty then 0 O then -score -fun map2(f, xs, ys) = if xs is +fun map2(f: Function, xs, ys) = if xs is Nil then Nil x :: xs and ys is @@ -184,9 +186,9 @@ fun score(board, win) = eval of sum of map of fun static(board) = interpret(0, map(x => score(board, x), wins)) -fun repTree(f, g, a) = Branch(a, map(x => repTree(g, f, x), f(a))) +fun repTree(f: Function, g, a) = Branch(a, map(x => repTree(g, f, x), f(a))) -fun mapTree(f, t) = if t is Branch(a, l) then Branch(f(a), map(x => mapTree(f, x), l)) +fun mapTree(f: Function, t) = if t is Branch(a, l) then Branch(f(a), map(x => mapTree(f, x), l)) fun prune(n, t) = if t is Branch(a, l) and n === 0 then Branch(a, Nil) @@ -198,7 +200,7 @@ fun opposite(p) = if p is O then X else throw Error("opposite") -fun best(f, bs, ss) = if bs is b :: bs and ss is s :: ss then +fun best(f: Function, bs, ss) = if bs is b :: bs and ss is s :: ss then fun best_(b, s, ls1, ls2) = if ls1 is Nil and ls2 is Nil then [b, s] b_ :: bs and ls2 is s_ :: ss and @@ -227,7 +229,7 @@ fun min_(e1, e2) = if x < y then Score(x) else Score(y) -fun mise(f, g, t) = if t is +fun mise(f, g: Function, t) = if t is Branch(a, Nil) then a Branch(_, l) then foldr(f, g(OWin, XWin), map(x => mise(g, f, x), l)) diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/para.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/para.mls index 49e62e4e52..f75db151ae 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/para.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/para.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -14,7 +16,7 @@ fun unwords(ws) = if ws is v :: vs then " " :: (v +: go(vs)) w +: go(ws) -fun break_(p, xs) = if xs is +fun break_(p: Function, xs) = if xs is Nil then [Nil, Nil] x :: xs and p(x) then [Nil, x :: xs] @@ -37,15 +39,15 @@ fun lines(s) = if break_(x => x === "\n", s) is [l, s_] then fun unlines(ls) = concat(map(l => l +: nofibStringToList("\n"), ls)) -fun all(p, xs) = if xs is +fun all(p: Function, xs) = if xs is Nil then true x :: xs then p(x) && all(p, xs) -fun fold1(f, g, xs) = if xs is +fun fold1(f: Function, g: Function, xs) = if xs is a :: Nil then g(a) a :: x then f(a, fold1(f, g, x)) -fun scan1(f, g, xs) = fold1((a, s) => f(a, head(s)) :: s, a => g(a) :: Nil, xs) +fun scan1(f: Function, g: Function, xs) = fold1((a, s) => f(a, head(s)) :: s, a => g(a) :: Nil, xs) fun tails(xs) = scan1((a, s) => a :: s, a => a :: Nil, xs) @@ -53,7 +55,7 @@ fun single(xs) = if xs is a :: Nil then true _ then false -fun minWith(f, xs) = fold1((a, b) => if f(a) < f(b) then a else b, x => x, xs) +fun minWith(f: Function, xs) = fold1((a, b) => if f(a) < f(b) then a else b, x => x, xs) fun new_(w, ls) = (w :: Nil) :: ls @@ -170,7 +172,7 @@ fun startr(a) = if fun ceildiv(n, m) = intDiv(n + m - 1, m) -fun fmtWith(par) = unparse(map(x => par(concat(x)), parse(par))) +fun fmtWith(par: Function) = unparse(map(x => par(concat(x)), parse(par))) fun stepr(w, ps_tw_tl) = if ps_tw_tl is [ps, tw, tl] then let tot_width = w + 1 + tw diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/power.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/power.mls index c8de74388c..a00335f39d 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/power.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/power.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -115,7 +117,7 @@ fun integral(fs_) = lazy of () => Pc(0, int1(fs_, 1)) -fun integralLz(fs_) = +fun integralLz(fs_ : Function) = fun int1(fss, n) = lazy of () => if force(fss) is Pz then Pz Pc(f, fs_) then Pc(f/n, int1(fs_, n + 1)) @@ -139,7 +141,7 @@ fun cosx() = minusPs((lazy of () => Pc(1, lazy of () => Pz)), integral(integralL fun sinx() = integral(minusPs((lazy of () => Pc(1, lazy of () => Pz)), integralLz(sinx))) -fun testPower_nofib(p) = +fun testPower_nofib(p): List[Num] = extract(p, minusPs(sinx(), sqrtPs(minusPs(fromIntegerPs(1), powerPs(cosx(), 2))))); extract(p, minusPs(divPs(sinx(), cosx()), revert(integral(divPs(fromIntegerPs(1), addPs(fromIntegerPs(1), powerPs(x_(), 2))))))); extract(p, ts()); diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/pretty.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/pretty.mls index e8a03507a3..94058d7d40 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/pretty.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/pretty.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -69,10 +71,10 @@ fun cShow(seq) = flatten(0, true, seq, Nil) data class MkPrettyRep(cseq: CSeq, n: Int, b1: Bool, b2: Bool) -fun ppShow(width, p) = if p(width, false) is +fun ppShow(width, p: Function) = if p(width, false) is MkPrettyRep(seq, ll, emp, sl) then cShow(seq) -fun ppUnformatted(p) = if p(80, false) is +fun ppUnformatted(p: Function) = if p(80, false) is MkPrettyRep(seq, ll, emp, sl) then cShow(seq) fun ppNil(width, is_vert) = MkPrettyRep(cNil, 0, true, width >= 0) @@ -105,16 +107,16 @@ fun andL(a, b) = if a then b else false fun orL(a, b) = if a then true else b -fun ppBeside(p1, p2, width, is_vert) = if p1(width, false) is +fun ppBeside(p1: Function, p2: Function, width, is_vert) = if p1(width, false) is MkPrettyRep(seq1, ll1, emp1, sl1) and p2(width - ll1, false) is MkPrettyRep(seq2, ll2, emp2, sl2) then MkPrettyRep(cAppend(seq1, cIndent(ll1, seq2)), ll1 + ll2, andL(emp1, emp2), andL(width >= 0, andL(sl1, sl2))) -fun ppBesides(ps) = if ps is +fun ppBesides(ps: List[Function]) = if ps is Nil then ppNil else foldr1((a, b) => (c, d) => ppBeside(a, b, c, d), ps) -fun ppBesideSP(p1, p2, width, is_vert) = if p1(width, false) is MkPrettyRep(seq1, ll1, emp1, sl1) then +fun ppBesideSP(p1: Function, p2: Function, width, is_vert) = if p1(width, false) is MkPrettyRep(seq1, ll1, emp1, sl1) then let li = if emp1 then 0 else ll1 + 1 if p2(width - li, false) is MkPrettyRep(seq2, ll2, emp2, sl2) then let wi = if emp1 then 0 else 1 @@ -123,20 +125,20 @@ fun ppBesideSP(p1, p2, width, is_vert) = if p1(width, false) is MkPrettyRep(seq1 fun ppCat(ps) = if ps is Nil then ppNil else foldr1((a, b) => (c, d) => ppBesideSP(a, b, c, d), ps) -fun ppAbove(p1, p2, width, is_vert) = if p1(width, true) is +fun ppAbove(p1: Function, p2: Function, width, is_vert) = if p1(width, true) is MkPrettyRep(seq1, ll1, emp1, sl1) and p2(width, true) is MkPrettyRep(seq2, ll2, emp2, sl2) then let nl = if orL(emp1, emp2) then cNil else cNL MkPrettyRep(cAppend(seq1, cAppend(nl, seq2)), ll2, andL(emp1, emp2), false) -fun ppAboves(ps, a, b) = if ps is Nil then ppNil(a, b) else foldr1((a, b) => (c, d) => ppAbove(a, b, c, d), ps)(a, b) +fun ppAboves(ps: List[Function], a, b) = if ps is Nil then ppNil(a, b) else foldr1((a, b) => (c, d) => ppAbove(a, b, c, d), ps)(a, b) -fun ppNest(n, p, width, is_vert) = if +fun ppNest(n, p: Function, width, is_vert) = if is_vert and p(width - n, true) is MkPrettyRep(seq, ll, emp, sl) then MkPrettyRep(cIndent(n, seq), ll + n, emp, sl) else p(width, false) -fun ppHang(p1, n, p2, width, is_vert) = if p1(width, false) is +fun ppHang(p1: Function, n, p2: Function, width, is_vert) = if p1(width, false) is MkPrettyRep(seq1, ll1, emp1, sl1) and p2(width - (ll1 + 1), false) is MkPrettyRep(seq2, ll2, emp2, sl2) and p2(width - n, false) is MkPrettyRep(seq2_, ll2_, emp2_, sl2_) and diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/primetest.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/primetest.mls index 8b3ed2275b..1c986e080c 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/primetest.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/primetest.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -16,7 +18,7 @@ fun int_val_of_string(s) = h :: t then f(t, 10 * a + int_val_of_char(h)) f(s, 0) -fun break_(p, ls) = if ls is +fun break_(p: Function, ls) = if ls is Nil then [Nil, Nil] x :: xs and p(x) then [Nil, x :: xs] @@ -115,7 +117,7 @@ fun multiTest(k, rs, n) = if ((n <= 1) || even(n)) then [n==2, rs] else mTest(k, rs) -fun doLine(cs, cont, rs) = +fun doLine(cs, cont: Function, rs) = let n = int_val_of_string(cs) if multiTest(100, rs, n) is [t, rs_] and t then "Probably prime" :: (cont(rs_)) diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/puzzle.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/puzzle.mls index d173a9b294..56904309f5 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/puzzle.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/puzzle.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/rsa.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/rsa.mls index e6b71c04f0..51a79a32de 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/rsa.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/rsa.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -16,7 +18,7 @@ fun z_div(x, y) = x / y fun z_mod(x, y) = x % y fun z_equal(x, y) = x === y fun z_sqr(x) = x * x -fun int_if_char(c) = c.codePointAt(0) +fun int_if_char(c: Str) = c.codePointAt(0) val const0 = z_of_int(0) //β”‚ const0 = 0 diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/scc.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/scc.mls index 58805f2a10..b9eefc716b 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/scc.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/scc.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -6,7 +8,7 @@ module scc with ... -fun dfs(r, vsns, xs) = if vsns is [vs, ns] and +fun dfs(r: Function, vsns, xs) = if vsns is [vs, ns] and xs is Nil then [vs, ns] x :: xs and diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/secretary.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/secretary.mls index 87d4e567fd..0ec5a3dd2f 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/secretary.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/secretary.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -13,7 +15,7 @@ fun infRand(m, s) = f(s) -fun simulate(n, m, proc) = +fun simulate(n, m, proc: Function) = fun lscomp(ls) = if ls is Nil then Nil seed :: t then proc(infRand(m, seed)) :: lscomp(t) diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/sphere.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/sphere.mls index aac6cd0e92..1f8962abda 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/sphere.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/sphere.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude diff --git a/hkmc2/shared/src/test/mlscript-compile/nofib/treejoin.mls b/hkmc2/shared/src/test/mlscript-compile/nofib/treejoin.mls index b0bad99e42..d7044b6aa9 100644 --- a/hkmc2/shared/src/test/mlscript-compile/nofib/treejoin.mls +++ b/hkmc2/shared/src/test/mlscript-compile/nofib/treejoin.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude @@ -8,7 +10,7 @@ module treejoin with ... fun isSpace(c) = c === " " || c === "\n" -fun isDigit(c) = +fun isDigit(c: Str) = let n = c.codePointAt(0) in n >= 48 && n <= 57 @@ -41,9 +43,9 @@ fun lookupT(k, t) = if t is Empty then None fun readInt(s) = - fun readInt_(n, cs) = if cs is + fun readInt_(n, cs: List[Str]) = if cs is c :: cs_ and - isDigit(c) then readInt_(n * 10 + c.codePointAt(0) - 48, cs_) + isDigit(c) then readInt_(n * 10 + (c as Str).codePointAt(0) - 48, cs_) else let s_ = leaveWhile(isSpace, c :: cs) in [n, s_] else let s_ = leaveWhile(isSpace, cs) in [n, s_] @@ -57,7 +59,7 @@ fun join(t1, t2, j) = if Some([d, e, f]) then insertT(c, [a, b, c, d, e], j) t1 is Node(k, l, r) then join(l, t2, join(r, t2, j)) -fun readTree(fk, s, t) = if +fun readTree(fk: Function, s, t) = if s is Nil then t readInt(s) is [f, s_] and readInt(s_) is [g, s__] and @@ -67,7 +69,7 @@ fun readTree(fk, s, t) = if readTree(fk, s___, insertT(k, e, t)) -fun testTreejoin_nofib(n) = +fun testTreejoin_nofib(n): Tree[[Int, Int, Int, Int, Int]] = let c1 = nofibStringToList of fs.readFileSync("./hkmc2/shared/src/test/mlscript/nofib/input/1500.1").toString() let c2 = nofibStringToList of fs.readFileSync("./hkmc2/shared/src/test/mlscript/nofib/input/1500.2").toString() let a = readTree(case { [xx, _, _] then xx }, c1, Empty) diff --git a/hkmc2/shared/src/test/mlscript-compile/quotes/CSPBar.mls b/hkmc2/shared/src/test/mlscript-compile/quotes/CSPBar.mls index 52ec4999ca..13c0fe2a76 100644 --- a/hkmc2/shared/src/test/mlscript-compile/quotes/CSPBar.mls +++ b/hkmc2/shared/src/test/mlscript-compile/quotes/CSPBar.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./../Example.mls" module CSPBar with ... fun res = diff --git a/hkmc2/shared/src/test/mlscript-compile/quotes/CSPBaz.mls b/hkmc2/shared/src/test/mlscript-compile/quotes/CSPBaz.mls index e44c6b8bba..bc6d648133 100644 --- a/hkmc2/shared/src/test/mlscript-compile/quotes/CSPBaz.mls +++ b/hkmc2/shared/src/test/mlscript-compile/quotes/CSPBaz.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./CSPNest.mls" module CSPBaz with ... fun res = diff --git a/hkmc2/shared/src/test/mlscript-compile/quotes/CSPFoo.mls b/hkmc2/shared/src/test/mlscript-compile/quotes/CSPFoo.mls index 3cb2a29866..6219a922a7 100644 --- a/hkmc2/shared/src/test/mlscript-compile/quotes/CSPFoo.mls +++ b/hkmc2/shared/src/test/mlscript-compile/quotes/CSPFoo.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./../CSP.mls" module CSPFoo with ... fun res = diff --git a/hkmc2/shared/src/test/mlscript-compile/quotes/CSPNest.mls b/hkmc2/shared/src/test/mlscript-compile/quotes/CSPNest.mls index 3897489e9d..d412c0fead 100644 --- a/hkmc2/shared/src/test/mlscript-compile/quotes/CSPNest.mls +++ b/hkmc2/shared/src/test/mlscript-compile/quotes/CSPNest.mls @@ -1,2 +1,4 @@ +#lang(0.3.x) + module CSPNest with ... fun nest_f() = 42 diff --git a/hkmc2/shared/src/test/mlscript-compile/quotes/Cubic.mls b/hkmc2/shared/src/test/mlscript-compile/quotes/Cubic.mls index 1b7055c23e..1dd0d0e970 100644 --- a/hkmc2/shared/src/test/mlscript-compile/quotes/Cubic.mls +++ b/hkmc2/shared/src/test/mlscript-compile/quotes/Cubic.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + module Cubic with ... fun res = diff --git a/hkmc2/shared/src/test/mlscript-compile/quotes/Gib12.mls b/hkmc2/shared/src/test/mlscript-compile/quotes/Gib12.mls index d191fdb538..aa7fe5c9b5 100644 --- a/hkmc2/shared/src/test/mlscript-compile/quotes/Gib12.mls +++ b/hkmc2/shared/src/test/mlscript-compile/quotes/Gib12.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + module Gib12 with ... fun res = diff --git a/hkmc2/shared/src/test/mlscript-compile/quotes/Opened.mls b/hkmc2/shared/src/test/mlscript-compile/quotes/Opened.mls index 45eb6634f7..f01cb51a3f 100644 --- a/hkmc2/shared/src/test/mlscript-compile/quotes/Opened.mls +++ b/hkmc2/shared/src/test/mlscript-compile/quotes/Opened.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "./../Predef.mls" module Opened with ... fun res = diff --git a/hkmc2/shared/src/test/mlscript-compile/quotes/QuoteFoo.mls b/hkmc2/shared/src/test/mlscript-compile/quotes/QuoteFoo.mls index f0e447e0e4..e50ae3bb2c 100644 --- a/hkmc2/shared/src/test/mlscript-compile/quotes/QuoteFoo.mls +++ b/hkmc2/shared/src/test/mlscript-compile/quotes/QuoteFoo.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + module QuoteFoo with ... fun res = diff --git a/hkmc2/shared/src/test/mlscript-compile/quotes/QuoteInc.mls b/hkmc2/shared/src/test/mlscript-compile/quotes/QuoteInc.mls index 461729c341..3aca81129e 100644 --- a/hkmc2/shared/src/test/mlscript-compile/quotes/QuoteInc.mls +++ b/hkmc2/shared/src/test/mlscript-compile/quotes/QuoteInc.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + module QuoteInc with ... fun res = diff --git a/hkmc2/shared/src/test/mlscript-compile/quotes/SafeDiv.mls b/hkmc2/shared/src/test/mlscript-compile/quotes/SafeDiv.mls index 37a40a58b6..eb33687708 100644 --- a/hkmc2/shared/src/test/mlscript-compile/quotes/SafeDiv.mls +++ b/hkmc2/shared/src/test/mlscript-compile/quotes/SafeDiv.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + module SafeDiv with ... fun res = diff --git a/hkmc2/shared/src/test/mlscript-compile/ups/DnfCnf.mls b/hkmc2/shared/src/test/mlscript-compile/ups/DnfCnf.mls index ba15362351..af671c45f8 100644 --- a/hkmc2/shared/src/test/mlscript-compile/ups/DnfCnf.mls +++ b/hkmc2/shared/src/test/mlscript-compile/ups/DnfCnf.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + open annotations module DnfCnf with ... diff --git a/hkmc2/shared/src/test/mlscript-compile/ups/EvaluationContext.mls b/hkmc2/shared/src/test/mlscript-compile/ups/EvaluationContext.mls index 6519116f08..7fae80829a 100644 --- a/hkmc2/shared/src/test/mlscript-compile/ups/EvaluationContext.mls +++ b/hkmc2/shared/src/test/mlscript-compile/ups/EvaluationContext.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "../Option.mls" open Option { Some, None } @@ -15,7 +17,7 @@ data class Term with let _freeVars = None // Compute free variables. Retrieve the cache if it exists. - fun freeVars = if _freeVars is + fun freeVars: Set[Str] = if _freeVars is Some(fv) then fv None then let fv = if this is @@ -54,7 +56,7 @@ data class Term with App(lhs, rhs) then lhs.show + " " + rhs.show Abs(Var(name), body) then "Ξ»" + name + ". " + body.show -fun subst(term, name, target) = if term is +fun subst(term, name, target: Term) = if term is Var(name') and name === name' then target else term diff --git a/hkmc2/shared/src/test/mlscript-compile/ups/EvenOddTree.mls b/hkmc2/shared/src/test/mlscript-compile/ups/EvenOddTree.mls index 29571df9ec..445a439a3d 100644 --- a/hkmc2/shared/src/test/mlscript-compile/ups/EvenOddTree.mls +++ b/hkmc2/shared/src/test/mlscript-compile/ups/EvenOddTree.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + open annotations module EvenOddTree with ... diff --git a/hkmc2/shared/src/test/mlscript-compile/ups/TruthyFalsy.mls b/hkmc2/shared/src/test/mlscript-compile/ups/TruthyFalsy.mls index 12fa7723f4..86f3a979fb 100644 --- a/hkmc2/shared/src/test/mlscript-compile/ups/TruthyFalsy.mls +++ b/hkmc2/shared/src/test/mlscript-compile/ups/TruthyFalsy.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + open annotations module TruthyFalsy with ... diff --git a/hkmc2/shared/src/test/mlscript-compile/wasm/Wasm.mls b/hkmc2/shared/src/test/mlscript-compile/wasm/Wasm.mls index 9c7dff74c2..0f7d03c61c 100644 --- a/hkmc2/shared/src/test/mlscript-compile/wasm/Wasm.mls +++ b/hkmc2/shared/src/test/mlscript-compile/wasm/Wasm.mls @@ -1,3 +1,5 @@ +#lang(0.3.x) + import "binaryen" import "../Predef.mls" @@ -6,7 +8,7 @@ import "../Runtime.mls" module Wasm with ... // WebAssembly utilities -fun instantiateWasmBinary(modBuf, importObject): WebAssembly.Instance = +fun instantiateWasmBinary(modBuf, importObject): dyn = assert WebAssembly.validate of modBuf let mod = new WebAssembly.Module(modBuf) new WebAssembly.Instance(mod, importObject) @@ -15,7 +17,7 @@ fun dumpWasmModImpExp(wasmMod) = console.log of "Imports: %s", WebAssembly.Module.imports(wasmMod) console.log of "Exports: %s", WebAssembly.Module.exports(wasmMod) -fun dumpWasmInstImpExp(wasmInst) = +fun dumpWasmInstImpExp(wasmInst: dyn) = console.log of "Imports: %s", wasmInst.imports console.log of "Exports: %s", wasmInst.exports @@ -33,7 +35,7 @@ fun binaryenFmtWat(wat, foldExprs): String = mod.dispose() fmtWat -fun binaryenCompileToModule(wat, importObject): WebAssembly.Instance = +fun binaryenCompileToModule(wat, importObject): dyn = let mod = binaryen.parseText of wat, binaryen.Features.All // Note: it seems `mod.validate()` sometimes returns `1` instead of `true`; so we don't use `assert` here @@ -44,7 +46,7 @@ fun binaryenCompileToModule(wat, importObject): WebAssembly.Instance = instantiateWasmBinary of modBuf, importObject -fun unwrapWasmException(err, tag) = +fun unwrapWasmException(err: dyn, tag) = if ( err !== null && err !== undefined && @@ -55,7 +57,7 @@ fun unwrapWasmException(err, tag) = then err.getArg(tag, 0) else err -fun binaryenRunFunc(wat, importObject, func): Any = +fun binaryenRunFunc(wat, importObject, func: Function): Any = let wasmInst = binaryenCompileToModule of wat, importObject let exnTag = wasmInst.exports.mlx_exn Runtime.try_catch of diff --git a/hkmc2/shared/src/test/mlscript/.mls b/hkmc2/shared/src/test/mlscript/.mls new file mode 100644 index 0000000000..54926c1b7d --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/.mls @@ -0,0 +1 @@ +#lang(0.3.x) diff --git a/hkmc2/shared/src/test/mlscript/OverloadedModulesInSignatures.mls b/hkmc2/shared/src/test/mlscript/OverloadedModulesInSignatures.mls index 0984173da8..27588b1b05 100644 --- a/hkmc2/shared/src/test/mlscript/OverloadedModulesInSignatures.mls +++ b/hkmc2/shared/src/test/mlscript/OverloadedModulesInSignatures.mls @@ -14,10 +14,10 @@ module TypMod with fun TrmMod = 42 module TrmMod with val whoami = "module" -//β”‚ ╔══[COMPILATION ERROR] Not yet supported: overloading of function 'TrmMod' +//β”‚ ╔══[COMPILATION ERROR] A module companion must be a function with a parameter list //β”‚ β•‘ l.14: fun TrmMod = 42 //β”‚ β•‘ ^^^^^^^^^^^^^^^ -//β”‚ β•Ÿβ”€β”€ with module of the same name +//β”‚ β•Ÿβ”€β”€ Arbitrary values cannot be overloaded with module 'TrmMod' //β”‚ β•‘ l.15: module TrmMod with //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ //β”‚ β•‘ l.16: val whoami = "module" diff --git a/hkmc2/shared/src/test/mlscript/apps/.mls b/hkmc2/shared/src/test/mlscript/apps/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/apps/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/apps/AccountingTest.mls b/hkmc2/shared/src/test/mlscript/apps/AccountingTest.mls index b099eb5570..1e739e7b78 100644 --- a/hkmc2/shared/src/test/mlscript/apps/AccountingTest.mls +++ b/hkmc2/shared/src/test/mlscript/apps/AccountingTest.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/apps/Accounting.mls" diff --git a/hkmc2/shared/src/test/mlscript/apps/CSVTest.mls b/hkmc2/shared/src/test/mlscript/apps/CSVTest.mls index 6d3fac655c..5b8b9f6fca 100644 --- a/hkmc2/shared/src/test/mlscript/apps/CSVTest.mls +++ b/hkmc2/shared/src/test/mlscript/apps/CSVTest.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/apps/CSV.mls" diff --git a/hkmc2/shared/src/test/mlscript/apps/IterTest.mls b/hkmc2/shared/src/test/mlscript/apps/IterTest.mls index a405b396ac..6661a67594 100644 --- a/hkmc2/shared/src/test/mlscript/apps/IterTest.mls +++ b/hkmc2/shared/src/test/mlscript/apps/IterTest.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/Iter.mls" @@ -114,7 +115,7 @@ open Stack { Cons, Nil } [1, 2, -3, 4, 5, 6] Iter.mapping of x => () => print(x + " was accessed"); x - Iter.some of get => get() < 0 + Iter.some of (get: () -> Int) => get() < 0 //β”‚ > 1 was accessed //β”‚ > 2 was accessed //β”‚ > -3 was accessed @@ -122,7 +123,7 @@ open Stack { Cons, Nil } [1, 2, 3, -4, 5, 6] Iter.mapping of x => () => print(x + " was accessed"); x - Iter.every of get => get() > 0 + Iter.every of (get: () -> Int) => get() > 0 //β”‚ > 1 was accessed //β”‚ > 2 was accessed //β”‚ > 3 was accessed diff --git a/hkmc2/shared/src/test/mlscript/apps/parsing-web-demo/.mls b/hkmc2/shared/src/test/mlscript/apps/parsing-web-demo/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/apps/parsing-web-demo/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/apps/parsing-web-demo/ExamplesTest.mls b/hkmc2/shared/src/test/mlscript/apps/parsing-web-demo/ExamplesTest.mls index a12b46c0bd..b843a19433 100644 --- a/hkmc2/shared/src/test/mlscript/apps/parsing-web-demo/ExamplesTest.mls +++ b/hkmc2/shared/src/test/mlscript/apps/parsing-web-demo/ExamplesTest.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Iter.mls" @@ -7,9 +8,9 @@ import "../../../mlscript-compile/apps/parsing-web-demo/Examples.mls" :silent val examples = (Examples.examples |> MutMap.values).sort of - (a, b) => a.name.localeCompare(b.name) + (a: Examples.Example, b: Examples.Example) => a.name.localeCompare(b.name) -examples Iter.each of case entry then +examples Iter.each of (entry: Examples.Example) => print of "Testing example: " + entry.name Test.example of entry.source //β”‚ > Testing example: Extensible Syntax diff --git a/hkmc2/shared/src/test/mlscript/apps/parsing/.mls b/hkmc2/shared/src/test/mlscript/apps/parsing/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/apps/parsing/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/apps/parsing/CamlLightTest.mls b/hkmc2/shared/src/test/mlscript/apps/parsing/CamlLightTest.mls index 5eb155c918..a035c8130b 100644 --- a/hkmc2/shared/src/test/mlscript/apps/parsing/CamlLightTest.mls +++ b/hkmc2/shared/src/test/mlscript/apps/parsing/CamlLightTest.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/apps/parsing/Test.mls" diff --git a/hkmc2/shared/src/test/mlscript/apps/parsing/DirectiveTest.mls b/hkmc2/shared/src/test/mlscript/apps/parsing/DirectiveTest.mls index 68f7c1c0f3..85ea1c7340 100644 --- a/hkmc2/shared/src/test/mlscript/apps/parsing/DirectiveTest.mls +++ b/hkmc2/shared/src/test/mlscript/apps/parsing/DirectiveTest.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/MutMap.mls" diff --git a/hkmc2/shared/src/test/mlscript/apps/parsing/LeftRecursion.mls b/hkmc2/shared/src/test/mlscript/apps/parsing/LeftRecursion.mls index e349f897c5..dbd247f4f2 100644 --- a/hkmc2/shared/src/test/mlscript/apps/parsing/LeftRecursion.mls +++ b/hkmc2/shared/src/test/mlscript/apps/parsing/LeftRecursion.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/MutMap.mls" diff --git a/hkmc2/shared/src/test/mlscript/apps/parsing/LexerTest.mls b/hkmc2/shared/src/test/mlscript/apps/parsing/LexerTest.mls index d5ba2e7694..64133aa892 100644 --- a/hkmc2/shared/src/test/mlscript/apps/parsing/LexerTest.mls +++ b/hkmc2/shared/src/test/mlscript/apps/parsing/LexerTest.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/apps/parsing/Lexer.mls" @@ -5,13 +6,13 @@ import "../../../mlscript-compile/apps/parsing/Token.mls" import "../../../mlscript-compile/Stack.mls" import "../../../mlscript-compile/Iter.mls" -fun lines(...strs) = strs.join("\n") +fun lines(...strs: Array[Str]) = strs.join("\n") open Lexer { lex } open Stack fun printLexed(text) = print of - lex(text, false) + lex(text, noWhitespace: false) Iter.fromStack() Iter.mapping of Token.display Iter.joined of "\n" @@ -240,10 +241,7 @@ fun take(pattern P, idx: Str, acc: Str) = set idx = idx + 1 set acc = acc + ch else [acc, idx] -//β”‚ ╔══[COMPILATION ERROR] Name not found: str -//β”‚ β•‘ l.238: while idx < str.length and str.charAt(idx) is ch and ch is P -//β”‚ ╙── ^^^ -//β”‚ ╔══[COMPILATION ERROR] Name not found: str -//β”‚ β•‘ l.238: while idx < str.length and str.charAt(idx) is ch and ch is P -//β”‚ ╙── ^^^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'length' has no resolved target +//β”‚ β•‘ l.239: while idx < str.length and str.charAt(idx) is ch and ch is P +//β”‚ ╙── ^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/apps/parsing/LoopExpressions.mls b/hkmc2/shared/src/test/mlscript/apps/parsing/LoopExpressions.mls index 9be1e49d34..92c4349381 100644 --- a/hkmc2/shared/src/test/mlscript/apps/parsing/LoopExpressions.mls +++ b/hkmc2/shared/src/test/mlscript/apps/parsing/LoopExpressions.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/apps/parsing/Test.mls" diff --git a/hkmc2/shared/src/test/mlscript/apps/parsing/ParseRuleTest.mls b/hkmc2/shared/src/test/mlscript/apps/parsing/ParseRuleTest.mls new file mode 100644 index 0000000000..a75adb8622 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/apps/parsing/ParseRuleTest.mls @@ -0,0 +1,32 @@ +:. +:js + +import "../../../mlscript-compile/apps/parsing/ParseRule.mls" +import "../../../mlscript-compile/apps/parsing/Keywords.mls" + +open ParseRule { Choice, rule } + +// Sequencing combines different result types and exposes the resulting rule interface. +:expect Some("xxx") +val first: ParseRule.ParseRule[Str] = rule("first", Choice.end("x")) +val second: ParseRule.ParseRule[Int] = rule("second", Choice.end(3)) +first.andThen(second, (text, count) => text.repeat(count)).endChoice +//β”‚ = Some("xxx") +//β”‚ first = ParseRule("first", Cons(End("x"), Nil)) +//β”‚ second = ParseRule("second", Cons(End(3), Nil)) + +// Recursion beneath a keyword preserves the result type of its continuation. +:expect true +val prefixed: ParseRule.ParseRule[Str] = rule("prefixed", Choice.keyword(Keywords._let)(Choice.end("x"))) +prefixed.andThen(second, (text, count) => text.repeat(count)).display.includes("let") +//β”‚ = true +//β”‚ prefixed = ParseRule( +//β”‚ "prefixed", +//β”‚ Cons( +//β”‚ Keyword( +//β”‚ Keyword(`let`, 3, 1), +//β”‚ ParseRule("`let` keyword", Cons(End("x"), Nil)) +//β”‚ ), +//β”‚ Nil +//β”‚ ) +//β”‚ ) diff --git a/hkmc2/shared/src/test/mlscript/apps/parsing/ParseRuleVisualizerTest.mls b/hkmc2/shared/src/test/mlscript/apps/parsing/ParseRuleVisualizerTest.mls index 09689789d6..400cdbe162 100644 --- a/hkmc2/shared/src/test/mlscript/apps/parsing/ParseRuleVisualizerTest.mls +++ b/hkmc2/shared/src/test/mlscript/apps/parsing/ParseRuleVisualizerTest.mls @@ -1,3 +1,4 @@ +:. :js :silent diff --git a/hkmc2/shared/src/test/mlscript/apps/parsing/ParserErrorTest.mls b/hkmc2/shared/src/test/mlscript/apps/parsing/ParserErrorTest.mls index 5ccf15d2e4..8880ad29b3 100644 --- a/hkmc2/shared/src/test/mlscript/apps/parsing/ParserErrorTest.mls +++ b/hkmc2/shared/src/test/mlscript/apps/parsing/ParserErrorTest.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/apps/parsing/Test.mls" diff --git a/hkmc2/shared/src/test/mlscript/apps/parsing/ParserTest.mls b/hkmc2/shared/src/test/mlscript/apps/parsing/ParserTest.mls index 860e8f3f82..932ab8e79a 100644 --- a/hkmc2/shared/src/test/mlscript/apps/parsing/ParserTest.mls +++ b/hkmc2/shared/src/test/mlscript/apps/parsing/ParserTest.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/apps/parsing/Test.mls" @@ -457,3 +458,13 @@ mkStr of "A", x is Str //β”‚ = "Afalse" + +// Leading separators must resume module parsing, not return the parser function. +example of ";; ;; 42" +//β”‚ > Β«42Β» + +// Exercise continuation lookup for application, operators, and type expressions. +example of "f x + y" +example of "let x: int = 42" +//β”‚ > Β«f x + yΒ» +//β”‚ > Β«let x : int = 42Β» diff --git a/hkmc2/shared/src/test/mlscript/apps/parsing/PrattParsingTest.mls b/hkmc2/shared/src/test/mlscript/apps/parsing/PrattParsingTest.mls index 4aca5615ea..d1a033afbb 100644 --- a/hkmc2/shared/src/test/mlscript/apps/parsing/PrattParsingTest.mls +++ b/hkmc2/shared/src/test/mlscript/apps/parsing/PrattParsingTest.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/apps/parsing/PrattParsing.mls" @@ -9,7 +10,7 @@ open Lexer { lex } open PrattParsing { parse } fun example(input) = - let tokens = lex of input, false + let tokens = lex of input, noWhitespace: false print of "Tokens: " + TokenHelpers.panorama(tokens) let expr = parse of tokens print of "Parsed: " + expr.toString() diff --git a/hkmc2/shared/src/test/mlscript/apps/parsing/RecursiveDescentTest.mls b/hkmc2/shared/src/test/mlscript/apps/parsing/RecursiveDescentTest.mls index 90faadd9aa..dd92dba250 100644 --- a/hkmc2/shared/src/test/mlscript/apps/parsing/RecursiveDescentTest.mls +++ b/hkmc2/shared/src/test/mlscript/apps/parsing/RecursiveDescentTest.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/apps/parsing/RecursiveDescent.mls" @@ -10,7 +11,7 @@ open RecursiveDescent { parse } open BasicExpr { Expr } fun example(input) = - let tokens = lex of input, false + let tokens = lex of input, noWhitespace: false print of "Tokens: " + TokenHelpers.panorama(tokens) let expr = parse of tokens print of "Parsed: " + expr.toString() diff --git a/hkmc2/shared/src/test/mlscript/apps/parsing/RulesTest.mls b/hkmc2/shared/src/test/mlscript/apps/parsing/RulesTest.mls index cbb1951d3f..b23ae381a2 100644 --- a/hkmc2/shared/src/test/mlscript/apps/parsing/RulesTest.mls +++ b/hkmc2/shared/src/test/mlscript/apps/parsing/RulesTest.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Iter.mls" @@ -105,7 +106,8 @@ print of Rules.declRule.display //β”‚ > | "#" Rules.syntaxKinds - Iter.mapping of case [name, rule] then rule.display + Iter.mapping of (entry: [Str, {display: Str}]) => + if entry is [name, rule] then rule.display Iter.joined("\n") print() //β”‚ > ::= "=" ["and" ] diff --git a/hkmc2/shared/src/test/mlscript/apps/parsing/TypeTest.mls b/hkmc2/shared/src/test/mlscript/apps/parsing/TypeTest.mls index 20ab8faf8e..f18a7002e3 100644 --- a/hkmc2/shared/src/test/mlscript/apps/parsing/TypeTest.mls +++ b/hkmc2/shared/src/test/mlscript/apps/parsing/TypeTest.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/apps/parsing/Test.mls" diff --git a/hkmc2/shared/src/test/mlscript/backlog/.mls b/hkmc2/shared/src/test/mlscript/backlog/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/backlog/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/backlog/DestructuringBindings.mls b/hkmc2/shared/src/test/mlscript/backlog/DestructuringBindings.mls index 7d2d0db58f..04c350cb51 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/DestructuringBindings.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/DestructuringBindings.mls @@ -1,43 +1,44 @@ +:. :js :todo [a, b] => [b, a] //β”‚ ╔══[COMPILATION ERROR] Illegal constraint syntax. -//β”‚ β•‘ l.5: [a, b] => [b, a] +//β”‚ β•‘ l.6: [a, b] => [b, a] //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Illegal constraint syntax. -//β”‚ β•‘ l.5: [a, b] => [b, a] +//β”‚ β•‘ l.6: [a, b] => [b, a] //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Name not found: b -//β”‚ β•‘ l.5: [a, b] => [b, a] +//β”‚ β•‘ l.6: [a, b] => [b, a] //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Name not found: a -//β”‚ β•‘ l.5: [a, b] => [b, a] +//β”‚ β•‘ l.6: [a, b] => [b, a] //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (constrained type) -//β”‚ β•‘ l.5: [a, b] => [b, a] +//β”‚ β•‘ l.6: [a, b] => [b, a] //β”‚ ╙── ^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. fun eqTup2([a, b], [c, d]) = a == c and b == d //β”‚ ╔══[COMPILATION ERROR] Expected a valid parameter, found tuple -//β”‚ β•‘ l.23: fun eqTup2([a, b], [c, d]) = a == c and b == d +//β”‚ β•‘ l.24: fun eqTup2([a, b], [c, d]) = a == c and b == d //β”‚ ╙── ^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Expected a valid parameter, found tuple -//β”‚ β•‘ l.23: fun eqTup2([a, b], [c, d]) = a == c and b == d +//β”‚ β•‘ l.24: fun eqTup2([a, b], [c, d]) = a == c and b == d //β”‚ ╙── ^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Name not found: a -//β”‚ β•‘ l.23: fun eqTup2([a, b], [c, d]) = a == c and b == d +//β”‚ β•‘ l.24: fun eqTup2([a, b], [c, d]) = a == c and b == d //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Name not found: c -//β”‚ β•‘ l.23: fun eqTup2([a, b], [c, d]) = a == c and b == d +//β”‚ β•‘ l.24: fun eqTup2([a, b], [c, d]) = a == c and b == d //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Name not found: b -//β”‚ β•‘ l.23: fun eqTup2([a, b], [c, d]) = a == c and b == d +//β”‚ β•‘ l.24: fun eqTup2([a, b], [c, d]) = a == c and b == d //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Name not found: d -//β”‚ β•‘ l.23: fun eqTup2([a, b], [c, d]) = a == c and b == d +//β”‚ β•‘ l.24: fun eqTup2([a, b], [c, d]) = a == c and b == d //β”‚ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/backlog/ForLLM.mls b/hkmc2/shared/src/test/mlscript/backlog/ForLLM.mls index fbb69ebdc5..cf0c304742 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/ForLLM.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/ForLLM.mls @@ -1,3 +1,4 @@ +:. :js // Instruction for agent: for each test case you solve, move it to its own test file outside of the `backlog` folder @@ -27,7 +28,7 @@ let x = 1 in x //β”‚ ╔══[COMPILATION ERROR] Illegal position for 'in' modifier. -//β”‚ β•‘ l.28: in x +//β”‚ β•‘ l.29: in x //β”‚ ╙── ^^ //β”‚ = 1 //β”‚ x = 1 @@ -109,7 +110,7 @@ if 2 :fixme // * Should be a call of two arguments, not a sequenced expression +(1,2) //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.110: +(1,2) +//β”‚ β•‘ l.111: +(1,2) //β”‚ ╙── ^ //β”‚ = 2 @@ -120,19 +121,19 @@ pattern ShapeArea = | (:radius) => 0 | x => 1 //β”‚ ╔══[COMPILATION ERROR] Found an inconsistent variable in disjunction patterns. -//β”‚ β•‘ l.120: | (:radius) => 0 +//β”‚ β•‘ l.121: | (:radius) => 0 //β”‚ β•‘ ^^^^^^ //β”‚ β•Ÿβ”€β”€ The variable is missing from this sub-pattern. -//β”‚ β•‘ l.121: | x => 1 +//β”‚ β•‘ l.122: | x => 1 //β”‚ ╙── ^^^^^^ :fixme pattern P = {:radius} => radius //β”‚ ╔══[COMPILATION ERROR] Expected a valid parameter, found type definition -//β”‚ β•‘ l.130: pattern P = {:radius} => radius +//β”‚ β•‘ l.131: pattern P = {:radius} => radius //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Name not found: radius -//β”‚ β•‘ l.130: pattern P = {:radius} => radius +//β”‚ β•‘ l.131: pattern P = {:radius} => radius //β”‚ ╙── ^^^^^^ //β”‚ = fun @@ -143,7 +144,7 @@ pattern P = ((:radius)) => radius :fixme pattern P = (:radius) => radius //β”‚ ╔══[WARNING] Unused pattern binding: radius. -//β”‚ β•‘ l.144: pattern P = (:radius) => radius +//β”‚ β•‘ l.145: pattern P = (:radius) => radius //β”‚ ╙── ^^^^^^ // β€”β€”β€” β€”β€”β€” β€”β€”β€” diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index c21ab4f447..1da30f957a 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -139,7 +139,7 @@ foo(2) //β”‚ ╔══[WARNING] Objects are not yet lifted. //β”‚ β•‘ l.129: object Foo with //β”‚ ╙── ^^^ -//β”‚ ═══[WARNING] Cannot yet lift definition `Bar` as it extends an unliftable class. +//β”‚ ═══[WARNING] Cannot yet lift definition `Bar` as it extends an expression. //β”‚ ═══[RUNTIME ERROR] TypeError: Class extends value [object Object] is not a constructor or null // `h` is lifted out, but then cannot access the BlockMemberSymbol M. diff --git a/hkmc2/shared/src/test/mlscript/backlog/NonReturningStatements.mls b/hkmc2/shared/src/test/mlscript/backlog/NonReturningStatements.mls index 3557776d76..597e7a0aa7 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/NonReturningStatements.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/NonReturningStatements.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/backlog/OfStatements.mls b/hkmc2/shared/src/test/mlscript/backlog/OfStatements.mls index 5a3f5abb8f..606b8f1ec9 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/OfStatements.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/OfStatements.mls @@ -1,3 +1,4 @@ +:. :js :todo @@ -6,10 +7,10 @@ print of let x = 1 x + 1 //β”‚ ╔══[COMPILATION ERROR] Expected a body for let bindings in expression position -//β”‚ β•‘ l.6: let x = 1 +//β”‚ β•‘ l.7: let x = 1 //β”‚ ╙── ^^^^^ //β”‚ ╔══[COMPILATION ERROR] Name not found: x -//β”‚ β•‘ l.7: x + 1 +//β”‚ β•‘ l.8: x + 1 //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -18,13 +19,13 @@ print of let x = 1 x + 1 //β”‚ ╔══[COMPILATION ERROR] Illegal position for 'open' statement. -//β”‚ β•‘ l.17: open Predef +//β”‚ β•‘ l.18: open Predef //β”‚ ╙── ^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Expected a body for let bindings in expression position -//β”‚ β•‘ l.18: let x = 1 +//β”‚ β•‘ l.19: let x = 1 //β”‚ ╙── ^^^^^ //β”‚ ╔══[COMPILATION ERROR] Name not found: x -//β”‚ β•‘ l.19: x + 1 +//β”‚ β•‘ l.20: x + 1 //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls b/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls index 4241c0e83c..cd2c42c7b2 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls @@ -82,7 +82,7 @@ fun main() = :ssjs object Cls(val x) with fun huh = x -//β”‚ ╔══[INTERNAL ERROR] Compiler reached an unsupported state: Some(ParamList(β€Ήβ€Ί,List(Param(β€Ήvalβ€Ί,x,None,Modulefulness(None))),None)).isDefined =/= false +//β”‚ ╔══[INTERNAL ERROR] Compiler reached an unsupported state: Missing constructor function for parameterized class Cls //β”‚ β•Ÿβ”€β”€ The compilation result may be incorrect. //β”‚ ╙── This is a known compiler limitation; if it is a blocker for you, please report it to the maintainers. //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (sanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” @@ -151,10 +151,13 @@ class D extends id(C) //β”‚ β•‘ l.149: class D extends id(C) //β”‚ β•‘ ^^ //β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//β”‚ ═══[COMPILATION ERROR] Too many parameter lists for parent class +//β”‚ ╔══[COMPILATION ERROR] Too many parameter lists for parent class +//β”‚ β•‘ l.149: class D extends id(C) +//β”‚ ╙── ^^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ let D1; -//β”‚ (class D extends Predef.id { +//β”‚ let D1, superBase; +//β”‚ superBase = Predef.id; +//β”‚ (class D extends superBase { //β”‚ static { //β”‚ D1 = this //β”‚ } @@ -178,14 +181,14 @@ let x = 0 :fixme set x += 1; () //β”‚ ╔══[COMPILATION ERROR] Expected a right-hand side for this assignment -//β”‚ β•‘ l.179: set x += 1; () +//β”‚ β•‘ l.182: set x += 1; () //β”‚ ╙── ^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e set (x += 1; ()) //β”‚ ╔══[COMPILATION ERROR] Expected a right-hand side for this assignment -//β”‚ β•‘ l.186: set (x += 1; ()) +//β”‚ β•‘ l.189: set (x += 1; ()) //β”‚ ╙── ^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -194,7 +197,7 @@ set (x += 1; ()) :fixme [x, set x += 1; x] //β”‚ ╔══[COMPILATION ERROR] Expected a right-hand side for this assignment -//β”‚ β•‘ l.195: [x, set x += 1; x] +//β”‚ β•‘ l.198: [x, set x += 1; x] //β”‚ ╙── ^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -205,13 +208,13 @@ import "../../mlscript-compile/Stack.mls" // The parser rejects this, but it should be allowed. open Stack { ::, Nil } //β”‚ ╔══[COMPILATION ERROR] Illegal 'open' statement shape. -//β”‚ β•‘ l.206: open Stack { ::, Nil } +//β”‚ β•‘ l.209: open Stack { ::, Nil } //β”‚ ╙── ^^^^^^^^^^^^^^^ // Instead, if we parenthesize the operator, it is rejected by the elaborator. open Stack { (::), Nil } //β”‚ ╔══[COMPILATION ERROR] Illegal 'open' statement element. -//β”‚ β•‘ l.212: open Stack { (::), Nil } +//β”‚ β•‘ l.215: open Stack { (::), Nil } //β”‚ ╙── ^^^^ open Stack { Nil, :: } @@ -237,46 +240,46 @@ id(Foo)(1) :e data class FooSpd(...args) //β”‚ ╔══[COMPILATION ERROR] Spread parameters are not supported in class parameters. -//β”‚ β•‘ l.238: data class FooSpd(...args) +//β”‚ β•‘ l.241: data class FooSpd(...args) //β”‚ ╙── ^^^^^^^ FooSpd(1, 2, 3).args //β”‚ ╔══[COMPILATION ERROR] Expected 0 arguments, got 3 -//β”‚ β•‘ l.243: FooSpd(1, 2, 3).args +//β”‚ β•‘ l.246: FooSpd(1, 2, 3).args //β”‚ ╙── ^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'args' yielded 'undefined' :todo if FooSpd(1, 2, 3) is FooSpd(...args) then args //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern (spread). -//β”‚ β•‘ l.250: if FooSpd(1, 2, 3) is FooSpd(...args) then args +//β”‚ β•‘ l.253: if FooSpd(1, 2, 3) is FooSpd(...args) then args //β”‚ ╙── ^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Name not found: args -//β”‚ β•‘ l.250: if FooSpd(1, 2, 3) is FooSpd(...args) then args +//β”‚ β•‘ l.253: if FooSpd(1, 2, 3) is FooSpd(...args) then args //β”‚ ╙── ^^^^ //β”‚ ╔══[COMPILATION ERROR] Expected 0 arguments, got 3 -//β”‚ β•‘ l.250: if FooSpd(1, 2, 3) is FooSpd(...args) then args +//β”‚ β•‘ l.253: if FooSpd(1, 2, 3) is FooSpd(...args) then args //β”‚ ╙── ^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] The constructor does not take any arguments but found one argument. -//β”‚ β•‘ l.250: if FooSpd(1, 2, 3) is FooSpd(...args) then args +//β”‚ β•‘ l.253: if FooSpd(1, 2, 3) is FooSpd(...args) then args //β”‚ ╙── ^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: match error if FooSpd(1, 2, 3) is FooSpd(a, b, c) then [a, b, c] //β”‚ ╔══[COMPILATION ERROR] Expected 0 arguments, got 3 -//β”‚ β•‘ l.265: if FooSpd(1, 2, 3) is FooSpd(a, b, c) then [a, b, c] +//β”‚ β•‘ l.268: if FooSpd(1, 2, 3) is FooSpd(a, b, c) then [a, b, c] //β”‚ ╙── ^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] The constructor does not take any arguments but found three arguments. -//β”‚ β•‘ l.265: if FooSpd(1, 2, 3) is FooSpd(a, b, c) then [a, b, c] +//β”‚ β•‘ l.268: if FooSpd(1, 2, 3) is FooSpd(a, b, c) then [a, b, c] //β”‚ ╙── ^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: match error if FooSpd(1, 2, 3) is FooSpd(arg) then arg //β”‚ ╔══[COMPILATION ERROR] Expected 0 arguments, got 3 -//β”‚ β•‘ l.274: if FooSpd(1, 2, 3) is FooSpd(arg) then arg +//β”‚ β•‘ l.277: if FooSpd(1, 2, 3) is FooSpd(arg) then arg //β”‚ ╙── ^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] The constructor does not take any arguments but found one argument. -//β”‚ β•‘ l.274: if FooSpd(1, 2, 3) is FooSpd(arg) then arg +//β”‚ β•‘ l.277: if FooSpd(1, 2, 3) is FooSpd(arg) then arg //β”‚ ╙── ^^^ //β”‚ ═══[RUNTIME ERROR] Error: match error @@ -287,10 +290,10 @@ fun foo() = let bar(x: Str): Str = x + x bar("test") //β”‚ ╔══[COMPILATION ERROR] Unsupported let binding shape -//β”‚ β•‘ l.287: let bar(x: Str): Str = x + x +//β”‚ β•‘ l.290: let bar(x: Str): Str = x + x //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Name not found: bar -//β”‚ β•‘ l.288: bar("test") +//β”‚ β•‘ l.291: bar("test") //β”‚ ╙── ^^^ // β€”β€”β€” β€”β€”β€” β€”β€”β€” @@ -304,7 +307,7 @@ module Foo(x) with print(x) //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' //β”‚ β•Ÿβ”€β”€ which references the symbol introduced here -//β”‚ β•‘ l.303: module Foo(x) with +//β”‚ β•‘ l.306: module Foo(x) with //β”‚ ╙── ^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let Foo5; @@ -322,7 +325,7 @@ module Foo(x) with //β”‚ }); //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' //β”‚ β•Ÿβ”€β”€ which references the symbol introduced here -//β”‚ β•‘ l.303: module Foo(x) with +//β”‚ β•‘ l.306: module Foo(x) with //β”‚ ╙── ^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ > 1 @@ -332,7 +335,7 @@ module Foo(x) with module Foo(val x) //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' //β”‚ β•Ÿβ”€β”€ which references the symbol introduced here -//β”‚ β•‘ l.332: module Foo(val x) +//β”‚ β•‘ l.335: module Foo(val x) //β”‚ ╙── ^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let Foo7; @@ -348,7 +351,7 @@ module Foo(val x) //β”‚ }); //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' //β”‚ β•Ÿβ”€β”€ which references the symbol introduced here -//β”‚ β•‘ l.332: module Foo(val x) +//β”‚ β•‘ l.335: module Foo(val x) //β”‚ ╙── ^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” @@ -357,36 +360,25 @@ module Foo(val x) // TODO support syntax? data class Foo(mut x) //β”‚ ╔══[COMPILATION ERROR] Expected a valid parameter, found 'mut'-modified identifier -//β”‚ β•‘ l.358: data class Foo(mut x) +//β”‚ β•‘ l.361: data class Foo(mut x) //β”‚ ╙── ^ // β€”β€”β€” β€”β€”β€” β€”β€”β€” -:breakme // FIXME should report a compilation error -:e -fun Foo -class Foo -//β”‚ ╔══[INTERNAL ERROR] Compiler reached an unsupported state: None.isDefined =/= true -//β”‚ β•Ÿβ”€β”€ The compilation result may be incorrect. -//β”‚ ╙── This is a known compiler limitation; if it is a blocker for you, please report it to the maintainers. -//β”‚ ═══[RUNTIME ERROR] TypeError: Cannot set properties of undefined (setting 'class') - -// β€”β€”β€” β€”β€”β€” β€”β€”β€” - // * TODO: improve errors (don't cascade meaningless errors) :e class Id(name: UnboundName) //β”‚ ╔══[COMPILATION ERROR] Name not found: UnboundName -//β”‚ β•‘ l.378: class Id(name: UnboundName) +//β”‚ β•‘ l.370: class Id(name: UnboundName) //β”‚ ╙── ^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Expected a type, got a non-type β€Ήerrorβ€Ί -//β”‚ β•‘ l.378: class Id(name: UnboundName) +//β”‚ β•‘ l.370: class Id(name: UnboundName) //β”‚ ╙── ^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Expected a type, got β€Ήerrorβ€Ί -//β”‚ β•‘ l.378: class Id(name: UnboundName) +//β”‚ β•‘ l.370: class Id(name: UnboundName) //β”‚ ╙── ^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Expected a type, got a non-type β€Ήerrorβ€Ί -//β”‚ β•‘ l.378: class Id(name: UnboundName) +//β”‚ β•‘ l.370: class Id(name: UnboundName) //β”‚ ╙── ^^^^^^^^^^^ // β€”β€”β€” β€”β€”β€” β€”β€”β€” diff --git a/hkmc2/shared/src/test/mlscript/backlog/UCS.mls b/hkmc2/shared/src/test/mlscript/backlog/UCS.mls index 9aef71c587..8a5872a2d1 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/UCS.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/UCS.mls @@ -1,3 +1,4 @@ +:. :js :ignore @@ -16,22 +17,22 @@ if 1 + 1 * 2 == 1 then "A" |> id is r then r //β”‚ ╔══[PARSE ERROR] Operator cannot be used inside this operator split -//β”‚ β•‘ l.15: * 2 == 4 then "X" +//β”‚ β•‘ l.16: * 2 == 4 then "X" //β”‚ β•‘ ^^ //β”‚ β•Ÿβ”€β”€ as it has lower precedence than the splitting operator here -//β”‚ β•‘ l.15: * 2 == 4 then "X" +//β”‚ β•‘ l.16: * 2 == 4 then "X" //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Unexpected literal in this operator split inner position -//β”‚ β•‘ l.15: * 2 == 4 then "X" +//β”‚ β•‘ l.16: * 2 == 4 then "X" //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ Note: the operator split starts here -//β”‚ β•‘ l.15: * 2 == 4 then "X" +//β”‚ β•‘ l.16: * 2 == 4 then "X" //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Expected end of input; found literal instead -//β”‚ β•‘ l.15: * 2 == 4 then "X" +//β”‚ β•‘ l.16: * 2 == 4 then "X" //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Unrecognized term split (integer literal) -//β”‚ β•‘ l.15: * 2 == 4 then "X" +//β”‚ β•‘ l.16: * 2 == 4 then "X" //β”‚ ╙── ^ //β”‚ ═══[COMPILATION ERROR] Unrecognized term split (reference term) //β”‚ ═══[RUNTIME ERROR] Error: match error @@ -82,18 +83,21 @@ let foo = case Tuple(...elements) then elements //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern (spread). -//β”‚ β•‘ l.83: Tuple(...elements) then elements +//β”‚ β•‘ l.84: Tuple(...elements) then elements //β”‚ ╙── ^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in constructor +//β”‚ β•‘ l.84: Tuple(...elements) then elements +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class definition 'Tuple' expected 3 pattern arguments, but got 1 +//β”‚ β•‘ l.80: data class Tuple(a, b, c) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Name not found: elements -//β”‚ β•‘ l.83: Tuple(...elements) then elements +//β”‚ β•‘ l.84: Tuple(...elements) then elements //β”‚ ╙── ^^^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Expected three arguments, but found only one argument. -//β”‚ β•‘ l.83: Tuple(...elements) then elements -//β”‚ ╙── ^^^^^ -//β”‚ foo = fun +//β”‚ foo = fun foo foo(Tuple(1, 2, 3)) -//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Error: match error // β€”β€”β€” β€”β€”β€” β€”β€”β€” @@ -106,21 +110,21 @@ fun foo(x) = if x is Foo. Bar then "Bar" Foo then "Foo" //β”‚ ╔══[PARSE ERROR] Expected end of input; found period instead -//β”‚ β•‘ l.105: fun foo(x) = if x is Foo. +//β”‚ β•‘ l.109: fun foo(x) = if x is Foo. //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern split (identifier). -//β”‚ β•‘ l.105: fun foo(x) = if x is Foo. +//β”‚ β•‘ l.109: fun foo(x) = if x is Foo. //β”‚ ╙── ^^^ fun foo(x) = if x is Foo .Bar then "Bar" .Foo then "Foo" //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern split (operator split). -//β”‚ β•‘ l.115: fun foo(x) = if x is Foo +//β”‚ β•‘ l.119: fun foo(x) = if x is Foo //β”‚ β•‘ ^^^ -//β”‚ β•‘ l.116: .Bar then "Bar" +//β”‚ β•‘ l.120: .Bar then "Bar" //β”‚ β•‘ ^^^^^^^^^^^^^^^^^ -//β”‚ β•‘ l.117: .Foo then "Foo" +//β”‚ β•‘ l.121: .Foo then "Foo" //β”‚ ╙── ^^^^^^^^^^^^^^^^^ // β€”β€”β€” β€”β€”β€” β€”β€”β€” diff --git a/hkmc2/shared/src/test/mlscript/basics/.mls b/hkmc2/shared/src/test/mlscript/basics/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/basics/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/basics/ADTs.mls b/hkmc2/shared/src/test/mlscript/basics/ADTs.mls index eb3c7bd2d5..50a395cad4 100644 --- a/hkmc2/shared/src/test/mlscript/basics/ADTs.mls +++ b/hkmc2/shared/src/test/mlscript/basics/ADTs.mls @@ -1,3 +1,4 @@ +:. :js :ignore @@ -8,7 +9,7 @@ class Expr constructor Lit(n: Int) constructor Add(lhs: Expr, rhs: Expr) //β”‚ ╔══[PARSE ERROR] Expected end of input; found indented block instead -//β”‚ β•‘ l.8: constructor Lit(n: Int) +//β”‚ β•‘ l.9: constructor Lit(n: Int) //β”‚ ╙── ^^ class Expr[A] with @@ -32,7 +33,7 @@ enum Expr = | Lit(n: Int) | Add(lhs: Expr, rhs: Expr) //β”‚ ╔══[COMPILATION ERROR] Unrecognized definitional assignment left-hand side: juxtaposition -//β”‚ β•‘ l.31: enum Expr = +//β”‚ β•‘ l.32: enum Expr = //β”‚ ╙── ^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/basics/AppOp.mls b/hkmc2/shared/src/test/mlscript/basics/AppOp.mls index e40a3d44bc..d0b974fafa 100644 --- a/hkmc2/shared/src/test/mlscript/basics/AppOp.mls +++ b/hkmc2/shared/src/test/mlscript/basics/AppOp.mls @@ -1,3 +1,4 @@ +:. :js @@ -107,7 +108,7 @@ pipeInto(1, _) @ inc :re 1 pipeInto @ inc //β”‚ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (identifier). -//β”‚ β•‘ l.108: 1 pipeInto @ inc +//β”‚ β•‘ l.109: 1 pipeInto @ inc //β”‚ ╙── ^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] TypeError: 1 is not a function diff --git a/hkmc2/shared/src/test/mlscript/basics/Assert.mls b/hkmc2/shared/src/test/mlscript/basics/Assert.mls index 55fc7befd5..63fc8288fc 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Assert.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Assert.mls @@ -1,21 +1,32 @@ +:. :js let xs = [1, 2, 3] //β”‚ xs = [1, 2, 3] +:sir :sjs assert true +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let scrut; +//β”‚ set scrut = true; +//β”‚ match scrut +//β”‚ true => +//β”‚ return runtime⁰.Unit⁰ +//β”‚ else +//β”‚ return runtime⁰.assertFail⁰("Assert.mls", "11") +//β”‚ end //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ if (true === true) { //β”‚ return runtime.Unit //β”‚ } -//β”‚ return runtime.safeCall(runtime.assertFail("Assert.mls", "9")); +//β”‚ throw runtime.safeCall(globalThis.Error("Assertion failed (Assert.mls:11)")); //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” :re assert false -//β”‚ ═══[RUNTIME ERROR] Error: Assertion failed (Assert.mls:18) +//β”‚ ═══[RUNTIME ERROR] Error: Assertion failed (Assert.mls:29) assert xs is Array @@ -28,9 +39,9 @@ assert(true); 123 //β”‚ if (123 === true) { //β”‚ return runtime.Unit //β”‚ } -//β”‚ return runtime.safeCall(runtime.assertFail("Assert.mls", "27")); +//β”‚ throw runtime.safeCall(globalThis.Error("Assertion failed (Assert.mls:38)")); //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ ═══[RUNTIME ERROR] Error: Assertion failed (Assert.mls:27) +//β”‚ ═══[RUNTIME ERROR] Error: Assertion failed (Assert.mls:38) // * FIXME: the `else` is parsed as part of the `assert`! :sjs @@ -78,7 +89,7 @@ ys //β”‚ = 5 :re -(assert false else throw Error("whoops")) + 3 +(assert false else throw new Error("whoops")) + 3 //β”‚ ═══[RUNTIME ERROR] Error: whoops diff --git a/hkmc2/shared/src/test/mlscript/basics/BadClasses.mls b/hkmc2/shared/src/test/mlscript/basics/BadClasses.mls index e7a3fa435d..417642be27 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadClasses.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadClasses.mls @@ -1,3 +1,4 @@ +:. class C { print("hi") } @@ -5,7 +6,7 @@ class C { print("hi") } :w class C { 1 } //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.6: class C { 1 } +//β”‚ β•‘ l.7: class C { 1 } //β”‚ ╙── ^ class C { } @@ -20,26 +21,26 @@ class C { class C { val a = 1 } with val b = 2 //β”‚ ╔══[COMPILATION ERROR] Multiple type definition bodies are not supported. -//β”‚ β•‘ l.21: val b = 2 +//β”‚ β•‘ l.22: val b = 2 //β”‚ ╙── ^^^^^^^^^ :e class C { val a = 1 } { val b = 2 } //β”‚ ╔══[COMPILATION ERROR] Multiple type definition bodies are not supported. -//β”‚ β•‘ l.27: class C { val a = 1 } { val b = 2 } +//β”‚ β•‘ l.28: class C { val a = 1 } { val b = 2 } //β”‚ ╙── ^^^^^^^^^^^^^ :e class C { val a = 1 } { val b = 2 } { val c = 2 } //β”‚ ╔══[COMPILATION ERROR] Multiple type definition bodies are not supported. -//β”‚ β•‘ l.33: class C { val a = 1 } { val b = 2 } { val c = 2 } +//β”‚ β•‘ l.34: class C { val a = 1 } { val b = 2 } { val c = 2 } //β”‚ ╙── ^^^^^^^^^^^^^ :e class C { val a = 1 } { val b = 2 } with val c = 2 //β”‚ ╔══[COMPILATION ERROR] Multiple type definition bodies are not supported. -//β”‚ β•‘ l.39: class C { val a = 1 } { val b = 2 } with +//β”‚ β•‘ l.40: class C { val a = 1 } { val b = 2 } with //β”‚ ╙── ^^^^^^^^^^^^^ @@ -48,21 +49,21 @@ class C { val a = 1 } { val b = 2 } with :w class Foo with { 1 } //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.49: class Foo with { 1 } +//β”‚ β•‘ l.50: class Foo with { 1 } //β”‚ ╙── ^ :w class Foo with 1 //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.56: 1 +//β”‚ β•‘ l.57: 1 //β”‚ ╙── ^ :e class Foo(42) //β”‚ ╔══[COMPILATION ERROR] Expected a valid parameter, found integer literal -//β”‚ β•‘ l.63: class Foo(42) +//β”‚ β•‘ l.64: class Foo(42) //β”‚ ╙── ^^ diff --git a/hkmc2/shared/src/test/mlscript/basics/BadConstructor.mls b/hkmc2/shared/src/test/mlscript/basics/BadConstructor.mls index 7895b9b62c..abc856703e 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadConstructor.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadConstructor.mls @@ -1,38 +1,39 @@ +:. :js :e constructor() //β”‚ ╔══[COMPILATION ERROR] Unsupported constructor in this position. -//β”‚ β•‘ l.5: constructor() +//β”‚ β•‘ l.6: constructor() //β”‚ ╙── ^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e constructor(x, y) //β”‚ ╔══[COMPILATION ERROR] 'constructor(...)' declarations are only allowed in class bodies -//β”‚ β•‘ l.12: constructor(x, y) +//β”‚ β•‘ l.13: constructor(x, y) //β”‚ ╙── ^^^^^^^^^^^^^^^^^ :e fun main() = constructor(x, y) //β”‚ ╔══[COMPILATION ERROR] 'constructor(...)' declarations are only allowed in class bodies -//β”‚ β•‘ l.19: constructor(x, y) +//β”‚ β•‘ l.20: constructor(x, y) //β”‚ ╙── ^^^^^^^^^^^^^^^^^ :e object A with constructor(x, y) //β”‚ ╔══[COMPILATION ERROR] 'constructor(...)' declarations are only allowed in class bodies -//β”‚ β•‘ l.26: constructor(x, y) +//β”‚ β•‘ l.27: constructor(x, y) //β”‚ ╙── ^^^^^^^^^^^^^^^^^ :e module A with constructor(x, y) //β”‚ ╔══[COMPILATION ERROR] 'constructor(...)' declarations are only allowed in class bodies -//β”‚ β•‘ l.33: constructor(x, y) +//β”‚ β•‘ l.34: constructor(x, y) //β”‚ ╙── ^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/basics/BadDefs.mls b/hkmc2/shared/src/test/mlscript/basics/BadDefs.mls index 865eaadc2a..196190f34a 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadDefs.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadDefs.mls @@ -1,3 +1,4 @@ +:. :js @@ -30,14 +31,14 @@ val ++ = 0 :e val ++ y = 1 //β”‚ ╔══[COMPILATION ERROR] Invalid value definition head: unexpected identifier in this position -//β”‚ β•‘ l.31: val ++ y = 1 +//β”‚ β•‘ l.32: val ++ y = 1 //β”‚ ╙── ^ //β”‚ ++ = 1 :e y //β”‚ ╔══[COMPILATION ERROR] Name not found: y -//β”‚ β•‘ l.38: y +//β”‚ β•‘ l.39: y //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -48,7 +49,7 @@ y :e fun ++ z = 0 //β”‚ ╔══[COMPILATION ERROR] Invalid function definition head: unexpected identifier in this position -//β”‚ β•‘ l.49: fun ++ z = 0 +//β”‚ β•‘ l.50: fun ++ z = 0 //β”‚ ╙── ^ :sjs @@ -62,7 +63,7 @@ fun ++ z = 0 :e fun (class Lol) foo = 0 //β”‚ ╔══[COMPILATION ERROR] This type definition is not a valid symbolic name -//β”‚ β•‘ l.63: fun (class Lol) foo = 0 +//β”‚ β•‘ l.64: fun (class Lol) foo = 0 //β”‚ ╙── ^^^^^^^^^ foo @@ -71,7 +72,7 @@ foo :e Lol //β”‚ ╔══[COMPILATION ERROR] Name not found: Lol -//β”‚ β•‘ l.72: Lol +//β”‚ β•‘ l.73: Lol //β”‚ ╙── ^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls b/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls index 74ba5ed90c..48da72d385 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls @@ -1,21 +1,20 @@ +:. :js :sjs :e 1::x -//β”‚ ╔══[COMPILATION ERROR] Integer literal is not a known class. -//β”‚ β•‘ l.6: 1::x -//β”‚ β•‘ ^ -//β”‚ β•Ÿβ”€β”€ Note: any expression of the form `β€Ήexpressionβ€Ί::β€Ήidentifierβ€Ί` is a member projection; -//β”‚ ╙── add a space before β€Ήidentifierβ€Ί to make it an operator application. -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found integer literal. -//β”‚ β•‘ l.6: 1::x +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.7: 1::x +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be used as a projection class. +//β”‚ β•‘ l.7: 1::x //β”‚ ╙── ^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let lambda; //β”‚ lambda = (undefined, function (self, ...args) { -//β”‚ return runtime.safeCall(self.x(...args)) +//β”‚ throw "This code cannot be run as its compilation yielded an error." //β”‚ }); //β”‚ return lambda //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” @@ -25,19 +24,23 @@ :e :re 1::x() -//β”‚ ╔══[COMPILATION ERROR] Integer literal is not a known class. -//β”‚ β•‘ l.27: 1::x() -//β”‚ β•‘ ^ -//β”‚ β•Ÿβ”€β”€ Note: any expression of the form `β€Ήexpressionβ€Ί::β€Ήidentifierβ€Ί` is a member projection; -//β”‚ ╙── add a space before β€Ήidentifierβ€Ί to make it an operator application. -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found integer literal. -//β”‚ β•‘ l.27: 1::x() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.26: 1::x() +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be used as a projection class. +//β”‚ β•‘ l.26: 1::x() //β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.26: 1::x() +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Function literal expected 1 argument, but got 0 +//β”‚ β•‘ l.26: 1::x() +//β”‚ ╙── ^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (sanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let lambda1; //β”‚ lambda1 = (undefined, function (self, ...args) { //β”‚ runtime.checkArgs("", 1, false, arguments.length); -//β”‚ return runtime.safeCall(self.x(...args)) +//β”‚ throw "This code cannot be run as its compilation yielded an error." //β”‚ }); //β”‚ block$res2 = runtime.safeCall(lambda1()); //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” @@ -57,25 +60,21 @@ let x = 1 :e "A"::x -//β”‚ ╔══[COMPILATION ERROR] String literal is not a known class. -//β”‚ β•‘ l.59: "A"::x -//β”‚ β•‘ ^^^ -//β”‚ β•Ÿβ”€β”€ Note: any expression of the form `β€Ήexpressionβ€Ί::β€Ήidentifierβ€Ί` is a member projection; -//β”‚ ╙── add a space before β€Ήidentifierβ€Ί to make it an operator application. -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found string literal. -//β”‚ β•‘ l.59: "A"::x +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.62: "A"::x +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ String literal cannot be used as a projection class. +//β”‚ β•‘ l.62: "A"::x //β”‚ ╙── ^^^ //β”‚ = fun :e "A" ::x -//β”‚ ╔══[COMPILATION ERROR] String literal is not a known class. -//β”‚ β•‘ l.71: "A" ::x -//β”‚ β•‘ ^^^ -//β”‚ β•Ÿβ”€β”€ Note: any expression of the form `β€Ήexpressionβ€Ί::β€Ήidentifierβ€Ί` is a member projection; -//β”‚ ╙── add a space before β€Ήidentifierβ€Ί to make it an operator application. -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found string literal. -//β”‚ β•‘ l.71: "A" ::x +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.72: "A" ::x +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ String literal cannot be used as a projection class. +//β”‚ β•‘ l.72: "A" ::x //β”‚ ╙── ^^^ //β”‚ = fun diff --git a/hkmc2/shared/src/test/mlscript/basics/BadModules.mls b/hkmc2/shared/src/test/mlscript/basics/BadModules.mls index db0027d9d7..e4ee6c73cf 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadModules.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadModules.mls @@ -1,3 +1,4 @@ +:. :js @@ -6,7 +7,7 @@ class Foo :ge module M extends Foo //β”‚ ╔══[COMPILATION ERROR] Modules cannot have an extension clause. -//β”‚ β•‘ l.7: module M extends Foo +//β”‚ β•‘ l.8: module M extends Foo //β”‚ ╙── ^^^ diff --git a/hkmc2/shared/src/test/mlscript/basics/BadMut.mls b/hkmc2/shared/src/test/mlscript/basics/BadMut.mls index 8a6eb23415..c524c056ae 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadMut.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadMut.mls @@ -1,24 +1,25 @@ +:. :js :e mut "oops" //β”‚ ╔══[COMPILATION ERROR] Illegal position for 'mut' modifier. -//β”‚ β•‘ l.5: mut "oops" +//β”‚ β•‘ l.6: mut "oops" //β”‚ ╙── ^^^ //β”‚ = "oops" :e mut a: 1 //β”‚ ╔══[COMPILATION ERROR] Unexpected record key shape. -//β”‚ β•‘ l.12: mut a: 1 +//β”‚ β•‘ l.13: mut a: 1 //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e mut {"oops"} //β”‚ ╔══[COMPILATION ERROR] Expected a record after 'mut' keyword; found a block -//β”‚ β•‘ l.19: mut {"oops"} +//β”‚ β•‘ l.20: mut {"oops"} //β”‚ ╙── ^^^^^^ //β”‚ = "oops" diff --git a/hkmc2/shared/src/test/mlscript/basics/BadOverloading.mls b/hkmc2/shared/src/test/mlscript/basics/BadOverloading.mls index 09ed809196..2fbac81aca 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadOverloading.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadOverloading.mls @@ -1,3 +1,4 @@ +:. :js @@ -6,33 +7,32 @@ class Foo class Foo //β”‚ ╔══[COMPILATION ERROR] Multiple definitions of symbol 'Foo' //β”‚ β•Ÿβ”€β”€ defined here -//β”‚ β•‘ l.5: class Foo +//β”‚ β•‘ l.6: class Foo //β”‚ β•‘ ^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ defined here -//β”‚ β•‘ l.6: class Foo +//β”‚ β•‘ l.7: class Foo //β”‚ ╙── ^^^^^^^^^ :e class Foo object Foo //β”‚ ╔══[COMPILATION ERROR] Illegal overloading of class 'Foo' -//β”‚ β•‘ l.16: class Foo +//β”‚ β•‘ l.17: class Foo //β”‚ β•‘ ^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ with object of the same name -//β”‚ β•‘ l.17: object Foo +//β”‚ β•‘ l.18: object Foo //β”‚ ╙── ^^^^^^^^^^ :e object Foo class Foo //β”‚ ╔══[COMPILATION ERROR] Illegal overloading of class 'Foo' -//β”‚ β•‘ l.27: class Foo +//β”‚ β•‘ l.28: class Foo //β”‚ β•‘ ^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ with object of the same name -//β”‚ β•‘ l.26: object Foo +//β”‚ β•‘ l.27: object Foo //β”‚ ╙── ^^^^^^^^^^ -:todo // TODO: silence such internal errors when a compilation error is already reported? :e fun Foo = 1 object Foo @@ -42,17 +42,17 @@ object Foo //β”‚ β•Ÿβ”€β”€ with object of the same name //β”‚ β•‘ l.38: object Foo //β”‚ ╙── ^^^^^^^^^^ -//β”‚ ╔══[INTERNAL ERROR] Compiler reached an unsupported state: None.isDefined =/= true -//β”‚ β•Ÿβ”€β”€ The compilation result may be incorrect. -//β”‚ ╙── This is a known compiler limitation; if it is a blocker for you, please report it to the maintainers. -:fixme -// * Note that this does not report an error at the moment: +:e +// A term declaration does not create a function that can own its companion. fun Foo: Int object Foo -//β”‚ ╔══[INTERNAL ERROR] Compiler reached an unsupported state: None.isDefined =/= true -//β”‚ β•Ÿβ”€β”€ The compilation result may be incorrect. -//β”‚ ╙── This is a known compiler limitation; if it is a blocker for you, please report it to the maintainers. +//β”‚ ╔══[COMPILATION ERROR] A term declaration cannot have an implemented object companion +//β”‚ β•‘ l.48: fun Foo: Int +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Companion 'Foo' is defined here +//β”‚ β•‘ l.49: object Foo +//β”‚ ╙── ^^^^^^^^^^ :todo :e @@ -65,10 +65,10 @@ class Foo //β”‚ β•Ÿβ”€β”€ with object of the same name //β”‚ β•‘ l.60: object Foo //β”‚ ╙── ^^^^^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Not yet supported: overloading of function 'Foo' +//β”‚ ╔══[COMPILATION ERROR] A class companion must be a function with a parameter list //β”‚ β•‘ l.59: fun Foo = 1 //β”‚ β•‘ ^^^^^^^^^^^ -//β”‚ β•Ÿβ”€β”€ with class of the same name +//β”‚ β•Ÿβ”€β”€ Arbitrary values cannot be overloaded with class 'Foo' //β”‚ β•‘ l.61: class Foo //β”‚ ╙── ^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Illegal overloading of class 'Foo' @@ -77,50 +77,26 @@ class Foo //β”‚ β•Ÿβ”€β”€ with object of the same name //β”‚ β•‘ l.60: object Foo //β”‚ ╙── ^^^^^^^^^^ -//β”‚ ╔══[INTERNAL ERROR] Compiler reached an unsupported state: None.isDefined =/= true -//β”‚ β•Ÿβ”€β”€ The compilation result may be incorrect. -//β”‚ ╙── This is a known compiler limitation; if it is a blocker for you, please report it to the maintainers. -//β”‚ ╔══[INTERNAL ERROR] Compiler reached an unsupported state: None.isDefined =/= true -//β”‚ β•Ÿβ”€β”€ The compilation result may be incorrect. -//β”‚ ╙── This is a known compiler limitation; if it is a blocker for you, please report it to the maintainers. //β”‚ ═══[RUNTIME ERROR] TypeError: Cannot assign to read only property 'class' of object '[object Object]' -:todo // TODO: support -:e +// Class and module companions require a function definition with parameters. class Foo -fun Foo = 1 -//β”‚ ╔══[COMPILATION ERROR] Not yet supported: overloading of function 'Foo' -//β”‚ β•‘ l.92: fun Foo = 1 -//β”‚ β•‘ ^^^^^^^^^^^ -//β”‚ β•Ÿβ”€β”€ with class of the same name -//β”‚ β•‘ l.91: class Foo -//β”‚ ╙── ^^^^^^^^^ -//β”‚ ╔══[INTERNAL ERROR] Compiler reached an unsupported state: None.isDefined =/= true -//β”‚ β•Ÿβ”€β”€ The compilation result may be incorrect. -//β”‚ ╙── This is a known compiler limitation; if it is a blocker for you, please report it to the maintainers. +fun Foo() = 1 -// TODO: support -:e -fun Foo = 1 +fun Foo() = 1 module Foo -//β”‚ ╔══[COMPILATION ERROR] Not yet supported: overloading of function 'Foo' -//β”‚ β•‘ l.105: fun Foo = 1 -//β”‚ β•‘ ^^^^^^^^^^^ -//β”‚ β•Ÿβ”€β”€ with module of the same name -//β”‚ β•‘ l.106: module Foo -//β”‚ ╙── ^^^^^^^^^^ - -// TODO: support + +// An existing value cannot own a companion. :e val Foo = 1 module Foo -//β”‚ ╔══[COMPILATION ERROR] Not yet supported: overloading of value 'Foo' -//β”‚ β•‘ l.116: val Foo = 1 -//β”‚ β•‘ ^^^^^^^^^^^ -//β”‚ β•Ÿβ”€β”€ with module of the same name -//β”‚ β•‘ l.117: module Foo -//β”‚ ╙── ^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] A module companion must be a function with a parameter list +//β”‚ β•‘ l.92: val Foo = 1 +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Arbitrary values cannot be overloaded with module 'Foo' +//β”‚ β•‘ l.93: module Foo +//β”‚ ╙── ^^^^^^^^^^ //β”‚ Foo = class Foo @@ -128,10 +104,10 @@ module Foo fun (**) f1(a, b) = "[" + a + " * " + b + "]" fun (**) f2(a, b) = "(" + a + " + " + b + ")" //β”‚ ╔══[COMPILATION ERROR] Symbolic name '**' of definition 'f1' is already used -//β”‚ β•‘ l.128: fun (**) f1(a, b) = "[" + a + " * " + b + "]" +//β”‚ β•‘ l.104: fun (**) f1(a, b) = "[" + a + " * " + b + "]" //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ by sibling member 'f2' -//β”‚ β•‘ l.129: fun (**) f2(a, b) = "(" + a + " + " + b + ")" +//β”‚ β•‘ l.105: fun (**) f2(a, b) = "(" + a + " + " + b + ")" //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1 ** 2 @@ -142,10 +118,10 @@ fun (**) t() = 0 fun (++) t() = 1 //β”‚ ╔══[COMPILATION ERROR] Multiple definitions of symbol 't' //β”‚ β•Ÿβ”€β”€ defined here -//β”‚ β•‘ l.141: fun (**) t() = 0 +//β”‚ β•‘ l.117: fun (**) t() = 0 //β”‚ β•‘ ^^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ defined here -//β”‚ β•‘ l.142: fun (++) t() = 1 +//β”‚ β•‘ l.118: fun (++) t() = 1 //β”‚ ╙── ^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/basics/BadParams.mls b/hkmc2/shared/src/test/mlscript/basics/BadParams.mls index 44d1cb7c85..c92a5fbcf1 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadParams.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadParams.mls @@ -1,43 +1,44 @@ +:. :js :e (val x) => x //β”‚ ╔══[COMPILATION ERROR] Illegal function parameter modifiers: val -//β”‚ β•‘ l.5: (val x) => x +//β”‚ β•‘ l.6: (val x) => x //β”‚ ╙── ^ //β”‚ = fun :e (mut x) => x //β”‚ ╔══[COMPILATION ERROR] Expected a valid parameter, found 'mut'-modified identifier -//β”‚ β•‘ l.12: (mut x) => x +//β”‚ β•‘ l.13: (mut x) => x //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Name not found: x -//β”‚ β•‘ l.12: (mut x) => x +//β”‚ β•‘ l.13: (mut x) => x //β”‚ ╙── ^ //β”‚ = fun :e (mut val x) => x //β”‚ ╔══[COMPILATION ERROR] Illegal function parameter modifiers: mut val -//β”‚ β•‘ l.22: (mut val x) => x +//β”‚ β•‘ l.23: (mut val x) => x //β”‚ ╙── ^ //β”‚ = fun :e fun f(val x) = x //β”‚ ╔══[COMPILATION ERROR] Illegal function parameter modifiers: val -//β”‚ β•‘ l.29: fun f(val x) = x +//β”‚ β•‘ l.30: fun f(val x) = x //β”‚ ╙── ^ :e (...val x) => x //β”‚ ╔══[COMPILATION ERROR] Expected a valid parameter, found spread -//β”‚ β•‘ l.35: (...val x) => x +//β”‚ β•‘ l.36: (...val x) => x //β”‚ ╙── ^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Name not found: x -//β”‚ β•‘ l.35: (...val x) => x +//β”‚ β•‘ l.36: (...val x) => x //β”‚ ╙── ^ //β”‚ = fun @@ -52,14 +53,14 @@ fun f(val x) = x :e (...x, ...y) => x //β”‚ ╔══[COMPILATION ERROR] Spread parameters must be the last in the parameter list. -//β”‚ β•‘ l.53: (...x, ...y) => x +//β”‚ β•‘ l.54: (...x, ...y) => x //β”‚ ╙── ^^^^ //β”‚ = fun :e (..., ...y) => y //β”‚ ╔══[COMPILATION ERROR] Spread parameters must be the last in the parameter list. -//β”‚ β•‘ l.60: (..., ...y) => y +//β”‚ β•‘ l.61: (..., ...y) => y //β”‚ ╙── ^^^ //β”‚ = fun @@ -67,7 +68,7 @@ fun f(val x) = x :e class Foo(class C) //β”‚ ╔══[COMPILATION ERROR] Expected a valid parameter, found type definition -//β”‚ β•‘ l.68: class Foo(class C) +//β”‚ β•‘ l.69: class Foo(class C) //β”‚ ╙── ^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/basics/BadTypeClasses.mls b/hkmc2/shared/src/test/mlscript/basics/BadTypeClasses.mls index 47ed1cb990..0c808e68f8 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadTypeClasses.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadTypeClasses.mls @@ -5,17 +5,25 @@ module M with //β”‚ ╔══[COMPILATION ERROR] Keyword `using` must occur before all parameters. //β”‚ β•‘ l.4: fun f(foo: Int, using bar: Int) //β”‚ ╙── ^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Module 'M' has unimplemented members +//β”‚ β•‘ l.3: module M with +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•‘ l.4: fun f(foo: Int, using bar: Int) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Member 'f' requires an implementation +//β”‚ β•‘ l.4: fun f(foo: Int, using bar: Int) +//β”‚ ╙── ^ module M with - fun f(using Int) + fun f(using Int) = () :e M.f //β”‚ ╔══[COMPILATION ERROR] Cannot query instance of type Int for call: -//β”‚ β•‘ l.13: M.f +//β”‚ β•‘ l.21: M.f //β”‚ β•‘ ^^^ //β”‚ β•Ÿβ”€β”€ Required by contextual parameter declaration: -//β”‚ β•‘ l.10: fun f(using Int) +//β”‚ β•‘ l.18: fun f(using Int) = () //β”‚ β•‘ ^^^^^^^^^ //β”‚ ╙── Missing instance: Expected: Int; Available: β€Ήnone availableβ€Ί @@ -29,20 +37,20 @@ val someInt = 42 :e using someInt = 42 //β”‚ ╔══[COMPILATION ERROR] Expected a non-module type; found reference. -//β”‚ β•‘ l.30: using someInt = 42 +//β”‚ β•‘ l.38: using someInt = 42 //β”‚ β•‘ ^^^^^^^ //β”‚ ╙── Implicit instance must be marked as returning a 'module' in order to have a module return type. //β”‚ ╔══[COMPILATION ERROR] Expected a type, got reference -//β”‚ β•‘ l.30: using someInt = 42 +//β”‚ β•‘ l.38: using someInt = 42 //β”‚ ╙── ^^^^^^^ :e using someInt: Int = 42 //β”‚ ╔══[COMPILATION ERROR] Expected a type, got block -//β”‚ β•‘ l.40: using someInt: Int = 42 +//β”‚ β•‘ l.48: using someInt: Int = 42 //β”‚ ╙── ^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Expected a type, got a non-type block -//β”‚ β•‘ l.40: using someInt: Int = 42 +//β”‚ β•‘ l.48: using someInt: Int = 42 //β”‚ ╙── ^^^^^^^^^^^^ module M with @@ -51,19 +59,19 @@ module M with :e M.bar[](1, 2) //β”‚ ╔══[COMPILATION ERROR] Expected 2 type arguments, got 0 -//β”‚ β•‘ l.52: M.bar[](1, 2) +//β”‚ β•‘ l.60: M.bar[](1, 2) //β”‚ ╙── ^^^^^^^ :e M.bar[Int](1, 2) //β”‚ ╔══[COMPILATION ERROR] Expected 2 type arguments, got 1 -//β”‚ β•‘ l.58: M.bar[Int](1, 2) +//β”‚ β•‘ l.66: M.bar[Int](1, 2) //β”‚ ╙── ^^^^^^^^^^ using Int = 42 module M with - fun foo()(using a: Int) + fun foo()(using a: Int) = () // This should fail because M.foo() returns a function that requires special handling :breakme @@ -74,18 +82,18 @@ M.foo() fun f[T](using t: T) = t f //β”‚ ╔══[COMPILATION ERROR] Cannot query instance of type β€Ήunspecifiedβ€Ί (type parameter T) for call: -//β”‚ β•‘ l.75: f +//β”‚ β•‘ l.83: f //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ Required by contextual parameter declaration: -//β”‚ β•‘ l.74: fun f[T](using t: T) = t +//β”‚ β•‘ l.82: fun f[T](using t: T) = t //β”‚ β•‘ ^^^^^^^^^^ //β”‚ ╙── Illegal query for an unspecified type variable T. :e fun f(using t: Int = 42) = t //β”‚ ╔══[COMPILATION ERROR] Expected a valid parameter, found term definition -//β”‚ β•‘ l.85: fun f(using t: Int = 42) = t +//β”‚ β•‘ l.93: fun f(using t: Int = 42) = t //β”‚ ╙── ^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Name not found: t -//β”‚ β•‘ l.85: fun f(using t: Int = 42) = t +//β”‚ β•‘ l.93: fun f(using t: Int = 42) = t //β”‚ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/basics/BlockArgs.mls b/hkmc2/shared/src/test/mlscript/basics/BlockArgs.mls index c58226b8c7..df2da02a0d 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BlockArgs.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BlockArgs.mls @@ -1,31 +1,32 @@ +:. :todo fun foo(x){f} = f(x) //β”‚ ╔══[COMPILATION ERROR] Name not found: f -//β”‚ β•‘ l.5: f(x) +//β”‚ β•‘ l.6: f(x) //β”‚ ╙── ^ foo(1){x => x} //β”‚ ╔══[COMPILATION ERROR] Unsupported refinement in this position. -//β”‚ β•‘ l.10: foo(1){x => x} +//β”‚ β•‘ l.11: foo(1){x => x} //β”‚ ╙── ^^^^^^^^^^^^^^ foo(1) x => x //β”‚ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (infix operator '=>'). -//β”‚ β•‘ l.16: x => x +//β”‚ β•‘ l.17: x => x //β”‚ ╙── ^^^^^^ foo(1) { x => x } //β”‚ ╔══[COMPILATION ERROR] Unsupported refinement in this position. -//β”‚ β•‘ l.21: foo(1) { x => x } +//β”‚ β•‘ l.22: foo(1) { x => x } //β”‚ ╙── ^^^^^^^^^^^^^^^^^ foo(1) with x => x //β”‚ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'with' here -//β”‚ β•‘ l.26: foo(1) with x => x +//β”‚ β•‘ l.27: foo(1) with x => x //β”‚ ╙── ^^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/basics/ByNameTailPosition.mls b/hkmc2/shared/src/test/mlscript/basics/ByNameTailPosition.mls index 296a65c834..897d89144a 100644 --- a/hkmc2/shared/src/test/mlscript/basics/ByNameTailPosition.mls +++ b/hkmc2/shared/src/test/mlscript/basics/ByNameTailPosition.mls @@ -1,3 +1,4 @@ +:. :js // `baz()` should be a call in tail position (like `print(1)`), not bound to a variable diff --git a/hkmc2/shared/src/test/mlscript/basics/CallSyntaxes.mls b/hkmc2/shared/src/test/mlscript/basics/CallSyntaxes.mls index bbbc1ad7b8..6a6c328b29 100644 --- a/hkmc2/shared/src/test/mlscript/basics/CallSyntaxes.mls +++ b/hkmc2/shared/src/test/mlscript/basics/CallSyntaxes.mls @@ -1,3 +1,4 @@ +:. :js @@ -28,10 +29,10 @@ tuple(123, 456) :re 123 |> tuple(., 456) //β”‚ ╔══[PARSE ERROR] Expected an expression; found period instead -//β”‚ β•‘ l.29: 123 |> tuple(., 456) +//β”‚ β•‘ l.30: 123 |> tuple(., 456) //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Unexpected period here -//β”‚ β•‘ l.29: 123 |> tuple(., 456) +//β”‚ β•‘ l.30: 123 |> tuple(., 456) //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/basics/Classes.mls b/hkmc2/shared/src/test/mlscript/basics/Classes.mls index ab759b9b37..70eed390fa 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Classes.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Classes.mls @@ -1,3 +1,4 @@ +:. :js @@ -21,7 +22,7 @@ class Foo { fun f = 1 } -class Foo(pred) { +class Foo(pred: {id: Function}) { val r = pred.id(42) fun callId(x) = pred.id(x) } @@ -30,7 +31,7 @@ class Foo(pred) { class Foo(x: Int) val y = x //β”‚ ╔══[PARSE ERROR] Expected end of input; found indented block instead -//β”‚ β•‘ l.31: val y = x +//β”‚ β•‘ l.32: val y = x //β”‚ ╙── ^^ class Foo(x: Int) { print("Hello!", x) } @@ -40,7 +41,7 @@ Foo(42) //β”‚ = Foo(_) -class Bar { val x: Int } +abstract class Bar { val x: Int } class Foo() extends Bar { val x = 1 @@ -52,24 +53,38 @@ Foo().x :todo class Foo() extends Bar { x = 1 } //β”‚ ╔══[COMPILATION ERROR] Name not found: x -//β”‚ β•‘ l.53: class Foo() extends Bar { x = 1 } +//β”‚ β•‘ l.54: class Foo() extends Bar { x = 1 } //β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] Class 'Foo' has unimplemented members +//β”‚ β•‘ l.54: class Foo() extends Bar { x = 1 } +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Member 'x' requires an implementation +//β”‚ β•‘ l.44: abstract class Bar { val x: Int } +//β”‚ ╙── ^ :e class Foo() extends Bar { val x = 1 } with val y = this.x //β”‚ ╔══[COMPILATION ERROR] Multiple type definition bodies are not supported. -//β”‚ β•‘ l.61: val y = this.x +//β”‚ β•‘ l.68: val y = this.x //β”‚ ╙── ^^^^^^^^^^^^^^ :e class Foo() extends Bar { x = 1 } with val y = this.x //β”‚ ╔══[COMPILATION ERROR] Multiple type definition bodies are not supported. -//β”‚ β•‘ l.68: val y = this.x +//β”‚ β•‘ l.75: val y = this.x //β”‚ ╙── ^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Name not found: x -//β”‚ β•‘ l.67: class Foo() extends Bar { x = 1 } with +//β”‚ β•‘ l.74: class Foo() extends Bar { x = 1 } with //β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] Class 'Foo' has unimplemented members +//β”‚ β•‘ l.74: class Foo() extends Bar { x = 1 } with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.75: val y = this.x +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Member 'x' requires an implementation +//β”‚ β•‘ l.44: abstract class Bar { val x: Int } +//β”‚ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/basics/CompanionFunctions.mls b/hkmc2/shared/src/test/mlscript/basics/CompanionFunctions.mls index b9f055c485..d3b877db8d 100644 --- a/hkmc2/shared/src/test/mlscript/basics/CompanionFunctions.mls +++ b/hkmc2/shared/src/test/mlscript/basics/CompanionFunctions.mls @@ -1,3 +1,4 @@ +:. :js @@ -6,17 +7,22 @@ fun Foo(x) = new Foo(x, 0) data class Foo(x, y) //β”‚ ╔══[COMPILATION ERROR] Not yet supported: overloading of function 'Foo' -//β”‚ β•‘ l.6: fun Foo(x) = new Foo(x, 0) +//β”‚ β•‘ l.7: fun Foo(x) = new Foo(x, 0) //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ with class of the same name -//β”‚ β•‘ l.7: data class Foo(x, y) +//β”‚ β•‘ l.8: data class Foo(x, y) //β”‚ ╙── ^^^^^^^^^^^^^^^ :fixme Foo(123) -//β”‚ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 -//β”‚ β•‘ l.16: Foo(123) -//β”‚ ╙── ^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.17: Foo(123) +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Foo' expected 2 arguments, but got 1 +//β”‚ β•‘ l.7: fun Foo(x) = new Foo(x, 0) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.8: data class Foo(x, y) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^ //β”‚ = Foo(123, undefined) diff --git a/hkmc2/shared/src/test/mlscript/basics/CompanionModules_Functions.mls b/hkmc2/shared/src/test/mlscript/basics/CompanionModules_Functions.mls index 28b0f72fbc..8ebaec5676 100644 --- a/hkmc2/shared/src/test/mlscript/basics/CompanionModules_Functions.mls +++ b/hkmc2/shared/src/test/mlscript/basics/CompanionModules_Functions.mls @@ -1,19 +1,11 @@ +:. :js -:fixme // TODO: support // :sjs fun foo(x) = x + 1 module foo with val zero = foo(0) -//β”‚ ╔══[COMPILATION ERROR] Not yet supported: overloading of function 'foo' -//β”‚ β•‘ l.6: fun foo(x) = x + 1 -//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ -//β”‚ β•Ÿβ”€β”€ with module of the same name -//β”‚ β•‘ l.7: module foo with -//β”‚ β•‘ ^^^^^^^^^^^^^^^ -//β”‚ β•‘ l.8: val zero = foo(0) -//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^ foo(123) //β”‚ = 124 diff --git a/hkmc2/shared/src/test/mlscript/basics/CompanionModules_Patterns.mls b/hkmc2/shared/src/test/mlscript/basics/CompanionModules_Patterns.mls index 3c71f39936..f253600603 100644 --- a/hkmc2/shared/src/test/mlscript/basics/CompanionModules_Patterns.mls +++ b/hkmc2/shared/src/test/mlscript/basics/CompanionModules_Patterns.mls @@ -1,3 +1,4 @@ +:. :js @@ -5,10 +6,10 @@ pattern Foo = 1 module Foo //β”‚ ╔══[COMPILATION ERROR] Not yet supported: overloading of pattern 'Foo' -//β”‚ β•‘ l.5: pattern Foo = 1 +//β”‚ β•‘ l.6: pattern Foo = 1 //β”‚ β•‘ ^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ with module of the same name -//β”‚ β•‘ l.6: module Foo +//β”‚ β•‘ l.7: module Foo //β”‚ ╙── ^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/basics/CompanionModules_Types.mls b/hkmc2/shared/src/test/mlscript/basics/CompanionModules_Types.mls index 6d9744f9ec..ded4f98a75 100644 --- a/hkmc2/shared/src/test/mlscript/basics/CompanionModules_Types.mls +++ b/hkmc2/shared/src/test/mlscript/basics/CompanionModules_Types.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/basics/CyclicValues.mls b/hkmc2/shared/src/test/mlscript/basics/CyclicValues.mls index 3ce2008fec..e6f5a54fc1 100644 --- a/hkmc2/shared/src/test/mlscript/basics/CyclicValues.mls +++ b/hkmc2/shared/src/test/mlscript/basics/CyclicValues.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/basics/DataClass.mls b/hkmc2/shared/src/test/mlscript/basics/DataClass.mls index 85084ad21c..0c51f3e264 100644 --- a/hkmc2/shared/src/test/mlscript/basics/DataClass.mls +++ b/hkmc2/shared/src/test/mlscript/basics/DataClass.mls @@ -1,3 +1,4 @@ +:. :js @@ -11,13 +12,13 @@ Foo(1).x :todo data Foo(x: Int) //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.12: data Foo(x: Int) +//β”‚ β•‘ l.13: data Foo(x: Int) //β”‚ β•‘ ^^^^ -//β”‚ β•Ÿβ”€β”€ This annotation is not supported on application of type Foo terms. -//β”‚ β•‘ l.12: data Foo(x: Int) +//β”‚ β•Ÿβ”€β”€ This annotation is not supported on application terms. +//β”‚ β•‘ l.13: data Foo(x: Int) //β”‚ ╙── ^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Symbol 'Int' is virtual (i.e., "compiler fiction"); cannot be used as a term -//β”‚ β•‘ l.12: data Foo(x: Int) +//β”‚ β•‘ l.13: data Foo(x: Int) //β”‚ ╙── ^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/basics/Declare.mls b/hkmc2/shared/src/test/mlscript/basics/Declare.mls index 1cf22cbdcd..68d65535c9 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Declare.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Declare.mls @@ -1,3 +1,4 @@ +:. :js @@ -58,29 +59,35 @@ fun isMD(x) = x is M.D C //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Blk: -//β”‚ res = Resolved{sym=class:C⁰}: -//β”‚ t = Ref{sym=member:CΒΉ} of member:CΒΉ -//β”‚ sym = class:C⁰ +//β”‚ res = MemberRef of member:C⁰ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” // * Also resolved M.D //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Blk: -//β”‚ res = Resolved{sym=class:D⁰}: -//β”‚ t = Sel{sym=member:DΒΉ}: -//β”‚ prefix = Resolved{sym=module:M⁰,typ=module:M⁰}: -//β”‚ t = Ref{sym=member:MΒΉ} of member:MΒΉ -//β”‚ sym = module:M⁰ -//β”‚ nme = Ident of "D" -//β”‚ sym = class:D⁰ +//β”‚ res = NewSel: +//β”‚ prefix = MemberRef of member:M⁰ +//β”‚ id = Ident of "D" //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” :fixme // FIXME: Should report a proper error about lack of declared C companion module C.oops -//β”‚ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'oops') - -:re +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.74: C.oops +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class value does not contain member 'oops' +//β”‚ β•‘ l.38: declare class C +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:ge M.D.oops -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'D' yielded 'undefined' +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.84: M.D.oops +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class value does not contain member 'oops' +//β”‚ β•‘ l.48: declare class D +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/basics/DisruptiveComments.mls b/hkmc2/shared/src/test/mlscript/basics/DisruptiveComments.mls index 5291d3efae..fd98bfe9be 100644 --- a/hkmc2/shared/src/test/mlscript/basics/DisruptiveComments.mls +++ b/hkmc2/shared/src/test/mlscript/basics/DisruptiveComments.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/basics/Drop.mls b/hkmc2/shared/src/test/mlscript/basics/Drop.mls index d93cb4721e..8e58550c18 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Drop.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Drop.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/basics/DynamicFields.mls b/hkmc2/shared/src/test/mlscript/basics/DynamicFields.mls index 9d457f2690..763328b058 100644 --- a/hkmc2/shared/src/test/mlscript/basics/DynamicFields.mls +++ b/hkmc2/shared/src/test/mlscript/basics/DynamicFields.mls @@ -1,3 +1,4 @@ +:. :js @@ -62,6 +63,7 @@ foo.["x"] class DynCtor() with + declare val dynField: Int set this.("dynField") = 42 DynCtor().dynField diff --git a/hkmc2/shared/src/test/mlscript/basics/DynamicInstantiation.mls b/hkmc2/shared/src/test/mlscript/basics/DynamicInstantiation.mls index 3f7c39daf4..fa9f579018 100644 --- a/hkmc2/shared/src/test/mlscript/basics/DynamicInstantiation.mls +++ b/hkmc2/shared/src/test/mlscript/basics/DynamicInstantiation.mls @@ -1,3 +1,4 @@ +:. :js @@ -51,19 +52,24 @@ new! (id(C)) :e :re new id(C) -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found selection. -//β”‚ β•‘ l.53: new id(C) -//β”‚ β•‘ ^^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//β”‚ ═══[RUNTIME ERROR] TypeError: Predef.id is not a constructor +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.54: new id(C) +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'id' cannot be instantiated with keyword 'new'. +//β”‚ β•‘ in ../../mlscript-compile/Predef.mls +//β”‚ β•‘ l.19: fun id(x) = x +//β”‚ ╙── ^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e new (id(C)) -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found application. -//β”‚ β•‘ l.61: new (id(C)) -//β”‚ β•‘ ^^^^^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//β”‚ = C +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.65: new (id(C)) +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Invalid class expression: application +//β”‚ β•‘ l.65: new (id(C)) +//β”‚ ╙── ^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. data class C(x, y) @@ -89,7 +95,7 @@ f(1, 2) //β”‚ ═══[RUNTIME ERROR] TypeError: f3 is not a function -new! C.class(1, 2) +new! C!class(1, 2) //β”‚ = C(1, 2) diff --git a/hkmc2/shared/src/test/mlscript/basics/DynamicSelection.mls b/hkmc2/shared/src/test/mlscript/basics/DynamicSelection.mls index bbe04bc183..1ba278e4cf 100644 --- a/hkmc2/shared/src/test/mlscript/basics/DynamicSelection.mls +++ b/hkmc2/shared/src/test/mlscript/basics/DynamicSelection.mls @@ -1,3 +1,4 @@ +:. :js @@ -12,6 +13,7 @@ r!a //β”‚ DynAccess: //β”‚ obj = Ident of "r" //β”‚ fld = Ident of "a" +//β”‚ dynamic = true //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = 1 @@ -25,10 +27,10 @@ r !a :fixme r! a //β”‚ ╔══[COMPILATION ERROR] Name not found: a -//β”‚ β•‘ l.26: r! a +//β”‚ β•‘ l.28: r! a //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Builtin '!' is not a binary operator -//β”‚ β•‘ l.26: r! a +//β”‚ β•‘ l.28: r! a //β”‚ ╙── ^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -36,10 +38,10 @@ r! a :fixme r ! a //β”‚ ╔══[COMPILATION ERROR] Name not found: a -//β”‚ β•‘ l.37: r ! a +//β”‚ β•‘ l.39: r ! a //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Builtin '!' is not a binary operator -//β”‚ β•‘ l.37: r ! a +//β”‚ β•‘ l.39: r ! a //β”‚ ╙── ^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -78,13 +80,13 @@ t.01 :fixme // * Strange that this is treated as a statement !(1,2) //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.79: !(1,2) +//β”‚ β•‘ l.81: !(1,2) //β”‚ ╙── ^ :ge (!)(1,2) //β”‚ ╔══[COMPILATION ERROR] Builtin '!' is not a binary operator -//β”‚ β•‘ l.85: (!)(1,2) +//β”‚ β•‘ l.87: (!)(1,2) //β”‚ ╙── ^^^^^^^^ //β”‚ ═══[COMPILATION ERROR] Cannot call non-binary builtin symbol '!' //β”‚ ═══[RUNTIME ERROR] Error: Cannot call non-binary builtin symbol '!' @@ -99,10 +101,10 @@ t.01 :fixme !1 //β”‚ ╔══[PARSE ERROR] Expected an expression; found dynamic selector instead -//β”‚ β•‘ l.100: !1 +//β”‚ β•‘ l.102: !1 //β”‚ ╙── ^^ //β”‚ ╔══[PARSE ERROR] Expected end of input; found dynamic selector instead -//β”‚ β•‘ l.100: !1 +//β”‚ β•‘ l.102: !1 //β”‚ ╙── ^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/basics/ExplicitLabels.mls b/hkmc2/shared/src/test/mlscript/basics/ExplicitLabels.mls index 46b5e8985d..6bbc148b51 100644 --- a/hkmc2/shared/src/test/mlscript/basics/ExplicitLabels.mls +++ b/hkmc2/shared/src/test/mlscript/basics/ExplicitLabels.mls @@ -1,3 +1,4 @@ +:. :js @@ -31,16 +32,22 @@ let i = 0 in do outLoop: :e missing.break(1) //β”‚ ╔══[COMPILATION ERROR] Name not found: missing -//β”‚ β•‘ l.32: missing.break(1) +//β”‚ β•‘ l.33: missing.break(1) //β”‚ ╙── ^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'break' has no resolved target +//β”‚ β•‘ l.33: missing.break(1) +//β”‚ ╙── ^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e missing.continue //β”‚ ╔══[COMPILATION ERROR] Name not found: missing -//β”‚ β•‘ l.40: missing.continue +//β”‚ β•‘ l.44: missing.continue //β”‚ ╙── ^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'continue' has no resolved target +//β”‚ β•‘ l.44: missing.continue +//β”‚ ╙── ^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -48,7 +55,7 @@ missing.continue () => do lbl: lbl.continue(1) //β”‚ ╔══[COMPILATION ERROR] 'continue' does not take arguments. -//β”‚ β•‘ l.49: lbl.continue(1) +//β”‚ β•‘ l.56: lbl.continue(1) //β”‚ ╙── ^^^^^^^^^^^^^^^ //β”‚ = fun @@ -57,7 +64,7 @@ missing.continue do lbl: lbl.break(1, 2) //β”‚ ╔══[COMPILATION ERROR] 'break' expects at most one argument. -//β”‚ β•‘ l.58: lbl.break(1, 2) +//β”‚ β•‘ l.65: lbl.break(1, 2) //β”‚ ╙── ^^^^^^^^^^^^^^^ @@ -65,8 +72,11 @@ do lbl: lbl: lbl.break(1) //β”‚ ╔══[COMPILATION ERROR] Name not found: lbl -//β”‚ β•‘ l.66: lbl.break(1) +//β”‚ β•‘ l.73: lbl.break(1) //β”‚ ╙── ^^^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'break' has no resolved target +//β”‚ β•‘ l.73: lbl.break(1) +//β”‚ ╙── ^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //β”‚ lbl = undefined @@ -110,11 +120,18 @@ do lbl: // * If a variable shadows a label, label control-flow selection should no longer resolve as a label op. +:ge :re do lbl: let lbl = 5 lbl.break -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'break' yielded 'undefined' +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.127: lbl.break +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal does not contain member 'break' +//β”‚ β•‘ l.126: let lbl = 5 +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // * Nested labels in a plain `do` block should be handled; later labels are nested in earlier ones. @@ -144,6 +161,9 @@ do only: only.break(x) //β”‚ ╔══[COMPILATION ERROR] Name not found: only -//β”‚ β•‘ l.145: only.break(x) +//β”‚ β•‘ l.162: only.break(x) //β”‚ ╙── ^^^^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'break' has no resolved target +//β”‚ β•‘ l.162: only.break(x) +//β”‚ ╙── ^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/basics/ExplicitSelections.mls b/hkmc2/shared/src/test/mlscript/basics/ExplicitSelections.mls index f72942cd53..a6846f354c 100644 --- a/hkmc2/shared/src/test/mlscript/basics/ExplicitSelections.mls +++ b/hkmc2/shared/src/test/mlscript/basics/ExplicitSelections.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/basics/FunDefs.mls b/hkmc2/shared/src/test/mlscript/basics/FunDefs.mls index 5735506001..f4119bebcb 100644 --- a/hkmc2/shared/src/test/mlscript/basics/FunDefs.mls +++ b/hkmc2/shared/src/test/mlscript/basics/FunDefs.mls @@ -1,3 +1,4 @@ +:. fun foo = 1 @@ -8,7 +9,15 @@ fun (+) plus: Int fun (+) : Int +// This operator denotes an integer, so applying it is invalid. +:e 1 + 2 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.14: 1 + 2 +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Int' cannot be called like a function. +//β”‚ β•‘ l.10: fun (+) : Int +//β”‚ ╙── ^^^ fun f = g @@ -22,10 +31,25 @@ fun (++)(x, y) = 1 fun (**) pow = 1 +:e 1 ** 2 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.35: 1 ** 2 +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be called like a function. +//β”‚ β•‘ l.32: fun (**) pow = 1 +//β”‚ ╙── ^ +// The local '+' still denotes an integer inside this generic operator body. +:e fun (:::) appendAll[A](a, b): A = a + b +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.46: fun (:::) appendAll[A](a, b): A = a + b +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Int' cannot be called like a function. +//β”‚ β•‘ l.10: fun (+) : Int +//β”‚ ╙── ^^^ fun (>>) foo(a, b) = a * b @@ -36,21 +60,27 @@ fun (>>) foo(a, b) = a * b :e fun ~ concat(a, b) = a + b //β”‚ ╔══[COMPILATION ERROR] Expected a valid parameter, found application -//β”‚ β•‘ l.37: fun ~ concat(a, b) = a + b +//β”‚ β•‘ l.61: fun ~ concat(a, b) = a + b //β”‚ ╙── ^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Name not found: a -//β”‚ β•‘ l.37: fun ~ concat(a, b) = a + b +//β”‚ β•‘ l.61: fun ~ concat(a, b) = a + b //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Name not found: b -//β”‚ β•‘ l.37: fun ~ concat(a, b) = a + b +//β”‚ β•‘ l.61: fun ~ concat(a, b) = a + b //β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.61: fun ~ concat(a, b) = a + b +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Int' cannot be called like a function. +//β”‚ β•‘ l.10: fun (+) : Int +//β”‚ ╙── ^^^ // * Note the strange parsing :pt :todo // better error? fun not(b) = !b //β”‚ ╔══[PARSE ERROR] Expected an expression; found dynamic selector instead -//β”‚ β•‘ l.51: fun not(b) = !b +//β”‚ β•‘ l.81: fun not(b) = !b //β”‚ ╙── ^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Parsed tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ DynAccess: @@ -63,8 +93,9 @@ fun not(b) = !b //β”‚ inner = Ident of "b" //β”‚ rhs = S of Error //β”‚ fld = Ident of "b" +//β”‚ dynamic = true //β”‚ ╔══[COMPILATION ERROR] Illegal definition in term position. -//β”‚ β•‘ l.51: fun not(b) = !b +//β”‚ β•‘ l.81: fun not(b) = !b //β”‚ ╙── ^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/basics/FunnyRecordKeys.mls b/hkmc2/shared/src/test/mlscript/basics/FunnyRecordKeys.mls index cfe651dd48..8a6a8faf5b 100644 --- a/hkmc2/shared/src/test/mlscript/basics/FunnyRecordKeys.mls +++ b/hkmc2/shared/src/test/mlscript/basics/FunnyRecordKeys.mls @@ -1,3 +1,4 @@ +:. :js :global diff --git a/hkmc2/shared/src/test/mlscript/basics/GenericClasses.mls b/hkmc2/shared/src/test/mlscript/basics/GenericClasses.mls index 7c4b5acf87..6508632fba 100644 --- a/hkmc2/shared/src/test/mlscript/basics/GenericClasses.mls +++ b/hkmc2/shared/src/test/mlscript/basics/GenericClasses.mls @@ -1,3 +1,4 @@ +:. class Foo[A] @@ -20,7 +21,7 @@ class Baz[C](x: C) class Bar[B](x: Foo[Int], y: Baz[B] -> Int) -class Foo[A] with +abstract class Foo[A] with fun foo: A -> A @@ -28,15 +29,27 @@ class Foo[A] with :e class Foo[A](x: Foo[A, A]) -//β”‚ ╔══[COMPILATION ERROR] Expected 1 type arguments, got 2 -//β”‚ β•‘ l.30: class Foo[A](x: Foo[A, A]) -//β”‚ ╙── ^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.31: class Foo[A](x: Foo[A, A]) +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Type 'Foo' accepts at most 1 type argument, but got 2. +//β”‚ β•‘ l.31: class Foo[A](x: Foo[A, A]) +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ The type parameters are declared here. +//β”‚ β•‘ l.31: class Foo[A](x: Foo[A, A]) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ :e class Foo[A](x: Bar[A]) class Bar(x: Foo[Int]) -//β”‚ ╔══[COMPILATION ERROR] Expected no type arguments, got 1 -//β”‚ β•‘ l.36: class Foo[A](x: Bar[A]) -//β”‚ ╙── ^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.43: class Foo[A](x: Bar[A]) +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Type 'Bar' accepts at most 0 type arguments, but got 1. +//β”‚ β•‘ l.43: class Foo[A](x: Bar[A]) +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ The type parameters are declared here. +//β”‚ β•‘ l.44: class Bar(x: Foo[Int]) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/basics/Getters.mls b/hkmc2/shared/src/test/mlscript/basics/Getters.mls index 50f80f3ebe..52436d61c0 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Getters.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Getters.mls @@ -1,3 +1,4 @@ +:. :js :ignore @@ -5,7 +6,7 @@ get foo = 123 //β”‚ ╔══[COMPILATION ERROR] Unrecognized definitional assignment left-hand side: juxtaposition -//β”‚ β•‘ l.6: get foo = 123 +//β”‚ β•‘ l.7: get foo = 123 //β”‚ ╙── ^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/basics/IdentEscape.mls b/hkmc2/shared/src/test/mlscript/basics/IdentEscape.mls index 5d3941860d..bff3b273bc 100644 --- a/hkmc2/shared/src/test/mlscript/basics/IdentEscape.mls +++ b/hkmc2/shared/src/test/mlscript/basics/IdentEscape.mls @@ -1,3 +1,4 @@ +:. let id"of" = 42 fun id"fun"(x: Int) = x + 1 @@ -13,13 +14,13 @@ module id"object" with :pe id"" //β”‚ ╔══[LEXICAL ERROR] unexpected identifier escape -//β”‚ β•‘ l.14: id"" +//β”‚ β•‘ l.15: id"" //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Expected an expression; found error instead -//β”‚ β•‘ l.14: id"" +//β”‚ β•‘ l.15: id"" //β”‚ ╙── ^^^^ //β”‚ ╔══[PARSE ERROR] Expected end of input; found error instead -//β”‚ β•‘ l.14: id"" +//β”‚ β•‘ l.15: id"" //β”‚ ╙── ^^^^ // Quoted names need not follow ordinary identifier syntax. @@ -60,53 +61,53 @@ id"hello\u0009δΈ–η•ŒπŸ˜€" :pe id" //β”‚ ╔══[LEXICAL ERROR] unclosed quotation mark -//β”‚ β•‘ l.61: id" +//β”‚ β•‘ l.62: id" //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Expected an expression; found error instead -//β”‚ β•‘ l.61: id" +//β”‚ β•‘ l.62: id" //β”‚ ╙── ^^^ //β”‚ ╔══[PARSE ERROR] Expected end of input; found error instead -//β”‚ β•‘ l.61: id" +//β”‚ β•‘ l.62: id" //β”‚ ╙── ^^^ :pe id"unterminated //β”‚ ╔══[LEXICAL ERROR] unclosed quotation mark -//β”‚ β•‘ l.73: id"unterminated +//β”‚ β•‘ l.74: id"unterminated //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Expected an expression; found error instead -//β”‚ β•‘ l.73: id"unterminated +//β”‚ β•‘ l.74: id"unterminated //β”‚ ╙── ^^^^^^^^^^^^^^^ //β”‚ ╔══[PARSE ERROR] Expected end of input; found error instead -//β”‚ β•‘ l.73: id"unterminated +//β”‚ β•‘ l.74: id"unterminated //β”‚ ╙── ^^^^^^^^^^^^^^^ :pe id"trailing escape\ //β”‚ ╔══[LEXICAL ERROR] Expected an escape character -//β”‚ β•‘ l.85: id"trailing escape\ +//β”‚ β•‘ l.86: id"trailing escape\ //β”‚ ╙── ^ //β”‚ ╔══[LEXICAL ERROR] unclosed quotation mark -//β”‚ β•‘ l.85: id"trailing escape\ +//β”‚ β•‘ l.86: id"trailing escape\ //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Expected an expression; found error instead -//β”‚ β•‘ l.85: id"trailing escape\ +//β”‚ β•‘ l.86: id"trailing escape\ //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[PARSE ERROR] Expected end of input; found error instead -//β”‚ β•‘ l.85: id"trailing escape\ +//β”‚ β•‘ l.86: id"trailing escape\ //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^ :pe id"unterminated let afterUnterminated = 1 //β”‚ ╔══[LEXICAL ERROR] unclosed quotation mark -//β”‚ β•‘ l.100: id"unterminated +//β”‚ β•‘ l.101: id"unterminated //β”‚ β•‘ ^ -//β”‚ β•‘ l.101: let afterUnterminated = 1 +//β”‚ β•‘ l.102: let afterUnterminated = 1 //β”‚ ╙── //β”‚ ╔══[PARSE ERROR] Expected an expression; found error instead -//β”‚ β•‘ l.100: id"unterminated +//β”‚ β•‘ l.101: id"unterminated //β”‚ ╙── ^^^^^^^^^^^^^^^ //β”‚ ╔══[PARSE ERROR] Expected end of input; found error instead -//β”‚ β•‘ l.100: id"unterminated +//β”‚ β•‘ l.101: id"unterminated //β”‚ ╙── ^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/basics/IfThenNewline.mls b/hkmc2/shared/src/test/mlscript/basics/IfThenNewline.mls index 384646a237..178e6a9eb3 100644 --- a/hkmc2/shared/src/test/mlscript/basics/IfThenNewline.mls +++ b/hkmc2/shared/src/test/mlscript/basics/IfThenNewline.mls @@ -1,3 +1,4 @@ +:. :js // Regression test: `if true` with `then` on a new indented line should work diff --git a/hkmc2/shared/src/test/mlscript/basics/Indentation.mls b/hkmc2/shared/src/test/mlscript/basics/Indentation.mls index 73c8a0bd67..70c7dcb558 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Indentation.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Indentation.mls @@ -1,3 +1,4 @@ +:. :js @@ -48,11 +49,11 @@ tuple( tuple() tuple() //β”‚ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (juxtaposition). -//β”‚ β•‘ l.47: tuple() +//β”‚ β•‘ l.48: tuple() //β”‚ β•‘ ^^^^^^^ -//β”‚ β•‘ l.48: tuple() +//β”‚ β•‘ l.49: tuple() //β”‚ β•‘ ^^^^^^^^^^^ -//β”‚ β•‘ l.49: tuple() +//β”‚ β•‘ l.50: tuple() //β”‚ ╙── ^^ //β”‚ = [[4]] @@ -72,12 +73,12 @@ tuple( 1 ) //β”‚ ╔══[WARNING] This literal should be indented -//β”‚ β•‘ l.72: 1 +//β”‚ β•‘ l.73: 1 //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ since it is a continuation of the new line here -//β”‚ β•‘ l.71: tuple( +//β”‚ β•‘ l.72: tuple( //β”‚ β•‘ ^ -//β”‚ β•‘ l.72: 1 +//β”‚ β•‘ l.73: 1 //β”‚ ╙── //β”‚ = [1] @@ -139,7 +140,7 @@ id //β”‚ Parsed: //β”‚ Jux(Ident(id),Block(List(IntLit(1)))) //β”‚ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (integer literal). -//β”‚ β•‘ l.137: 1 +//β”‚ β•‘ l.138: 1 //β”‚ ╙── ^ //β”‚ = fun id @@ -152,9 +153,17 @@ id(0 //β”‚ Parsed: //β”‚ App(App(Ident(id),Tup(List(Jux(IntLit(0),Block(List(Ident(id))))))),Tup(List(IntLit(1)))) //β”‚ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (identifier). -//β”‚ β•‘ l.150: id)(1) +//β”‚ β•‘ l.151: id)(1) //β”‚ ╙── ^^ -//β”‚ ═══[RUNTIME ERROR] TypeError: 0 is not a function +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.150: id(0 +//β”‚ β•‘ ^^^^ +//β”‚ β•‘ l.151: id)(1) +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be called like a function. +//β”‚ β•‘ l.150: id(0 +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :soir diff --git a/hkmc2/shared/src/test/mlscript/basics/Inheritance.mls b/hkmc2/shared/src/test/mlscript/basics/Inheritance.mls index 5b191127c3..2495b50b99 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Inheritance.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Inheritance.mls @@ -1,3 +1,4 @@ +:. :js @@ -8,14 +9,8 @@ class Foo with object Bar extends Foo -:todo // recognize fields that are inherited from superclasses +:expect [10, 11] [Bar.x, Bar.foo] -//β”‚ ╔══[COMPILATION ERROR] Object 'Bar' does not contain member 'x' -//β”‚ β•‘ l.12: [Bar.x, Bar.foo] -//β”‚ ╙── ^^ -//β”‚ ╔══[COMPILATION ERROR] Object 'Bar' does not contain member 'foo' -//β”‚ β•‘ l.12: [Bar.x, Bar.foo] -//β”‚ ╙── ^^^^ //β”‚ = [10, 11] let b = id(Bar) in [b.x, b.foo] @@ -95,7 +90,7 @@ data class Some[out T](x: T) extends Option[T] data class Barr(x: Int)(y: Num) with - fun f = x + y + open fun f = x + y data class Bazz(z: Int) extends Barr(z % 10)(z / 10) with fun g = z @@ -106,7 +101,7 @@ Bazz(42).f new Barr(40)(2) with - fun f = 43 + override fun f = 43 //β”‚ = $anon { x: 40, y: 2 } diff --git a/hkmc2/shared/src/test/mlscript/basics/LabeledIfWhileBodies.mls b/hkmc2/shared/src/test/mlscript/basics/LabeledIfWhileBodies.mls index 40d798b484..438df15cf9 100644 --- a/hkmc2/shared/src/test/mlscript/basics/LabeledIfWhileBodies.mls +++ b/hkmc2/shared/src/test/mlscript/basics/LabeledIfWhileBodies.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/basics/LazySpreads.mls b/hkmc2/shared/src/test/mlscript/basics/LazySpreads.mls index 9eb9a5259b..1d6015100f 100644 --- a/hkmc2/shared/src/test/mlscript/basics/LazySpreads.mls +++ b/hkmc2/shared/src/test/mlscript/basics/LazySpreads.mls @@ -1,3 +1,4 @@ +:. :js :expect [[1, 2], 3] @@ -78,13 +79,43 @@ sum2(arr) :e fun f(..xs) = xs //β”‚ ╔══[COMPILATION ERROR] Lazy spread parameters not allowed. -//β”‚ β•‘ l.79: fun f(..xs) = xs +//β”‚ β•‘ l.80: fun f(..xs) = xs //β”‚ ╙── ^^^^ :ge sum2(..arr) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.86: sum2(..arr) +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function literal expected 1 argument, but got 0 +//β”‚ β•‘ l.47: fun sum2 = case +//β”‚ β•‘ ^^^^ +//β”‚ β•‘ l.48: [] then 0 +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•‘ l.49: [x, ..xs, y] then x + y + sum2(xs) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.86: sum2(..arr) +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function literal expected 1 argument, but got 2 +//β”‚ β•‘ l.47: fun sum2 = case +//β”‚ β•‘ ^^^^ +//β”‚ β•‘ l.48: [] then 0 +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•‘ l.49: [x, ..xs, y] then x + y + sum2(xs) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.86: sum2(..arr) +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function literal expected 1 argument, but got 4 +//β”‚ β•‘ l.47: fun sum2 = case +//β”‚ β•‘ ^^^^ +//β”‚ β•‘ l.48: [] then 0 +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•‘ l.49: [x, ..xs, y] then x + y + sum2(xs) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Lazy spreads are not supported in call arguments -//β”‚ β•‘ l.85: sum2(..arr) +//β”‚ β•‘ l.86: sum2(..arr) //β”‚ ╙── ^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: Function expected 1 argument but got 2 diff --git a/hkmc2/shared/src/test/mlscript/basics/LetBindings.mls b/hkmc2/shared/src/test/mlscript/basics/LetBindings.mls index 89aec1198a..d651a47ca4 100644 --- a/hkmc2/shared/src/test/mlscript/basics/LetBindings.mls +++ b/hkmc2/shared/src/test/mlscript/basics/LetBindings.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/basics/LiteralSelection.mls b/hkmc2/shared/src/test/mlscript/basics/LiteralSelection.mls index 3e374f2a7e..3692a0bb99 100644 --- a/hkmc2/shared/src/test/mlscript/basics/LiteralSelection.mls +++ b/hkmc2/shared/src/test/mlscript/basics/LiteralSelection.mls @@ -1,3 +1,4 @@ +:. :js @@ -41,7 +42,7 @@ obj :pe obj. a //β”‚ ╔══[PARSE ERROR] Expected end of input; found period instead -//β”‚ β•‘ l.42: obj. a +//β”‚ β•‘ l.43: obj. a //β”‚ ╙── ^ //β”‚ = {a: 1} @@ -55,20 +56,20 @@ obj .("a") obj .("a") //β”‚ ╔══[PARSE ERROR] Expected an expression; found period instead -//β”‚ β•‘ l.56: .("a") +//β”‚ β•‘ l.57: .("a") //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Unexpected period here -//β”‚ β•‘ l.56: .("a") +//β”‚ β•‘ l.57: .("a") //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (β€Ήerroneous syntaxβ€Ί). -//β”‚ β•‘ l.56: .("a") +//β”‚ β•‘ l.57: .("a") //β”‚ ╙── ^ //β”‚ = {a: 1} :pe obj. ("a") //β”‚ ╔══[PARSE ERROR] Expected end of input; found period instead -//β”‚ β•‘ l.69: obj. ("a") +//β”‚ β•‘ l.70: obj. ("a") //β”‚ ╙── ^ //β”‚ = {a: 1} @@ -82,20 +83,20 @@ obj ."a" obj ."a" //β”‚ ╔══[PARSE ERROR] Expected an expression; found period instead -//β”‚ β•‘ l.83: ."a" +//β”‚ β•‘ l.84: ."a" //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Unexpected period here -//β”‚ β•‘ l.83: ."a" +//β”‚ β•‘ l.84: ."a" //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (β€Ήerroneous syntaxβ€Ί). -//β”‚ β•‘ l.83: ."a" +//β”‚ β•‘ l.84: ."a" //β”‚ ╙── ^ //β”‚ = {a: 1} :pe obj. "a" //β”‚ ╔══[PARSE ERROR] Expected end of input; found period instead -//β”‚ β•‘ l.96: obj. "a" +//β”‚ β•‘ l.97: obj. "a" //β”‚ ╙── ^ //β”‚ = {a: 1} @@ -111,7 +112,7 @@ obj." " :w obj."Β―\_(ツ)_/Β―" //β”‚ ╔══[WARNING] Found invalid escape character -//β”‚ β•‘ l.112: obj."Β―\_(ツ)_/Β―" +//β”‚ β•‘ l.113: obj."Β―\_(ツ)_/Β―" //β”‚ ╙── ^ //β”‚ = 2 @@ -123,21 +124,21 @@ obj."Β―\\_(ツ)_/Β―" :w "Β―\_(ツ)_/Β―" //β”‚ ╔══[WARNING] Found invalid escape character -//β”‚ β•‘ l.124: "Β―\_(ツ)_/Β―" +//β”‚ β•‘ l.125: "Β―\_(ツ)_/Β―" //β”‚ ╙── ^ //β”‚ = "Β―_(ツ)_/Β―" :w let obj = "Β―\_(ツ)_/Β―": 2 //β”‚ ╔══[WARNING] Found invalid escape character -//β”‚ β•‘ l.131: let obj = "Β―\_(ツ)_/Β―": 2 +//β”‚ β•‘ l.132: let obj = "Β―\_(ツ)_/Β―": 2 //β”‚ ╙── ^ //β”‚ obj = {"Β―_(ツ)_/Β―": 2} :w obj."Β―\_(ツ)_/Β―" //β”‚ ╔══[WARNING] Found invalid escape character -//β”‚ β•‘ l.138: obj."Β―\_(ツ)_/Β―" +//β”‚ β•‘ l.139: obj."Β―\_(ツ)_/Β―" //β”‚ ╙── ^ //β”‚ = 2 diff --git a/hkmc2/shared/src/test/mlscript/basics/Literals.mls b/hkmc2/shared/src/test/mlscript/basics/Literals.mls index 0b50e2de03..6574f18ba1 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Literals.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Literals.mls @@ -1,3 +1,4 @@ +:. :js @@ -15,14 +16,14 @@ :w (1, 2) //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.16: (1, 2) +//β”‚ β•‘ l.17: (1, 2) //β”‚ ╙── ^ //β”‚ = 2 :w (1, 2,) //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.23: (1, 2,) +//β”‚ β•‘ l.24: (1, 2,) //β”‚ ╙── ^ //β”‚ = 2 @@ -54,7 +55,7 @@ undefined :todo NaN //β”‚ ╔══[COMPILATION ERROR] Name not found: NaN -//β”‚ β•‘ l.55: NaN +//β”‚ β•‘ l.56: NaN //β”‚ ╙── ^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -74,7 +75,7 @@ NaN :e 'AA BB' //β”‚ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (identifier). -//β”‚ β•‘ l.75: 'AA BB' +//β”‚ β•‘ l.76: 'AA BB' //β”‚ ╙── ^^^ //β”‚ = "AA" @@ -97,42 +98,42 @@ NaN :w "\uFE&&" //β”‚ ╔══[WARNING] Invalid Unicode escape sequence: expected 4 hexadecimal digits but got 2 -//β”‚ β•‘ l.98: "\uFE&&" +//β”‚ β•‘ l.99: "\uFE&&" //β”‚ ╙── ^^ //β”‚ = "&&" :w "\u{}" //β”‚ ╔══[WARNING] Expected at least one hexadecimal digit in Unicode escape sequence -//β”‚ β•‘ l.105: "\u{}" +//β”‚ β•‘ l.106: "\u{}" //β”‚ ╙── ^ //β”‚ = "" :w "\u{ffffffff}" //β”‚ ╔══[WARNING] Too many hexadecimal digits in Unicode escape sequence -//β”‚ β•‘ l.112: "\u{ffffffff}" +//β”‚ β•‘ l.113: "\u{ffffffff}" //β”‚ ╙── ^^ //β”‚ = "" :w "\u{efefef}" //β”‚ ╔══[WARNING] Unicode code point out of range: 0xefefef -//β”‚ β•‘ l.119: "\u{efefef}" +//β”‚ β•‘ l.120: "\u{efefef}" //β”‚ ╙── ^^^^^^ //β”‚ = "" :w "\u{55" //β”‚ ╔══[WARNING] Unterminated Unicode escape sequence: missing '}' -//β”‚ β•‘ l.126: "\u{55" +//β”‚ β•‘ l.127: "\u{55" //β”‚ ╙── ^ //β”‚ = "U" :w "\u{41****" //β”‚ ╔══[WARNING] Unterminated Unicode escape sequence: missing '}' -//β”‚ β•‘ l.133: "\u{41****" +//β”‚ β•‘ l.134: "\u{41****" //β”‚ ╙── ^ //β”‚ = "A****" diff --git a/hkmc2/shared/src/test/mlscript/basics/LocalVal.mls b/hkmc2/shared/src/test/mlscript/basics/LocalVal.mls index 99fa0bac75..644d6a65ab 100644 --- a/hkmc2/shared/src/test/mlscript/basics/LocalVal.mls +++ b/hkmc2/shared/src/test/mlscript/basics/LocalVal.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls b/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls index b60b96b6cc..2af6a31173 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls @@ -1,3 +1,4 @@ +:. :js @@ -36,10 +37,10 @@ M.Foo ::n (foo, 2) :e M.Foo:: n(foo, 2) //β”‚ ╔══[COMPILATION ERROR] Name not found: :: -//β”‚ β•‘ l.37: M.Foo:: n(foo, 2) +//β”‚ β•‘ l.38: M.Foo:: n(foo, 2) //β”‚ ╙── ^^ //β”‚ ╔══[COMPILATION ERROR] Name not found: n -//β”‚ β•‘ l.37: M.Foo:: n(foo, 2) +//β”‚ β•‘ l.38: M.Foo:: n(foo, 2) //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -78,52 +79,57 @@ do :e foo.Foo#m() //β”‚ ╔══[COMPILATION ERROR] Name not found: Foo -//β”‚ β•‘ l.79: foo.Foo#m() +//β”‚ β•‘ l.80: foo.Foo#m() //β”‚ ╙── ^^^^ -//β”‚ ╔══[COMPILATION ERROR] Identifier `Foo` does not name a known class symbol. -//β”‚ β•‘ l.79: foo.Foo#m() +//β”‚ ╔══[COMPILATION ERROR] Cannot resolve the projection class +//β”‚ β•‘ l.80: foo.Foo#m() //β”‚ ╙── ^^^^ -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found β€Ήerrorβ€Ί. -//β”‚ β•‘ l.79: foo.Foo#m() -//β”‚ ╙── ^^^^ -//β”‚ = 124 +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e :re foo.M.Foo::m() -//β”‚ ╔══[COMPILATION ERROR] Selection is not a known class. -//β”‚ β•‘ l.93: foo.M.Foo::m() -//β”‚ β•‘ ^^^^^^^^^ -//β”‚ β•Ÿβ”€β”€ Note: any expression of the form `β€Ήexpressionβ€Ί::β€Ήidentifierβ€Ί` is a member projection; -//β”‚ ╙── add a space before β€Ήidentifierβ€Ί to make it an operator application. +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.91: foo.M.Foo::m() +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Function literal expected 1 argument, but got 0 +//β”‚ β•‘ l.91: foo.M.Foo::m() +//β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.91: foo.M.Foo::m() +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Foo' does not contain member 'M' +//β”‚ β•‘ l.11: let foo = M.Foo(123) +//β”‚ ╙── ^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Cannot resolve the projection class +//β”‚ β•‘ l.91: foo.M.Foo::m() +//β”‚ ╙── ^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: Function expected at least 1 argument but got 0 :e :re foo.M.Foo#m() //β”‚ ╔══[COMPILATION ERROR] Name not found: Foo -//β”‚ β•‘ l.103: foo.M.Foo#m() -//β”‚ ╙── ^^^^ -//β”‚ ╔══[COMPILATION ERROR] Identifier `Foo` does not name a known class symbol. -//β”‚ β•‘ l.103: foo.M.Foo#m() +//β”‚ β•‘ l.111: foo.M.Foo#m() //β”‚ ╙── ^^^^ -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found β€Ήerrorβ€Ί. -//β”‚ β•‘ l.103: foo.M.Foo#m() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.111: foo.M.Foo#m() +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Foo' does not contain member 'M' +//β”‚ β•‘ l.11: let foo = M.Foo(123) +//β”‚ ╙── ^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Cannot resolve the projection class +//β”‚ β•‘ l.111: foo.M.Foo#m() //β”‚ ╙── ^^^^ -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'M' yielded 'undefined' +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e Foo::m //β”‚ ╔══[COMPILATION ERROR] Name not found: Foo -//β”‚ β•‘ l.116: Foo::m +//β”‚ β•‘ l.127: Foo::m //β”‚ ╙── ^^^ -//β”‚ ╔══[COMPILATION ERROR] Identifier is not a known class. -//β”‚ β•‘ l.116: Foo::m -//β”‚ β•‘ ^^^ -//β”‚ β•Ÿβ”€β”€ Note: any expression of the form `β€Ήexpressionβ€Ί::β€Ήidentifierβ€Ί` is a member projection; -//β”‚ ╙── add a space before β€Ήidentifierβ€Ί to make it an operator application. -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found β€Ήerrorβ€Ί. -//β”‚ β•‘ l.116: Foo::m +//β”‚ ╔══[COMPILATION ERROR] Cannot resolve the projection class +//β”‚ β•‘ l.127: Foo::m //β”‚ ╙── ^^^ //β”‚ = fun @@ -132,23 +138,18 @@ Foo::m :noInline Foo::n(foo, 2) //β”‚ ╔══[COMPILATION ERROR] Name not found: Foo -//β”‚ β•‘ l.133: Foo::n(foo, 2) +//β”‚ β•‘ l.139: Foo::n(foo, 2) //β”‚ ╙── ^^^ -//β”‚ ╔══[COMPILATION ERROR] Identifier is not a known class. -//β”‚ β•‘ l.133: Foo::n(foo, 2) -//β”‚ β•‘ ^^^ -//β”‚ β•Ÿβ”€β”€ Note: any expression of the form `β€Ήexpressionβ€Ί::β€Ήidentifierβ€Ί` is a member projection; -//β”‚ ╙── add a space before β€Ήidentifierβ€Ί to make it an operator application. -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found β€Ήerrorβ€Ί. -//β”‚ β•‘ l.133: Foo::n(foo, 2) +//β”‚ ╔══[COMPILATION ERROR] Cannot resolve the projection class +//β”‚ β•‘ l.139: Foo::n(foo, 2) //β”‚ ╙── ^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let lambda2; //β”‚ lambda2 = (undefined, function (self7, ...args7) { -//β”‚ return runtime.safeCall(self7.n(...args7)) +//β”‚ throw "This code cannot be run as its compilation yielded an error." //β”‚ }); //β”‚ return runtime.safeCall(lambda2(foo, 2)) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ = 125 +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/basics/MiscArrayTests.mls b/hkmc2/shared/src/test/mlscript/basics/MiscArrayTests.mls index fb121754f0..e4c6aaded0 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MiscArrayTests.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MiscArrayTests.mls @@ -1,3 +1,4 @@ +:. :js @@ -26,8 +27,16 @@ f() xs.reverse() //β”‚ = [2, 1, 0] +:e :re xs.map(x => x * 2) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in function type +//β”‚ β•‘ l.260: fun map[U](f: (T, Int, Array[T]) -> U): Array[U] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Callback parameter list does not match its declared function type. +//β”‚ β•‘ in MiscArrayTests.mls:32 +//β”‚ β•‘ l.32: xs.map(x => x * 2) +//β”‚ ╙── ^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: Function expected 1 argument but got 3 xs.map((x, i, a) => x * 2) @@ -39,20 +48,23 @@ xs.map((x, ...) => x * 2) xs.map((x, ..._) => x * 2) //β”‚ = [4, 2, 0] -fun map(xs, f) = xs.map((x, ...bs) => f(x)) +fun map(xs: Array, f) = xs.map((x, ...bs) => f(x)) xs \map(x => x * 2) \print() //β”‚ > [4, 2, 0] -// * Note: the RHS of `\` is a self-contained block, thus implicitly parenthesized. +// * Note: the RHS of this `\` is a self-contained block, thus implicitly parenthesized. :sjs :e :re xs \ map(x => x * 2) -//β”‚ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 -//β”‚ β•‘ l.52: map(x => x * 2) -//β”‚ ╙── ^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.61: map(x => x * 2) +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'map' expected 2 arguments, but got 1 +//β”‚ β•‘ l.51: fun map(xs: Array, f) = xs.map((x, ...bs) => f(x)) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let lambda5, tmp; //β”‚ lambda5 = (undefined, function (x) { @@ -67,9 +79,12 @@ xs \ :re xs\ map(x => x * 2) -//β”‚ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 -//β”‚ β•‘ l.69: map(x => x * 2) -//β”‚ ╙── ^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.81: map(x => x * 2) +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'map' expected 2 arguments, but got 1 +//β”‚ β•‘ l.51: fun map(xs: Array, f) = xs.map((x, ...bs) => f(x)) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: Function 'map' expected 2 arguments but got 1 :pt diff --git a/hkmc2/shared/src/test/mlscript/basics/Modules.mls b/hkmc2/shared/src/test/mlscript/basics/Modules.mls index 1f45a66355..a20a58211d 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Modules.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Modules.mls @@ -1,3 +1,4 @@ +:. // :flow @@ -6,20 +7,23 @@ module Foo :pe module Foo with //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position; found end of input instead -//β”‚ β•‘ l.7: module Foo with +//β”‚ β•‘ l.8: module Foo with //β”‚ ╙── ^ :e module Foo with val x = 1 //β”‚ ╔══[COMPILATION ERROR] Illegal body of module definition (should be a block; found term definition). -//β”‚ β•‘ l.13: module Foo with val x = 1 +//β”‚ β•‘ l.14: module Foo with val x = 1 //β”‚ ╙── ^^^^^^^^^ :e Foo.x -//β”‚ ╔══[COMPILATION ERROR] Module 'Foo' does not contain member 'x' -//β”‚ β•‘ l.19: Foo.x -//β”‚ ╙── ^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.20: Foo.x +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Module 'Foo' does not contain member 'x' +//β”‚ β•‘ l.14: module Foo with val x = 1 +//β”‚ ╙── ^^^ module Foo with val x = 1 diff --git a/hkmc2/shared/src/test/mlscript/basics/MultiParamListClasses.mls b/hkmc2/shared/src/test/mlscript/basics/MultiParamListClasses.mls index fa8985b063..2d2384972e 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MultiParamListClasses.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MultiParamListClasses.mls @@ -85,16 +85,24 @@ Foo(1, 2) :sjs let f = Foo // should compile to `(x, y) => Foo(x, y)(use[Int])` //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ let f, f1; -//β”‚ f1 = function f(x, y) { -//β”‚ let tmp2; +//β”‚ let f2, f3; +//β”‚ f3 = function f(x1, y1) { +//β”‚ let x2, y2, z2, tmp2, lambda4, f4; +//β”‚ x2 = x1; +//β”‚ y2 = y1; +//β”‚ z2 = instance$Ident$_Int$_; //β”‚ tmp2 = new Foo03(); -//β”‚ tmp2.x = x; -//β”‚ tmp2.y = y; -//β”‚ tmp2.z = instance$Ident$_Int$_; +//β”‚ lambda4 = (undefined, function (res) { +//β”‚ res.x = x2; +//β”‚ res.y = y2; +//β”‚ res.z = z2; +//β”‚ return runtime.Unit +//β”‚ }); +//β”‚ f4 = lambda4; +//β”‚ runtime.safeCall(f4(tmp2)); //β”‚ return tmp2 //β”‚ }; -//β”‚ f = f1; +//β”‚ f2 = f3; //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ f = fun f diff --git a/hkmc2/shared/src/test/mlscript/basics/MultiParamLists.mls b/hkmc2/shared/src/test/mlscript/basics/MultiParamLists.mls index 7500c8d032..91d237d784 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MultiParamLists.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MultiParamLists.mls @@ -1,3 +1,4 @@ +:. :js :global diff --git a/hkmc2/shared/src/test/mlscript/basics/MultilineExpressions.mls b/hkmc2/shared/src/test/mlscript/basics/MultilineExpressions.mls index ca54d78305..daf393d2bd 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MultilineExpressions.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MultilineExpressions.mls @@ -1,3 +1,4 @@ +:. :js @@ -113,23 +114,23 @@ if 1 + 2 * 3 then 0 + 4 then 1 //β”‚ ╔══[PARSE ERROR] Operator cannot be used inside this operator split -//β”‚ β•‘ l.114: + 4 then 1 +//β”‚ β•‘ l.115: + 4 then 1 //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ as it has lower precedence than the splitting operator here -//β”‚ β•‘ l.113: * 3 then 0 +//β”‚ β•‘ l.114: * 3 then 0 //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Unexpected 'then' keyword in this operator split inner position -//β”‚ β•‘ l.114: + 4 then 1 +//β”‚ β•‘ l.115: + 4 then 1 //β”‚ β•‘ ^^^^ //β”‚ β•Ÿβ”€β”€ Note: the operator split starts here -//β”‚ β•‘ l.113: * 3 then 0 +//β”‚ β•‘ l.114: * 3 then 0 //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here -//β”‚ β•‘ l.112: if 1 + 2 +//β”‚ β•‘ l.113: if 1 + 2 //β”‚ β•‘ ^ -//β”‚ β•‘ l.113: * 3 then 0 +//β”‚ β•‘ l.114: * 3 then 0 //β”‚ β•‘ ^^^^^^^^^^^^ -//β”‚ β•‘ l.114: + 4 then 1 +//β”‚ β•‘ l.115: + 4 then 1 //β”‚ ╙── ^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/basics/MutArr.mls b/hkmc2/shared/src/test/mlscript/basics/MutArr.mls index ba4cec87cb..6e861bdeb7 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MutArr.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MutArr.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/basics/MutCls.mls b/hkmc2/shared/src/test/mlscript/basics/MutCls.mls index 44f19580c0..2963822636 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MutCls.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MutCls.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/basics/MutRcd.mls b/hkmc2/shared/src/test/mlscript/basics/MutRcd.mls index 2334819a1c..c5ae4b41f5 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MutRcd.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MutRcd.mls @@ -1,7 +1,9 @@ +:. :js -let r = {a: 0} +// Dynamic receivers keep property additions and missing-field checks observable at runtime. +let r = ({a: 0} as dyn) //β”‚ r = {a: 0} set @@ -17,7 +19,7 @@ r.foo //β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'foo' yielded 'undefined' -let r = mut {a: 1} +let r = mut {a: 1} as dyn //β”‚ r = {a: 1} set @@ -33,7 +35,7 @@ r.foo //β”‚ = 1 -let r = mut {} +let r = mut {} as dyn //β”‚ r = {} set r.foo = 1 @@ -44,7 +46,7 @@ r.foo :sjs -let r = {} +let r = ({} as dyn) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let r3; r3 = runtime.Unit; //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” @@ -61,10 +63,10 @@ r.foo //β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'foo' yielded 'undefined' :re -().foo +(() as dyn).foo //β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'foo' yielded 'undefined' -let s = {} +let s = ({} as dyn) //β”‚ s = () set s.foo = 2 @@ -77,7 +79,7 @@ r.foo :e mut() //β”‚ ╔══[COMPILATION ERROR] Illegal position for 'mut' modifier. -//β”‚ β•‘ l.78: mut() +//β”‚ β•‘ l.80: mut() //β”‚ ╙── ^^^ diff --git a/hkmc2/shared/src/test/mlscript/basics/MutVal.mls b/hkmc2/shared/src/test/mlscript/basics/MutVal.mls index c5eee2532a..37b7458ef7 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MutVal.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MutVal.mls @@ -1,3 +1,4 @@ +:. :js @@ -51,7 +52,8 @@ class Foo(x, val y, mut val z) with //β”‚ val yΒ²; //β”‚ val zΒ²; //β”‚ constructor Foo⁴(x, y, z) { -//β”‚ set Foo⁡.this.x⁰ = x; +//β”‚ @private +//β”‚ define xΒΉ as val x⁰ = x; //β”‚ define yΒ² as val yΒ³ = y; //β”‚ define zΒ² as val zΒ³ = z; //β”‚ do Predef⁰.print⁰(Foo⁡.this.x⁰); diff --git a/hkmc2/shared/src/test/mlscript/basics/NamedArgs.mls b/hkmc2/shared/src/test/mlscript/basics/NamedArgs.mls index ce10cfd96a..3c21e07c90 100644 --- a/hkmc2/shared/src/test/mlscript/basics/NamedArgs.mls +++ b/hkmc2/shared/src/test/mlscript/basics/NamedArgs.mls @@ -1,3 +1,4 @@ +:. :js @@ -34,9 +35,9 @@ id(a: 0) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Elaborated tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Blk: //β”‚ res = App: -//β”‚ lhs = SynthSel{sym=member:id⁰}: -//β”‚ prefix = Ref{sym=member:Predef⁰} of member:Predef⁰ -//β”‚ nme = Ident of "id" +//β”‚ lhs = NewSel: +//β”‚ prefix = MemberRef of member:Predef⁰ +//β”‚ id = Ident of "id" //β”‚ rhs = Tup of Ls of //β”‚ Fld: //β”‚ term = Lit of StrLit of "a" diff --git a/hkmc2/shared/src/test/mlscript/basics/NestedBlocks.mls b/hkmc2/shared/src/test/mlscript/basics/NestedBlocks.mls index e26a4780cb..ee9c6b358a 100644 --- a/hkmc2/shared/src/test/mlscript/basics/NestedBlocks.mls +++ b/hkmc2/shared/src/test/mlscript/basics/NestedBlocks.mls @@ -1,3 +1,4 @@ +:. :js @@ -10,10 +11,13 @@ object Foo with :e :re Foo.x -//β”‚ ╔══[COMPILATION ERROR] Object 'Foo' does not contain member 'x' -//β”‚ β•‘ l.12: Foo.x -//β”‚ ╙── ^^ -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.13: Foo.x +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Object 'Foo' does not contain member 'x' +//β”‚ β•‘ l.7: object Foo with +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. module Oops with @@ -29,18 +33,32 @@ object Oops with val fakeField = 1 print(fakeField) print(this.fakeField) // shouldn't work! -//β”‚ ╔══[COMPILATION ERROR] Object 'Oops' does not contain member 'fakeField' -//β”‚ β•‘ l.31: print(this.fakeField) // shouldn't work! -//β”‚ ╙── ^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.35: print(this.fakeField) // shouldn't work! +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Module/object definition 'Oops' does not contain member 'fakeField' +//β”‚ β•‘ l.31: object Oops with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.32: if true then +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•‘ l.33: val fakeField = 1 +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.34: print(fakeField) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.35: ... (more lines omitted) ... +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ > 1 -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'fakeField' yielded 'undefined' +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e :re Oops.fakeField -//β”‚ ╔══[COMPILATION ERROR] Object 'Oops' does not contain member 'fakeField' -//β”‚ β•‘ l.40: Oops.fakeField -//β”‚ ╙── ^^^^^^^^^^ -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'fakeField' yielded 'undefined' +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.55: Oops.fakeField +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Object 'Oops' does not contain member 'fakeField' +//β”‚ β•‘ l.31: object Oops with +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/basics/New.mls b/hkmc2/shared/src/test/mlscript/basics/New.mls index 68fffaef3f..fd2a085f0b 100644 --- a/hkmc2/shared/src/test/mlscript/basics/New.mls +++ b/hkmc2/shared/src/test/mlscript/basics/New.mls @@ -1,3 +1,4 @@ +:. :js @@ -14,59 +15,73 @@ new //β”‚ = Foo :e -:w new Foo Foo -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found block. -//β”‚ β•‘ l.20: Foo -//β”‚ β•‘ ^^^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//β”‚ ╔══[WARNING] Pure expression in statement position +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.18: new +//β”‚ β•‘ ^^^ //β”‚ β•‘ l.19: Foo +//β”‚ β•‘ ^^^^^ +//β”‚ β•‘ l.20: Foo +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Invalid class expression: block +//β”‚ β•‘ l.20: Foo //β”‚ ╙── ^^^ -//β”‚ = Foo +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // * Interesting... it tries to call `Foo()` and instantiate the resulting class value... :e :re new Foo() -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found application. -//β”‚ β•‘ l.34: Foo() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.37: Foo() //β”‚ β•‘ ^^^^^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//β”‚ ═══[RUNTIME ERROR] TypeError: Class constructor Foo cannot be invoked without 'new' +//β”‚ β•Ÿβ”€β”€ Class 'Foo' must be instantiated with 'new'. +//β”‚ β•‘ l.5: class Foo +//β”‚ ╙── ^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.36: new +//β”‚ β•‘ ^^^ +//β”‚ β•‘ l.37: Foo() +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Invalid class expression: application +//β”‚ β•‘ l.37: Foo() +//β”‚ ╙── ^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e new Foo () //β”‚ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (unit). -//β”‚ β•‘ l.44: () +//β”‚ β•‘ l.57: () //β”‚ ╙── ^^ //β”‚ = Foo :e -:w :re new Foo () -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found block. -//β”‚ β•‘ l.55: () -//β”‚ β•‘ ^^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.54: Foo -//β”‚ ╙── ^^^ -//β”‚ ═══[RUNTIME ERROR] TypeError: runtime.Unit is not a constructor +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.65: new +//β”‚ β•‘ ^^^ +//β”‚ β•‘ l.66: Foo +//β”‚ β•‘ ^^^^^ +//β”‚ β•‘ l.67: () +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Invalid class expression: block +//β”‚ β•‘ l.67: () +//β”‚ ╙── ^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e new Foo () //β”‚ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (unit). -//β”‚ β•‘ l.67: () +//β”‚ β•‘ l.82: () //β”‚ ╙── ^^ //β”‚ = Foo @@ -79,10 +94,24 @@ data class Foo(a,b,c) new Foo(...xs) //β”‚ = Foo(1, 2, 3) +:e new Foo(...[1, 2]) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.98: new Foo(...[1, 2]) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Foo' expected 3 arguments, but got 2 +//β”‚ β•‘ l.92: data class Foo(a,b,c) +//β”‚ ╙── ^^^^^^^^^^^^^^^^ //β”‚ = Foo(1, 2, undefined) +:e new Foo(...[1, 2, 3, 4]) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.108: new Foo(...[1, 2, 3, 4]) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Foo' expected 3 arguments, but got 4 +//β”‚ β•‘ l.92: data class Foo(a,b,c) +//β”‚ ╙── ^^^^^^^^^^^^^^^^ //β”‚ = Foo(1, 2, 3) diff --git a/hkmc2/shared/src/test/mlscript/basics/NewMut.mls b/hkmc2/shared/src/test/mlscript/basics/NewMut.mls index dc7e4df11a..ec90c06401 100644 --- a/hkmc2/shared/src/test/mlscript/basics/NewMut.mls +++ b/hkmc2/shared/src/test/mlscript/basics/NewMut.mls @@ -1,3 +1,4 @@ +:. :js @@ -7,8 +8,9 @@ class Foo with set x += 1 +// Dynamic receivers allow these tests to add fields while retaining runtime checks. :sjs -let f = new Foo +let f = new Foo as dyn //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let f; f = globalThis.Object.freeze(new Foo1()); //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” @@ -37,7 +39,7 @@ f.y :sjs // :elt -let f = new mut Foo +let f = new mut Foo as dyn //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let f1; f1 = new Foo1(); //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” @@ -61,21 +63,23 @@ f.y :e let f = mut new Foo //β”‚ ╔══[COMPILATION ERROR] Illegal position for 'mut' modifier. -//β”‚ β•‘ l.62: let f = mut new Foo +//β”‚ β•‘ l.64: let f = mut new Foo //β”‚ ╙── ^^^ //β”‚ f = Foo { x: 0 } :e :re new mut Foo.x -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found selection. -//β”‚ β•‘ l.70: new mut Foo.x +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.72: new mut Foo.x //β”‚ β•‘ ^^^^^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' +//β”‚ β•Ÿβ”€β”€ Class value does not contain member 'x' +//β”‚ β•‘ l.5: class Foo with +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. -let f = new mut Foo() +let f = new mut Foo() as dyn //β”‚ f = Foo { x: 0 } set @@ -124,7 +128,7 @@ f.x //β”‚ = 1 -let f = Foo(0) +let f = Foo(0) as dyn //β”‚ f = Foo(0) set @@ -164,8 +168,8 @@ f.y -let f = new Object with - val x = 12 +let f = (new Object with + val x = 12) as dyn //β”‚ f = $anon { x: 12 } set @@ -180,8 +184,8 @@ f.y //β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'y' yielded 'undefined' -let f = new mut Object with - val x = 12 +let f = (new mut Object with + val x = 12) as dyn //β”‚ f = $anon { x: 12 } set diff --git a/hkmc2/shared/src/test/mlscript/basics/NewlineOperatorEnd.mls b/hkmc2/shared/src/test/mlscript/basics/NewlineOperatorEnd.mls index 9d3001c0b7..0ec57cfeea 100644 --- a/hkmc2/shared/src/test/mlscript/basics/NewlineOperatorEnd.mls +++ b/hkmc2/shared/src/test/mlscript/basics/NewlineOperatorEnd.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/basics/NewlineOps.mls b/hkmc2/shared/src/test/mlscript/basics/NewlineOps.mls index 560f978d59..5d7ca1b208 100644 --- a/hkmc2/shared/src/test/mlscript/basics/NewlineOps.mls +++ b/hkmc2/shared/src/test/mlscript/basics/NewlineOps.mls @@ -1,3 +1,4 @@ +:. :js @@ -57,7 +58,7 @@ let xs = [1, 2, 3] let ys = [4, 5, 6] -fun len(xs) = xs.length +fun len(xs: Array) = xs.length //β”‚ xs = [1, 2, 3] //β”‚ ys = [4, 5, 6] @@ -84,7 +85,7 @@ let r = 10 in r //β”‚ rhs = Tup of Ls of //β”‚ IntLit of 1 //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.74: let r = 10 in r +//β”‚ β•‘ l.75: let r = 10 in r //β”‚ ╙── ^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = -1 @@ -160,11 +161,20 @@ let x .a //β”‚ = 2 +:e :re 2 |> (a: _) .a -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'a' yielded 'undefined' +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.167: |> (a: _) +//β”‚ β•‘ ^^^^^^ +//β”‚ β•‘ l.168: .a +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function literal does not contain member 'a' +//β”‚ β•‘ l.167: |> (a: _) +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :ge :re @@ -172,7 +182,7 @@ let x |> (a: _) .a //β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (leading dot selection) -//β”‚ β•‘ l.173: .a +//β”‚ β•‘ l.183: .a //β”‚ ╙── ^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -319,10 +329,10 @@ let incr = _ + 1 :w 2, * 3 //β”‚ ╔══[PARSE ERROR] Expected end of input; found literal instead -//β”‚ β•‘ l.320: 2, * 3 +//β”‚ β•‘ l.330: 2, * 3 //β”‚ ╙── ^ //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.320: 2, * 3 +//β”‚ β•‘ l.330: 2, * 3 //β”‚ ╙── ^ //β”‚ = fun @@ -346,7 +356,7 @@ let incr = _ + 1 //β”‚ = 16 module Arr with - val len = _.length + val len = (xs: Array) => xs.length let xs = [1, 2] @@ -376,9 +386,9 @@ xs |> Arr.len ,+ ys |> Arr.len //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position; found new line instead -//β”‚ β•‘ l.376: ,+ +//β”‚ β•‘ l.386: ,+ //β”‚ β•‘ ^ -//β”‚ β•‘ l.377: ys |> Arr.len +//β”‚ β•‘ l.387: ys |> Arr.len //β”‚ ╙── //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -392,9 +402,9 @@ xs |> Arr.len +, ys |> Arr.len //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position; found new line instead -//β”‚ β•‘ l.392: +, +//β”‚ β•‘ l.402: +, //β”‚ β•‘ ^ -//β”‚ β•‘ l.393: ys |> Arr.len +//β”‚ β•‘ l.403: ys |> Arr.len //β”‚ ╙── //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -425,10 +435,10 @@ new Foo(1) is Foo :fixme // ? new Foo(1) ,is Foo //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position; found 'is' keyword instead -//β”‚ β•‘ l.426: new Foo(1) ,is Foo +//β”‚ β•‘ l.436: new Foo(1) ,is Foo //β”‚ ╙── ^^ //β”‚ ╔══[PARSE ERROR] Expected end of input; found identifier instead -//β”‚ β•‘ l.426: new Foo(1) ,is Foo +//β”‚ β•‘ l.436: new Foo(1) ,is Foo //β”‚ ╙── ^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -436,10 +446,10 @@ new Foo(1) ,is Foo new Foo(1) is Foo //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position; found 'is' keyword instead -//β”‚ β•‘ l.437: is Foo +//β”‚ β•‘ l.447: is Foo //β”‚ ╙── ^^ //β”‚ ╔══[PARSE ERROR] Expected end of input; found identifier instead -//β”‚ β•‘ l.437: is Foo +//β”‚ β•‘ l.447: is Foo //β”‚ ╙── ^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/basics/NewlineSels.mls b/hkmc2/shared/src/test/mlscript/basics/NewlineSels.mls index e523778f47..b66f2e5a88 100644 --- a/hkmc2/shared/src/test/mlscript/basics/NewlineSels.mls +++ b/hkmc2/shared/src/test/mlscript/basics/NewlineSels.mls @@ -1,3 +1,4 @@ +:. :js @@ -39,10 +40,10 @@ rcd rcd .a //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.39: rcd +//β”‚ β•‘ l.40: rcd //β”‚ ╙── ^^^ //β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (leading dot selection) -//β”‚ β•‘ l.40: .a +//β”‚ β•‘ l.41: .a //β”‚ ╙── ^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -55,7 +56,7 @@ let rcd = let rcd = a: x => {b: y => c: x + y} //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position; found end of input instead -//β”‚ β•‘ l.56: a: x => {b: y => c: x + y} +//β”‚ β•‘ l.57: a: x => {b: y => c: x + y} //β”‚ ╙── ^ //β”‚ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Unexpected effectful in subterm position: Effectful(InfixApp(Ident(b),Keywrd(keyword ':'),InfixApp(Tup(List(Ident(y))),Keywrd(keyword '=>'),InfixApp(Ident(c),Keywrd(keyword ':'),OpApp(Ident(x),Ident(+),List(Ident(y)))))),Error()) diff --git a/hkmc2/shared/src/test/mlscript/basics/ObjectExtensions.mls b/hkmc2/shared/src/test/mlscript/basics/ObjectExtensions.mls index 2008e7c484..b63a031826 100644 --- a/hkmc2/shared/src/test/mlscript/basics/ObjectExtensions.mls +++ b/hkmc2/shared/src/test/mlscript/basics/ObjectExtensions.mls @@ -1,3 +1,4 @@ +:. :js @@ -5,11 +6,13 @@ object Foo :fixme // Support? new Foo -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found reference of type Foo. -//β”‚ β•‘ l.7: new Foo -//β”‚ β•‘ ^^^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//β”‚ ═══[RUNTIME ERROR] TypeError: Foo1 is not a constructor +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.8: new Foo +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Object 'Foo' cannot be instantiated with keyword 'new'. +//β”‚ β•‘ l.5: object Foo +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :re new! Foo @@ -17,37 +20,46 @@ new! Foo :fixme // Support? new! Foo.class -//β”‚ ╔══[COMPILATION ERROR] Object 'Foo' does not contain member 'class' -//β”‚ β•‘ l.19: new! Foo.class -//β”‚ ╙── ^^^^^^ -//β”‚ = Foo +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.22: new! Foo.class +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Object 'Foo' does not contain member 'class' +//β”‚ β•‘ l.5: object Foo +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. new! Foo!class //β”‚ = Foo :fixme // Support? object Bar extends Foo -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found reference of type Foo. -//β”‚ β•‘ l.29: object Bar extends Foo +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.35: object Bar extends Foo //β”‚ β•‘ ^^^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//β”‚ ═══[RUNTIME ERROR] TypeError: Class extends value [object Object] is not a constructor or null +//β”‚ β•Ÿβ”€β”€ Object 'Foo' cannot be instantiated with keyword 'new'. +//β”‚ β•‘ l.5: object Foo +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e :re object Bar extends Foo!class -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found dynamic selection. -//β”‚ β•‘ l.38: object Bar extends Foo!class +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.46: object Bar extends Foo!class //β”‚ β•‘ ^^^^^^^^^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//β”‚ ═══[RUNTIME ERROR] TypeError: Cannot redefine property: class +//β”‚ β•Ÿβ”€β”€ Invalid class expression: dynamic selection +//β”‚ β•‘ l.46: object Bar extends Foo!class +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :fixme // Support? object Bar extends! Foo!class -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found reference. -//β”‚ β•‘ l.46: object Bar extends! Foo!class -//β”‚ β•‘ ^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//β”‚ ═══[COMPILATION ERROR] Too many parameter lists for parent class +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.56: object Bar extends! Foo!class +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Invalid class expression: reference +//β”‚ β•‘ l.56: object Bar extends! Foo!class +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/basics/OfLambdaArgs.mls b/hkmc2/shared/src/test/mlscript/basics/OfLambdaArgs.mls index 1c234ac025..f771e6303c 100644 --- a/hkmc2/shared/src/test/mlscript/basics/OfLambdaArgs.mls +++ b/hkmc2/shared/src/test/mlscript/basics/OfLambdaArgs.mls @@ -1,3 +1,4 @@ +:. :js @@ -14,12 +15,18 @@ foo of (id of x => 1), id of y => 2 :e :re foo(id of x => 1, id of y => 2) -//β”‚ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 -//β”‚ β•‘ l.16: foo(id of x => 1, id of y => 2) -//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Expected 1 arguments, got 2 -//β”‚ β•‘ l.16: foo(id of x => 1, id of y => 2) -//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.17: foo(id of x => 1, id of y => 2) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'id' expected 1 argument, but got 2 +//β”‚ β•‘ l.5: fun id(x) = x +//β”‚ ╙── ^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.17: foo(id of x => 1, id of y => 2) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'foo' expected 2 arguments, but got 1 +//β”‚ β•‘ l.6: fun foo(f, g) = f(g(1)) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: Function 'id' expected 1 argument but got 2 diff --git a/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls b/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls index 946c3d33e4..fad13cd63a 100644 --- a/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls +++ b/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls @@ -1,3 +1,4 @@ +:. :js @@ -95,12 +96,12 @@ + 1 * 3 //β”‚ ╔══[PARSE ERROR] Unexpected indented block in this operator split inner position -//β”‚ β•‘ l.95: + 1 +//β”‚ β•‘ l.96: + 1 //β”‚ β•‘ ^^^^^^^ -//β”‚ β•‘ l.96: * 3 +//β”‚ β•‘ l.97: * 3 //β”‚ β•‘ ^^ //β”‚ β•Ÿβ”€β”€ Note: the operator split starts here -//β”‚ β•‘ l.94: * 2 +//β”‚ β•‘ l.95: * 2 //β”‚ ╙── ^ //β”‚ = 18 @@ -110,10 +111,10 @@ 1 * 3 //β”‚ ╔══[PARSE ERROR] Operator cannot be used inside this operator split -//β”‚ β•‘ l.109: * 2 + +//β”‚ β•‘ l.110: * 2 + //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ as it has lower precedence than the splitting operator here -//β”‚ β•‘ l.109: * 2 + +//β”‚ β•‘ l.110: * 2 + //β”‚ ╙── ^ //β”‚ = 19 @@ -123,10 +124,10 @@ * 3 + 1 //β”‚ ╔══[PARSE ERROR] Unexpected indented block in this operator split inner position -//β”‚ β•‘ l.124: + 1 +//β”‚ β•‘ l.125: + 1 //β”‚ β•‘ ^^^^ //β”‚ β•Ÿβ”€β”€ Note: the operator split starts here -//β”‚ β•‘ l.122: * 2 +//β”‚ β•‘ l.123: * 2 //β”‚ ╙── ^ //β”‚ = 14 @@ -144,10 +145,10 @@ * 3 + 1 * 3 //β”‚ ╔══[PARSE ERROR] Operator cannot be used inside this operator split -//β”‚ β•‘ l.144: * 3 + 1 +//β”‚ β•‘ l.145: * 3 + 1 //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ as it has lower precedence than the splitting operator here -//β”‚ β•‘ l.144: * 3 + 1 +//β”‚ β•‘ l.145: * 3 + 1 //β”‚ ╙── ^ //β”‚ = 28 @@ -174,7 +175,7 @@ 1 0 //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.174: 1 +//β”‚ β•‘ l.175: 1 //β”‚ ╙── ^ //β”‚ = true @@ -369,9 +370,9 @@ fun f(x) = if x foo(A) then a bar(B) then b //β”‚ ╔══[COMPILATION ERROR] Unrecognized term split (juxtaposition) -//β”‚ β•‘ l.368: fun f(x) = if x +//β”‚ β•‘ l.369: fun f(x) = if x //β”‚ β•‘ ^ -//β”‚ β•‘ l.369: foo(A) then a +//β”‚ β•‘ l.370: foo(A) then a //β”‚ ╙── ^^ diff --git a/hkmc2/shared/src/test/mlscript/basics/OpDivPrecedence.mls b/hkmc2/shared/src/test/mlscript/basics/OpDivPrecedence.mls index 19e9edfaa1..cc9b798817 100644 --- a/hkmc2/shared/src/test/mlscript/basics/OpDivPrecedence.mls +++ b/hkmc2/shared/src/test/mlscript/basics/OpDivPrecedence.mls @@ -1,3 +1,4 @@ +:. :js // Division should have the same precedence as multiplication diff --git a/hkmc2/shared/src/test/mlscript/basics/OutParams.mls b/hkmc2/shared/src/test/mlscript/basics/OutParams.mls index 131ee8d754..63ed811ee9 100644 --- a/hkmc2/shared/src/test/mlscript/basics/OutParams.mls +++ b/hkmc2/shared/src/test/mlscript/basics/OutParams.mls @@ -1,3 +1,4 @@ +:. :js :todo @@ -15,22 +16,22 @@ fun typeTerm(_, set ctx) = print(ctx) ctx = 123 //β”‚ ╔══[COMPILATION ERROR] Expected a valid parameter, found set -//β”‚ β•‘ l.14: fun typeTerm(_, set ctx) = +//β”‚ β•‘ l.15: fun typeTerm(_, set ctx) = //β”‚ ╙── ^^^ //β”‚ ╔══[COMPILATION ERROR] Name not found: ctx -//β”‚ β•‘ l.15: print(ctx) +//β”‚ β•‘ l.16: print(ctx) //β”‚ ╙── ^^^ //β”‚ ╔══[COMPILATION ERROR] Name not found: ctx -//β”‚ β•‘ l.16: ctx = 123 +//β”‚ β•‘ l.17: ctx = 123 //β”‚ ╙── ^^^ fun typeTerm(_, let ctx) = ctx = 123 //β”‚ ╔══[COMPILATION ERROR] Expected a valid parameter, found let -//β”‚ β•‘ l.27: fun typeTerm(_, let ctx) = +//β”‚ β•‘ l.28: fun typeTerm(_, let ctx) = //β”‚ ╙── ^^^ //β”‚ ╔══[COMPILATION ERROR] Name not found: ctx -//β”‚ β•‘ l.28: ctx = 123 +//β”‚ β•‘ l.29: ctx = 123 //β”‚ ╙── ^^^ diff --git a/hkmc2/shared/src/test/mlscript/basics/Overloading.mls b/hkmc2/shared/src/test/mlscript/basics/Overloading.mls index 2cc8de7505..2e054ba1f0 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Overloading.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Overloading.mls @@ -1,18 +1,12 @@ +:. :js :global // :d -:todo fun Foo() = 1 module Foo with { val x = 1 } -//β”‚ ╔══[COMPILATION ERROR] Not yet supported: overloading of function 'Foo' -//β”‚ β•‘ l.8: fun Foo() = 1 -//β”‚ β•‘ ^^^^^^^^^^^^^ -//β”‚ β•Ÿβ”€β”€ with module of the same name -//β”‚ β•‘ l.9: module Foo with { val x = 1 } -//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Foo //β”‚ = class Foo { x: 1 } diff --git a/hkmc2/shared/src/test/mlscript/basics/PartialApps.mls b/hkmc2/shared/src/test/mlscript/basics/PartialApps.mls index 704b703943..781bb4d878 100644 --- a/hkmc2/shared/src/test/mlscript/basics/PartialApps.mls +++ b/hkmc2/shared/src/test/mlscript/basics/PartialApps.mls @@ -1,3 +1,4 @@ +:. :js @@ -5,10 +6,10 @@ :re . //β”‚ ╔══[PARSE ERROR] Expected an expression; found period instead -//β”‚ β•‘ l.6: . +//β”‚ β•‘ l.7: . //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Expected end of input; found period instead -//β”‚ β•‘ l.6: . +//β”‚ β•‘ l.7: . //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -16,10 +17,10 @@ :re . // //β”‚ ╔══[PARSE ERROR] Expected an expression; found period instead -//β”‚ β•‘ l.17: . // +//β”‚ β•‘ l.18: . // //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Expected end of input; found period instead -//β”‚ β•‘ l.17: . // +//β”‚ β•‘ l.18: . // //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -27,10 +28,10 @@ :fixme id(.) //β”‚ ╔══[PARSE ERROR] Expected an expression; found period instead -//β”‚ β•‘ l.28: id(.) +//β”‚ β•‘ l.29: id(.) //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Unexpected period here -//β”‚ β•‘ l.28: id(.) +//β”‚ β•‘ l.29: id(.) //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -52,10 +53,10 @@ passTo(2, incr @ _)() :fixme passTo(2, incr(.) @ _)() //β”‚ ╔══[PARSE ERROR] Expected an expression; found period instead -//β”‚ β•‘ l.53: passTo(2, incr(.) @ _)() +//β”‚ β•‘ l.54: passTo(2, incr(.) @ _)() //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Unexpected period here -//β”‚ β•‘ l.53: passTo(2, incr(.) @ _)() +//β”‚ β•‘ l.54: passTo(2, incr(.) @ _)() //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -70,11 +71,17 @@ passTo(2, add(_, 2))() :fixme passTo(2, add(., 2))() //β”‚ ╔══[PARSE ERROR] Expected an expression; found period instead -//β”‚ β•‘ l.71: passTo(2, add(., 2))() +//β”‚ β•‘ l.72: passTo(2, add(., 2))() //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Unexpected period here -//β”‚ β•‘ l.71: passTo(2, add(., 2))() +//β”‚ β•‘ l.72: passTo(2, add(., 2))() //β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.72: passTo(2, add(., 2))() +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function literal expected 2 arguments, but got 1 +//β”‚ β•‘ l.40: fun add = _ + _ +//β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :breakme @@ -91,21 +98,27 @@ passTo(1, (add(_, 1)) @ _ + _)(2) :fixme passTo(1, incr(.) @ _ + _)(2) //β”‚ ╔══[PARSE ERROR] Expected an expression; found period instead -//β”‚ β•‘ l.92: passTo(1, incr(.) @ _ + _)(2) +//β”‚ β•‘ l.99: passTo(1, incr(.) @ _ + _)(2) //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Unexpected period here -//β”‚ β•‘ l.92: passTo(1, incr(.) @ _ + _)(2) +//β”‚ β•‘ l.99: passTo(1, incr(.) @ _ + _)(2) //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :fixme passTo(1, add(., 1) @ _ + _)(2) //β”‚ ╔══[PARSE ERROR] Expected an expression; found period instead -//β”‚ β•‘ l.102: passTo(1, add(., 1) @ _ + _)(2) +//β”‚ β•‘ l.109: passTo(1, add(., 1) @ _ + _)(2) //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Unexpected period here -//β”‚ β•‘ l.102: passTo(1, add(., 1) @ _ + _)(2) +//β”‚ β•‘ l.109: passTo(1, add(., 1) @ _ + _)(2) //β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.109: passTo(1, add(., 1) @ _ + _)(2) +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function literal expected 2 arguments, but got 1 +//β”‚ β•‘ l.40: fun add = _ + _ +//β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -120,11 +133,17 @@ let f = add(_, 1) :fixme let f = add(., 1) //β”‚ ╔══[PARSE ERROR] Expected an expression; found period instead -//β”‚ β•‘ l.121: let f = add(., 1) +//β”‚ β•‘ l.134: let f = add(., 1) //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Unexpected period here -//β”‚ β•‘ l.121: let f = add(., 1) +//β”‚ β•‘ l.134: let f = add(., 1) //β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.134: let f = add(., 1) +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function literal expected 2 arguments, but got 1 +//β”‚ β•‘ l.40: fun add = _ + _ +//β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //β”‚ f = undefined diff --git a/hkmc2/shared/src/test/mlscript/basics/PostfixOps.mls b/hkmc2/shared/src/test/mlscript/basics/PostfixOps.mls index 2523548438..08d42a8c2b 100644 --- a/hkmc2/shared/src/test/mlscript/basics/PostfixOps.mls +++ b/hkmc2/shared/src/test/mlscript/basics/PostfixOps.mls @@ -1,3 +1,4 @@ +:. :js @@ -6,10 +7,10 @@ :re 0 | //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position; found end of input instead -//β”‚ β•‘ l.7: 0 | +//β”‚ β•‘ l.8: 0 | //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (alternation) -//β”‚ β•‘ l.7: 0 | +//β”‚ β•‘ l.8: 0 | //β”‚ ╙── ^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -20,14 +21,14 @@ 0 | 1 //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position; found new line instead -//β”‚ β•‘ l.20: 0 | +//β”‚ β•‘ l.21: 0 | //β”‚ β•‘ ^ -//β”‚ β•‘ l.21: 1 +//β”‚ β•‘ l.22: 1 //β”‚ ╙── //β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (alternation) -//β”‚ β•‘ l.20: 0 | +//β”‚ β•‘ l.21: 0 | //β”‚ β•‘ ^^^ -//β”‚ β•‘ l.21: 1 +//β”‚ β•‘ l.22: 1 //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -36,14 +37,18 @@ fun inc(x) = x + 1 :todo // ? inc of 1 | + 1 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in alternation +//β”‚ β•‘ l.39: inc of 1 | + 1 +//β”‚ β•‘ ^^^^^^^ +//β”‚ ╙── A type cannot be used as a runtime value. //β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (alternation) -//β”‚ β•‘ l.38: inc of 1 | + 1 +//β”‚ β•‘ l.39: inc of 1 | + 1 //β”‚ ╙── ^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :todo // ? 1 |> inc | + 1 //β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (alternation) -//β”‚ β•‘ l.45: 1 |> inc | + 1 +//β”‚ β•‘ l.50: 1 |> inc | + 1 //β”‚ ╙── ^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/basics/Puns.mls b/hkmc2/shared/src/test/mlscript/basics/Puns.mls index b379d09c0d..ba5eececc2 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Puns.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Puns.mls @@ -1,3 +1,4 @@ +:. :js @@ -47,7 +48,7 @@ if r is if r is a: x then x //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern split (infix operator ':'). -//β”‚ β•‘ l.48: a: x then x +//β”‚ β•‘ l.49: a: x then x //β”‚ ╙── ^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -89,7 +90,7 @@ foo(:a) :todo // would mean `val a = β€Ήthe old aβ€Ί`? val =a //β”‚ ╔══[PARSE ERROR] Expected expression after 'val' binding keyword; found '=' keyword instead -//β”‚ β•‘ l.90: val =a +//β”‚ β•‘ l.91: val =a //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -97,7 +98,7 @@ val =a :re let =a //β”‚ ╔══[PARSE ERROR] Expected expression after 'let' binding keyword; found '=' keyword instead -//β”‚ β•‘ l.98: let =a +//β”‚ β•‘ l.99: let =a //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/basics/PureTermStatements.mls b/hkmc2/shared/src/test/mlscript/basics/PureTermStatements.mls index 35009b2c8b..d4341556d7 100644 --- a/hkmc2/shared/src/test/mlscript/basics/PureTermStatements.mls +++ b/hkmc2/shared/src/test/mlscript/basics/PureTermStatements.mls @@ -1,22 +1,23 @@ +:. :js :w do 1 //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.5: do 1 +//β”‚ β•‘ l.6: do 1 //β”‚ ╙── ^ :w if true do 1 //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.11: if true do 1 +//β”‚ β•‘ l.12: if true do 1 //β”‚ ╙── ^ :w if true do () else 1 //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.17: if true do () else 1 +//β”‚ β•‘ l.18: if true do () else 1 //β”‚ ╙── ^ if true then () else 1 @@ -25,7 +26,7 @@ if true then () else 1 :w x => x, 0 //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.26: x => x, 0 +//β”‚ β•‘ l.27: x => x, 0 //β”‚ ╙── ^^^^^^ //β”‚ = 0 @@ -33,14 +34,14 @@ x => x, 0 () => 1 2 //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.33: () => 1 +//β”‚ β•‘ l.34: () => 1 //β”‚ ╙── ^^^^^^^ //β”‚ = 2 :w case x then x, 0 //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.41: case x then x, 0 +//β”‚ β•‘ l.42: case x then x, 0 //β”‚ ╙── ^^^^^^^^^^^^^ //β”‚ = 0 @@ -48,7 +49,7 @@ case x then x, 0 case x then x case x then x //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.48: case x then x +//β”‚ β•‘ l.49: case x then x //β”‚ ╙── ^^^^^^^^^^^^^ //β”‚ = fun @@ -58,7 +59,7 @@ fun foo(x) = x x //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.58: x +//β”‚ β•‘ l.59: x //β”‚ ╙── ^ :w @@ -67,7 +68,7 @@ fun foo() = x x //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.67: x +//β”‚ β•‘ l.68: x //β”‚ ╙── ^ :w @@ -76,7 +77,7 @@ fun foo() = x x //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.76: x +//β”‚ β•‘ l.77: x //β”‚ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/basics/Return.mls b/hkmc2/shared/src/test/mlscript/basics/Return.mls index 1f25cac6ee..cf6c8347d5 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Return.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Return.mls @@ -1,3 +1,4 @@ +:. :js @@ -39,14 +40,14 @@ foo(123) :e return "whoops" //β”‚ ╔══[COMPILATION ERROR] Return statements are not allowed outside of functions. -//β”‚ β•‘ l.40: return "whoops" +//β”‚ β•‘ l.41: return "whoops" //β”‚ ╙── ^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e x => return "whoops" //β”‚ ╔══[COMPILATION ERROR] Return statements are not allowed outside of functions. -//β”‚ β•‘ l.47: x => return "whoops" +//β”‚ β•‘ l.48: x => return "whoops" //β”‚ ╙── ^^^^^^^^^^^^^^^ //β”‚ = fun @@ -54,19 +55,13 @@ x => return "whoops" fun foo() = x => return "whoops" //β”‚ ╔══[COMPILATION ERROR] Non-local return statements are only supported with effect handlers enabled. -//β”‚ β•‘ l.55: x => return "whoops" +//β”‚ β•‘ l.56: x => return "whoops" //β”‚ ╙── ^^^^^^^^^^^^^^^ :e fun foo(x: return "whoops") = 1 //β”‚ ╔══[COMPILATION ERROR] Return statements are not allowed in this context. -//β”‚ β•‘ l.61: fun foo(x: return "whoops") = 1 -//β”‚ ╙── ^^^^^^^^^^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Expected a type, got a non-type β€Ήerrorβ€Ί -//β”‚ β•‘ l.61: fun foo(x: return "whoops") = 1 -//β”‚ ╙── ^^^^^^^^^^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Expected a type, got β€Ήerrorβ€Ί -//β”‚ β•‘ l.61: fun foo(x: return "whoops") = 1 +//β”‚ β•‘ l.62: fun foo(x: return "whoops") = 1 //β”‚ ╙── ^^^^^^^^^^^^^^^ :e @@ -74,13 +69,7 @@ fun foo() = fun bar(x: return "whoops") = 1 bar(123) //β”‚ ╔══[COMPILATION ERROR] Return statements are not allowed in this context. -//β”‚ β•‘ l.74: fun bar(x: return "whoops") = 1 -//β”‚ ╙── ^^^^^^^^^^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Expected a type, got a non-type β€Ήerrorβ€Ί -//β”‚ β•‘ l.74: fun bar(x: return "whoops") = 1 -//β”‚ ╙── ^^^^^^^^^^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Expected a type, got β€Ήerrorβ€Ί -//β”‚ β•‘ l.74: fun bar(x: return "whoops") = 1 +//β”‚ β•‘ l.69: fun bar(x: return "whoops") = 1 //β”‚ ╙── ^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/basics/SealedClasses.mls b/hkmc2/shared/src/test/mlscript/basics/SealedClasses.mls new file mode 100644 index 0000000000..0824b0ce8e --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/basics/SealedClasses.mls @@ -0,0 +1,19 @@ +:. +:js + +import "../../mlscript-compile/SealedClasses.mls" + +:expect 2 +class OpenChild extends SealedClasses.Open +(new OpenChild).value() +//β”‚ = 2 + +:e +class Invalid extends SealedClasses.Closed +//β”‚ ╔══[COMPILATION ERROR] Cannot extend sealed class 'Closed' outside its compilation unit +//β”‚ β•‘ l.12: class Invalid extends SealedClasses.Closed +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Closed' is defined here; declare it 'open' or 'abstract' to allow external subclasses +//β”‚ β•‘ in ../../mlscript-compile/SealedClasses.mls +//β”‚ β•‘ l.3: class Closed { fun value() = 1 } +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/basics/SetIndented.mls b/hkmc2/shared/src/test/mlscript/basics/SetIndented.mls index f79bba5b3e..b3956c320f 100644 --- a/hkmc2/shared/src/test/mlscript/basics/SetIndented.mls +++ b/hkmc2/shared/src/test/mlscript/basics/SetIndented.mls @@ -1,3 +1,4 @@ +:. :js // Same-line set with member selection parses fine @@ -6,11 +7,14 @@ set this.pc = 0 set (this).pc = 0 //β”‚ ╔══[COMPILATION ERROR] Cannot use 'this' outside of an object scope -//β”‚ β•‘ l.5: set this.pc = 0 +//β”‚ β•‘ l.6: set this.pc = 0 //β”‚ ╙── ^^^^ //β”‚ ╔══[COMPILATION ERROR] Cannot use 'this' outside of an object scope -//β”‚ β•‘ l.7: (this).pc = 0 +//β”‚ β•‘ l.8: (this).pc = 0 //β”‚ ╙── ^^^^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'pc' has no resolved target +//β”‚ β•‘ l.6: set this.pc = 0 +//β”‚ ╙── ^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // Indented set with member selection should also parse fine @@ -18,6 +22,9 @@ set set this.pc = 0 //β”‚ ╔══[COMPILATION ERROR] Cannot use 'this' outside of an object scope -//β”‚ β•‘ l.19: this.pc = 0 +//β”‚ β•‘ l.23: this.pc = 0 //β”‚ ╙── ^^^^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'pc' has no resolved target +//β”‚ β•‘ l.23: this.pc = 0 +//β”‚ ╙── ^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/basics/ShortcircuitingOps.mls b/hkmc2/shared/src/test/mlscript/basics/ShortcircuitingOps.mls index 4fde079240..f1e20720e2 100644 --- a/hkmc2/shared/src/test/mlscript/basics/ShortcircuitingOps.mls +++ b/hkmc2/shared/src/test/mlscript/basics/ShortcircuitingOps.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/basics/StrTest.mls b/hkmc2/shared/src/test/mlscript/basics/StrTest.mls index 1722f0d3da..3aa1f7e180 100644 --- a/hkmc2/shared/src/test/mlscript/basics/StrTest.mls +++ b/hkmc2/shared/src/test/mlscript/basics/StrTest.mls @@ -1,8 +1,10 @@ +:. :js import "../../mlscript-compile/StrOps.mls" open StrOps +open StrOps { concat2, ~ } :sjs @@ -27,32 +29,40 @@ concat of :e (~)("a", "b", "c") -//β”‚ ╔══[COMPILATION ERROR] Expected 2 arguments, got 3 -//β”‚ β•‘ l.29: (~)("a", "b", "c") -//β”‚ ╙── ^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.31: (~)("a", "b", "c") +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'concat2' expected 2 arguments, but got 3 +//β”‚ β•‘ in ../../mlscript-compile/StrOps.mls +//β”‚ β•‘ l.6: fun (~) concat2(a, b) = a + b +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ = "ab" :e (~)("a") -//β”‚ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 -//β”‚ β•‘ l.37: (~)("a") -//β”‚ ╙── ^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.43: (~)("a") +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'concat2' expected 2 arguments, but got 1 +//β”‚ β•‘ in ../../mlscript-compile/StrOps.mls +//β”‚ β•‘ l.6: fun (~) concat2(a, b) = a + b +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ = "aundefined" :ge ~"a" //β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (negation type) -//β”‚ β•‘ l.44: ~"a" -//β”‚ ╙── ^^^ +//β”‚ β•‘ l.54: ~"a" +//β”‚ ╙── ^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :ge ~("a", "b") //β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (negation type) -//β”‚ β•‘ l.52: ~("a", "b") -//β”‚ ╙── ^^^ +//β”‚ β•‘ l.62: ~("a", "b") +//β”‚ ╙── ^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -60,14 +70,14 @@ concat of :ge ~ of "a", "b" //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position; found 'of' keyword instead -//β”‚ β•‘ l.61: ~ of "a", "b" +//β”‚ β•‘ l.71: ~ of "a", "b" //β”‚ ╙── ^^ //β”‚ ╔══[PARSE ERROR] Expected end of input; found literal instead -//β”‚ β•‘ l.61: ~ of "a", "b" +//β”‚ β•‘ l.71: ~ of "a", "b" //β”‚ ╙── ^^^ //β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (negation type) -//β”‚ β•‘ l.61: ~ of "a", "b" -//β”‚ ╙── ^^^ +//β”‚ β•‘ l.71: ~ of "a", "b" +//β”‚ ╙── ^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. (~) of "a", "b" diff --git a/hkmc2/shared/src/test/mlscript/basics/Super.mls b/hkmc2/shared/src/test/mlscript/basics/Super.mls new file mode 100644 index 0000000000..3541054409 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/basics/Super.mls @@ -0,0 +1,9 @@ +:. +:js + +class Base { open fun f(x) = x + 1 } +class Child extends Base { override fun f(x) = super.f(x + 1) * 2 } + +:expect 10 +(new Child).f(3) +//β”‚ = 10 diff --git a/hkmc2/shared/src/test/mlscript/basics/SuspensionComments.mls b/hkmc2/shared/src/test/mlscript/basics/SuspensionComments.mls index 20d6ace54a..46063dbcdf 100644 --- a/hkmc2/shared/src/test/mlscript/basics/SuspensionComments.mls +++ b/hkmc2/shared/src/test/mlscript/basics/SuspensionComments.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/basics/SymbolicSuffixIdentifiers.mls b/hkmc2/shared/src/test/mlscript/basics/SymbolicSuffixIdentifiers.mls index e5a605d4ba..14fbc84542 100644 --- a/hkmc2/shared/src/test/mlscript/basics/SymbolicSuffixIdentifiers.mls +++ b/hkmc2/shared/src/test/mlscript/basics/SymbolicSuffixIdentifiers.mls @@ -1,3 +1,4 @@ +:. :js @@ -29,7 +30,7 @@ box_#.add_++(1) :sjs fun (++) foo_+(x) = x + 1 //β”‚ ╔══[COMPILATION ERROR] Cannot combine an explicit symbolic name with a symbolic suffix identifier. -//β”‚ β•‘ l.30: fun (++) foo_+(x) = x + 1 +//β”‚ β•‘ l.31: fun (++) foo_+(x) = x + 1 //β”‚ ╙── ^^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let foo; foo = function foo(x) { return x + 1 }; @@ -39,7 +40,7 @@ fun (++) foo_+(x) = x + 1 :sjs foo_+ //β”‚ ╔══[COMPILATION ERROR] Name not found: foo_+ -//β”‚ β•‘ l.40: foo_+ +//β”‚ β•‘ l.41: foo_+ //β”‚ ╙── ^^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ throw "This code cannot be run as its compilation yielded an error." @@ -50,7 +51,7 @@ foo_+ :sjs (++) //β”‚ ╔══[COMPILATION ERROR] Name not found: ++ -//β”‚ β•‘ l.51: (++) +//β”‚ β•‘ l.52: (++) //β”‚ ╙── ^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ throw "This code cannot be run as its compilation yielded an error." diff --git a/hkmc2/shared/src/test/mlscript/basics/ThenRecord.mls b/hkmc2/shared/src/test/mlscript/basics/ThenRecord.mls index 5dd844d7d6..44f7958d86 100644 --- a/hkmc2/shared/src/test/mlscript/basics/ThenRecord.mls +++ b/hkmc2/shared/src/test/mlscript/basics/ThenRecord.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/basics/TryFinally.mls b/hkmc2/shared/src/test/mlscript/basics/TryFinally.mls index 598130de01..889de8b6dc 100644 --- a/hkmc2/shared/src/test/mlscript/basics/TryFinally.mls +++ b/hkmc2/shared/src/test/mlscript/basics/TryFinally.mls @@ -1,3 +1,4 @@ +:. :js @@ -28,10 +29,10 @@ finally print("done") try 42 finally print("done") //β”‚ ╔══[PARSE ERROR] Unexpected 'finally' keyword here -//β”‚ β•‘ l.29: 42 finally print("done") +//β”‚ β•‘ l.30: 42 finally print("done") //β”‚ ╙── ^^^^^^^ //β”‚ ╔══[PARSE ERROR] Expected 'finally' keyword after `try` body; found end of input instead -//β”‚ β•‘ l.29: 42 finally print("done") +//β”‚ β•‘ l.30: 42 finally print("done") //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -41,13 +42,13 @@ try 42 finally print("done") //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position; found 'finally' keyword instead -//β”‚ β•‘ l.42: finally print("done") +//β”‚ β•‘ l.43: finally print("done") //β”‚ ╙── ^^^^^^^ //β”‚ ╔══[PARSE ERROR] Unexpected identifier here -//β”‚ β•‘ l.42: finally print("done") +//β”‚ β•‘ l.43: finally print("done") //β”‚ ╙── ^^^^^ //β”‚ ╔══[PARSE ERROR] Expected 'finally' keyword after `try` body; found end of input instead -//β”‚ β•‘ l.41: 42 +//β”‚ β•‘ l.42: 42 //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/basics/Underscores.mls b/hkmc2/shared/src/test/mlscript/basics/Underscores.mls index cc1a77315f..29b8ce1bc7 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Underscores.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Underscores.mls @@ -1,3 +1,4 @@ +:. :js @@ -14,7 +15,7 @@ _ :e y: _ //β”‚ ╔══[COMPILATION ERROR] Illegal position for '_' placeholder. -//β”‚ β•‘ l.15: y: _ +//β”‚ β•‘ l.16: y: _ //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //β”‚ y = undefined @@ -53,14 +54,14 @@ open Iter filtering of [], _ => 1 -//β”‚ = Iterable(_) +//β”‚ = IterableImpl(_) print(_) //β”‚ = fun :sjs -let test = _.f(0, _, 2) +let test = (_.f(0, _, 2)) as (({f: (Int, Int, Int) -> ()}, Int) -> ()) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let test, test1; //β”‚ test1 = function test(_0, _1) { return runtime.safeCall(_0.f(0, _1, 2)) }; @@ -68,9 +69,25 @@ let test = _.f(0, _, 2) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ test = fun test +:e :re test(f: print)(1) -//β”‚ ═══[RUNTIME ERROR] Error: Function 'test' expected 2 arguments but got 1 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.74: test(f: print)(1) +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function with a declared signature expected 2 arguments, but got 1 +//β”‚ β•‘ l.64: let test = (_.f(0, _, 2)) as (({f: (Int, Int, Int) -> ()}, Int) -> ()) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.74: test(f: print)(1) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value with no known interface cannot be called like a function. +//β”‚ β•‘ l.64: let test = (_.f(0, _, 2)) as (({f: (Int, Int, Int) -> ()}, Int) -> ()) +//β”‚ β•‘ ^^ +//β”‚ β•Ÿβ”€β”€ No member interface is known for this type. +//β”‚ β•‘ l.64: let test = (_.f(0, _, 2)) as (({f: (Int, Int, Int) -> ()}, Int) -> ()) +//β”‚ ╙── ^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. test((f: print), 1) //β”‚ > 0 1 2 @@ -139,7 +156,7 @@ mkObj(1, 2) :re let mkObj = x: _, y: _, z: 3 //β”‚ ╔══[COMPILATION ERROR] Illegal position for '_' placeholder. -//β”‚ β•‘ l.140: let mkObj = x: _, y: _, z: 3 +//β”‚ β•‘ l.157: let mkObj = x: _, y: _, z: 3 //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //β”‚ mkObj = fun mkObj diff --git a/hkmc2/shared/src/test/mlscript/basics/WeirdCalls.mls b/hkmc2/shared/src/test/mlscript/basics/WeirdCalls.mls index a00ae7a617..2d93e75fc3 100644 --- a/hkmc2/shared/src/test/mlscript/basics/WeirdCalls.mls +++ b/hkmc2/shared/src/test/mlscript/basics/WeirdCalls.mls @@ -1,3 +1,4 @@ +:. :js @@ -14,11 +15,11 @@ print "A" ) //β”‚ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (parenthesis section). -//β”‚ β•‘ l.13: ( +//β”‚ β•‘ l.14: ( //β”‚ β•‘ ^ -//β”‚ β•‘ l.14: "A" +//β”‚ β•‘ l.15: "A" //β”‚ β•‘ ^^^^^^^ -//β”‚ β•‘ l.15: ) +//β”‚ β•‘ l.16: ) //β”‚ ╙── ^^^ //β”‚ = fun print @@ -28,16 +29,16 @@ print of "A" ) //β”‚ ╔══[WARNING] This parenthesis section should be indented -//β”‚ β•‘ l.27: ( +//β”‚ β•‘ l.28: ( //β”‚ β•‘ ^ -//β”‚ β•‘ l.28: "A" +//β”‚ β•‘ l.29: "A" //β”‚ β•‘ ^^^^^ -//β”‚ β•‘ l.29: ) +//β”‚ β•‘ l.30: ) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ since it is a continuation of the new line here -//β”‚ β•‘ l.26: print of +//β”‚ β•‘ l.27: print of //β”‚ β•‘ ^ -//β”‚ β•‘ l.27: ( +//β”‚ β•‘ l.28: ( //β”‚ ╙── //β”‚ > A @@ -45,12 +46,12 @@ print of print of ("A") //β”‚ ╔══[WARNING] This parenthesis section should be indented -//β”‚ β•‘ l.46: ("A") +//β”‚ β•‘ l.47: ("A") //β”‚ β•‘ ^^^^^ //β”‚ β•Ÿβ”€β”€ since it is a continuation of the new line here -//β”‚ β•‘ l.45: print of +//β”‚ β•‘ l.46: print of //β”‚ β•‘ ^ -//β”‚ β•‘ l.46: ("A") +//β”‚ β•‘ l.47: ("A") //β”‚ ╙── //β”‚ > A @@ -66,15 +67,15 @@ print of "A" ) //β”‚ ╔══[PARSE ERROR] Mistmatched closing indentation -//β”‚ β•‘ l.66: "A" +//β”‚ β•‘ l.67: "A" //β”‚ β•‘ ^ -//β”‚ β•‘ l.67: ) +//β”‚ β•‘ l.68: ) //β”‚ β•‘ //β”‚ β•Ÿβ”€β”€ does not correspond to opening parenthesis -//β”‚ β•‘ l.65: ( +//β”‚ β•‘ l.66: ( //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Unexpected closing parenthesis -//β”‚ β•‘ l.67: ) +//β”‚ β•‘ l.68: ) //β”‚ ╙── ^ //β”‚ > A diff --git a/hkmc2/shared/src/test/mlscript/block-staging/.mls b/hkmc2/shared/src/test/mlscript/block-staging/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/block-staging/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/block-staging/Casts.mls b/hkmc2/shared/src/test/mlscript/block-staging/Casts.mls index ff734fd6e3..31b8da7bfc 100644 --- a/hkmc2/shared/src/test/mlscript/block-staging/Casts.mls +++ b/hkmc2/shared/src/test/mlscript/block-staging/Casts.mls @@ -1,3 +1,4 @@ +:. // The staged-module compiler (`ReflectionInstrumenter`) has to keep a `Cast` as data rather than // dropping it: the staged mirror (`Block.mls`) has a `Cast` node, so the coercion survives into the // produced IR instead of being silently discarded on the way in. diff --git a/hkmc2/shared/src/test/mlscript/block-staging/Nested.mls b/hkmc2/shared/src/test/mlscript/block-staging/Nested.mls index dd89573888..3a53c297e1 100644 --- a/hkmc2/shared/src/test/mlscript/block-staging/Nested.mls +++ b/hkmc2/shared/src/test/mlscript/block-staging/Nested.mls @@ -1,3 +1,4 @@ +:. :js :ftc :sir diff --git a/hkmc2/shared/src/test/mlscript/block-staging/PrintCode.mls b/hkmc2/shared/src/test/mlscript/block-staging/PrintCode.mls index 621c69fca0..0029728606 100644 --- a/hkmc2/shared/src/test/mlscript/block-staging/PrintCode.mls +++ b/hkmc2/shared/src/test/mlscript/block-staging/PrintCode.mls @@ -1,3 +1,4 @@ +:. :staging :js @@ -5,6 +6,7 @@ import "../../mlscript-compile/Block.mls" import "../../mlscript-compile/Option.mls" open Block +open Block { Symbol } open Option printCode(FunDefn(Symbol("f"), [[Symbol("x")]], Return(ValueLit(1)))) diff --git a/hkmc2/shared/src/test/mlscript/block-staging/Syntax.mls b/hkmc2/shared/src/test/mlscript/block-staging/Syntax.mls index 767ab21e60..16a7fa830c 100644 --- a/hkmc2/shared/src/test/mlscript/block-staging/Syntax.mls +++ b/hkmc2/shared/src/test/mlscript/block-staging/Syntax.mls @@ -1,3 +1,4 @@ +:. :pt staged module A diff --git a/hkmc2/shared/src/test/mlscript/codegen/.mls b/hkmc2/shared/src/test/mlscript/codegen/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/codegen/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/codegen/Arrays.mls b/hkmc2/shared/src/test/mlscript/codegen/Arrays.mls index 2a90026bb7..7b5ad0d051 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Arrays.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Arrays.mls @@ -1,12 +1,20 @@ +:. :js let empty = [] //β”‚ empty = [] +:ge :re empty.0 -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field '0' yielded 'undefined' +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.10: empty.0 +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Tuple literal does not contain member '0' +//β”‚ β•‘ l.5: let empty = [] +//β”‚ ╙── ^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :sjs let single = [1] @@ -44,13 +52,27 @@ val triple = [1, 2, 3] triple.2 //β”‚ = 3 +:ge :re triple.3 -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field '3' yielded 'undefined' - +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.57: triple.3 +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Tuple literal does not contain member '3' +//β”‚ β•‘ l.49: val triple = [1, 2, 3] +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:ge :re triple.4 -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field '4' yielded 'undefined' +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.68: triple.4 +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Tuple literal does not contain member '4' +//β”‚ β•‘ l.49: val triple = [1, 2, 3] +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // * Array hacking (taken from Anto's original port of nofib, which was slightly deranged) diff --git a/hkmc2/shared/src/test/mlscript/codegen/AuxiliaryConstructors.mls b/hkmc2/shared/src/test/mlscript/codegen/AuxiliaryConstructors.mls index 577e09f113..cc6d47453a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/AuxiliaryConstructors.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/AuxiliaryConstructors.mls @@ -1,8 +1,16 @@ +:. :js // === Basic constructor with single param list === +:fixme +class Foo with + constructor() +//β”‚ ╔══[COMPILATION ERROR] Unsupported constructor in this position. +//β”‚ β•‘ l.9: constructor() +//β”‚ ╙── ^^^^^^^^^^^^^ + // Plain class with constructor(x, y) generates JS constructor with field assignments :sjs class Foo with @@ -10,10 +18,10 @@ class Foo with fun getX = x fun getY = y //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ let Foo1; -//β”‚ (class Foo { +//β”‚ let Foo3; +//β”‚ (class Foo2 { //β”‚ static { -//β”‚ Foo1 = this +//β”‚ Foo3 = this //β”‚ } //β”‚ constructor(x, y) { //β”‚ this.#x = x; @@ -116,7 +124,9 @@ class B extends A(1)(2)(3) :e class B extends A(0) -//β”‚ ═══[COMPILATION ERROR] Extending a partially applied class is not supported +//β”‚ ╔══[COMPILATION ERROR] Extending a partially applied class is not supported +//β”‚ β•‘ l.126: class B extends A(0) +//β”‚ ╙── ^^^^ new B //β”‚ = B { x: 0 } @@ -125,17 +135,27 @@ class C :e class D extends C(0) -//β”‚ ═══[COMPILATION ERROR] Too many parameter lists for parent class +//β”‚ ╔══[COMPILATION ERROR] Too many parameter lists for parent class +//β”‚ β•‘ l.137: class D extends C(0) +//β”‚ ╙── ^^^^ :e new A(1)(2) with fun a = 0 -//β”‚ ═══[COMPILATION ERROR] Extending a partially applied class is not supported +//β”‚ ╔══[COMPILATION ERROR] Extending a partially applied class is not supported +//β”‚ β•‘ l.143: new A(1)(2) with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.144: fun a = 0 +//β”‚ ╙── ^^^^^^^^^^^ //β”‚ = $anon { x: 1 } :e new A(1)(2)(3)(4) with fun a = 0 //? = $anon { x: 1 } -//β”‚ ═══[COMPILATION ERROR] Too many parameter lists for parent class +//β”‚ ╔══[COMPILATION ERROR] Too many parameter lists for parent class +//β”‚ β•‘ l.153: new A(1)(2)(3)(4) with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.154: fun a = 0 +//β”‚ ╙── ^^^^^^^^^^^ //β”‚ = $anon { x: 1 } diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadConfigDirective.mls b/hkmc2/shared/src/test/mlscript/codegen/BadConfigDirective.mls index f3107be307..c171e66e37 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadConfigDirective.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadConfigDirective.mls @@ -1,13 +1,14 @@ +:. :js :w @config(tailRecOpt: false) "hi" //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.5: @config(tailRecOpt: false) "hi" +//β”‚ β•‘ l.6: @config(tailRecOpt: false) "hi" //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ This annotation is not supported on string literal terms. -//β”‚ β•‘ l.5: @config(tailRecOpt: false) "hi" +//β”‚ β•‘ l.6: @config(tailRecOpt: false) "hi" //β”‚ ╙── ^^^^ //β”‚ = "hi" @@ -20,7 +21,7 @@ :e #lang(1.0.x) //β”‚ ╔══[COMPILATION ERROR] Unknown language version '1.0.x' -//β”‚ β•‘ l.21: #lang(1.0.x) +//β”‚ β•‘ l.22: #lang(1.0.x) //β”‚ β•‘ ^^^^^ //β”‚ ╙── Available language versions: 0.2.x, 0.3.x @@ -28,12 +29,12 @@ :e #lang(unknownLanguageField: true) //β”‚ ╔══[COMPILATION ERROR] Unsupported Language argument -//β”‚ β•‘ l.29: #lang(unknownLanguageField: true) +//β”‚ β•‘ l.30: #lang(unknownLanguageField: true) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ :e #lang(versionName: "custom") //β”‚ ╔══[COMPILATION ERROR] Unsupported Language argument -//β”‚ β•‘ l.36: #lang(versionName: "custom") +//β”‚ β•‘ l.37: #lang(versionName: "custom") //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadFunctions.mls b/hkmc2/shared/src/test/mlscript/codegen/BadFunctions.mls index 8925cdf882..5c37acd9ab 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadFunctions.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadFunctions.mls @@ -1,13 +1,14 @@ +:. :js :fixme fun (let) oops = 1 //β”‚ ╔══[PARSE ERROR] Expected expression after 'let' binding keyword; found end of input instead -//β”‚ β•‘ l.5: fun (let) oops = 1 +//β”‚ β•‘ l.6: fun (let) oops = 1 //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] This β€Ήerroneous syntaxβ€Ί is not a valid symbolic name -//β”‚ β•‘ l.5: fun (let) oops = 1 +//β”‚ β•‘ l.6: fun (let) oops = 1 //β”‚ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadGenerators.mls b/hkmc2/shared/src/test/mlscript/codegen/BadGenerators.mls index 0f34cf4694..9ce0e689e1 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadGenerators.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadGenerators.mls @@ -1,3 +1,4 @@ +:. :js open annotations @@ -5,7 +6,7 @@ open annotations :ge yield 1 //β”‚ ╔══[COMPILATION ERROR] Yield expressions are not allowed in this context. -//β”‚ β•‘ l.6: yield 1 +//β”‚ β•‘ l.7: yield 1 //β”‚ ╙── ^^^^^^^ //β”‚ = 1 @@ -13,10 +14,10 @@ yield 1 @generator () => 3 //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.13: @generator +//β”‚ β•‘ l.14: @generator //β”‚ β•‘ ^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ This annotation is not supported on function literal terms. -//β”‚ β•‘ l.14: () => 3 +//β”‚ β•‘ l.15: () => 3 //β”‚ ╙── ^^^^^^^ //β”‚ = fun @@ -27,21 +28,21 @@ class C with fun f = yield 1 //β”‚ ╔══[COMPILATION ERROR] Generators are not supported on functions without a parameter list -//β”‚ β•‘ l.26: @generator +//β”‚ β•‘ l.27: @generator //β”‚ β•‘ ^^^^^^^^^^ -//β”‚ β•‘ l.27: fun f = +//β”‚ β•‘ l.28: fun f = //β”‚ β•‘ ^^^^^^^^^ -//β”‚ β•‘ l.28: yield 1 +//β”‚ β•‘ l.29: yield 1 //β”‚ ╙── ^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Yield expressions are not allowed in this context. -//β”‚ β•‘ l.28: yield 1 +//β”‚ β•‘ l.29: yield 1 //β”‚ ╙── ^^^^^^^ :ge @generator fun bad = 1 //β”‚ ╔══[COMPILATION ERROR] Generators are not supported on functions without a parameter list -//β”‚ β•‘ l.41: @generator +//β”‚ β•‘ l.42: @generator //β”‚ β•‘ ^^^^^^^^^^ -//β”‚ β•‘ l.42: fun bad = 1 +//β”‚ β•‘ l.43: fun bad = 1 //β”‚ ╙── ^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadImport.mls b/hkmc2/shared/src/test/mlscript/codegen/BadImport.mls index 038491fbe8..a2dfa008b6 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadImport.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadImport.mls @@ -1,10 +1,11 @@ +:. :js :e import 123 //β”‚ ╔══[COMPILATION ERROR] Expected string literal after 'import' keyword -//β”‚ β•‘ l.5: import 123 +//β”‚ β•‘ l.6: import 123 //β”‚ ╙── ^^^ diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadInit.mls b/hkmc2/shared/src/test/mlscript/codegen/BadInit.mls index ce196a50da..69dfa9e6a8 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadInit.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadInit.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls b/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls index 3f7703c54f..597395ac32 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls @@ -1,3 +1,4 @@ +:. :js @@ -5,44 +6,50 @@ :re new //β”‚ ╔══[PARSE ERROR] Expected 'with' keyword, expression, or block after `new` keyword; found end of input instead -//β”‚ β•‘ l.6: new +//β”‚ β•‘ l.7: new //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e :re new 2 -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found integer literal. -//β”‚ β•‘ l.14: new 2 -//β”‚ β•‘ ^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//β”‚ ═══[RUNTIME ERROR] TypeError: 2 is not a constructor +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.15: new 2 +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Invalid class expression: integer literal +//β”‚ β•‘ l.15: new 2 +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e :re new 2 + 2 -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found integer literal. -//β”‚ β•‘ l.23: new 2 + 2 -//β”‚ β•‘ ^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//β”‚ ═══[RUNTIME ERROR] TypeError: 2 is not a constructor +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.26: new 2 + 2 +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Invalid class expression: integer literal +//β”‚ β•‘ l.26: new 2 + 2 +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :sjs :re new! 2 + 2 //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ let tmp1; tmp1 = globalThis.Object.freeze(new 2()); return tmp1 + 2 +//β”‚ let tmp; tmp = globalThis.Object.freeze(new 2()); return tmp + 2 //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ ═══[RUNTIME ERROR] TypeError: 2 is not a constructor :e :re new() -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found unit value. -//β”‚ β•‘ l.40: new() -//β”‚ β•‘ ^^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//β”‚ ═══[RUNTIME ERROR] TypeError: runtime.Unit is not a constructor +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.45: new() +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Invalid class expression: unit value +//β”‚ β•‘ l.45: new() +//β”‚ ╙── ^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :re new!() @@ -51,14 +58,16 @@ new!() :e :re new {} -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found unit value. -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//β”‚ ═══[RUNTIME ERROR] TypeError: runtime.Unit is not a constructor +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.60: new {} +//β”‚ β•‘ ^^^ +//β”‚ ╙── Invalid class expression: unit value +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :pe new! {} //β”‚ ╔══[PARSE ERROR] Expected expression after prefix keyword 'new!'; found end of block instead -//β”‚ β•‘ l.59: new! {} +//β”‚ β•‘ l.68: new! {} //β”‚ ╙── ^^ @@ -75,17 +84,19 @@ new { x = 1 } //β”‚ lhs = Ident of "x" //β”‚ rhs = IntLit of 1 //β”‚ ╔══[COMPILATION ERROR] Name not found: x -//β”‚ β•‘ l.70: new { x = 1 } +//β”‚ β•‘ l.79: new { x = 1 } //β”‚ ╙── ^ -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found block. -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.79: new { x = 1 } +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ ╙── Invalid class expression: block //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e new! { x = 1 } //β”‚ ╔══[COMPILATION ERROR] Unrecognized definitional assignment left-hand side: prefix operator 'new!' -//β”‚ β•‘ l.86: new! { x = 1 } +//β”‚ β•‘ l.97: new! { x = 1 } //β”‚ ╙── ^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -93,12 +104,8 @@ new! { x = 1 } :e let xs = new Oopsie //β”‚ ╔══[COMPILATION ERROR] Name not found: Oopsie -//β”‚ β•‘ l.94: let xs = new Oopsie -//β”‚ ╙── ^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found β€Ήerrorβ€Ί. -//β”‚ β•‘ l.94: let xs = new Oopsie -//β”‚ β•‘ ^^^^^^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. +//β”‚ β•‘ l.105: let xs = new Oopsie +//β”‚ ╙── ^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //β”‚ xs = undefined diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadSpreads.mls b/hkmc2/shared/src/test/mlscript/codegen/BadSpreads.mls index f526b1e2ee..a7cca186f9 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadSpreads.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadSpreads.mls @@ -1,17 +1,18 @@ +:. :js :e ..., 1 //β”‚ ╔══[COMPILATION ERROR] Illegal position for '...' spread operator. -//β”‚ β•‘ l.5: ..., 1 +//β”‚ β•‘ l.6: ..., 1 //β”‚ ╙── ^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e fun f = ..., 1 //β”‚ ╔══[COMPILATION ERROR] Illegal position for '...' spread operator. -//β”‚ β•‘ l.12: fun f = ..., 1 +//β”‚ β•‘ l.13: fun f = ..., 1 //β”‚ ╙── ^^^ //β”‚ = 1 @@ -24,13 +25,13 @@ fun f = ..., 1 :e fun foo(a, ...b, c) = [a, ...b, c] //β”‚ ╔══[COMPILATION ERROR] Spread parameters must be the last in the parameter list. -//β”‚ β•‘ l.25: fun foo(a, ...b, c) = [a, ...b, c] +//β”‚ β•‘ l.26: fun foo(a, ...b, c) = [a, ...b, c] //β”‚ ╙── ^^^^ :e fun foo(a, ...b, c) = [a, ...b] //β”‚ ╔══[COMPILATION ERROR] Spread parameters must be the last in the parameter list. -//β”‚ β•‘ l.31: fun foo(a, ...b, c) = [a, ...b] +//β”‚ β•‘ l.32: fun foo(a, ...b, c) = [a, ...b] //β”‚ ╙── ^^^^ :re @@ -43,22 +44,25 @@ fun foo(a, ...b) = [a, ...b] :e :re foo() -//β”‚ ╔══[COMPILATION ERROR] Expected at least 1 arguments, got 0 -//β”‚ β•‘ l.45: foo() -//β”‚ ╙── ^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.46: foo() +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'foo' expected 1 argument, but got 0 +//β”‚ β•‘ l.42: fun foo(a, ...b) = [a, ...b] +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: Function 'foo' expected at least 1 argument but got 0 :e fun foo(1) = 2 //β”‚ ╔══[COMPILATION ERROR] Expected a valid parameter, found integer literal -//β”‚ β•‘ l.53: fun foo(1) = 2 +//β”‚ β•‘ l.57: fun foo(1) = 2 //β”‚ ╙── ^ :e fun foo(...1) = 2 //β”‚ ╔══[COMPILATION ERROR] Expected a valid parameter, found spread -//β”‚ β•‘ l.59: fun foo(...1) = 2 +//β”‚ β•‘ l.63: fun foo(...1) = 2 //β”‚ ╙── ^^^^ diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadThis.mls b/hkmc2/shared/src/test/mlscript/codegen/BadThis.mls index 0e530bb6d7..9ee5fe7a3b 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadThis.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadThis.mls @@ -1,3 +1,4 @@ +:. :js @@ -18,9 +19,16 @@ val globalThis = "oops" //β”‚ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'freeze') //β”‚ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'freeze') +:e :re globalThis.clearInterval -//β”‚ ═══[RUNTIME ERROR] TypeError: globalThis.Error is not a function +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.24: globalThis.clearInterval +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ String literal does not contain member 'clearInterval' +//β”‚ β•‘ l.18: val globalThis = "oops" +//β”‚ ╙── ^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //β”‚ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'freeze') :re diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadValInit.mls b/hkmc2/shared/src/test/mlscript/codegen/BadValInit.mls index db4c9bd2e4..ce8e7061c0 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadValInit.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadValInit.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadWhile.mls b/hkmc2/shared/src/test/mlscript/codegen/BadWhile.mls index fe71d8b35d..d645e9322a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadWhile.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadWhile.mls @@ -1,3 +1,4 @@ +:. :js @@ -5,7 +6,7 @@ :sjs while {} do() //β”‚ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'do' here -//β”‚ β•‘ l.6: while {} do() +//β”‚ β•‘ l.7: while {} do() //β”‚ ╙── ^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ throw "This code cannot be run as its compilation yielded an error." @@ -21,5 +22,5 @@ fun f() = set i -= 1 i //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.22: i +//β”‚ β•‘ l.23: i //β”‚ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/codegen/BasicTerms.mls b/hkmc2/shared/src/test/mlscript/codegen/BasicTerms.mls index 9ee03eeac2..bb3bb9a24a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BasicTerms.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BasicTerms.mls @@ -1,3 +1,4 @@ +:. :js :global @@ -15,7 +16,7 @@ 1 2 //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.15: 1 +//β”‚ β•‘ l.16: 1 //β”‚ ╙── ^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR Tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Program: @@ -57,11 +58,18 @@ print("Hi") //β”‚ = 2 +:e :re 2(2) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.63: 2(2) +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be called like a function. +//β”‚ β•‘ l.63: 2(2) +//β”‚ ╙── ^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ return runtime.safeCall(2(2)) +//β”‚ throw "This code cannot be run as its compilation yielded an error." //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ ═══[RUNTIME ERROR] TypeError: 2 is not a function +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/codegen/Bindings.mls b/hkmc2/shared/src/test/mlscript/codegen/Bindings.mls index bc83b775bc..0c22fe05a5 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Bindings.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Bindings.mls @@ -1,3 +1,4 @@ +:. :js // :sjs // :elt @@ -54,21 +55,21 @@ let x |>= incr :e let 2 //β”‚ ╔══[COMPILATION ERROR] Unsupported let binding shape -//β”‚ β•‘ l.55: let 2 +//β”‚ β•‘ l.56: let 2 //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e let x += 1 = 0 //β”‚ ╔══[COMPILATION ERROR] Unsupported let binding shape -//β”‚ β•‘ l.62: let x += 1 = 0 +//β”‚ β•‘ l.63: let x += 1 = 0 //β”‚ ╙── ^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e let x += 1 = 0 in x //β”‚ ╔══[COMPILATION ERROR] Unsupported let binding shape -//β”‚ β•‘ l.69: let x += 1 = 0 in x +//β”‚ β•‘ l.70: let x += 1 = 0 in x //β”‚ ╙── ^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -76,20 +77,20 @@ let x += 1 = 0 in x :e (let x = 1) //β”‚ ╔══[COMPILATION ERROR] Expected a body for let bindings in expression position -//β”‚ β•‘ l.77: (let x = 1) +//β”‚ β•‘ l.78: (let x = 1) //β”‚ ╙── ^^^^^ :e (let x = 1) + 1 //β”‚ ╔══[COMPILATION ERROR] Expected a body for let bindings in expression position -//β”‚ β•‘ l.83: (let x = 1) + 1 +//β”‚ β•‘ l.84: (let x = 1) + 1 //β”‚ ╙── ^^^^^ //β”‚ = "()1" :e (let f(x) = x) + 1 //β”‚ ╔══[COMPILATION ERROR] Expected a body for let bindings in expression position -//β”‚ β•‘ l.90: (let f(x) = x) + 1 +//β”‚ β•‘ l.91: (let f(x) = x) + 1 //β”‚ ╙── ^^^^^^^^ //β”‚ = "()1" diff --git a/hkmc2/shared/src/test/mlscript/codegen/BlockPrinter.mls b/hkmc2/shared/src/test/mlscript/codegen/BlockPrinter.mls index 4b2aa99b4f..1f7e436ba3 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BlockPrinter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BlockPrinter.mls @@ -1,3 +1,4 @@ +:. :js :sir @@ -147,7 +148,7 @@ new C(123) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ define C⁰ as class CΒ² { //β”‚ private val x⁡; -//β”‚ constructor CΒΉ(x) { set CΒ².this.x⁡ = x; end } +//β”‚ constructor CΒΉ(x) { @private define x⁢ as val x⁡ = x; end } //β”‚ }; //β”‚ do CΒΉ(123); //β”‚ return new CΒ²(123) @@ -165,14 +166,15 @@ A.x //β”‚ define A⁰ as class AΒΉ //β”‚ module AΒ² { //β”‚ private val y⁰; -//β”‚ val x⁢; +//β”‚ val x⁷; //β”‚ constructor { -//β”‚ define x⁢ as val x⁷ = 2; +//β”‚ define x⁷ as val x⁸ = 2; //β”‚ set AΒ².this.y⁰ = 3; //β”‚ define CΒ³ as class C⁡ { -//β”‚ private val x⁸; +//β”‚ private val x⁹; //β”‚ constructor C⁴(x) { -//β”‚ set C⁡.this.x⁸ = x; +//β”‚ @private +//β”‚ define x¹⁰ as val x⁹ = x; //β”‚ end //β”‚ } //β”‚ method f⁴ = fun f⁡(x1) { @@ -183,7 +185,7 @@ A.x //β”‚ } //β”‚ method g⁰ = fun gΒΉ(x) { return 3 } //β”‚ }; -//β”‚ return AΒ².x⁷ +//β”‚ return AΒ².x⁸ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = 2 @@ -192,8 +194,8 @@ module A with A.x //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ define AΒ³ as class A⁴ -//β”‚ module A⁡ { val x⁹; constructor { define x⁹ as val x¹⁰ = 3; end } }; -//β”‚ return A⁡.x¹⁰ +//β”‚ module A⁡ { val xΒΉΒΉ; constructor { define xΒΉΒΉ as val xΒΉΒ² = 3; end } }; +//β”‚ return A⁡.xΒΉΒ² //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = 3 @@ -203,9 +205,16 @@ class B(x) extends A with //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ define A⁢ as class A⁷; //β”‚ define B⁰ as class BΒ² extends A⁷ { -//β”‚ private val xΒΉΒΉ; +//β”‚ private val xΒΉΒ³; //β”‚ val yΒΉ; -//β”‚ constructor BΒΉ(x) { do super⁰(); end; set BΒ².this.xΒΉΒΉ = x; define yΒΉ as val yΒ² = 2; end } +//β”‚ constructor BΒΉ(x) { +//β”‚ do super⁰(); +//β”‚ end; +//β”‚ @private +//β”‚ define x¹⁴ as val xΒΉΒ³ = x; +//β”‚ define yΒΉ as val yΒ² = 2; +//β”‚ end +//β”‚ } //β”‚ }; //β”‚ end //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” @@ -237,7 +246,7 @@ object A with let x = [1, 2, 3] x.0 //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ set xΒΉΒ² = [1, 2, 3]; return xΒΉΒ².0οΉ– +//β”‚ set x¹⁡ = [1, 2, 3]; return x¹⁡.0 //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = 1 //β”‚ x = [1, 2, 3] diff --git a/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls b/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls index 09c77e58ad..b4fbd37596 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls @@ -1,3 +1,4 @@ +:. :js @@ -29,7 +30,7 @@ :w +(2, 3) //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.30: +(2, 3) +//β”‚ β•‘ l.31: +(2, 3) //β”‚ ╙── ^ //β”‚ = 3 @@ -39,7 +40,7 @@ :ge *(1) //β”‚ ╔══[COMPILATION ERROR] Builtin '*' is not a unary operator -//β”‚ β•‘ l.40: *(1) +//β”‚ β•‘ l.41: *(1) //β”‚ ╙── ^^^^ //β”‚ ═══[COMPILATION ERROR] Cannot call non-unary builtin symbol '*' //β”‚ ═══[RUNTIME ERROR] Error: Cannot call non-unary builtin symbol '*' @@ -56,7 +57,7 @@ :ge (*)(2) //β”‚ ╔══[COMPILATION ERROR] Builtin '*' is not a unary operator -//β”‚ β•‘ l.57: (*)(2) +//β”‚ β•‘ l.58: (*)(2) //β”‚ ╙── ^^^^^^ //β”‚ ═══[COMPILATION ERROR] Cannot call non-unary builtin symbol '*' //β”‚ ═══[RUNTIME ERROR] Error: Cannot call non-unary builtin symbol '*' diff --git a/hkmc2/shared/src/test/mlscript/codegen/BuiltinSymbols.mls b/hkmc2/shared/src/test/mlscript/codegen/BuiltinSymbols.mls index c8ffd860ff..75c928c20d 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BuiltinSymbols.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BuiltinSymbols.mls @@ -1,3 +1,4 @@ +:. :js @@ -6,21 +7,21 @@ :ge Int //β”‚ ╔══[COMPILATION ERROR] Symbol 'Int' is virtual (i.e., "compiler fiction"); cannot be used as a term -//β”‚ β•‘ l.7: Int +//β”‚ β•‘ l.8: Int //β”‚ ╙── ^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :ge Str //β”‚ ╔══[COMPILATION ERROR] Symbol 'Str' is virtual (i.e., "compiler fiction"); cannot be used as a term -//β”‚ β•‘ l.14: Str +//β”‚ β•‘ l.15: Str //β”‚ ╙── ^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :ge Bool //β”‚ ╔══[COMPILATION ERROR] Symbol 'Bool' is virtual (i.e., "compiler fiction"); cannot be used as a term -//β”‚ β•‘ l.21: Bool +//β”‚ β•‘ l.22: Bool //β”‚ ╙── ^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/codegen/CaseOfCase.mls b/hkmc2/shared/src/test/mlscript/codegen/CaseOfCase.mls index 78e68efd28..05f45d1492 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/CaseOfCase.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/CaseOfCase.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls b/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls index d7ce43f175..66020f8c1a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls @@ -1,3 +1,4 @@ +:. :js @@ -77,10 +78,10 @@ case :todo // TODO: support this braceless syntax? case [x] then x, [] then 0 //β”‚ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here -//β”‚ β•‘ l.78: case [x] then x, [] then 0 +//β”‚ β•‘ l.79: case [x] then x, [] then 0 //β”‚ ╙── ^^^^^^^^^ //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.78: case [x] then x, [] then 0 +//β”‚ β•‘ l.79: case [x] then x, [] then 0 //β”‚ ╙── ^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -89,12 +90,12 @@ case 0 then true _ then false //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ let lambda5; -//β”‚ lambda5 = (undefined, function (caseScrut) { +//β”‚ let lambda10; +//β”‚ lambda10 = (undefined, function (caseScrut) { //β”‚ if (caseScrut === 0) { return true } //β”‚ return false; //β”‚ }); -//β”‚ return lambda5 +//β”‚ return lambda10 //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = fun @@ -106,8 +107,8 @@ val isDefined = case Some then true None then false //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ let isDefined, lambda6; -//β”‚ lambda6 = (undefined, function (caseScrut) { +//β”‚ let isDefined, lambda11; +//β”‚ lambda11 = (undefined, function (caseScrut) { //β”‚ if (caseScrut instanceof Some1.class) { //β”‚ return true //β”‚ } else if (caseScrut instanceof None1.class) { @@ -115,7 +116,7 @@ val isDefined = case //β”‚ } //β”‚ throw globalThis.Object.freeze(new globalThis.Error("match error")); //β”‚ }); -//β”‚ isDefined = lambda6; +//β”‚ isDefined = lambda11; //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ isDefined = fun @@ -123,6 +124,6 @@ val isDefined = case :todo case 0 | 1 //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern split (infix operator '|'). -//β”‚ β•‘ l.124: case 0 | 1 +//β”‚ β•‘ l.125: case 0 | 1 //β”‚ ╙── ^^^^^ //β”‚ = fun diff --git a/hkmc2/shared/src/test/mlscript/codegen/CheckedCasts.mls b/hkmc2/shared/src/test/mlscript/codegen/CheckedCasts.mls index 3e58a2f6a5..c2fac25dc9 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/CheckedCasts.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/CheckedCasts.mls @@ -1,3 +1,4 @@ +:. // `:noCheckCasts` is the only input to a `Cast`'s `check` flag, via `Config.checkCasts`. Nothing // expands a check into a runtime type test since `CheckedCastExpansion` was removed, so the two // blocks below generate the same code and differ only in how the IR prints the cast: `as!` when diff --git a/hkmc2/shared/src/test/mlscript/codegen/ClassInClass.mls b/hkmc2/shared/src/test/mlscript/codegen/ClassInClass.mls index f854d4c4cc..368c490709 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ClassInClass.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ClassInClass.mls @@ -1,3 +1,4 @@ +:. :js @@ -55,7 +56,7 @@ data class Outer(a, b) with //β”‚ tmp = this.Inner(this.a); //β”‚ this.i = tmp; //β”‚ Predef.print(this.i.c); -//β”‚ tmp1 = runtime.safeCall(this.i.i1(1)); +//β”‚ tmp1 = this.i.i1(1); //β”‚ Predef.print(tmp1); //β”‚ } //β”‚ o1(c) { diff --git a/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls index a63ca02fe7..e429205764 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls @@ -1,3 +1,4 @@ +:. :js @@ -69,9 +70,16 @@ test(123) :e :re test() -//β”‚ ╔══[COMPILATION ERROR] Expected 1 arguments, got 0 -//β”‚ β•‘ l.71: test() -//β”‚ ╙── ^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.72: test() +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'test' expected 1 argument, but got 0 +//β”‚ β•‘ l.38: fun test(x) = +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•‘ l.39: data class Foo(a, b) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.40: Foo(x, x + 1) +//β”‚ ╙── ^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: Function 'test' expected 1 argument but got 0 diff --git a/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls b/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls index 4e9b5d1f24..3dacfbf2f4 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls @@ -1,3 +1,4 @@ +:. :js @@ -123,7 +124,7 @@ M.d fun f(x) = if x is M.D then 1 else 0 -M.D.class +M.D!class //β”‚ = class D M is M.D diff --git a/hkmc2/shared/src/test/mlscript/codegen/Classes.mls b/hkmc2/shared/src/test/mlscript/codegen/Classes.mls index f249319e22..60e965dc9a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Classes.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Classes.mls @@ -1,3 +1,4 @@ +:. :js :global diff --git a/hkmc2/shared/src/test/mlscript/codegen/CodegenScratch.mls b/hkmc2/shared/src/test/mlscript/codegen/CodegenScratch.mls index ef0cd50ba4..fb9812439e 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/CodegenScratch.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/CodegenScratch.mls @@ -1,3 +1,4 @@ +:. :js :global diff --git a/hkmc2/shared/src/test/mlscript/codegen/Comma.mls b/hkmc2/shared/src/test/mlscript/codegen/Comma.mls index faf901b723..8001f4a9b8 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Comma.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Comma.mls @@ -1,3 +1,4 @@ +:. :js :global diff --git a/hkmc2/shared/src/test/mlscript/codegen/ConsoleLog.mls b/hkmc2/shared/src/test/mlscript/codegen/ConsoleLog.mls index 1d3ef73438..4fda1fe031 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ConsoleLog.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ConsoleLog.mls @@ -1,3 +1,4 @@ +:. :js :global @@ -5,7 +6,7 @@ :silent -declare val console +declare val console: {log: Function} //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” @@ -26,7 +27,7 @@ console.log("b") let l = console.log l(123) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ let l; l = globalThis.console.log; return runtime.safeCall(l(123)) +//β”‚ let l; l = globalThis.console.log; return runtime.safeCall(globalThis.console.log(123)) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ > 123 //β”‚ l = fun log diff --git a/hkmc2/shared/src/test/mlscript/codegen/CurriedClassInheritance.mls b/hkmc2/shared/src/test/mlscript/codegen/CurriedClassInheritance.mls index 9eecf5e6ae..6677b169da 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/CurriedClassInheritance.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/CurriedClassInheritance.mls @@ -1,3 +1,4 @@ +:. :js @@ -6,7 +7,9 @@ class Foo(val x)(val y) :e :sjs class Bar(val a)(val d) extends Foo(a) -//β”‚ ═══[COMPILATION ERROR] Extending a partially applied class is not supported +//β”‚ ╔══[COMPILATION ERROR] Extending a partially applied class is not supported +//β”‚ β•‘ l.9: class Bar(val a)(val d) extends Foo(a) +//β”‚ ╙── ^^^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let Bar1; //β”‚ Bar1 = function Bar(a) { @@ -34,9 +37,16 @@ let b = Bar(123)(1234) b.a //β”‚ = 123 +:ge :re let c = b(100) -//β”‚ ═══[RUNTIME ERROR] TypeError: b is not a function +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.42: let c = b(100) +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Bar' cannot receive more argument lists. +//β”‚ β•‘ l.34: let b = Bar(123)(1234) +//β”‚ ╙── ^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //β”‚ c = undefined :fixme @@ -45,11 +55,30 @@ c.a :fixme Bar(123)(1234)(100).a -//β”‚ ═══[RUNTIME ERROR] TypeError: baseCall is not a function - +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.57: Bar(123)(1234)(100).a +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Bar' cannot receive more argument lists. +//β”‚ β•‘ l.57: Bar(123)(1234)(100).a +//β”‚ ╙── ^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:ge :re [c.x, c.y] -//β”‚ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'x') +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.68: [c.x, c.y] +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Bar' does not contain member 'x' +//β”‚ β•‘ l.42: let c = b(100) +//β”‚ ╙── ^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.68: [c.x, c.y] +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Bar' does not contain member 'y' +//β”‚ β•‘ l.42: let c = b(100) +//β”‚ ╙── ^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. let b0 = Bar //β”‚ b0 = fun Bar { class: class Bar } @@ -57,9 +86,16 @@ let b0 = Bar let b1 = b0(123) //β”‚ b1 = fun +:ge :re b1.a -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'a' yielded 'undefined' +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.91: b1.a +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Bar' does not contain member 'a' +//β”‚ β•‘ l.86: let b1 = b0(123) +//β”‚ ╙── ^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. let b = b1(1234) //β”‚ b = Bar(123) @@ -67,18 +103,38 @@ let b = b1(1234) b.a //β”‚ = 123 +:ge :re let c = b(100) -//β”‚ ═══[RUNTIME ERROR] TypeError: b2 is not a function +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.108: let c = b(100) +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Bar' cannot receive more argument lists. +//β”‚ β•‘ l.100: let b = b1(1234) +//β”‚ ╙── ^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //β”‚ c = undefined :fixme c.a //β”‚ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'a') +:ge :re [c.x, c.y] -//β”‚ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'x') +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.124: [c.x, c.y] +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Bar' does not contain member 'x' +//β”‚ β•‘ l.108: let c = b(100) +//β”‚ ╙── ^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.124: [c.x, c.y] +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Bar' does not contain member 'y' +//β”‚ β•‘ l.108: let c = b(100) +//β”‚ ╙── ^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. class Baz(a)(val d) extends Foo(a)(a + 1) @@ -86,9 +142,16 @@ class Baz(a)(val d) extends Foo(a)(a + 1) let b = Baz(123)(1234) //β”‚ b = Baz(_) +:ge :re let c = b(100) -//β”‚ ═══[RUNTIME ERROR] TypeError: b3 is not a function +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.147: let c = b(100) +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Baz' cannot receive more argument lists. +//β”‚ β•‘ l.142: let b = Baz(123)(1234) +//β”‚ ╙── ^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //β”‚ c = undefined :re diff --git a/hkmc2/shared/src/test/mlscript/codegen/CurriedClasses.mls b/hkmc2/shared/src/test/mlscript/codegen/CurriedClasses.mls index bf065ed09e..2ae37d2a6e 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/CurriedClasses.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/CurriedClasses.mls @@ -1,3 +1,4 @@ +:. :js @@ -76,8 +77,8 @@ new A(...[4])(...[5]).y //β”‚ = 5 -// * First-class class value; FIXME: should be forbidden -let a = A.class +// * Reflect the raw JavaScript class behind the curried constructor. +let a = A!class //β”‚ a = class A // * Dynamic instantiation of a raw JS class is not eta-expanded. @@ -106,22 +107,22 @@ h.y class A(x1, x2, ...xs)(y1, ...ys) with fun fields = [x1, x2, xs, y1, ys] //β”‚ ╔══[COMPILATION ERROR] Spread parameters are not supported in class parameters. -//β”‚ β•‘ l.106: class A(x1, x2, ...xs)(y1, ...ys) with +//β”‚ β•‘ l.107: class A(x1, x2, ...xs)(y1, ...ys) with //β”‚ ╙── ^^^^^ //β”‚ ╔══[COMPILATION ERROR] Spread parameters are not supported in class parameters. -//β”‚ β•‘ l.106: class A(x1, x2, ...xs)(y1, ...ys) with +//β”‚ β•‘ l.107: class A(x1, x2, ...xs)(y1, ...ys) with //β”‚ ╙── ^^^^^ //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'xs' -//β”‚ β•‘ l.107: fun fields = [x1, x2, xs, y1, ys] +//β”‚ β•‘ l.108: fun fields = [x1, x2, xs, y1, ys] //β”‚ β•‘ ^^ //β”‚ β•Ÿβ”€β”€ which references the symbol introduced here -//β”‚ β•‘ l.106: class A(x1, x2, ...xs)(y1, ...ys) with +//β”‚ β•‘ l.107: class A(x1, x2, ...xs)(y1, ...ys) with //β”‚ ╙── ^^ //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'ys' -//β”‚ β•‘ l.107: fun fields = [x1, x2, xs, y1, ys] +//β”‚ β•‘ l.108: fun fields = [x1, x2, xs, y1, ys] //β”‚ β•‘ ^^ //β”‚ β•Ÿβ”€β”€ which references the symbol introduced here -//β”‚ β•‘ l.106: class A(x1, x2, ...xs)(y1, ...ys) with +//β”‚ β•‘ l.107: class A(x1, x2, ...xs)(y1, ...ys) with //β”‚ ╙── ^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let A3; @@ -155,21 +156,37 @@ class A(x1, x2, ...xs)(y1, ...ys) with //β”‚ static [definitionMetadata] = ["class", "A", [null, null]]; //β”‚ }); //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'xs' -//β”‚ β•‘ l.107: fun fields = [x1, x2, xs, y1, ys] +//β”‚ β•‘ l.108: fun fields = [x1, x2, xs, y1, ys] //β”‚ β•‘ ^^ //β”‚ β•Ÿβ”€β”€ which references the symbol introduced here -//β”‚ β•‘ l.106: class A(x1, x2, ...xs)(y1, ...ys) with +//β”‚ β•‘ l.107: class A(x1, x2, ...xs)(y1, ...ys) with //β”‚ ╙── ^^ //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'ys' -//β”‚ β•‘ l.107: fun fields = [x1, x2, xs, y1, ys] +//β”‚ β•‘ l.108: fun fields = [x1, x2, xs, y1, ys] //β”‚ β•‘ ^^ //β”‚ β•Ÿβ”€β”€ which references the symbol introduced here -//β”‚ β•‘ l.106: class A(x1, x2, ...xs)(y1, ...ys) with +//β”‚ β•‘ l.107: class A(x1, x2, ...xs)(y1, ...ys) with //β”‚ ╙── ^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” :todo new A(1, 2, 3)(4, 5, 6).fields +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.173: new A(1, 2, 3)(4, 5, 6).fields +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'A' expected 2 arguments, but got 3 +//β”‚ β•‘ l.107: class A(x1, x2, ...xs)(y1, ...ys) with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.108: fun fields = [x1, x2, xs, y1, ys] +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.173: new A(1, 2, 3)(4, 5, 6).fields +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'A' expected 1 argument, but got 3 +//β”‚ β•‘ l.107: class A(x1, x2, ...xs)(y1, ...ys) with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.108: fun fields = [x1, x2, xs, y1, ys] +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] ReferenceError: xs is not defined diff --git a/hkmc2/shared/src/test/mlscript/codegen/CurriedFunctions.mls b/hkmc2/shared/src/test/mlscript/codegen/CurriedFunctions.mls index fe52043950..3dedf45c6a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/CurriedFunctions.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/CurriedFunctions.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls b/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls index edc1ed3175..4f9fec780a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls @@ -1,3 +1,4 @@ +:. :js :global @@ -51,7 +52,7 @@ let y = 1 :e z = 1 //β”‚ ╔══[COMPILATION ERROR] Name not found: z -//β”‚ β•‘ l.52: z = 1 +//β”‚ β•‘ l.53: z = 1 //β”‚ ╙── ^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (sanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ throw "This code cannot be run as its compilation yielded an error." @@ -108,7 +109,7 @@ if true then foo = 0 else foo = 1 //β”‚ ╔══[PARSE ERROR] Expected end of input; found '=' keyword instead -//β”‚ β•‘ l.108: then foo = 0 +//β”‚ β•‘ l.109: then foo = 0 //β”‚ ╙── ^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (sanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let foo1; @@ -160,7 +161,7 @@ foo :fixme fun f() = foo = 0 //β”‚ ╔══[PARSE ERROR] Expected end of input; found '=' keyword instead -//β”‚ β•‘ l.161: fun f() = foo = 0 +//β”‚ β•‘ l.162: fun f() = foo = 0 //β”‚ ╙── ^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (sanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let f4; diff --git a/hkmc2/shared/src/test/mlscript/codegen/Do.mls b/hkmc2/shared/src/test/mlscript/codegen/Do.mls index 4bc1993423..c1e2256551 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Do.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Do.mls @@ -1,3 +1,4 @@ +:. :js @@ -17,7 +18,7 @@ do console.log("ok") :w do 1 //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.18: do 1 +//β”‚ β•‘ l.19: do 1 //β”‚ ╙── ^ do @@ -39,18 +40,18 @@ val f = case //β”‚ Split with nested patterns: //β”‚ > case //β”‚ > caseScrut is 0 then "null" -//β”‚ > let tmp:unused = member:console⁰.logβ€Ήmember:logβ€Ί("non-null") +//β”‚ > let tmp:unused = member:console⁰^term:f⁰.log("non-null") //β”‚ > caseScrut is 1 then "unit" //β”‚ > let res = "other" -//β”‚ > let tmp:unused = (member:Predef⁰.)printβ€Ήmember:printβ€Ί(res⁰) +//β”‚ > let tmp:unused = member:Predef⁰.print^term:f⁰(res⁰) //β”‚ > caseScrut is _ then res⁰ //β”‚ Expanded split with flattened patterns: //β”‚ > β€Ήif|whileβ€Ί //β”‚ > caseScrut is 0 then "null" -//β”‚ > let tmp:unused = member:console⁰.logβ€Ήmember:logβ€Ί("non-null") +//β”‚ > let tmp:unused = member:console⁰^term:f⁰.log("non-null") //β”‚ > caseScrut is 1 then "unit" //β”‚ > let res = "other" -//β”‚ > let tmp:unused = (member:Predef⁰.)printβ€Ήmember:printβ€Ί(res⁰) +//β”‚ > let tmp:unused = member:Predef⁰.print^term:f⁰(res⁰) //β”‚ > else res⁰ //β”‚ f = fun diff --git a/hkmc2/shared/src/test/mlscript/codegen/EarlyReturn.mls b/hkmc2/shared/src/test/mlscript/codegen/EarlyReturn.mls index d61a5367f2..6e65525d02 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/EarlyReturn.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/EarlyReturn.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/ElseLess.mls b/hkmc2/shared/src/test/mlscript/codegen/ElseLess.mls index 47e6789d3a..38e29ed2cc 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ElseLess.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ElseLess.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/ErasedTypes.mls b/hkmc2/shared/src/test/mlscript/codegen/ErasedTypes.mls index 90a50147fa..eb3f1813ec 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ErasedTypes.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ErasedTypes.mls @@ -1044,9 +1044,18 @@ fun viaTyped(x, y): Str = addInts(x, y) // A member is reached by *selection*, which - unlike a bare reference - is never auto-invoked, so its // signature is its own type. module Plain with - fun f: Int -> Int -//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ define Plain⁰ as class PlainΒΉ module PlainΒ²; end + fun f: Int -> Int = (x: Int) => x +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ define Plain⁰ as class PlainΒΉ +//β”‚ module PlainΒ² { +//β”‚ method f⁢ = fun f⁷: Function⁰ { +//β”‚ let lambda; +//β”‚ @private +//β”‚ define lambda as fun lambda⁴(x: Int⁰): ? { return x }; +//β”‚ return (lambda⁴ as! Function⁰) +//β”‚ } +//β”‚ }; +//β”‚ end declare module Ext with fun member: Int -> Int @@ -1061,7 +1070,7 @@ fun useExt() = useFn(Ext.member) fun usePlain() = useFn(Plain.f) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ define usePlain⁰ as fun usePlainΒΉ(): ? { return useFnΒΉ(PlainΒ².f⁢) }; end +//β”‚ define usePlain⁰ as fun usePlainΒΉ(): ? { return useFnΒΉ(PlainΒ².f⁷) }; end // *Applying* the selected member is a call on that function value, and since `Function` does not store its @@ -1079,7 +1088,7 @@ fun applyPlain() = useInt(Plain.f(1)) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ define applyPlain⁰ as fun applyPlainΒΉ(): ? { //β”‚ let tmp: ?; -//β”‚ set tmp = PlainΒ².f⁢(1); +//β”‚ set tmp = PlainΒ².f⁷(1); //β”‚ return useIntΒΉ((tmp as! Int⁰)) //β”‚ }; //β”‚ end @@ -1122,7 +1131,7 @@ fun selUntyped(): Int = @untyped Plain.f //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ define selUntyped⁰ as fun selUntypedΒΉ(): Int⁰ { //β”‚ let tmp: Unknown; -//β”‚ set tmp = PlainΒ².f⁢; +//β”‚ set tmp = PlainΒ².f⁷; //β”‚ return (tmp as! Int⁰) //β”‚ }; //β”‚ end @@ -1131,7 +1140,7 @@ fun selUntyped(): Int = @untyped Plain.f fun selTyped(): Int = Plain.f //β”‚ ═══[COMPILATION ERROR] Cannot use a value of type 'Function' at an unrelated type 'Int' //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ define selTyped⁰ as fun selTypedΒΉ(): Int⁰ { return PlainΒ².f⁢ }; end +//β”‚ define selTyped⁰ as fun selTypedΒΉ(): Int⁰ { return PlainΒ².f⁷ }; end // A lambda is disclaimed too: it reaches `LambdaRewriter` as the binding's RHS and is floated into a @@ -1141,10 +1150,10 @@ fun lamUntyped(): Int = @untyped ((x: Int) => x) //β”‚ define lamUntyped⁰ as fun lamUntypedΒΉ(): Int⁰ { //β”‚ let tmp: Unknown, lambda; //β”‚ @private -//β”‚ define lambda as fun lambda⁴(x: Int⁰): ? { +//β”‚ define lambda as fun lambda⁡(x: Int⁰): ? { //β”‚ return x //β”‚ }; -//β”‚ set tmp = lambda⁴; +//β”‚ set tmp = lambda⁡; //β”‚ return (tmp as! Int⁰) //β”‚ }; //β”‚ end @@ -1253,7 +1262,7 @@ fun tagged(x: Int) = fun describe() = Tag.label() describe() //β”‚ ╔══[WARNING] Objects are not yet lifted. -//β”‚ β•‘ l.1251: object Tag with +//β”‚ β•‘ l.1260: object Tag with //β”‚ ╙── ^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let describe; @@ -1280,7 +1289,7 @@ fun mk() = fun get() = Tag.label() new Holder //β”‚ ╔══[WARNING] Objects are not yet lifted. -//β”‚ β•‘ l.1277: object Tag with +//β”‚ β•‘ l.1286: object Tag with //β”‚ ╙── ^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let Holder; diff --git a/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls b/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls index db70877fd3..d85258ddbf 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls @@ -1,3 +1,4 @@ +:. :js @@ -10,13 +11,13 @@ data class Foo(val x) with Foo(42).x //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Elaborated tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Blk: -//β”‚ res = Sel: +//β”‚ res = NewSel: //β”‚ prefix = App: -//β”‚ lhs = Ref{sym=member:Foo⁰} of member:Foo⁰ +//β”‚ lhs = MemberRef of member:Foo⁰ //β”‚ rhs = Tup of Ls of //β”‚ Fld: //β”‚ term = Lit of IntLit of 42 -//β”‚ nme = Ident of "x" +//β”‚ id = Ident of "x" //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = 42 @@ -29,9 +30,9 @@ let foo = Foo(42) foo.x //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Elaborated tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Blk: -//β”‚ res = Sel: -//β”‚ prefix = Ref{sym=foo⁰} of foo⁰ -//β”‚ nme = Ident of "x" +//β”‚ res = NewSel: +//β”‚ prefix = SimpleRef of foo⁰ +//β”‚ id = Ident of "x" //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = 42 @@ -41,9 +42,9 @@ foo.x foo.y //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Elaborated tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Blk: -//β”‚ res = Sel: -//β”‚ prefix = Ref{sym=foo⁰} of foo⁰ -//β”‚ nme = Ident of "y" +//β”‚ res = NewSel: +//β”‚ prefix = SimpleRef of foo⁰ +//β”‚ id = Ident of "y" //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = 42 @@ -68,12 +69,12 @@ case //β”‚ branch = Match: //β”‚ scrutinee = Ref{sym=caseScrut⁰} of caseScrut⁰ //β”‚ pattern = Constructor: -//β”‚ target = Ref{sym=member:Foo⁰} of member:Foo⁰ +//β”‚ target = MemberRef of member:Foo⁰ //β”‚ arguments = S of Ls of //β”‚ Alias: //β”‚ pattern = Wildcard //β”‚ id = Ident of "a" -//β”‚ consequent = Else of Ref{sym=a⁰} of a⁰ +//β”‚ consequent = Else of SimpleRef of a⁰ //β”‚ tail = End //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let lambda; diff --git a/hkmc2/shared/src/test/mlscript/codegen/Formatting.mls b/hkmc2/shared/src/test/mlscript/codegen/Formatting.mls index 673fc1c5cf..e607eb602a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Formatting.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Formatting.mls @@ -1,3 +1,4 @@ +:. :js :sjs diff --git a/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls b/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls index b4eeaaa5f4..50f78aae01 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/Functions.mls b/hkmc2/shared/src/test/mlscript/codegen/Functions.mls index 9361b87dc7..4240a29969 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Functions.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Functions.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/FunctionsThis.mls b/hkmc2/shared/src/test/mlscript/codegen/FunctionsThis.mls index 55876338f3..25314b140d 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/FunctionsThis.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/FunctionsThis.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/FunnyOpen.mls b/hkmc2/shared/src/test/mlscript/codegen/FunnyOpen.mls index eb533cfa08..1dcae0a54f 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/FunnyOpen.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/FunnyOpen.mls @@ -1,3 +1,4 @@ +:. :js @@ -8,9 +9,6 @@ module Mod with :ctx open Mod //β”‚ Env: -//β”‚ pipee -> SelElem(RefElem(member:Mod),pipee,Some(member:pipee),true) -//β”‚ printt -> SelElem(RefElem(member:Mod),printt,Some(member:printt),true) -//β”‚ |>> -> SelElem(RefElem(member:Mod),pipee,Some(member:pipee),true) pipee //β”‚ = fun pipee @@ -25,14 +23,11 @@ pipee :e Mod.|>> //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position; found end of input instead -//β”‚ β•‘ l.26: Mod.|>> +//β”‚ β•‘ l.24: Mod.|>> //β”‚ ╙── ^ -//β”‚ ╔══[COMPILATION ERROR] Name not found: .|>> -//β”‚ β•‘ l.26: Mod.|>> +//β”‚ ╔══[COMPILATION ERROR] Name not found in wildcard opens: .|>> +//β”‚ β•‘ l.24: Mod.|>> //β”‚ ╙── ^^^^ -//β”‚ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type Mod. -//β”‚ β•‘ l.26: Mod.|>> -//β”‚ ╙── ^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -44,17 +39,17 @@ Mod.|>> :e open Mod { |>> } //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position; found end of input instead -//β”‚ β•‘ l.45: open Mod { |>> } +//β”‚ β•‘ l.40: open Mod { |>> } //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Illegal 'open' statement shape. -//β”‚ β•‘ l.45: open Mod { |>> } +//β”‚ β•‘ l.40: open Mod { |>> } //β”‚ ╙── ^^^^^^^^^^ :e open Mod { (|>>) } //β”‚ ╔══[COMPILATION ERROR] Illegal 'open' statement element. -//β”‚ β•‘ l.55: open Mod { (|>>) } +//β”‚ β•‘ l.50: open Mod { (|>>) } //β”‚ ╙── ^^^^^ 12 |>> print diff --git a/hkmc2/shared/src/test/mlscript/codegen/Getters.mls b/hkmc2/shared/src/test/mlscript/codegen/Getters.mls index f7791cbcaa..220bdfd69f 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Getters.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Getters.mls @@ -1,3 +1,4 @@ +:. :js :noInline @@ -300,7 +301,7 @@ data class Foo(x) with //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -fun foo(f) = +fun foo(f: {oops: Int}) = f.oops @@ -308,7 +309,7 @@ foo(Foo(42)) //β”‚ = 42 -fun foo(f) = +fun foo(f: {oops: Int}) = f.oops data class Foo(x) with @@ -317,8 +318,15 @@ data class Foo(x) with foo(Foo(42)) //β”‚ = 42 +:e :re foo(Foo(42))() -//β”‚ ═══[RUNTIME ERROR] TypeError: baseCall3 is not a function +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.323: foo(Foo(42))() +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Int' cannot be called like a function. +//β”‚ β•‘ l.312: fun foo(f: {oops: Int}) = +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/codegen/GlobalThis.mls b/hkmc2/shared/src/test/mlscript/codegen/GlobalThis.mls index 44e7977a36..263baa47c3 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/GlobalThis.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/GlobalThis.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls index 47fe350c4e..25a813dc67 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls @@ -1,3 +1,4 @@ +:. :js :noModuleCheck @@ -54,9 +55,16 @@ val Test = "oops" //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Test = "oops" +:e :re Test.foo() -//β”‚ ═══[RUNTIME ERROR] TypeError: Test2.foo is not a function +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.60: Test.foo() +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ String literal does not contain member 'foo' +//β”‚ β•‘ l.52: val Test = "oops" +//β”‚ ╙── ^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :sjs @@ -78,10 +86,10 @@ module Test with val x = 1 let x = 2 //β”‚ ╔══[COMPILATION ERROR] Name 'x' is already used -//β”‚ β•‘ l.79: let x = 2 +//β”‚ β•‘ l.87: let x = 2 //β”‚ β•‘ ^^^^^ //β”‚ β•Ÿβ”€β”€ by a member declared in the same block -//β”‚ β•‘ l.78: val x = 1 +//β”‚ β•‘ l.86: val x = 1 //β”‚ ╙── ^^^^^^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (sanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let Test4; @@ -185,7 +193,7 @@ module Whoops with val w: module Whoops = this fun g() = f() //β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'module Whoops' at an unrelated type 'module Whoops.Whoops' -//β”‚ β•‘ l.182: val v: module Whoops = this +//β”‚ β•‘ l.190: val v: module Whoops = this //β”‚ ╙── ^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let Whoops2; @@ -230,7 +238,7 @@ Whoops.Whoops.g() :e Runtime //β”‚ ╔══[COMPILATION ERROR] Name not found: Runtime -//β”‚ β•‘ l.231: Runtime +//β”‚ β•‘ l.239: Runtime //β”‚ ╙── ^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls b/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls index bb83055261..e6db47a1ed 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/ImperativeConditionals.mls b/hkmc2/shared/src/test/mlscript/codegen/ImperativeConditionals.mls index 8d47a28991..017df4b71d 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ImperativeConditionals.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ImperativeConditionals.mls @@ -1,3 +1,4 @@ +:. :js :ignore @@ -12,10 +13,10 @@ fun f(x) = if x < 0 return 0 Math.sqrt(x) //β”‚ ╔══[PARSE ERROR] Unexpected 'return' keyword here -//β”‚ β•‘ l.12: if x < 0 return 0 +//β”‚ β•‘ l.13: if x < 0 return 0 //β”‚ ╙── ^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Unrecognized term split (integer literal) -//β”‚ β•‘ l.12: if x < 0 return 0 +//β”‚ β•‘ l.13: if x < 0 return 0 //β”‚ ╙── ^ @@ -25,19 +26,19 @@ fun hasZeroElement(xs) = Cons(hd, tl) do set xs = tl Nil return false //β”‚ ╔══[PARSE ERROR] Unexpected 'return' keyword here -//β”‚ β•‘ l.24: Cons(0, tl) return true +//β”‚ β•‘ l.25: Cons(0, tl) return true //β”‚ ╙── ^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern split (integer literal). -//β”‚ β•‘ l.24: Cons(0, tl) return true +//β”‚ β•‘ l.25: Cons(0, tl) return true //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern split (identifier). -//β”‚ β•‘ l.24: Cons(0, tl) return true +//β”‚ β•‘ l.25: Cons(0, tl) return true //β”‚ ╙── ^^ //β”‚ ╔══[WARNING] This catch-all clause makes the following branches unreachable. -//β”‚ β•‘ l.24: Cons(0, tl) return true +//β”‚ β•‘ l.25: Cons(0, tl) return true //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ This branch is unreachable. -//β”‚ β•‘ l.24: Cons(0, tl) return true +//β”‚ β•‘ l.25: Cons(0, tl) return true //β”‚ ╙── ^^ diff --git a/hkmc2/shared/src/test/mlscript/codegen/ImportAlias.mls b/hkmc2/shared/src/test/mlscript/codegen/ImportAlias.mls index 146759266e..65a47617a3 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ImportAlias.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ImportAlias.mls @@ -1,3 +1,4 @@ +:. :js @@ -6,8 +7,11 @@ import "../../mlscript-compile/Example.mls" as AnotherExample :e Example.inc(0) //β”‚ ╔══[COMPILATION ERROR] Name not found: Example -//β”‚ β•‘ l.7: Example.inc(0) +//β”‚ β•‘ l.8: Example.inc(0) //β”‚ ╙── ^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'inc' has no resolved target +//β”‚ β•‘ l.8: Example.inc(0) +//β”‚ ╙── ^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :expect 1 @@ -21,8 +25,11 @@ import "../../mlscript-compile/Example.mjs" as JSExample :e Example.inc(0) //β”‚ ╔══[COMPILATION ERROR] Name not found: Example -//β”‚ β•‘ l.22: Example.inc(0) +//β”‚ β•‘ l.26: Example.inc(0) //β”‚ ╙── ^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'inc' has no resolved target +//β”‚ β•‘ l.26: Example.inc(0) +//β”‚ ╙── ^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :expect 1 @@ -33,13 +40,13 @@ JSExample.inc(0) :pe import "../../mlscript-compile/Example.mls" as //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position; found end of input instead -//β”‚ β•‘ l.34: import "../../mlscript-compile/Example.mls" as +//β”‚ β•‘ l.41: import "../../mlscript-compile/Example.mls" as //β”‚ ╙── ^ :e import "../../mlscript-compile/Example.mls" as "Examle" //β”‚ ╔══[COMPILATION ERROR] Expected identifier after 'as' in import statement -//β”‚ β•‘ l.40: import "../../mlscript-compile/Example.mls" as "Examle" +//β”‚ β•‘ l.47: import "../../mlscript-compile/Example.mls" as "Examle" //β”‚ ╙── ^^^^^^^^ import "../../mlscript-compile/Example.mls" diff --git a/hkmc2/shared/src/test/mlscript/codegen/ImportConflicts.mls b/hkmc2/shared/src/test/mlscript/codegen/ImportConflicts.mls index 6ed479eddf..813e461d1e 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ImportConflicts.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ImportConflicts.mls @@ -1,3 +1,4 @@ +:. :js @@ -25,7 +26,7 @@ fun Some3 = "333" open Option3 { Some3 } Some3 //β”‚ ╔══[WARNING] Imported name 'Some3' is shadowed by a name already defined in the same scope -//β”‚ β•‘ l.25: open Option3 { Some3 } +//β”‚ β•‘ l.26: open Option3 { Some3 } //β”‚ ╙── ^^^^^ //β”‚ = "333" @@ -45,8 +46,9 @@ module Option5 with { fun Some5 = 5 } fun Some5 = "555" fun bar = open Option5 + // Note that the wildcard import does not shadow names that are already bound in scope Some5 bar -//β”‚ = 5 +//β”‚ = "555" diff --git a/hkmc2/shared/src/test/mlscript/codegen/ImportExample.mls b/hkmc2/shared/src/test/mlscript/codegen/ImportExample.mls index 9f79666925..5a6592302f 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ImportExample.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ImportExample.mls @@ -1,3 +1,4 @@ +:. :js @@ -18,14 +19,18 @@ Example.pubFun :e :re Example.privFun -//β”‚ ╔══[COMPILATION ERROR] Module 'Example' does not contain member 'privFun' -//β”‚ β•‘ l.20: Example.privFun -//β”‚ ╙── ^^^^^^^^ -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'privFun' yielded 'undefined' +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.21: Example.privFun +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Module 'Example' does not contain member 'privFun' +//β”‚ β•‘ in ../../mlscript-compile/Example.mls +//β”‚ β•‘ l.9: module Example with ... +//β”‚ ╙── ^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :re Example.assertFail() -//β”‚ ═══[RUNTIME ERROR] Error: Assertion failed (mlscript-compile/Example.mls:27) +//β”‚ ═══[RUNTIME ERROR] Error: Assertion failed (mlscript-compile/Example.mls:28) let n = 42 @@ -42,6 +47,7 @@ n / 2 // * Builtin operators can be shadowed/rebound open Example +open Example { funnySlash, / } :sjs inc / 2 @@ -78,7 +84,7 @@ test / inc / 123 :re test / inc / 123 * 0.5 -//β”‚ ═══[RUNTIME ERROR] TypeError: inlinedVal1 is not a function +//β”‚ ═══[RUNTIME ERROR] TypeError: f2 is not a function let c = Curried(1) diff --git a/hkmc2/shared/src/test/mlscript/codegen/ImportJSClass.mls b/hkmc2/shared/src/test/mlscript/codegen/ImportJSClass.mls index 7a92461d77..184e52c0bc 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ImportJSClass.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ImportJSClass.mls @@ -1,3 +1,4 @@ +:. :js @@ -29,7 +30,7 @@ fun foo() = import "../../js/MyClass.mjs" () //β”‚ ╔══[COMPILATION ERROR] Imports must be at the top level -//β”‚ β•‘ l.29: import "../../js/MyClass.mjs" +//β”‚ β•‘ l.30: import "../../js/MyClass.mjs" //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/codegen/ImportJSModule.mls b/hkmc2/shared/src/test/mlscript/codegen/ImportJSModule.mls index ea0e5c828e..4fbc580c0a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ImportJSModule.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ImportJSModule.mls @@ -1,3 +1,4 @@ +:. :js import "../../js/Test.mjs" diff --git a/hkmc2/shared/src/test/mlscript/codegen/ImportMLs.mls b/hkmc2/shared/src/test/mlscript/codegen/ImportMLs.mls index abbb799d49..221cb9e7d2 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ImportMLs.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ImportMLs.mls @@ -1,3 +1,4 @@ +:. :js @@ -20,11 +21,11 @@ open Option :sjs None isDefined() //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ let x1; -//β”‚ x1 = Option.None; -//β”‚ if (x1 instanceof Option.Some.class) { +//β”‚ let x2; +//β”‚ x2 = Option.None; +//β”‚ if (x2 instanceof Option.Some.class) { //β”‚ return true -//β”‚ } else if (x1 instanceof Option.None.class) { +//β”‚ } else if (x2 instanceof Option.None.class) { //β”‚ return false //β”‚ } //β”‚ throw globalThis.Object.freeze(new globalThis.Error("match error")); @@ -81,25 +82,47 @@ None == Option.None :e :re Option.oops -//β”‚ ╔══[COMPILATION ERROR] Module 'Option' does not contain member 'oops' -//β”‚ β•‘ l.83: Option.oops -//β”‚ ╙── ^^^^^ -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'oops' yielded 'undefined' +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.84: Option.oops +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Module 'Option' does not contain member 'oops' +//β”‚ β•‘ in ../../mlscript-compile/Option.mls +//β”‚ β•‘ l.6: module Option with ... +//β”‚ ╙── ^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e open Option { oops } -//β”‚ ╔══[COMPILATION ERROR] Module 'Option' does not contain member 'oops' -//β”‚ β•‘ l.90: open Option { oops } -//β”‚ ╙── ^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.95: open Option { oops } +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Module 'Option' does not contain member 'oops' +//β”‚ β•‘ in ../../mlscript-compile/Option.mls +//β”‚ β•‘ l.6: module Option with ... +//β”‚ ╙── ^^^^^^ +:e +:re oops +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.106: oops +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Module 'Option' does not contain member 'oops' +//β”‚ β•‘ in ../../mlscript-compile/Option.mls +//β”‚ β•‘ l.6: module Option with ... +//β”‚ ╙── ^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e :re Option.None.oops -//β”‚ ╔══[COMPILATION ERROR] Object 'None' does not contain member 'oops' -//β”‚ β•‘ l.99: Option.None.oops -//β”‚ ╙── ^^^^^ -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'oops' yielded 'undefined' +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.118: Option.None.oops +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Object 'None' does not contain member 'oops' +//β”‚ β•‘ in ../../mlscript-compile/Option.mls +//β”‚ β•‘ l.10: object None +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/codegen/ImportMLsJS.mls b/hkmc2/shared/src/test/mlscript/codegen/ImportMLsJS.mls index 328c6a8ebb..edc4b035b3 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ImportMLsJS.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ImportMLsJS.mls @@ -1,3 +1,4 @@ +:. :js @@ -6,11 +7,11 @@ import "../../mlscript-compile/Option.mjs" :e Option.isDefined(new Option.Some(1)) -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found selection. -//β”‚ β•‘ l.8: Option.isDefined(new Option.Some(1)) -//β”‚ β•‘ ^^^^^^^^^^^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//β”‚ = true +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.9: Option.isDefined(new Option.Some(1)) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ ╙── Dynamic value cannot be instantiated with keyword 'new'. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. Option.isDefined(new! Option.Some(1)) //β”‚ = true diff --git a/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls b/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls index c08d02b2b6..2e4885893b 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls @@ -1,14 +1,15 @@ +:. :js module M with - fun (~) concat(a, b) = a + b + fun (~~) concat(a, b) = a + b open M :sjs fun foo() = - "a" ~ "b" ~ "c" + "a" ~~ "b" ~~ "c" foo() //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let foo; foo = function foo() { return "abc" }; return "abc" @@ -21,7 +22,7 @@ let name = "_" let display(balance) = balance //β”‚ display = fun display -"b" ~ display("-") +"b" ~~ display("-") //β”‚ = "b-" diff --git a/hkmc2/shared/src/test/mlscript/codegen/InlineLambdas.mls b/hkmc2/shared/src/test/mlscript/codegen/InlineLambdas.mls index e0f8e06c10..05beb46b73 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/InlineLambdas.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/InlineLambdas.mls @@ -1,3 +1,4 @@ +:. :js :noInline diff --git a/hkmc2/shared/src/test/mlscript/codegen/InlineMultiArgLists.mls b/hkmc2/shared/src/test/mlscript/codegen/InlineMultiArgLists.mls index e2f095a247..649f5b4ffd 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/InlineMultiArgLists.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/InlineMultiArgLists.mls @@ -1,3 +1,4 @@ +:. :js // * TODO: take `private` into accoutn and inline these single-use functions regardless of threshold diff --git a/hkmc2/shared/src/test/mlscript/codegen/Inliner.mls b/hkmc2/shared/src/test/mlscript/codegen/Inliner.mls index d6342946a5..0425d81741 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Inliner.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Inliner.mls @@ -1,3 +1,4 @@ +:. :js #config(deadBranchRemoval: true) diff --git a/hkmc2/shared/src/test/mlscript/codegen/InnerNameHygiene.mls b/hkmc2/shared/src/test/mlscript/codegen/InnerNameHygiene.mls index 2678d1eba3..c383885ec4 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/InnerNameHygiene.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/InnerNameHygiene.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/InterleavedRecords.mls b/hkmc2/shared/src/test/mlscript/codegen/InterleavedRecords.mls index 3bb5656eef..4c3b5650a5 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/InterleavedRecords.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/InterleavedRecords.mls @@ -1,3 +1,4 @@ +:. :js @@ -71,7 +72,7 @@ let rcd = a: 0 42 //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.72: 42 +//β”‚ β•‘ l.73: 42 //β”‚ ╙── ^^ //β”‚ > Hello! //β”‚ rcd = {a: 0} diff --git a/hkmc2/shared/src/test/mlscript/codegen/Juxtapositions.mls b/hkmc2/shared/src/test/mlscript/codegen/Juxtapositions.mls index 1e2dd77a1e..a8ec00817a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Juxtapositions.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Juxtapositions.mls @@ -1,3 +1,4 @@ +:. :js @@ -39,11 +40,14 @@ fun add(x, y) = x + y :w 42 add(2) -//β”‚ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 -//β”‚ β•‘ l.41: add(2) -//β”‚ ╙── ^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.42: add(2) +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'add' expected 2 arguments, but got 1 +//β”‚ β•‘ l.30: fun add(x, y) = x + y +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.40: 42 +//β”‚ β•‘ l.41: 42 //β”‚ ╙── ^^ //β”‚ ═══[RUNTIME ERROR] Error: Function 'add' expected 2 arguments but got 1 @@ -68,7 +72,7 @@ add(2) :e // Note: this used to parse as a juxtaposition (the curly braces were treated equally to an indented section) 42 { inc() } //β”‚ ╔══[COMPILATION ERROR] Unsupported refinement in this position. -//β”‚ β•‘ l.69: 42 { inc() } +//β”‚ β•‘ l.73: 42 { inc() } //β”‚ ╙── ^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -76,42 +80,42 @@ add(2) 42 { inc() } inc() //β”‚ ╔══[COMPILATION ERROR] Unsupported refinement in this position. -//β”‚ β•‘ l.76: 42 { inc() } +//β”‚ β•‘ l.80: 42 { inc() } //β”‚ ╙── ^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e // Note: idem 42 { add(2) } inc() //β”‚ ╔══[COMPILATION ERROR] Unsupported refinement in this position. -//β”‚ β•‘ l.84: 42 { add(2) } inc() +//β”‚ β•‘ l.88: 42 { add(2) } inc() //β”‚ ╙── ^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e // Note: idem 42 { add(2) } { inc() } //β”‚ ╔══[COMPILATION ERROR] Unsupported refinement in this position. -//β”‚ β•‘ l.91: 42 { add(2) } { inc() } +//β”‚ β•‘ l.95: 42 { add(2) } { inc() } //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e // Note: idem 42 { add(2), inc() } //β”‚ ╔══[COMPILATION ERROR] Unsupported refinement in this position. -//β”‚ β•‘ l.98: 42 { add(2), inc() } -//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.102: 42 { add(2), inc() } +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e 42 { add(2) inc() } //β”‚ ╔══[COMPILATION ERROR] Unsupported refinement in this position. -//β”‚ β•‘ l.105: 42 { add(2) inc() } +//β”‚ β•‘ l.109: 42 { add(2) inc() } //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e 42 { add(2) { inc() } } //β”‚ ╔══[COMPILATION ERROR] Unsupported refinement in this position. -//β”‚ β•‘ l.112: 42 { add(2) { inc() } } +//β”‚ β•‘ l.116: 42 { add(2) { inc() } } //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -147,9 +151,9 @@ inc(2) * 2 inc() * 1 add(2) //β”‚ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (operator application). -//β”‚ β•‘ l.147: inc() * 1 +//β”‚ β•‘ l.151: inc() * 1 //β”‚ β•‘ ^^^^^^^^^ -//β”‚ β•‘ l.148: add(2) +//β”‚ β•‘ l.152: add(2) //β”‚ ╙── ^^^^ //β”‚ = 42 @@ -158,9 +162,9 @@ inc(2) * 2 inc() add(2) //β”‚ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (juxtaposition). -//β”‚ β•‘ l.158: inc() +//β”‚ β•‘ l.162: inc() //β”‚ β•‘ ^^^^^ -//β”‚ β•‘ l.159: add(2) +//β”‚ β•‘ l.163: add(2) //β”‚ ╙── ^^^^ //β”‚ = 42 @@ -170,7 +174,7 @@ inc(2) * 2 42 inc() + 1 //β”‚ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (operator application). -//β”‚ β•‘ l.171: inc() + 1 +//β”‚ β•‘ l.175: inc() + 1 //β”‚ ╙── ^^^^^^^^^ //β”‚ = 42 @@ -179,7 +183,7 @@ inc(2) * 2 add(2) + 1 inc() //β”‚ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (operator application). -//β”‚ β•‘ l.179: add(2) + 1 +//β”‚ β•‘ l.183: add(2) + 1 //β”‚ ╙── ^^^^^^^^^^ //β”‚ = 43 @@ -188,7 +192,7 @@ inc(2) * 2 add(2) inc() + 1 //β”‚ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (operator application). -//β”‚ β•‘ l.189: inc() + 1 +//β”‚ β•‘ l.193: inc() + 1 //β”‚ ╙── ^^^^^^^^^ //β”‚ = 44 @@ -222,9 +226,12 @@ inc(2) * 2 (2 * 2 inc()) inc() -//β”‚ ╔══[COMPILATION ERROR] Expected 1 arguments, got 0 -//β”‚ β•‘ l.224: inc() -//β”‚ ╙── ^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.228: inc() +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'inc' expected 1 argument, but got 0 +//β”‚ β•‘ l.15: fun inc(x) = x + 1 +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: Function 'inc' expected 1 argument but got 0 :e @@ -233,9 +240,12 @@ inc() inc() ) inc() -//β”‚ ╔══[COMPILATION ERROR] Expected 1 arguments, got 0 -//β”‚ β•‘ l.235: inc() -//β”‚ ╙── ^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.242: inc() +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'inc' expected 1 argument, but got 0 +//β”‚ β•‘ l.15: fun inc(x) = x + 1 +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: Function 'inc' expected 1 argument but got 0 @@ -251,7 +261,7 @@ inc() :e 42 console.log //β”‚ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (selection). -//β”‚ β•‘ l.252: 42 console.log +//β”‚ β•‘ l.262: 42 console.log //β”‚ ╙── ^^^^^^^^^^^ //β”‚ = 42 diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls b/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls index 2f8c5fcfab..7ed2f8e4c8 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls @@ -1,3 +1,4 @@ +:. :js @@ -31,8 +32,12 @@ f(true, true) :e x => if x is 0 | 1 then 1 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in alternation +//β”‚ β•‘ l.34: x => if x is 0 | 1 then 1 +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ ╙── A type cannot be used as a runtime value. //β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (alternation) -//β”‚ β•‘ l.33: x => if x is 0 | 1 then 1 +//β”‚ β•‘ l.34: x => if x is 0 | 1 then 1 //β”‚ ╙── ^^^^^^^^^^ //β”‚ = fun @@ -66,7 +71,7 @@ if (foo of x => x + 1) then 1 :e if foo of x => x + 1 then 1 //β”‚ ╔══[COMPILATION ERROR] Unrecognized term split (application) -//β”‚ β•‘ l.67: if foo of x => x + 1 then 1 +//β”‚ β•‘ l.72: if foo of x => x + 1 then 1 //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: match error diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lazy.mls b/hkmc2/shared/src/test/mlscript/codegen/Lazy.mls index 265ac12db1..a14014b5eb 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lazy.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lazy.mls @@ -1,7 +1,8 @@ +:. :js -data class Lazy[out A](init: () -> A) with +data abstract class Lazy[out A](init: () -> A) with mut val cached: A | () = () fun get: A // fun eq(that: Lazy[A]) = this.cached == that.cached // TODO `this` diff --git a/hkmc2/shared/src/test/mlscript/codegen/LetPatterns.mls b/hkmc2/shared/src/test/mlscript/codegen/LetPatterns.mls index 548dc14ac8..0b65b2720e 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/LetPatterns.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/LetPatterns.mls @@ -1,3 +1,4 @@ +:. :js @@ -6,7 +7,7 @@ data class Some[out A](value: A) :todo let Some(x) = Some(42) //β”‚ ╔══[COMPILATION ERROR] Unsupported let binding shape -//β”‚ β•‘ l.7: let Some(x) = Some(42) +//β”‚ β•‘ l.8: let Some(x) = Some(42) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -19,7 +20,7 @@ let N = None :todo let (None) = N //β”‚ ╔══[COMPILATION ERROR] Unsupported let binding shape -//β”‚ β•‘ l.20: let (None) = N +//β”‚ β•‘ l.21: let (None) = N //β”‚ ╙── ^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -29,17 +30,17 @@ data class Foo(a) :todo let Foo(x) = Foo(1) in print(x) //β”‚ ╔══[COMPILATION ERROR] Unsupported let binding shape -//β”‚ β•‘ l.30: let Foo(x) = Foo(1) in print(x) +//β”‚ β•‘ l.31: let Foo(x) = Foo(1) in print(x) //β”‚ ╙── ^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Name not found: x -//β”‚ β•‘ l.30: let Foo(x) = Foo(1) in print(x) +//β”‚ β•‘ l.31: let Foo(x) = Foo(1) in print(x) //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :todo let Foo(x) and x > 0 = Foo(1) in print("ok") //β”‚ ╔══[COMPILATION ERROR] Unsupported let binding shape -//β”‚ β•‘ l.40: let Foo(x) and x > 0 = Foo(1) in print("ok") +//β”‚ β•‘ l.41: let Foo(x) and x > 0 = Foo(1) in print("ok") //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/codegen/MergeMatchArms.mls b/hkmc2/shared/src/test/mlscript/codegen/MergeMatchArms.mls index 0fc8dfc3b5..ca86b55a2a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/MergeMatchArms.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/MergeMatchArms.mls @@ -1,3 +1,4 @@ +:. :js #config(commentGeneratedCode: true) diff --git a/hkmc2/shared/src/test/mlscript/codegen/Misc.mls b/hkmc2/shared/src/test/mlscript/codegen/Misc.mls index 21c9c4ebb3..2fa1771979 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Misc.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Misc.mls @@ -1,3 +1,4 @@ +:. :js @@ -13,7 +14,7 @@ :ge // Shouldn't allow using `Int` in term position 1 : Int //β”‚ ╔══[COMPILATION ERROR] Symbol 'Int' is virtual (i.e., "compiler fiction"); cannot be used as a term -//β”‚ β•‘ l.14: 1 : Int +//β”‚ β•‘ l.15: 1 : Int //β”‚ ╙── ^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -68,28 +69,28 @@ bar(1, 2) :e wasm //β”‚ ╔══[COMPILATION ERROR] Module 'wasm' is virtual (i.e., "compiler fiction"); cannot be used directly -//β”‚ β•‘ l.69: wasm +//β”‚ β•‘ l.70: wasm //β”‚ ╙── ^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e wasm.i32 //β”‚ ╔══[COMPILATION ERROR] Module 'wasm' is virtual (i.e., "compiler fiction"); cannot be used directly -//β”‚ β•‘ l.76: wasm.i32 +//β”‚ β•‘ l.77: wasm.i32 //β”‚ ╙── ^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e wasm.i32.const //β”‚ ╔══[COMPILATION ERROR] Module 'wasm' is virtual (i.e., "compiler fiction"); cannot be used directly -//β”‚ β•‘ l.83: wasm.i32.const +//β”‚ β•‘ l.84: wasm.i32.const //β”‚ ╙── ^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e wasm.i32.const(42) //β”‚ ╔══[COMPILATION ERROR] WebAssembly intrinsics are not available when targeting JS -//β”‚ β•‘ l.90: wasm.i32.const(42) +//β”‚ β•‘ l.91: wasm.i32.const(42) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/codegen/ModuleMatching.mls b/hkmc2/shared/src/test/mlscript/codegen/ModuleMatching.mls index 8178706682..7b62c8047b 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ModuleMatching.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ModuleMatching.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/NestedLoops.mls b/hkmc2/shared/src/test/mlscript/codegen/NestedLoops.mls index e791ec99c5..31f7e7a819 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/NestedLoops.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/NestedLoops.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/NestedScoped.mls b/hkmc2/shared/src/test/mlscript/codegen/NestedScoped.mls index 4237a4d21f..19fa8184f7 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/NestedScoped.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/NestedScoped.mls @@ -1,3 +1,4 @@ +:. :js @@ -57,7 +58,7 @@ scope.locally of scope.locally of ( :e x //β”‚ ╔══[COMPILATION ERROR] Name not found: x -//β”‚ β•‘ l.58: x +//β”‚ β•‘ l.59: x //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -153,7 +154,7 @@ fun foo(x, y) = :ge scope.locally() //β”‚ ╔══[COMPILATION ERROR] Unsupported form for scope.locally. -//β”‚ β•‘ l.154: scope.locally() +//β”‚ β•‘ l.155: scope.locally() //β”‚ ╙── ^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -161,7 +162,7 @@ scope.locally() :ge scope.locally of (let x = 1 in x), (let y = 2 in y) //β”‚ ╔══[COMPILATION ERROR] Unsupported form for scope.locally. -//β”‚ β•‘ l.162: scope.locally of (let x = 1 in x), (let y = 2 in y) +//β”‚ β•‘ l.163: scope.locally of (let x = 1 in x), (let y = 2 in y) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -170,10 +171,10 @@ scope.locally of (let x = 1 in x), (let y = 2 in y) :ge scope.locally of (let x = 1 in x), (let y = 2 in x) //β”‚ ╔══[COMPILATION ERROR] Name not found: x -//β”‚ β•‘ l.171: scope.locally of (let x = 1 in x), (let y = 2 in x) +//β”‚ β•‘ l.172: scope.locally of (let x = 1 in x), (let y = 2 in x) //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Unsupported form for scope.locally. -//β”‚ β•‘ l.171: scope.locally of (let x = 1 in x), (let y = 2 in x) +//β”‚ β•‘ l.172: scope.locally of (let x = 1 in x), (let y = 2 in x) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/codegen/NestedTypes.mls b/hkmc2/shared/src/test/mlscript/codegen/NestedTypes.mls index 38a65ee381..3b35b7ecb8 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/NestedTypes.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/NestedTypes.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/NoFreeze.mls b/hkmc2/shared/src/test/mlscript/codegen/NoFreeze.mls index 408783a04a..3a9e185a20 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/NoFreeze.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/NoFreeze.mls @@ -1,3 +1,4 @@ +:. :js :ssjs diff --git a/hkmc2/shared/src/test/mlscript/codegen/NoInline.mls b/hkmc2/shared/src/test/mlscript/codegen/NoInline.mls index 6542cfc5f3..02da1f5d46 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/NoInline.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/NoInline.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/ObjectInit.mls b/hkmc2/shared/src/test/mlscript/codegen/ObjectInit.mls index 0130205358..e615b1fc73 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ObjectInit.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ObjectInit.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/Open.mls b/hkmc2/shared/src/test/mlscript/codegen/Open.mls index 2343238a5d..bab91e2990 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Open.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Open.mls @@ -1,3 +1,4 @@ +:. :js @@ -20,15 +21,18 @@ x + y :e z //β”‚ ╔══[COMPILATION ERROR] Name not found: z -//β”‚ β•‘ l.21: z +//β”‚ β•‘ l.22: z //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e open Foo { a } -//β”‚ ╔══[COMPILATION ERROR] Module 'Foo' does not contain member 'a' -//β”‚ β•‘ l.28: open Foo { a } -//β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.29: open Foo { a } +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Module 'Foo' does not contain member 'a' +//β”‚ β•‘ l.5: module Foo with +//β”‚ ╙── ^^^ open Bar { y, Baz } @@ -46,12 +50,27 @@ module Bar with val str = "oops" //β”‚ str = "oops" +:e open str { length, oops } +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.54: open str { length, oops } +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ String literal does not contain member 'oops' +//β”‚ β•‘ l.50: val str = "oops" +//β”‚ ╙── ^^^^^^ length //β”‚ = 4 +:e +:re oops === () -//β”‚ = false +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.67: oops === () +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ String literal does not contain member 'oops' +//β”‚ β•‘ l.50: val str = "oops" +//β”‚ ╙── ^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/codegen/OpenJS.mls b/hkmc2/shared/src/test/mlscript/codegen/OpenJS.mls index 5678f6aaec..a5404fafcd 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/OpenJS.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/OpenJS.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls b/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls index 46318322d6..2026cd2124 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls b/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls index 91640435b1..c0b992746b 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls @@ -1,3 +1,4 @@ +:. :js @@ -112,7 +113,8 @@ i.i1(20) //β”‚ = 120 -// * This is strange, but does not need to be an error +// The new resolver rejects duplicate constructor members. +:e :sjs class Foo(x, val y, z, val z, z) with let x = 1 @@ -122,6 +124,72 @@ class Foo(x, val y, z, val z, z) with print("this.y = " + this.y) print("z = " + z) print("this.z = " + this.z) +//β”‚ ╔══[COMPILATION ERROR] Duplicate definition of member named 'z'. +//β”‚ β•Ÿβ”€β”€ Defined at: +//β”‚ β•‘ l.119: class Foo(x, val y, z, val z, z) with +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Defined at: +//β”‚ β•‘ l.119: class Foo(x, val y, z, val z, z) with +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Defined at: +//β”‚ β•‘ l.119: class Foo(x, val y, z, val z, z) with +//β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.121: let y += 1 +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Class definition 'Foo' does not contain member 'y' +//β”‚ β•‘ l.119: class Foo(x, val y, z, val z, z) with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.120: let x = 1 +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•‘ l.121: let y += 1 +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•‘ l.122: print("x = " + x) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.123: ... (more lines omitted) ... +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.124: print("this.y = " + this.y) +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class definition 'Foo' does not contain member 'y' +//β”‚ β•‘ l.119: class Foo(x, val y, z, val z, z) with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.120: let x = 1 +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•‘ l.121: let y += 1 +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•‘ l.122: print("x = " + x) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.123: ... (more lines omitted) ... +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.125: print("z = " + z) +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Class definition 'Foo' does not contain member 'z' +//β”‚ β•‘ l.119: class Foo(x, val y, z, val z, z) with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.120: let x = 1 +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•‘ l.121: let y += 1 +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•‘ l.122: print("x = " + x) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.123: ... (more lines omitted) ... +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.126: print("this.z = " + this.z) +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class definition 'Foo' does not contain member 'z' +//β”‚ β•‘ l.119: class Foo(x, val y, z, val z, z) with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.120: let x = 1 +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•‘ l.121: let y += 1 +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•‘ l.122: print("x = " + x) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.123: ... (more lines omitted) ... +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let Foo9; //β”‚ Foo9 = function Foo(x, y, z, z1, z2) { @@ -132,48 +200,26 @@ class Foo(x, val y, z, val z, z) with //β”‚ Foo9.class = this //β”‚ } //β”‚ constructor(x, y, z, z1, z2) { -//β”‚ let tmp3, tmp4, tmp5, tmp6, tmp7; -//β”‚ this.y = y; -//β”‚ this.z = z1; -//β”‚ this.#z = z2; -//β”‚ this.#x = 1; -//β”‚ this.#y = this.y + 1; -//β”‚ tmp3 = "x = " + this.#x; -//β”‚ Predef.print(tmp3); -//β”‚ tmp4 = "y = " + this.#y; -//β”‚ Predef.print(tmp4); -//β”‚ tmp5 = "this.y = " + this.y; -//β”‚ Predef.print(tmp5); -//β”‚ tmp6 = "z = " + this.#z; -//β”‚ Predef.print(tmp6); -//β”‚ tmp7 = "this.z = " + this.z; -//β”‚ Predef.print(tmp7); +//β”‚ throw "This code cannot be run as its compilation yielded an error."; //β”‚ } -//β”‚ #z; -//β”‚ #x; -//β”‚ #y; //β”‚ toString() { return runtime.render(this); } //β”‚ static [definitionMetadata] = ["class", "Foo", [null, "y", null, "z", null]]; //β”‚ }); //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +:re Foo(10, 20, 30, 40, 50) -//β”‚ > x = 1 -//β”‚ > y = 21 -//β”‚ > this.y = 20 -//β”‚ > z = 50 -//β”‚ > this.z = 40 -//β”‚ = Foo(_, 20, _, 40, _) +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e class Foo(val z, val z) //β”‚ ╔══[COMPILATION ERROR] Duplicate definition of member named 'z'. //β”‚ β•Ÿβ”€β”€ Defined at: -//β”‚ β•‘ l.170: class Foo(val z, val z) +//β”‚ β•‘ l.216: class Foo(val z, val z) //β”‚ β•‘ ^^^^^ //β”‚ β•Ÿβ”€β”€ Defined at: -//β”‚ β•‘ l.170: class Foo(val z, val z) +//β”‚ β•‘ l.216: class Foo(val z, val z) //β”‚ ╙── ^^^^^ Foo(1, 2) diff --git a/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls b/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls index 1550f01f60..fe8d954166 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls @@ -1,3 +1,4 @@ +:. :js @@ -25,7 +26,14 @@ let g = f(_, 3) g(2) //β”‚ = [1, 2, 3] +:e foo(_, _, 3) of 1, 2 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.30: foo(_, _, 3) of 1, 2 +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Tuple literal cannot be called like a function. +//β”‚ β•‘ l.5: fun foo(x, y, z) = [x, y, z] +//β”‚ ╙── ^^^^^^^^^ //β”‚ = fun {foo(_, _, 3)} of 1, 2 @@ -87,8 +95,15 @@ let i = _(0, 1, _) i(print, 2) //β”‚ > 0 1 2 +:e :re i(print)(2) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.100: i(print)(2) +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function literal expected 2 arguments, but got 1 +//β”‚ β•‘ l.89: let i = _(0, 1, _) +//β”‚ ╙── ^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: Function 'i' expected 2 arguments but got 1 // :re @@ -96,7 +111,20 @@ i((x, y, z) => x + y + z, 2) //β”‚ = 3 // :re +:e i((...args) => [...args], _)(2) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.100: i(print)(2) +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Tuple literal cannot be called like a function. +//β”‚ β•‘ l.89: let i = _(0, 1, _) +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.115: i((...args) => [...args], _)(2) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Tuple literal cannot be called like a function. +//β”‚ β•‘ l.89: let i = _(0, 1, _) +//β”‚ ╙── ^^^^^^^^^ //β”‚ = fun @@ -110,8 +138,16 @@ i((...args) => [...args], _)(2) 123 |> ((_ + 1) >> (_ * 2)) //β”‚ = 248 +:e :re 123 |> (_ + 1 >> _ * 2) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in function type +//β”‚ β•‘ l.30: fun (|>) pipeInto[A, B](x: A, f: A -> B): B = f(x) +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Callback parameter list does not match its declared function type. +//β”‚ β•‘ in PartialApps.mls:143 +//β”‚ β•‘ l.143: 123 |> (_ + 1 >> _ * 2) +//β”‚ ╙── ^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: Function expected 2 arguments but got 1 (_ + 1 + _ * 2) of 123, 456 @@ -201,16 +237,20 @@ _ - _ of 1, 2 :pe |> 1 //β”‚ ╔══[PARSE ERROR] Expected end of input; found literal instead -//β”‚ β•‘ l.202: |> 1 +//β”‚ β•‘ l.238: |> 1 //β”‚ ╙── ^ //β”‚ = fun pipeInto :e :re |> (1) -//β”‚ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 -//β”‚ β•‘ l.210: |> (1) -//β”‚ ╙── ^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.246: |> (1) +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'pipeInto' expected 2 arguments, but got 1 +//β”‚ β•‘ in ../../mlscript-compile/Predef.mls +//β”‚ β•‘ l.30: fun (|>) pipeInto[A, B](x: A, f: A -> B): B = f(x) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] TypeError: f is not a function |> (1, _ - 2) @@ -227,21 +267,21 @@ _ - _ of 1, 2 :sjs 1 \ (_ - 2) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ let lambda8; -//β”‚ lambda8 = (undefined, function (_0) { return _0 - 2 }); -//β”‚ return Predef.passTo(1, lambda8) +//β”‚ let lambda32; +//β”‚ lambda32 = (undefined, function (_0) { return _0 - 2 }); +//β”‚ return Predef.passTo(1, lambda32) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = fun :sjs 1 \ (_ - 2)() //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ let lambda9, args; -//β”‚ lambda9 = (undefined, function (_0) { +//β”‚ let lambda33, args; +//β”‚ lambda33 = (undefined, function (_0) { //β”‚ return _0 - 2 //β”‚ }); //β”‚ args = []; -//β”‚ return runtime.safeCall(lambda9(1, ...args)) +//β”‚ return runtime.safeCall(lambda33(1, ...args)) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = -1 @@ -254,13 +294,13 @@ _ - _ of 1, 2 :w let f = if _ then 1 else 0 //β”‚ ╔══[WARNING] This catch-all clause makes the following branches unreachable. -//β”‚ β•‘ l.255: let f = if _ then 1 else 0 +//β”‚ β•‘ l.295: let f = if _ then 1 else 0 //β”‚ β•‘ ^^^^^^ //β”‚ β•Ÿβ”€β”€ This branch is unreachable. -//β”‚ β•‘ l.255: let f = if _ then 1 else 0 +//β”‚ β•‘ l.295: let f = if _ then 1 else 0 //β”‚ ╙── ^^^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ let f13; f13 = 1; +//β”‚ let f23; f23 = 1; //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ f = 1 @@ -270,8 +310,8 @@ fun f(x) = 0 then 1 _ then 2 //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ let f14; -//β”‚ f14 = function f(x3) { +//β”‚ let f24; +//β”‚ f24 = function f(x3) { //β”‚ let scrut; //β”‚ scrut = Predef.equals(x3, 0); //β”‚ if (scrut === true) { return 1 } diff --git a/hkmc2/shared/src/test/mlscript/codegen/PlainClasses.mls b/hkmc2/shared/src/test/mlscript/codegen/PlainClasses.mls index 9cc5bf21ba..4a04413ff6 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/PlainClasses.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/PlainClasses.mls @@ -1,3 +1,4 @@ +:. :js :global @@ -49,12 +50,19 @@ Foo //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = class Foo +:ge :re Foo() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.55: Foo() +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Foo' must be instantiated with 'new'. +//β”‚ β•‘ l.8: class Foo +//β”‚ ╙── ^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ return runtime.safeCall(Foo1()) +//β”‚ throw "This code cannot be run as its compilation yielded an error." //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ ═══[RUNTIME ERROR] TypeError: Class constructor Foo cannot be invoked without 'new' +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. data class Foo with { print("hi") } @@ -117,14 +125,10 @@ let t = test() //β”‚ > ok //β”‚ t = class Foo -:e +:expect Foo new t -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found reference. -//β”‚ β•‘ l.121: new t -//β”‚ β•‘ ^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ return globalThis.Object.freeze(new t()) +//β”‚ let classValue; classValue = t; return globalThis.Object.freeze(new classValue()) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ > hi //β”‚ = Foo @@ -136,14 +140,10 @@ new! t //β”‚ > hi //β”‚ = Foo -:e +:expect Foo new t() -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found reference. -//β”‚ β•‘ l.140: new t() -//β”‚ β•‘ ^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ return globalThis.Object.freeze(new t()) +//β”‚ let classValue1; classValue1 = t; return globalThis.Object.freeze(new classValue1()) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ > hi //β”‚ = Foo @@ -245,9 +245,9 @@ print(a.bar(1)) //β”‚ let a, tmp, tmp1; //β”‚ a = globalThis.Object.freeze(new Foo12()); //β”‚ Predef.print(a.x); -//β”‚ tmp = runtime.safeCall(a.foo(1)); +//β”‚ tmp = a.foo(1); //β”‚ Predef.print(tmp); -//β”‚ tmp1 = runtime.safeCall(a.bar(1)); +//β”‚ tmp1 = a.bar(1); //β”‚ return Predef.print(tmp1) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ > 1 diff --git a/hkmc2/shared/src/test/mlscript/codegen/PredefUsage.mls b/hkmc2/shared/src/test/mlscript/codegen/PredefUsage.mls index b4a2926f36..d03163ebce 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/PredefUsage.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/PredefUsage.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/Primes.mls b/hkmc2/shared/src/test/mlscript/codegen/Primes.mls index a0b2967457..13fd0f824c 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Primes.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Primes.mls @@ -1,3 +1,4 @@ +:. :js @@ -19,17 +20,20 @@ x' object Foo with val x' = 0 //β”‚ ╔══[COMPILATION ERROR] Illegal value member name: 'x'' -//β”‚ β•‘ l.20: val x' = 0 +//β”‚ β•‘ l.21: val x' = 0 //β”‚ β•‘ ^^ //β”‚ ╙── Member names must start with a letter or underscore, followed by letters, digits, or underscores. :e :re Foo.x' -//β”‚ ╔══[COMPILATION ERROR] Object 'Foo' does not contain member 'x'' -//β”‚ β•‘ l.28: Foo.x' -//β”‚ ╙── ^^^^^^ -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'x'' yielded 'undefined' +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.29: Foo.x' +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Object 'Foo' does not contain member 'x'' +//β”‚ β•‘ l.20: object Foo with +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. Foo.("x'") @@ -37,20 +41,26 @@ Foo.("x'") object Foo with mut val x' = 0 //β”‚ ╔══[COMPILATION ERROR] Illegal mutable value member name: 'x'' -//β”‚ β•‘ l.38: mut val x' = 0 +//β”‚ β•‘ l.42: mut val x' = 0 //β”‚ β•‘ ^^ //β”‚ ╙── Member names must start with a letter or underscore, followed by letters, digits, or underscores. :e :re set Foo.x' += 1 -//β”‚ ╔══[COMPILATION ERROR] Object 'Foo' does not contain member 'x'' -//β”‚ β•‘ l.46: set Foo.x' += 1 -//β”‚ ╙── ^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Object 'Foo' does not contain member 'x'' -//β”‚ β•‘ l.46: set Foo.x' += 1 -//β”‚ ╙── ^^^^^^ -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'x'' yielded 'undefined' +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.50: set Foo.x' += 1 +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Object 'Foo' does not contain member 'x'' +//β”‚ β•‘ l.41: object Foo with +//β”‚ ╙── ^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.50: set Foo.x' += 1 +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Object 'Foo' does not contain member 'x'' +//β”‚ β•‘ l.41: object Foo with +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. Foo.("x'") @@ -59,7 +69,7 @@ Foo.("x'") class Foo with fun f' = 1 //β”‚ ╔══[COMPILATION ERROR] Illegal function member name: 'f'' -//β”‚ β•‘ l.60: fun f' = 1 +//β”‚ β•‘ l.70: fun f' = 1 //β”‚ β•‘ ^^ //β”‚ ╙── Member names must start with a letter or underscore, followed by letters, digits, or underscores. @@ -67,7 +77,7 @@ class Foo with class Foo with class F' //β”‚ ╔══[COMPILATION ERROR] Illegal class member name: 'F'' -//β”‚ β•‘ l.68: class F' +//β”‚ β•‘ l.78: class F' //β”‚ β•‘ ^^ //β”‚ ╙── Member names must start with a letter or underscore, followed by letters, digits, or underscores. @@ -75,7 +85,7 @@ class Foo with class Foo with object F' //β”‚ ╔══[COMPILATION ERROR] Illegal object member name: 'F'' -//β”‚ β•‘ l.76: object F' +//β”‚ β•‘ l.86: object F' //β”‚ β•‘ ^^ //β”‚ ╙── Member names must start with a letter or underscore, followed by letters, digits, or underscores. @@ -83,7 +93,7 @@ class Foo with class Foo with module F' //β”‚ ╔══[COMPILATION ERROR] Illegal module member name: 'F'' -//β”‚ β•‘ l.84: module F' +//β”‚ β•‘ l.94: module F' //β”‚ β•‘ ^^ //β”‚ ╙── Member names must start with a letter or underscore, followed by letters, digits, or underscores. diff --git a/hkmc2/shared/src/test/mlscript/codegen/PrivateConstructors.mls b/hkmc2/shared/src/test/mlscript/codegen/PrivateConstructors.mls new file mode 100644 index 0000000000..eeeb13e150 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/codegen/PrivateConstructors.mls @@ -0,0 +1,20 @@ +:. +:js + + +:todo +class Foo with + private constructor(val x, val y) +//β”‚ ╔══[WARNING] This annotation has no effect +//β”‚ β•‘ l.7: private constructor(val x, val y) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ A target term is expected at the end of block +//β”‚ β•‘ l.7: private constructor(val x, val y) +//β”‚ ╙── ^ + +:breakme +:e +new Foo(1, 2) +//β”‚ = Foo + + diff --git a/hkmc2/shared/src/test/mlscript/codegen/PrivateMemberExtrusion.mls b/hkmc2/shared/src/test/mlscript/codegen/PrivateMemberExtrusion.mls index be71632d7d..ccb42e44cd 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/PrivateMemberExtrusion.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/PrivateMemberExtrusion.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/Pwd.mls b/hkmc2/shared/src/test/mlscript/codegen/Pwd.mls index 74db669543..a10ecf6a8e 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Pwd.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Pwd.mls @@ -1,13 +1,14 @@ +:. :js :sjs -let folderName1 = process.env.PWD.split("/").pop() +let folderName1 = process.env!PWD.split("/").pop() in let folderName2 = process.cwd().split("/").pop() in folderName2 === folderName1 || folderName2 === "shared" //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let folderName1, folderName2, tmp, tmp1, tmp2, tmp3; -//β”‚ tmp = runtime.safeCall(globalThis.process.env.PWD.split("/")); +//β”‚ tmp = runtime.safeCall(globalThis.process.env["PWD"].split("/")); //β”‚ folderName1 = runtime.safeCall(tmp.pop()); //β”‚ tmp1 = runtime.safeCall(globalThis.process.cwd()); //β”‚ tmp2 = runtime.safeCall(tmp1.split("/")); diff --git a/hkmc2/shared/src/test/mlscript/codegen/QQImport.mls b/hkmc2/shared/src/test/mlscript/codegen/QQImport.mls index 3bbaa93b76..3abcf21223 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/QQImport.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/QQImport.mls @@ -1,3 +1,4 @@ +:. :js :qq diff --git a/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls b/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls index 48c5e6d59e..5b309a7a46 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/RecordSpreads.mls b/hkmc2/shared/src/test/mlscript/codegen/RecordSpreads.mls index 66f61d5291..071093b122 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/RecordSpreads.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/RecordSpreads.mls @@ -1,3 +1,4 @@ +:. :js @@ -12,7 +13,7 @@ let rcd = :e (...rcd) //β”‚ ╔══[COMPILATION ERROR] Illegal position for '...' spread operator. -//β”‚ β•‘ l.13: (...rcd) +//β”‚ β•‘ l.14: (...rcd) //β”‚ ╙── ^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/codegen/Repl.mls b/hkmc2/shared/src/test/mlscript/codegen/Repl.mls index ea9fe5297c..7deec19bb2 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Repl.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Repl.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/RuntimeUsage.mls b/hkmc2/shared/src/test/mlscript/codegen/RuntimeUsage.mls index b1d56e52d2..e2d02d1b69 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/RuntimeUsage.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/RuntimeUsage.mls @@ -1,3 +1,4 @@ +:. :js @@ -62,29 +63,29 @@ print of Tuple.get([1, 2, 3, 4], -1) print of Tuple.get([1, 2, 3, 4], 5) //β”‚ ═══[RUNTIME ERROR] RangeError: Tuple.get: index out of bounds -print of Str.leaveOut("hello, world", 2) +print of StrOps.leaveOut("hello, world", 2) //β”‚ > llo, world -print of Str.leaveOut("hello, world", -2) +print of StrOps.leaveOut("hello, world", -2) //β”‚ > ld -print of Str.get("hello, world", 2) +print of StrOps.get("hello, world", 2) //β”‚ > l :re -print of Str.get("hello, world", 22) +print of StrOps.get("hello, world", 22) //β”‚ ═══[RUNTIME ERROR] RangeError: Str.get: index out of bounds -print of Str.get("hello, world", -1) +print of StrOps.get("hello, world", -1) //β”‚ > d -print of Str.startsWith("hello, world", "hello") +print of StrOps.startsWith("hello, world", "hello") //β”‚ > true -print of Str.startsWith("hello, world", "") +print of StrOps.startsWith("hello, world", "") //β”‚ > true -print of Str.startsWith("hello, world", "farewell") +print of StrOps.startsWith("hello, world", "farewell") //β”‚ > false diff --git a/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls b/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls index d2150de51e..6ca71298ed 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls @@ -1,6 +1,8 @@ +:. :js -// * In this test, we use `id` to wrap functions so that compile-time arity checks get bypassed. +// * Use dynamic values to exercise runtime checks even for statically invalid operations. +fun unchecked(x): dyn = x :ssjs @@ -12,7 +14,7 @@ f(2, 3) //β”‚ runtime.checkArgs("f", 2, true, arguments.length); //β”‚ return x + y //β”‚ }; -//β”‚ block$res1 = 5; +//β”‚ block$res2 = 5; //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = 5 @@ -27,17 +29,17 @@ f(2, 3) //β”‚ return x + y //β”‚ }; //β”‚ f1 = f2; -//β”‚ block$res2 = 5; +//β”‚ block$res3 = 5; //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = 5 //β”‚ f = fun f :re -f(2) +unchecked(f)(2) //β”‚ ═══[RUNTIME ERROR] Error: Function 'f' expected 2 arguments but got 1 :re -f(2, 3, 4) +unchecked(f)(2, 3, 4) //β”‚ ═══[RUNTIME ERROR] Error: Function 'f' expected 2 arguments but got 3 @@ -45,18 +47,18 @@ f(2, 3, 4) :expect NaN :noSanityCheck fun f2(x, y) = x + y -id(f2)(2) +unchecked(f2)(2) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (sanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let f21; -//β”‚ f21 = function f2(x, y) { return x + y }; block$res5 = runtime.safeCall(f21(2)); +//β”‚ f21 = function f2(x, y) { return x + y }; block$res6 = runtime.safeCall(f21(2)); //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = NaN :ssjs :re -id(f)(2) +unchecked(f)(2) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (sanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ block$res6 = runtime.safeCall(f1(2)); +//β”‚ block$res7 = runtime.safeCall(f1(2)); //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ ═══[RUNTIME ERROR] Error: Function 'f' expected 2 arguments but got 1 @@ -64,7 +66,7 @@ id(f)(2) :ssjs // :re // Argument numbers are no longer checked for lambdas, including extra parameter lists fun f(x)(y, z) = x + y + z -id(f)(3)(4) +unchecked(f)(3)(4) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (sanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let f3; //β”‚ f3 = function f(x) { @@ -75,7 +77,7 @@ id(f)(3)(4) //β”‚ return tmp + z //β”‚ } //β”‚ }; -//β”‚ block$res7 = runtime.safeCall(f3(3)(4)); +//β”‚ block$res8 = runtime.safeCall(f3(3)(4)); //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = NaN @@ -83,13 +85,13 @@ id(f)(3)(4) // :ssjs :expect NaN :noSanityCheck -let f = (x, y) => x + y in f(2) +let f = (x, y) => x + y in unchecked(f)(2) //β”‚ = NaN // :ssjs :re let f = (x, y) => x + y -f(2) +unchecked(f)(2) //β”‚ ═══[RUNTIME ERROR] Error: Function 'f' expected 2 arguments but got 1 //β”‚ f = fun f @@ -99,14 +101,14 @@ f(2) :noSanityCheck data class Cls(x, y) with fun f(z, p) = x + y + z + p -id(Cls(1, 2)).f(3) +unchecked(Cls(1, 2)).f(3) //β”‚ = NaN // :ssjs :re data class Cls(x, y) with fun f(z, p) = x + y + z + p -id(Cls(1, 2)).f(3) +unchecked(Cls(1, 2)).f(3) //β”‚ ═══[RUNTIME ERROR] Error: Function 'f' expected 2 arguments but got 1 @@ -115,7 +117,7 @@ id(Cls(1, 2)).f(3) // :re // Argument numbers are no longer checked for lambdas, including extra parameter lists data class Cls(x, y) with fun f(z, p)(q, s) = x + y + z + p + q + s -id(Cls(1, 2)).f(3, 4)(5) +unchecked(Cls(1, 2)).f(3, 4)(5) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (sanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let Cls5, tmp2, callPrefix; //β”‚ Cls5 = function Cls(x, y) { @@ -146,7 +148,7 @@ id(Cls(1, 2)).f(3, 4)(5) //β”‚ }); //β”‚ tmp2 = runtime.checkCall(Cls5(1, 2)); //β”‚ callPrefix = runtime.safeCall(tmp2.f(3, 4)); -//β”‚ block$res12 = runtime.safeCall(callPrefix(5)); +//β”‚ block$res13 = runtime.safeCall(callPrefix(5)); //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = NaN @@ -176,7 +178,7 @@ module M with :ssjs :re -if M.A(1).y is +if unchecked(M.A(1)).y is x and x == 1 then x else 0 //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (sanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” @@ -184,7 +186,7 @@ if M.A(1).y is //β”‚ tmp3 = runtime.checkCall(M1.A(1)); //β”‚ scrut = runtime.checkSelect(tmp3.y, "y", tmp3); //β”‚ scrut1 = runtime.checkCall(Predef.equals(scrut, 1)); -//β”‚ if (scrut1 === true) { block$res17 = scrut; } else { block$res17 = 0; } +//β”‚ if (scrut1 === true) { block$res18 = scrut; } else { block$res18 = 0; } //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'y' yielded 'undefined' @@ -192,7 +194,7 @@ if M.A(1).y is // :ssjs :re -M.A(1).y +unchecked(M.A(1)).y console.log() //β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'y' yielded 'undefined' @@ -200,22 +202,22 @@ M.A(1).y // :ssjs :noSanityCheck -M.A(1).y +unchecked(M.A(1)).y console.log() //β”‚ > undefined :noSanityCheck :expect false -M.A(2).y > 1 +unchecked(M.A(2)).y > 1 //β”‚ = false :re -M.A(2).y > 1 +unchecked(M.A(2)).y > 1 //β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'y' yielded 'undefined' // :ssjs :re -M.A(1).g(0) +unchecked(M.A(1)).g(0) //β”‚ ═══[RUNTIME ERROR] TypeError: tmp8.g is not a function @@ -229,7 +231,7 @@ M.A(1).f(0) :ssjs M.g(1, 2) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (sanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ block$res24 = 3; +//β”‚ block$res25 = 3; //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = 3 diff --git a/hkmc2/shared/src/test/mlscript/codegen/Scoping.mls b/hkmc2/shared/src/test/mlscript/codegen/Scoping.mls index 7f55899ea7..73b44f685e 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Scoping.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Scoping.mls @@ -1,3 +1,4 @@ +:. :js :expect 4 diff --git a/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls b/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls index 3b8ed23c41..062fe5bb07 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/SetStmt.mls b/hkmc2/shared/src/test/mlscript/codegen/SetStmt.mls index 7d39e7d169..9c616ff6b7 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SetStmt.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SetStmt.mls @@ -20,9 +20,22 @@ g set g.0 |>= Math.sign +// Without parentheses, the placeholder abstracts the entire assignment; +// its body would pass a number to the pipe operator as a function. +:e set g.2 |>= Math.pow(_, 5) +//β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Num' at an unrelated type 'Function' +//β”‚ β•‘ l.26: set g.2 |>= Math.pow(_, 5) +//β”‚ ╙── ^^^^^^^^^^^^^^ //β”‚ = fun g //β”‚ = [1, 1, 2] + +// Parenthesizing the callback scopes the placeholder to Math.pow. +set g.2 |>= (Math.pow(_, 5)) + +:expect [1, 1, 32] +g +//β”‚ = [1, 1, 32] diff --git a/hkmc2/shared/src/test/mlscript/codegen/ShortCircuitReturn.mls b/hkmc2/shared/src/test/mlscript/codegen/ShortCircuitReturn.mls index e6c99323be..25ba61c9ee 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ShortCircuitReturn.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ShortCircuitReturn.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/SimplePatMat.mls b/hkmc2/shared/src/test/mlscript/codegen/SimplePatMat.mls index cf92e966a3..663e3a6bfe 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SimplePatMat.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SimplePatMat.mls @@ -1,3 +1,4 @@ +:. :js @@ -7,7 +8,7 @@ data class Some[out A](value: A) :todo let Some(x) = Some(42) //β”‚ ╔══[COMPILATION ERROR] Unsupported let binding shape -//β”‚ β•‘ l.8: let Some(x) = Some(42) +//β”‚ β•‘ l.9: let Some(x) = Some(42) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/codegen/SingletonInit.mls b/hkmc2/shared/src/test/mlscript/codegen/SingletonInit.mls index 463e88cf59..92b9aa6b93 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SingletonInit.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SingletonInit.mls @@ -1,3 +1,4 @@ +:. :js @@ -25,7 +26,7 @@ module A with A val y = 2 //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.25: A +//β”‚ β•‘ l.26: A //β”‚ ╙── ^ //β”‚ > 1 undefined diff --git a/hkmc2/shared/src/test/mlscript/codegen/SourceLocations.mls b/hkmc2/shared/src/test/mlscript/codegen/SourceLocations.mls index 0ff4d0f70e..82775ba8e7 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SourceLocations.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SourceLocations.mls @@ -1,3 +1,4 @@ +:. :js open annotations @@ -8,10 +9,10 @@ class First @inline class Second //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.6: @inline +//β”‚ β•‘ l.7: @inline //β”‚ ╙── ^^^^^^^ //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.8: @inline +//β”‚ β•‘ l.9: @inline //β”‚ ╙── ^^^^^^^ // Annotations on let bindings point to the annotation occurrence. @@ -19,7 +20,7 @@ class Second @tailrec let value = 1 //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.19: @tailrec +//β”‚ β•‘ l.20: @tailrec //β”‚ ╙── ^^^^^^^^ //β”‚ value = 1 @@ -28,7 +29,7 @@ let value = 1 @inline fun abstractFunction() //β”‚ ╔══[WARNING] Only functions with a body may be marked as @inline. -//β”‚ β•‘ l.28: @inline +//β”‚ β•‘ l.29: @inline //β”‚ ╙── ^^^^^^^ // This error is emitted from lowered IR after the prefix has become a temporary. @@ -37,6 +38,6 @@ fun abstractFunction() let index = 0 [(x => x)].(index)(1) //β”‚ ╔══[COMPILATION ERROR] Cannot determine if the dynamic selection is a function object. -//β”‚ β•‘ l.38: [(x => x)].(index)(1) +//β”‚ β•‘ l.39: [(x => x)].(index)(1) //β”‚ ╙── ^^^^^^^^^^^^^^^^^ //β”‚ index = 0 diff --git a/hkmc2/shared/src/test/mlscript/codegen/SpecialJSNames.mls b/hkmc2/shared/src/test/mlscript/codegen/SpecialJSNames.mls index 6774b165fb..17f8af21b2 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SpecialJSNames.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SpecialJSNames.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls b/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls index 1297e4b80a..a2e51df7f9 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls @@ -1,3 +1,4 @@ +:. :js @@ -23,8 +24,15 @@ foo(1, ...[2, 3], 4) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = [1, 2, 3, 4] +:e :re foo(...a) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.29: foo(...a) +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'foo' expected 4 arguments, but got 3 +//β”‚ β•‘ l.12: fun foo(w, x, y, z) = [w, x, y, z] +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: Function 'foo' expected 4 arguments but got 3 @@ -59,9 +67,21 @@ data class A(...r) let x = new A(1, 2, 3) x.r //β”‚ ╔══[COMPILATION ERROR] Spread parameters are not supported in class parameters. -//β”‚ β•‘ l.58: data class A(...r) +//β”‚ β•‘ l.66: data class A(...r) //β”‚ ╙── ^^^^ -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'r' yielded 'undefined' +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.67: let x = new A(1, 2, 3) +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'A' expected 0 arguments, but got 3 +//β”‚ β•‘ l.66: data class A(...r) +//β”‚ ╙── ^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.68: x.r +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'A' does not contain member 'r' +//β”‚ β•‘ l.67: let x = new A(1, 2, 3) +//β”‚ ╙── ^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //β”‚ ═══[RUNTIME ERROR] Expected: '[1, 2, 3]', got: 'undefined' //β”‚ x = A() @@ -71,13 +91,21 @@ data class A(...r) with fun getR = r new A(1, 2, 3).getR //β”‚ ╔══[COMPILATION ERROR] Spread parameters are not supported in class parameters. -//β”‚ β•‘ l.70: data class A(...r) with +//β”‚ β•‘ l.90: data class A(...r) with //β”‚ ╙── ^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.92: new A(1, 2, 3).getR +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'A' expected 0 arguments, but got 3 +//β”‚ β•‘ l.90: data class A(...r) with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.91: fun getR = r +//β”‚ ╙── ^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'r' -//β”‚ β•‘ l.71: fun getR = r +//β”‚ β•‘ l.91: fun getR = r //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ which references the symbol introduced here -//β”‚ β•‘ l.70: data class A(...r) with +//β”‚ β•‘ l.90: data class A(...r) with //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] ReferenceError: r is not defined //β”‚ ═══[RUNTIME ERROR] Expected: '[1, 2, 3]', got: 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/codegen/SwitchSpecialization.mls b/hkmc2/shared/src/test/mlscript/codegen/SwitchSpecialization.mls index 28920cc604..17a8a402f3 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SwitchSpecialization.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SwitchSpecialization.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/TailRecFormerFailure.mls b/hkmc2/shared/src/test/mlscript/codegen/TailRecFormerFailure.mls index 804a65e63c..dc07b292f2 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/TailRecFormerFailure.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/TailRecFormerFailure.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/This.mls b/hkmc2/shared/src/test/mlscript/codegen/This.mls index 0ed3ece219..559cd0806a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/This.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/This.mls @@ -1,3 +1,4 @@ +:. :js @@ -7,8 +8,11 @@ fun test(x) = [this.a, x] //β”‚ ╔══[COMPILATION ERROR] Cannot use 'this' outside of an object scope -//β”‚ β•‘ l.8: [this.a, x] +//β”‚ β•‘ l.9: [this.a, x] //β”‚ ╙── ^^^^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'a' has no resolved target +//β”‚ β•‘ l.9: [this.a, x] +//β”‚ ╙── ^^^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let test; //β”‚ test = function test(x) { throw "This code cannot be run as its compilation yielded an error." }; diff --git a/hkmc2/shared/src/test/mlscript/codegen/Throw.mls b/hkmc2/shared/src/test/mlscript/codegen/Throw.mls index 6decf80e0a..4e57453244 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Throw.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Throw.mls @@ -1,13 +1,14 @@ +:. :js :re -throw Error("e") +throw new Error("e") //β”‚ ═══[RUNTIME ERROR] Error: e :re -fun f(x, y) = throw Error("e") +fun f(x, y) = throw new Error("e") f(2, 2) //β”‚ ═══[RUNTIME ERROR] Error: e @@ -15,7 +16,7 @@ f(2, 2) :re fun f(x, y) = if x == y then x + y - x == (y + 1) then throw Error("not equal") + x == (y + 1) then throw new Error("not equal") f(3, 2) //β”‚ ═══[RUNTIME ERROR] Error: not equal @@ -27,15 +28,15 @@ f(2, 3) :re :sjs fun f(x) = - let y = throw Error("e") + let y = throw new Error("e") return y f(1) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let f2; //β”‚ f2 = function f(x) { -//β”‚ throw runtime.safeCall(globalThis.Error("e")) +//β”‚ throw globalThis.Object.freeze(new globalThis.Error("e")) //β”‚ }; -//β”‚ throw runtime.safeCall(globalThis.Error("e")) +//β”‚ throw globalThis.Object.freeze(new globalThis.Error("e")) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ ═══[RUNTIME ERROR] Error: e @@ -43,20 +44,20 @@ f(1) :re :sjs fun f(x) = - throw (if x then Error("x") else Error("y")) + throw (if x then new Error("x") else new Error("y")) f(false) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let f3; //β”‚ f3 = function f(x) { //β”‚ if (x === true) { -//β”‚ throw runtime.safeCall(globalThis.Error("x")) +//β”‚ throw globalThis.Object.freeze(new globalThis.Error("x")) //β”‚ } -//β”‚ throw runtime.safeCall(globalThis.Error("y")); +//β”‚ throw globalThis.Object.freeze(new globalThis.Error("y")); //β”‚ }; //β”‚ if (false === true) { -//β”‚ throw runtime.safeCall(globalThis.Error("x")) +//β”‚ throw globalThis.Object.freeze(new globalThis.Error("x")) //β”‚ } -//β”‚ throw runtime.safeCall(globalThis.Error("y")); +//β”‚ throw globalThis.Object.freeze(new globalThis.Error("y")); //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ ═══[RUNTIME ERROR] Error: y @@ -92,7 +93,7 @@ foo(42) //β”‚ ═══[RUNTIME ERROR] 42 :re -foo(Error("hi")) +foo(new Error("hi")) //β”‚ > caught something else //β”‚ ═══[RUNTIME ERROR] Error: hi diff --git a/hkmc2/shared/src/test/mlscript/codegen/TraceLog.mls b/hkmc2/shared/src/test/mlscript/codegen/TraceLog.mls index bc14133686..80735a4ae3 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/TraceLog.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/TraceLog.mls @@ -1,3 +1,4 @@ +:. :js :traceJS :noSanityCheck diff --git a/hkmc2/shared/src/test/mlscript/codegen/TraceLogIndent.mls b/hkmc2/shared/src/test/mlscript/codegen/TraceLogIndent.mls index 0f281880a1..565a88a1a3 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/TraceLogIndent.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/TraceLogIndent.mls @@ -1,3 +1,4 @@ +:. :js fun plus(x, y) = x + y @@ -10,9 +11,12 @@ fun g(z) = z == -1 then 42 z == 0 then plus(0) z > 0 then g(z - 1) -//β”‚ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 -//β”‚ β•‘ l.11: z == 0 then plus(0) -//β”‚ ╙── ^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.12: z == 0 then plus(0) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'plus' expected 2 arguments, but got 1 +//β”‚ β•‘ l.4: fun plus(x, y) = x + y +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^ fun f(x, y)(z) = x + y + g(z) diff --git a/hkmc2/shared/src/test/mlscript/codegen/TypedIRShortCircuit.mls b/hkmc2/shared/src/test/mlscript/codegen/TypedIRShortCircuit.mls index cd09c336ec..d3c46670ad 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/TypedIRShortCircuit.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/TypedIRShortCircuit.mls @@ -1,3 +1,4 @@ +:. :global :js @@ -11,7 +12,7 @@ val n: Int = true && 42 n //β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Bool' at an unrelated type 'Int' -//β”‚ β•‘ l.11: val n: Int = true && 42 +//β”‚ β•‘ l.12: val n: Int = true && 42 //β”‚ ╙── ^^^^^^^^^^ //β”‚ = 42 //β”‚ n = 42 @@ -21,7 +22,7 @@ n val s: Str = false || "ok" s //β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Bool' at an unrelated type 'Str' -//β”‚ β•‘ l.21: val s: Str = false || "ok" +//β”‚ β•‘ l.22: val s: Str = false || "ok" //β”‚ ╙── ^^^^^^^^^^^^^ //β”‚ = "ok" //β”‚ s = "ok" diff --git a/hkmc2/shared/src/test/mlscript/codegen/UnitValue.mls b/hkmc2/shared/src/test/mlscript/codegen/UnitValue.mls index b9c3ef6a79..58683fbf65 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/UnitValue.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/UnitValue.mls @@ -1,3 +1,4 @@ +:. :js @@ -57,7 +58,7 @@ print of Box(foo()).value fun foo() = while false do 0 //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.58: while false do 0 +//β”‚ β•‘ l.59: while false do 0 //β”‚ ╙── ^ print of Box(foo()).value @@ -88,7 +89,7 @@ print of Box(foo(1)).value :e fun f = let x = 1 //β”‚ ╔══[COMPILATION ERROR] Expected a body for let bindings in expression position -//β”‚ β•‘ l.89: fun f = let x = 1 +//β”‚ β•‘ l.90: fun f = let x = 1 //β”‚ ╙── ^^^^^ print of f diff --git a/hkmc2/shared/src/test/mlscript/codegen/While.mls b/hkmc2/shared/src/test/mlscript/codegen/While.mls index 5294b2c49f..1ec80eb1b2 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/While.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/While.mls @@ -1,3 +1,4 @@ +:. :js @@ -16,7 +17,7 @@ while false then () () => while true then 0 //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.17: while true then 0 +//β”‚ β•‘ l.18: while true then 0 //β”‚ ╙── ^ //β”‚ = fun @@ -75,10 +76,17 @@ f //β”‚ > Hello World //β”‚ = 42 +:e fun f = while (print("Hello World"), false) then 0(0) else return 1 f +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.81: then 0(0) +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be called like a function. +//β”‚ β•‘ l.81: then 0(0) +//β”‚ ╙── ^ //β”‚ > Hello World //β”‚ = 1 @@ -201,14 +209,14 @@ fun f(ls) = //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let f6; //β”‚ f6 = function f(ls) { -//β”‚ lbl9: while (true) { +//β”‚ lbl8: while (true) { //β”‚ let arg$Cons$0$, arg$Cons$1$; //β”‚ if (ls instanceof Cons1.class) { //β”‚ arg$Cons$0$ = ls.hd; //β”‚ arg$Cons$1$ = ls.tl; //β”‚ ls = arg$Cons$1$; //β”‚ Predef.print(arg$Cons$0$); -//β”‚ continue lbl9 +//β”‚ continue lbl8 //β”‚ } //β”‚ break; //β”‚ } @@ -251,8 +259,8 @@ let x = 1 :sjs while x is {} do() //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ lbl11: while (true) { -//β”‚ if (x3 instanceof globalThis.Object) { continue lbl11 } +//β”‚ lbl10: while (true) { +//β”‚ if (x3 instanceof globalThis.Object) { continue lbl10 } //β”‚ break; //β”‚ } //β”‚ return runtime.Unit @@ -274,7 +282,7 @@ fun f() = savedLambda = () => local set cnt -= 1 // Calls the saved lambda - savedLambda() + if savedLambda is ~null as run then run() f() //β”‚ = 2 @@ -327,10 +335,10 @@ while print("Hello World"); false then 0(0) else 1 //β”‚ ╔══[PARSE ERROR] Unexpected 'then' keyword here -//β”‚ β•‘ l.327: then 0(0) +//β”‚ β•‘ l.335: then 0(0) //β”‚ ╙── ^^^^ //β”‚ ╔══[COMPILATION ERROR] Unrecognized term split (false literal) -//β”‚ β•‘ l.326: while print("Hello World"); false +//β”‚ β•‘ l.334: while print("Hello World"); false //β”‚ ╙── ^^^^^ //β”‚ > Hello World @@ -339,12 +347,12 @@ while { print("Hello World"), false } then 0(0) else 1 //β”‚ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here -//β”‚ β•‘ l.338: while { print("Hello World"), false } +//β”‚ β•‘ l.346: while { print("Hello World"), false } //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//β”‚ β•‘ l.339: then 0(0) +//β”‚ β•‘ l.347: then 0(0) //β”‚ ╙── ^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Illegal position for prefix keyword 'else'. -//β”‚ β•‘ l.340: else 1 +//β”‚ β•‘ l.348: else 1 //β”‚ ╙── ^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -355,14 +363,14 @@ while then 0(0) else 1 //β”‚ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here -//β”‚ β•‘ l.353: print("Hello World") +//β”‚ β•‘ l.361: print("Hello World") //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ -//β”‚ β•‘ l.354: false +//β”‚ β•‘ l.362: false //β”‚ β•‘ ^^^^^^^^^ -//β”‚ β•‘ l.355: then 0(0) +//β”‚ β•‘ l.363: then 0(0) //β”‚ ╙── ^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Illegal position for prefix keyword 'else'. -//β”‚ β•‘ l.356: else 1 +//β”‚ β•‘ l.364: else 1 //β”‚ ╙── ^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/codegen/WhileDefaults.mls b/hkmc2/shared/src/test/mlscript/codegen/WhileDefaults.mls index 9231f27125..de280c75ae 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/WhileDefaults.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/WhileDefaults.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/codegen/i382.mls b/hkmc2/shared/src/test/mlscript/codegen/i382.mls index a9f3a1b03d..595c4fa126 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/i382.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/i382.mls @@ -1,3 +1,4 @@ +:. :js // https://github.com/hkust-taco/mlscript/issues/382 diff --git a/hkmc2/shared/src/test/mlscript/ctx/.mls b/hkmc2/shared/src/test/mlscript/ctx/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ctx/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ctx/MissingDefinitions1.mls b/hkmc2/shared/src/test/mlscript/ctx/MissingDefinitions1.mls index 443f5c7d07..d33abab462 100644 --- a/hkmc2/shared/src/test/mlscript/ctx/MissingDefinitions1.mls +++ b/hkmc2/shared/src/test/mlscript/ctx/MissingDefinitions1.mls @@ -1,3 +1,4 @@ +:. fun f: Any -> Any @@ -5,7 +6,7 @@ fun f: Any -> Any :e g(5) //β”‚ ╔══[COMPILATION ERROR] Name not found: g -//β”‚ β•‘ l.6: g(5) +//β”‚ β•‘ l.7: g(5) //β”‚ ╙── ^ :js @@ -13,10 +14,10 @@ g(5) :re f(1) //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'f' -//β”‚ β•‘ l.14: f(1) +//β”‚ β•‘ l.15: f(1) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ which references the symbol introduced here -//β”‚ β•‘ l.3: fun f: Any -> Any +//β”‚ β•‘ l.4: fun f: Any -> Any //β”‚ ╙── ^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] ReferenceError: f is not defined @@ -30,10 +31,10 @@ fun g(x) = f(x) :re g(0) //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'f' -//β”‚ β•‘ l.26: fun g(x) = f(x) +//β”‚ β•‘ l.27: fun g(x) = f(x) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ which references the symbol introduced here -//β”‚ β•‘ l.3: fun f: Any -> Any +//β”‚ β•‘ l.4: fun f: Any -> Any //β”‚ ╙── ^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] ReferenceError: f is not defined @@ -41,10 +42,10 @@ g(0) :ge fun g(x) = f(x) //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'f' -//β”‚ β•‘ l.42: fun g(x) = f(x) +//β”‚ β•‘ l.43: fun g(x) = f(x) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ which references the symbol introduced here -//β”‚ β•‘ l.3: fun f: Any -> Any +//β”‚ β•‘ l.4: fun f: Any -> Any //β”‚ ╙── ^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ctx/MissingDefinitions2.mls b/hkmc2/shared/src/test/mlscript/ctx/MissingDefinitions2.mls index d53861141c..33a32f0970 100644 --- a/hkmc2/shared/src/test/mlscript/ctx/MissingDefinitions2.mls +++ b/hkmc2/shared/src/test/mlscript/ctx/MissingDefinitions2.mls @@ -1,3 +1,4 @@ +:. :js @@ -5,7 +6,7 @@ :fixme let x: Int //β”‚ ╔══[COMPILATION ERROR] Unsupported let binding shape -//β”‚ β•‘ l.6: let x: Int +//β”‚ β•‘ l.7: let x: Int //β”‚ ╙── ^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/ctx/NestedUsingInstances.mls b/hkmc2/shared/src/test/mlscript/ctx/NestedUsingInstances.mls index c8745c63bb..0dbfd62560 100644 --- a/hkmc2/shared/src/test/mlscript/ctx/NestedUsingInstances.mls +++ b/hkmc2/shared/src/test/mlscript/ctx/NestedUsingInstances.mls @@ -30,10 +30,13 @@ module Ex with //β”‚ k = Ins //β”‚ sym = member:instance$Ident(Int)⁰ //β”‚ tsym = term:instance$Ident(Int)ΒΉ -//β”‚ sign = S of Resolved{sym=class:Int⁰}: -//β”‚ t = Ref{sym=member:IntΒΉ} of member:IntΒΉ -//β”‚ sym = class:Int⁰ +//β”‚ sign = S of Capture: +//β”‚ base = Resolved{sym=class:Int⁰}: +//β”‚ t = Ref{sym=member:IntΒΉ} of member:IntΒΉ +//β”‚ sym = class:Int⁰ +//β”‚ thru = term:instance$Ident(Int)ΒΉ //β”‚ body = S of Lit of IntLit of 1 +//β”‚ flags = (result annotation) //β”‚ modulefulness = Modulefulness of N //β”‚ res = App: //β”‚ lhs = Resolved{sym=term:get⁰}: diff --git a/hkmc2/shared/src/test/mlscript/ctx/Summon.mls b/hkmc2/shared/src/test/mlscript/ctx/Summon.mls index c992ee90aa..92b965272a 100644 --- a/hkmc2/shared/src/test/mlscript/ctx/Summon.mls +++ b/hkmc2/shared/src/test/mlscript/ctx/Summon.mls @@ -20,7 +20,7 @@ use[Num] //β”‚ β•‘ ^^^^^^^^ //β”‚ β•Ÿβ”€β”€ Required by contextual parameter declaration: //β”‚ β•‘ in ../../mlscript-compile/Predef.mls -//β”‚ β•‘ l.133: fun use[T](using instance: T) = instance +//β”‚ β•‘ l.136: fun use[T](using instance: T) = instance //β”‚ β•‘ ^^^^^^^^^^^^^^^^^ //β”‚ ╙── Missing instance: Expected: Num (type parameter T); Available: Int, Str //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -57,7 +57,7 @@ fun f(using Int) = use[Num] //β”‚ β•‘ ^^^^^^^^ //β”‚ β•Ÿβ”€β”€ Required by contextual parameter declaration: //β”‚ β•‘ in ../../mlscript-compile/Predef.mls -//β”‚ β•‘ l.133: fun use[T](using instance: T) = instance +//β”‚ β•‘ l.136: fun use[T](using instance: T) = instance //β”‚ β•‘ ^^^^^^^^^^^^^^^^^ //β”‚ ╙── Missing instance: Expected: Num (type parameter T); Available: Int, Str @@ -87,7 +87,7 @@ use([Some[Int]].value) //β”‚ β•‘ ^^^ //β”‚ β•Ÿβ”€β”€ Required by contextual parameter declaration: //β”‚ β•‘ in ../../mlscript-compile/Predef.mls -//β”‚ β•‘ l.133: fun use[T](using instance: T) = instance +//β”‚ β•‘ l.136: fun use[T](using instance: T) = instance //β”‚ β•‘ ^^^^^^^^^^^^^^^^^ //β”‚ ╙── Illegal query for an unspecified type variable T. //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/ctx/TypeResolution.mls b/hkmc2/shared/src/test/mlscript/ctx/TypeResolution.mls index 0ade97538c..65c95d56e8 100644 --- a/hkmc2/shared/src/test/mlscript/ctx/TypeResolution.mls +++ b/hkmc2/shared/src/test/mlscript/ctx/TypeResolution.mls @@ -1,3 +1,4 @@ +:. class Foo with val foo: Foo = new Foo @@ -6,10 +7,8 @@ class Foo with new Foo //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Blk: -//β”‚ res = New{typ=class:Foo⁰}: -//β”‚ cls = Resolved{sym=class:Foo⁰}: -//β”‚ t = Ref{sym=member:FooΒΉ} of member:FooΒΉ -//β”‚ sym = class:Foo⁰ +//β”‚ res = New: +//β”‚ cls = MemberRef of member:Foo⁰ // * TODO: dynamic new-expression should also be resolvable for its type :rt @@ -17,9 +16,7 @@ new! Foo //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Blk: //β”‚ res = DynNew: -//β”‚ cls = Resolved{sym=class:Foo⁰}: -//β”‚ t = Ref{sym=member:FooΒΉ} of member:FooΒΉ -//β”‚ sym = class:Foo⁰ +//β”‚ cls = MemberRef of member:Foo⁰ val foo: Foo = new Foo @@ -27,21 +24,15 @@ val foo: Foo = new Foo foo //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Blk: -//β”‚ res = Resolved{sym=term:foo⁰,typ=class:Foo⁰}: -//β”‚ t = Ref{sym=member:fooΒΉ} of member:fooΒΉ -//β”‚ sym = term:foo⁰ +//β”‚ res = MemberRef of member:foo⁰ :rt foo.foo //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Blk: -//β”‚ res = Resolved{sym=term:Foo⁰/fooΒ²,typ=class:Foo⁰}: -//β”‚ t = Sel{sym=member:fooΒ³}: -//β”‚ prefix = Resolved{sym=term:foo⁰,typ=class:Foo⁰}: -//β”‚ t = Ref{sym=member:fooΒΉ} of member:fooΒΉ -//β”‚ sym = term:foo⁰ -//β”‚ nme = Ident of "foo" -//β”‚ sym = term:Foo⁰/fooΒ² +//β”‚ res = NewSel: +//β”‚ prefix = MemberRef of member:foo⁰ +//β”‚ id = Ident of "foo" class Foo2[A, B] with val foo: Foo2[Int, Str] = new Foo2 @@ -50,10 +41,8 @@ class Foo2[A, B] with new Foo2 //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Blk: -//β”‚ res = New{typ=class:Foo2⁰}: -//β”‚ cls = Resolved{sym=class:Foo2⁰}: -//β”‚ t = Ref{sym=member:Foo2ΒΉ} of member:Foo2ΒΉ -//β”‚ sym = class:Foo2⁰ +//β”‚ res = New: +//β”‚ cls = MemberRef of member:Foo2⁰ // * TODO: dynamic new-expression should also be resolvable for its type :rt @@ -61,9 +50,7 @@ new! Foo2 //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Blk: //β”‚ res = DynNew: -//β”‚ cls = Resolved{sym=class:Foo2⁰}: -//β”‚ t = Ref{sym=member:Foo2ΒΉ} of member:Foo2ΒΉ -//β”‚ sym = class:Foo2⁰ +//β”‚ cls = MemberRef of member:Foo2⁰ val foo: Foo2[Str, Int] = new Foo2 @@ -71,21 +58,15 @@ val foo: Foo2[Str, Int] = new Foo2 foo //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Blk: -//β”‚ res = Resolved{sym=term:foo⁴,typ=class:Foo2⁰[class:Str⁰, class:Int⁰]}: -//β”‚ t = Ref{sym=member:foo⁡} of member:foo⁡ -//β”‚ sym = term:foo⁴ +//β”‚ res = MemberRef of member:fooΒΉ :rt foo.foo //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Blk: -//β”‚ res = Resolved{sym=term:Foo2⁰/foo⁢,typ=class:Foo2⁰[class:Int⁰, class:Str⁰]}: -//β”‚ t = Sel{sym=member:foo⁷}: -//β”‚ prefix = Resolved{sym=term:foo⁴,typ=class:Foo2⁰[class:Str⁰, class:Int⁰]}: -//β”‚ t = Ref{sym=member:foo⁡} of member:foo⁡ -//β”‚ sym = term:foo⁴ -//β”‚ nme = Ident of "foo" -//β”‚ sym = term:Foo2⁰/foo⁢ +//β”‚ res = NewSel: +//β”‚ prefix = MemberRef of member:fooΒΉ +//β”‚ id = Ident of "foo" :rt fun f(x: Foo) = x @@ -101,11 +82,11 @@ fun f(x: Foo) = x //β”‚ params = Ls of //β”‚ Param: //β”‚ sym = x⁰ -//β”‚ sign = S of Resolved{sym=class:Foo⁰}: -//β”‚ t = Ref{sym=member:FooΒΉ} of member:FooΒΉ -//β”‚ sym = class:Foo⁰ +//β”‚ sign = S of Capture: +//β”‚ base = MemberRef of member:Foo⁰ +//β”‚ thru = term:fΒΉ //β”‚ modulefulness = Modulefulness of N -//β”‚ body = S of Ref{sym=x⁰,typ=class:Foo⁰} of x⁰ +//β”‚ body = S of SimpleRef of x⁰ //β”‚ modulefulness = Modulefulness of N //β”‚ res = Lit of UnitLit of false @@ -116,17 +97,13 @@ object O M //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Blk: -//β”‚ res = Resolved{sym=module:M⁰,typ=module:M⁰}: -//β”‚ t = Ref{sym=member:MΒΉ} of member:MΒΉ -//β”‚ sym = module:M⁰ +//β”‚ res = MemberRef of member:M⁰ :rt O //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Blk: -//β”‚ res = Resolved{sym=object:O⁰,typ=object:O⁰}: -//β”‚ t = Ref{sym=member:OΒΉ} of member:OΒΉ -//β”‚ sym = object:O⁰ +//β”‚ res = MemberRef of member:O⁰ val m: module M = M val o: O = O @@ -135,17 +112,13 @@ val o: O = O m //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Blk: -//β”‚ res = Resolved{sym=term:m⁰,typ=module:M⁰}: -//β”‚ t = Ref{sym=member:mΒΉ} of member:mΒΉ -//β”‚ sym = term:m⁰ +//β”‚ res = MemberRef of member:m⁰ :rt o //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ Blk: -//β”‚ res = Resolved{sym=term:o⁰,typ=object:O⁰}: -//β”‚ t = Ref{sym=member:oΒΉ} of member:oΒΉ -//β”‚ sym = term:o⁰ +//β”‚ res = MemberRef of member:o⁰ :rt fun f(module m: M): module M = m @@ -160,16 +133,17 @@ fun f(module m: M): module M = m //β”‚ ParamList: //β”‚ params = Ls of //β”‚ Param: -//β”‚ sym = mΒ² -//β”‚ sign = S of Resolved{sym=module:M⁰,typ=module:M⁰}: -//β”‚ t = Ref{sym=member:MΒΉ} of member:MΒΉ -//β”‚ sym = module:M⁰ -//β”‚ modulefulness = Modulefulness of S of module:M⁰ -//β”‚ sign = S of Resolved{sym=module:M⁰,typ=module:M⁰}: -//β”‚ t = Ref{sym=member:MΒΉ} of member:MΒΉ -//β”‚ sym = module:M⁰ -//β”‚ body = S of Ref{sym=mΒ²,typ=module:M⁰} of mΒ² -//β”‚ modulefulness = Modulefulness of S of module:M⁰ +//β”‚ sym = mΒΉ +//β”‚ sign = S of Capture: +//β”‚ base = MemberRef of member:M⁰ +//β”‚ thru = term:fΒ³ +//β”‚ modulefulness = Modulefulness of N +//β”‚ sign = S of Capture: +//β”‚ base = MemberRef of member:M⁰ +//β”‚ thru = term:fΒ³ +//β”‚ body = S of SimpleRef of mΒΉ +//β”‚ flags = (result annotation) +//β”‚ modulefulness = Modulefulness of N //β”‚ res = Lit of UnitLit of false :rt @@ -185,12 +159,12 @@ fun f(o: O) = o //β”‚ ParamList: //β”‚ params = Ls of //β”‚ Param: -//β”‚ sym = oΒ² -//β”‚ sign = S of Resolved{sym=object:O⁰,typ=object:O⁰}: -//β”‚ t = Ref{sym=member:OΒΉ} of member:OΒΉ -//β”‚ sym = object:O⁰ +//β”‚ sym = oΒΉ +//β”‚ sign = S of Capture: +//β”‚ base = MemberRef of member:O⁰ +//β”‚ thru = term:f⁡ //β”‚ modulefulness = Modulefulness of N -//β”‚ body = S of Ref{sym=oΒ²,typ=object:O⁰} of oΒ² +//β”‚ body = S of SimpleRef of oΒΉ //β”‚ modulefulness = Modulefulness of N //β”‚ res = Lit of UnitLit of false @@ -204,22 +178,20 @@ class Foo with //β”‚ stats = Ls of //β”‚ Plain: //β”‚ kind = Cls -//β”‚ sym = class:FooΒ² -//β”‚ bsym = member:FooΒ³ +//β”‚ sym = class:FooΒΉ +//β”‚ bsym = member:FooΒ² //β”‚ body = ObjBody of Blk: //β”‚ stats = Ls of //β”‚ TermDefinition: //β”‚ k = ImmutVal -//β”‚ sym = member:foo⁸ -//β”‚ tsym = term:FooΒ²/foo⁹ +//β”‚ sym = member:fooΒ² +//β”‚ tsym = term:FooΒΉ/fooΒ³ //β”‚ body = S of Lit of IntLit of 42 //β”‚ flags = (method) //β”‚ modulefulness = Modulefulness of N -//β”‚ res = Resolved{sym=term:FooΒ²/foo⁹}: -//β”‚ t = SynthSel{sym=member:foo⁸}: -//β”‚ prefix = Ref{sym=class:FooΒ²,typ=class:FooΒ²} of class:FooΒ² -//β”‚ nme = Ident of "foo" -//β”‚ sym = term:FooΒ²/foo⁹ +//β”‚ res = NewSel: +//β”‚ prefix = SelfRef of class:FooΒΉ +//β”‚ id = Ident of "foo" //β”‚ res = Lit of UnitLit of false // The explicit `this` (class:Foo) in the Sel(class:Foo, "foo") should be resolved correctly. @@ -232,20 +204,18 @@ class Foo with //β”‚ stats = Ls of //β”‚ Plain: //β”‚ kind = Cls -//β”‚ sym = class:Foo⁴ -//β”‚ bsym = member:Foo⁡ +//β”‚ sym = class:FooΒ³ +//β”‚ bsym = member:Foo⁴ //β”‚ body = ObjBody of Blk: //β”‚ stats = Ls of //β”‚ TermDefinition: //β”‚ k = ImmutVal -//β”‚ sym = member:foo¹⁰ -//β”‚ tsym = term:Foo⁴/fooΒΉΒΉ +//β”‚ sym = member:foo⁴ +//β”‚ tsym = term:FooΒ³/foo⁡ //β”‚ body = S of Lit of IntLit of 42 //β”‚ flags = (method) //β”‚ modulefulness = Modulefulness of N -//β”‚ res = Resolved{sym=term:Foo⁴/fooΒΉΒΉ}: -//β”‚ t = Sel{sym=member:foo¹⁰}: -//β”‚ prefix = Ref{sym=class:Foo⁴,typ=class:Foo⁴} of class:Foo⁴ -//β”‚ nme = Ident of "foo" -//β”‚ sym = term:Foo⁴/fooΒΉΒΉ +//β”‚ res = NewSel: +//β”‚ prefix = SelfRef of class:FooΒ³ +//β”‚ id = Ident of "foo" //β”‚ res = Lit of UnitLit of false diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/basic.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/basic.mls index e7c5116f10..b483c3fa52 100644 --- a/hkmc2/shared/src/test/mlscript/dead-param-elim/basic.mls +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/basic.mls @@ -1,3 +1,4 @@ +:. :js :noInline :flowBasedOpt debugDpe @@ -145,13 +146,14 @@ foo.bar(99) //β”‚ define Foo⁰ as class FooΒ² { //β”‚ private val x⁰; //β”‚ constructor FooΒΉ(x) { -//β”‚ set FooΒ².this.x⁰ = x; +//β”‚ @private +//β”‚ define xΒΉ as val x⁰ = x; //β”‚ end //β”‚ } //β”‚ method bar⁰ = fun barΒΉ(a) { return FooΒ².this.x⁰ } //β”‚ }; //β”‚ set foo⁰ = FooΒΉ(1); -//β”‚ return foo⁰.barοΉ–(99) +//β”‚ return foo⁰.barΒΉ(99) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = 1 //β”‚ foo = Foo(_) @@ -298,7 +300,7 @@ fun f(c) = Runtime.Tuple.get(c, 1) + 1 //β”‚ set tmp = Runtime⁰.Tuple⁰.get⁰(c, 1); //β”‚ return +⁰(tmp, 1) //β”‚ else -//β”‚ return runtime⁰.unwindοΉ–(f²⁴, -1, "basic.mls:288:1", null, null, 1, 1, c, 0) +//β”‚ return runtime⁰.unwindοΉ–(f²⁴, -1, "basic.mls:290:1", null, null, 1, 1, c, 0) //β”‚ end //β”‚ }; //β”‚ end diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/clash.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/clash.mls index b0b2ba3e2e..89ab2727d1 100644 --- a/hkmc2/shared/src/test/mlscript/dead-param-elim/clash.mls +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/clash.mls @@ -1,3 +1,4 @@ +:. :js :noInline :flowBasedOpt debugDpe @@ -92,7 +93,7 @@ m() //β”‚ let tmp, lambda; //β”‚ set tmp = [1, 2, 3]; //β”‚ define lambda as fun lambda⁰(x, i, a) { return +⁰(x, 1) }; -//β”‚ return tmp.mapοΉ–(lambda⁰) +//β”‚ return tmp.map⁰((lambda⁰ as! Function⁰)) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = [2, 3, 4] diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/class-in-fun.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/class-in-fun.mls index b5bad809d7..d0492225a7 100644 --- a/hkmc2/shared/src/test/mlscript/dead-param-elim/class-in-fun.mls +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/class-in-fun.mls @@ -1,3 +1,4 @@ +:. :js :flowBasedOpt debugDpe @@ -31,7 +32,7 @@ f(1, 2).get() //β”‚ @private //β”‚ define f⁰ as fun fΒΉ(used) { return new C⁰(used) }; //β”‚ set tmp = fΒΉ(1); -//β”‚ return tmp.getοΉ–() +//β”‚ return tmp.getΒΉ() //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = 1 diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/config-flags.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/config-flags.mls index 7d8424c728..28e3e93bc6 100644 --- a/hkmc2/shared/src/test/mlscript/dead-param-elim/config-flags.mls +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/config-flags.mls @@ -1,3 +1,4 @@ +:. :js :noInline diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/dead-ref.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/dead-ref.mls index f892134b6a..62a9db3326 100644 --- a/hkmc2/shared/src/test/mlscript/dead-param-elim/dead-ref.mls +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/dead-ref.mls @@ -1,3 +1,4 @@ +:. :js :noInline :flowBasedOpt debugDpe diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/inherited-field.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/inherited-field.mls new file mode 100644 index 0000000000..b2417aa68c --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/inherited-field.mls @@ -0,0 +1,30 @@ +:. +:.. +:js + +// FIXME: The producer is Child but the selected field belongs to Parent. +// FlowConstraintSolver drops that flow instead of keeping the constructor input +// live, so dead-parameter elimination replaces value with undefined. +// Disable inlining to keep read available for the dead-parameter analysis. +:fixme +:flowBasedOpt debugDpe +:noInline +:expect 21 +class Parent(val value) +class Child(value) extends Parent(value) +private fun read(value) = (new Child(value)).Parent#value +read(21) +//β”‚ dead-param-elim > >>> dead-param-elim results >>> +//β”‚ dead-param-elim > read⁰#0 @ -> eliminable: {0} +//β”‚ dead-param-elim > <<< dead-param-elim results <<< +//β”‚ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. +//β”‚ ═══[RUNTIME ERROR] Expected: '21', got: 'undefined' + +:flowBasedOpt off +:noInline +:expect 21 +class Parent(val value) +class Child(value) extends Parent(value) +private fun read(value) = (new Child(value)).Parent#value +read(21) +//β”‚ = 21 diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/lambda.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/lambda.mls index 43c381c908..ca15062346 100644 --- a/hkmc2/shared/src/test/mlscript/dead-param-elim/lambda.mls +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/lambda.mls @@ -1,3 +1,4 @@ +:. :js :flowBasedOpt debugDpe diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/module.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/module.mls index 2a9c65247d..2b30744932 100644 --- a/hkmc2/shared/src/test/mlscript/dead-param-elim/module.mls +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/module.mls @@ -1,3 +1,4 @@ +:. :js :flowBasedOpt debugDpe :noInline diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/multiArgLists.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/multiArgLists.mls index 55cf60139d..504a6a0eb3 100644 --- a/hkmc2/shared/src/test/mlscript/dead-param-elim/multiArgLists.mls +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/multiArgLists.mls @@ -1,3 +1,4 @@ +:. :js :noInline diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/purity.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/purity.mls index 97bc033d89..aa2710e7de 100644 --- a/hkmc2/shared/src/test/mlscript/dead-param-elim/purity.mls +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/purity.mls @@ -1,13 +1,15 @@ +:. :js :noInline :flowBasedOpt debugDpe +// Dynamic receivers keep the invalid null accesses in the generated code. // an eliminated arg with effects is still evaluated :re :soir private fun f(a) = 0 -fun g(o) = f(o.x) +fun g(o: dyn) = f(o.x) g(null) //β”‚ dead-param-elim > >>> dead-param-elim results >>> //β”‚ dead-param-elim > f⁰#0 @ -> eliminable: {0} @@ -24,7 +26,7 @@ g(null) :re :soir private fun f(a, b) = a -fun g(o, p) = f(o.x, p.y) +fun g(o: dyn, p: dyn) = f(o.x, p.y) g(null, null) //β”‚ dead-param-elim > >>> dead-param-elim results >>> //β”‚ dead-param-elim > fΒ²#0 @ -> eliminable: {1} @@ -64,12 +66,12 @@ g(new A) //β”‚ define g⁴ as fun g⁡(o) { //β”‚ let arg; //β”‚ set arg = n⁰; -//β”‚ do o.bumpοΉ–; +//β”‚ do o.bump⁰; //β”‚ return f⁡(arg) //β”‚ }; //β”‚ set n⁰ = 0; //β”‚ define A⁰ as class AΒΉ { -//β”‚ method bump⁰ = fun bumpΒΉ { +//β”‚ method bumpΒΉ = fun bump⁰ { //β”‚ set n⁰ = 1; //β”‚ return runtime⁰.Unit⁰ //β”‚ } diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/recursive.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/recursive.mls index 7be4c8ff0e..6b53763880 100644 --- a/hkmc2/shared/src/test/mlscript/dead-param-elim/recursive.mls +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/recursive.mls @@ -1,3 +1,4 @@ +:. :js :flowBasedOpt debugDpe :noInline diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/refresher.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/refresher.mls index 9e3a89b6aa..b691050471 100644 --- a/hkmc2/shared/src/test/mlscript/dead-param-elim/refresher.mls +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/refresher.mls @@ -1,3 +1,4 @@ +:. :js :flowBasedOpt debugDpe poly :noInline @@ -90,7 +91,7 @@ wrapper(42, 0) //β”‚ }; //β”‚ set tmp = new A⁰(); //β”‚ set tmp1 = tmp.makeBΒΉ(); -//β”‚ return tmp1.getValοΉ–() +//β”‚ return tmp1.getValΒΉ() //β”‚ }; //β”‚ define wrapper⁰ as fun wrapperΒΉ(val1, dead) { //β”‚ let A, tmp, tmp1; @@ -107,7 +108,7 @@ wrapper(42, 0) //β”‚ }; //β”‚ set tmp = new AΒΉ(); //β”‚ set tmp1 = tmp.makeBΒ³(); -//β”‚ return tmp1.getValοΉ–() +//β”‚ return tmp1.getValΒ³() //β”‚ }; //β”‚ return wrapper$wrapper⁰(42) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/todos.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/todos.mls index 35b80f59b1..3c616b5098 100644 --- a/hkmc2/shared/src/test/mlscript/dead-param-elim/todos.mls +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/todos.mls @@ -1,3 +1,4 @@ +:. :js :deforest :flowBasedOpt debugEta debugDpe diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/ups.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/ups.mls index de3a2e6c69..5a5cc936cd 100644 --- a/hkmc2/shared/src/test/mlscript/dead-param-elim/ups.mls +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/ups.mls @@ -1,10 +1,11 @@ +:. :js :flowBasedOpt debugDpe :ucs ups pattern Middle = ([x] => x) | ([x, ...(Middle as m), y] => m) -//β”‚ elaborated pattern body: [x] => x⁰ ∨ [x, ...Middle as m, y] => m⁰ +//β”‚ elaborated pattern body: [xβ€Ήx⁰›] => xΒΉ ∨ [xβ€ΉxΒ²β€Ί, ...member:Middle⁰ as mβ€Ήm⁰›, yβ€Ήy⁰›] => mΒΉ //β”‚ dead-param-elim > >>> dead-param-elim results >>> //β”‚ dead-param-elim > lambda⁰#0 @ -> eliminable: {0, 1} //β”‚ dead-param-elim > <<< dead-param-elim results <<< diff --git a/hkmc2/shared/src/test/mlscript/decls/Prelude.mls b/hkmc2/shared/src/test/mlscript/decls/Prelude.mls index 9ce9ecc860..2e546f1e7f 100644 --- a/hkmc2/shared/src/test/mlscript/decls/Prelude.mls +++ b/hkmc2/shared/src/test/mlscript/decls/Prelude.mls @@ -1,102 +1,230 @@ - +#lang(0.3.x) declare type Any declare type Anything declare type Nothing -declare class Object -declare module Object with +declare open class Object { + fun toString(): Str +} +declare fun Object(...args): Object // TODO: use an optional parameter when supported +declare module Object { + fun entries(target): Array[[Str, Any]] + fun getOwnPropertyDescriptor(target, key): Object | undefined + fun getOwnPropertyDescriptors(target): Object fun create freeze assign - entries prototype fromEntries getPrototypeOf defineProperty - getOwnPropertyDescriptor - getOwnPropertyDescriptors +} + declare class JSON -declare module JSON with +declare module JSON { fun stringify -declare class Number -declare module Number with +} + +declare class Number: Num +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/Number +declare fun Number(...args): Number // TODO: use an optional parameter when supported +declare module Number { val - MIN_VALUE - MAX_VALUE - MIN_SAFE_INTEGER - MAX_SAFE_INTEGER - NEGATIVE_INFINITY - POSITIVE_INFINITY -declare class BigInt + MIN_VALUE: Num + MAX_VALUE: Num + MIN_SAFE_INTEGER: Int + MAX_SAFE_INTEGER: Int + NEGATIVE_INFINITY: Num + POSITIVE_INFINITY: Num +} + +declare class BigInt { + // private constructor() // FIXME: Unsupported constructor in this position. + fun toString(...radix): Str +} +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/BigInt +declare fun BigInt(value: Str | Int | Bool | BigInt): BigInt declare module BigInt -declare class Function + +declare open class Function { + val name: Str + fun bind(thisArg, ...args): Function + fun apply(thisArg, args: Array): Any +} declare module Function + declare class String -declare module String with +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/String +declare fun String(...args): Str // TODO: use an optional parameter when supported +declare module String { fun fromCharCode fromCodePoint raw -declare class RegExp +} + +declare class RegExp { + fun toString(): Str + fun exec(input: Str): Array[Str] | null + fun test(input: Str): Bool +} declare module RegExp -declare class Set[V] + +declare class Set[V] { + val size: Int + fun add(value: V): Set[V] + fun has(value: V): Bool + fun delete(value: V): Bool + fun clear(): undefined + fun values(): Iterator[V] + fun union(other: Set[V]): Set[V] + fun difference(other: Set[V]): Set[V] +} declare module Set -declare class Map[K, V] + +declare class Map[K, V] { + val size: Int + fun get(key: K): V | undefined + fun has(key: K): Bool + fun delete(key: K): Bool + fun clear(): undefined + fun id"set"(key: K, value: V): Map[K, V] + fun keys(): Iterator[K] + fun values(): Iterator[V] + fun entries(): Iterator[[K, V]] + fun forEach(f: (V, K, Map[K, V]) -> Any): undefined +} + +declare class Iterator[out T] { + fun next(): { value: T, done: Bool } +} + declare module Map + declare class WeakSet[V] declare module WeakSet -declare class WeakMap[K, V] + +declare class WeakMap[K, V] { + fun get(key: K): V | undefined + fun has(key: K): Bool + fun delete(key: K): Bool + fun id"set"(key: K, value: V): WeakMap[K, V] +} declare module WeakMap -declare class Error//(info) // TODO: handle JS classes that can be instantiated without `new` specially in codegen. -declare class TypeError//(info) // TODO: handle JS classes that can be instantiated without `new` specially in codegen. -declare class RangeError//(info) // TODO: handle JS classes that can be instantiated without `new` specially in codegen. + +declare class Error { + val message: Str + val name: Str + val stack: Str | undefined +} +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Error +declare fun Error(...args): Error // TODO: use optional parameters when supported + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError/TypeError +declare class TypeError extends Error +declare fun TypeError(...args): TypeError + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RangeError/RangeError +declare class RangeError extends Error +declare fun RangeError(...args): RangeError + declare class Date declare module Date declare class ArrayBuffer declare module ArrayBuffer + declare class TypedArray declare module TypedArray + declare class Int8Array declare module Int8Array + declare class Uint8Array declare module Uint8Array + declare class Uint8ClampedArray declare module Uint8ClampedArray + declare class Int16Array declare module Int16Array + declare class Uint16Array declare module Uint16Array + declare class Int32Array declare module Int32Array + declare class Uint32Array declare module Uint32Array + declare class Float16Array declare module Float16Array + declare class Float32Array declare module Float32Array + declare class Float64Array declare module Float64Array + declare class BigInt64Array declare module BigInt64Array + declare class BigUint64Array declare module BigUint64Array -declare class Promise -declare module Promise with - fun - all - resolve - reject +// Awaited recursively unwraps promises, as JavaScript's promise resolution does. +declare type Awaited[out A] +// Follow TypeScript's Promise declarations, translating void returns to undefined. +// Optional parameters and default type arguments are not supported yet: callers +// must supply the first callback, and then's rejection callback uses a rest list. +// Structural PromiseLike values and tuple-mapped all results remain unsupported. +declare class Promise[out A](executor: ((A | Promise[A]) -> undefined, Any -> undefined) -> undefined) { + fun id"then"[B, C](onFulfilled: Awaited[A] -> (B | Promise[B]), ...onRejected: Array[(Any -> (C | Promise[C])) | undefined | null]): Promise[Awaited[B | C]] + fun id"catch"[B](onRejected: (Any -> (B | Promise[B])) | undefined | null): Promise[Awaited[A | B]] + fun id"finally"(onFinally: (() -> undefined) | undefined | null): Promise[A] +} +declare module Promise { + fun resolve[A](value: A | Promise[A]): Promise[Awaited[A]] + fun reject(reason: Any): Promise[Nothing] + fun all[A](values: Array[A]): Promise[Array[Awaited[A]]] + fun race[A](values: Array[A]): Promise[Awaited[A]] + fun any[A](values: Array[A]): Promise[Awaited[A]] +} // MLscript-specific types declare class Bool -declare class Num +declare class Num { + fun toString(...radix): Str + fun toFixed(...digits): Str +} declare class Int extends Num -declare class Str +declare class Str { + val length: Int + fun toString(): Str + fun at(index: Int): Str | undefined + fun charAt(index: Int): Str + fun charCodeAt(index: Int): Num + fun codePointAt(index: Int): Int | undefined + fun indexOf(value: Str, ...from): Int + fun lastIndexOf(value: Str, ...from): Int + fun includes(value: Str, ...from): Bool + fun startsWith(value: Str, ...from): Bool + fun endsWith(value: Str, ...until): Bool + fun slice(...bounds): Str + fun substring(...bounds): Str + fun split(...separatorAndLimit): Array[Str] + fun replace(search: Str | RegExp, replacement: Str | Function): Str + fun replaceAll(search: Str | RegExp, replacement: Str | Function): Str + fun repeat(count: Int): Str + fun toUpperCase(): Str + fun toLowerCase(): Str + fun localeCompare(other: Str, ...localesAndOptions): Int + fun trim(): Str +} declare class Class // The `Array` class/function is a footgun: @@ -106,25 +234,72 @@ declare class Class // [ <1 empty item> ] // > Array(1, 2) // [ 1, 2 ] -// We used to declare it as a class taking exactly one argument here to avoid that footgun: -// declare class Array[T](val length: Int): Array[T] -// but this made instance checks wrongly use `Array.class`; -// TODO: handle Array and other JS classes that can be instantiated without `new` specially in codegen. -declare class Array[T] -declare module Array with +// Expose only the length-taking constructor; use tuple literals for initial elements. +// Foreign constructor signatures describe the native binding and do not introduce +// the `.class` storage used by generated MLscript constructor functions. +// The `val length` parameter also supports matching with `Array(length)`. +declare class Array[T](val length: Int) { + fun join(...separators): Str + fun reverse(): Array[T] + fun sort(...compare): Array[T] + fun slice(...bounds): Array[T] + fun copyWithin(target: Int, start: Int, ...end): Array[T] + fun entries(): Iterator[[Int, T]] + fun keys(): Iterator[Int] + fun toReversed(): Array[T] + fun toSorted(...compare): Array[T] + fun toSpliced(start: Int, ...items): Array[T] + fun id"with"(index: Int, value: T): Array[T] + fun at(index: Int): T | undefined + fun fill(value: T, ...bounds): Array[T] + fun splice(start: Int, ...items): Array[T] + fun push(...items: Array[T]): Int + fun unshift(...items: Array[T]): Int + fun pop(): T | undefined + fun shift(): T | undefined + fun concat(...arrays): Array[Any] + fun map[U](f: (T, Int, Array[T]) -> U): Array[U] + fun forEach(f: (T, Int, Array[T]) -> Any): undefined + fun filter(f: (T, Int, Array[T]) -> Bool): Array[T] + fun find(f: (T, Int, Array[T]) -> Bool, ...thisArg): T | undefined + fun findIndex(f: (T, Int, Array[T]) -> Bool, ...thisArg): Int + fun findLast(f: (T, Int, Array[T]) -> Bool, ...thisArg): T | undefined + fun findLastIndex(f: (T, Int, Array[T]) -> Bool, ...thisArg): Int + fun flat(...depth): Array[Any] + fun flatMap[U](f: (T, Int, Array[T]) -> (U | Array[U]), ...thisArg): Array[U] + fun every(f: (T, Int, Array[T]) -> Bool): Bool + fun some(f: (T, Int, Array[T]) -> Bool): Bool + // Without an initial value, the first element becomes the accumulator, so the + // accumulator type would also have to include T. Methods cannot be overloaded + // by arity, so only the form with an initial value is declared. + fun reduce[U](f: (U, T, Int, Array[T]) -> U, initial: U): U + fun reduceRight[U](f: (U, T, Int, Array[T]) -> U, initial: U): U + fun indexOf(value: T, ...from): Int + fun lastIndexOf(value: T, ...from): Int + fun includes(value: T, ...from): Bool + fun toString(): Str + fun toLocaleString(...options): Str +} +declare module Array { + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from + fun from(...args): Array[Any] // TODO: use optional parameters when supported fun - from concat isArray - prototype + val prototype: Array[Any] +} -declare class Symbol -declare module Symbol with +declare class Symbol { + val description: Str | undefined +} +declare fun Symbol(...description): Symbol +declare module Symbol { // The `TermDef` needs `rhs` to be defined to be recognized as `isMLsFun`. // Otherwise, it would be wrapped in `runtime.safeCall`, which accesses the // uninitialized `runtime` in the `Rendering` module. fun for: Str -> Any = () val iterator: Any +} // MLwasm-specific types declare class Int32 @@ -134,7 +309,7 @@ declare class Float64 declare class Int31 extends Int // `i31ref` // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math -declare module Math with +declare module Math { declare val E: Num @@ -182,17 +357,21 @@ declare module Math with tan: Num -> Num tanh: Num -> Num trunc: Num -> Num +} -declare module Reflect with +declare module Reflect { + // Prototype objects may expose arbitrary host properties. + fun getPrototypeOf(target): dyn fun get - // set // TODO: handle keyword-named members + id"set" has ownKeys - getPrototypeOf apply construct -declare module console with +} + +declare module console { declare fun log @@ -213,25 +392,35 @@ declare module console with time timeEnd trace +} -declare val process // TODO make `module` -declare val fs // TODO make `module` +declare module process { + fun cwd(): Str + val env +} + +// Host APIs remain explicitly dynamic until their interfaces are modeled. +declare val fs: dyn declare val Infinity // Wasm support -declare object WebAssembly with - declare class Instance +declare object WebAssembly { + declare class Instance { + val exports: dyn + } declare class Memory declare class Module - declare module Module with + declare module Module { fun exports imports + } fun instantiate validate +} // declare fun typeof: (Any) -> Str @@ -239,22 +428,24 @@ declare fun parseInt(str: Str, radix: Int): Int declare fun parseFloat(str: Str): Num -declare module source with +declare module source { object line name file +} -declare module js with +declare module js { fun bitand bitnot bitor shl try_catch +} -declare module wasm with - module i32 with +declare module wasm { + module i32 { fun const(c: Int): Int32 add(i1: Int32, i2: Int32): Int32 @@ -273,7 +464,8 @@ declare module wasm with ge_s(i1: Int32, i2: Int32): Int32 eqz(i: Int32): Int32 reinterpret_f32(f: Float32): Int32 - module i64 with + } + module i64 { fun const(c: Int): Int64 add(i1: Int64, i2: Int64): Int64 @@ -292,7 +484,8 @@ declare module wasm with ge_s(i1: Int64, i2: Int64): Int32 eqz(i: Int64): Int32 reinterpret_f64(f: Float64): Int64 - module f32 with + } + module f32 { fun const(c: Num): Float32 add(f1: Float32, f2: Float32): Float32 @@ -311,7 +504,8 @@ declare module wasm with gt(f1: Float32, f2: Float32): Int32 ge(f1: Float32, f2: Float32): Int32 reinterpret_i32(i: Int32): Float32 - module f64 with + } + module f64 { fun const(c: Num): Float64 add(f1: Float64, f2: Float64): Float64 @@ -330,13 +524,16 @@ declare module wasm with gt(f1: Float64, f2: Float64): Int32 ge(f1: Float64, f2: Float64): Int32 reinterpret_i64(i: Int64): Float64 - module ref with + } + module ref { fun i31(i: Int32): Int31 - module i31 with + } + module i31 { fun get_s(i: Int31): Int32 get_u(i: Int31): Int32 + } fun plus_impl minus_impl @@ -352,11 +549,13 @@ declare module wasm with neg_impl pos_impl not_impl +} -declare module debug with +declare module debug { fun printStack +} -declare module annotations with +declare module annotations { object pure object untyped object tailrec @@ -368,21 +567,44 @@ declare module annotations with object bufferable object generator object async +} -declare module handlers with - fun await +declare module handlers { + fun await[A](promise: Promise[A]): Awaited[A] +} -declare module scope with +declare module scope { fun locally +} -declare module runtime with +declare module runtime { fun suspend handle_suspension +} // HTML DOM API definitions. // Move them to a separate Prelude file when there are enough of them. -declare val document -declare val customElements -declare class HTMLElement +declare val document: dyn +declare val customElements: dyn +// Preserve the nominal superclass while its host members remain dynamic. +declare open class HTMLElement { + val textContent: dyn + val innerHTML: dyn + val shadowRoot: dyn + val appendChild: dyn + val attachShadow: dyn + val addEventListener: dyn + val querySelector: dyn + val classList: dyn +} + +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +// Shared read interfaces for arrays, strings, and lazy views. Element types are +// covariant, so using these interfaces does not widen an invariant collection. +type Indexable[out A] = {length: Int, at: Int -> A, slice: Function} +type Iterable[out A] = {iterator: () -> Iterator[A]} +type IterableIndexable[out A] = Indexable[A] & Iterable[A] + diff --git a/hkmc2/shared/src/test/mlscript/deforest/.mls b/hkmc2/shared/src/test/mlscript/deforest/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/deforest/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/deforest/append.mls b/hkmc2/shared/src/test/mlscript/deforest/append.mls index 207c27b4c0..8dbd227175 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/append.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/append.mls @@ -1,3 +1,4 @@ +:. :js :deforest diff --git a/hkmc2/shared/src/test/mlscript/deforest/basic.mls b/hkmc2/shared/src/test/mlscript/deforest/basic.mls index 4ab8c533bc..f76cb4e375 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/basic.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/basic.mls @@ -1,3 +1,4 @@ +:. :js :deforest diff --git a/hkmc2/shared/src/test/mlscript/deforest/clashes.mls b/hkmc2/shared/src/test/mlscript/deforest/clashes.mls index cee48bd145..780cb7f8c8 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/clashes.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/clashes.mls @@ -1,3 +1,4 @@ +:. :js :deforest diff --git a/hkmc2/shared/src/test/mlscript/deforest/cyclic.mls b/hkmc2/shared/src/test/mlscript/deforest/cyclic.mls index 70d783d6c9..1469ecf40e 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/cyclic.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/cyclic.mls @@ -1,3 +1,4 @@ +:. :js data class A(x) diff --git a/hkmc2/shared/src/test/mlscript/deforest/determinism.mls b/hkmc2/shared/src/test/mlscript/deforest/determinism.mls index 5eda4eebe8..b3b1aa9b6a 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/determinism.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/determinism.mls @@ -1,3 +1,4 @@ +:. :js data class A(a) diff --git a/hkmc2/shared/src/test/mlscript/deforest/dropLast.mls b/hkmc2/shared/src/test/mlscript/deforest/dropLast.mls index 37041a8b7f..1e752e6d6c 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/dropLast.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/dropLast.mls @@ -1,3 +1,4 @@ +:. :js data class (::) Cons(h, t) diff --git a/hkmc2/shared/src/test/mlscript/deforest/erasedTypes.mls b/hkmc2/shared/src/test/mlscript/deforest/erasedTypes.mls index 24eda9a541..281a6ab578 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/erasedTypes.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/erasedTypes.mls @@ -1,3 +1,4 @@ +:. :global :sir :siret diff --git a/hkmc2/shared/src/test/mlscript/deforest/eta-expansion/.mls b/hkmc2/shared/src/test/mlscript/deforest/eta-expansion/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/deforest/eta-expansion/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/deforest/eta-expansion/basic.mls b/hkmc2/shared/src/test/mlscript/deforest/eta-expansion/basic.mls index ed996642f1..cd995d1f7f 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/eta-expansion/basic.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/eta-expansion/basic.mls @@ -1,3 +1,4 @@ +:. :js :deforest :flowBasedOpt debugEta diff --git a/hkmc2/shared/src/test/mlscript/deforest/eta-expansion/recursive.mls b/hkmc2/shared/src/test/mlscript/deforest/eta-expansion/recursive.mls index 5d0af3a286..3f275d7479 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/eta-expansion/recursive.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/eta-expansion/recursive.mls @@ -1,3 +1,4 @@ +:. :js :deforest :flowBasedOpt debugEta diff --git a/hkmc2/shared/src/test/mlscript/deforest/fusibility.mls b/hkmc2/shared/src/test/mlscript/deforest/fusibility.mls index e69111885d..9940f05947 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/fusibility.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/fusibility.mls @@ -1,3 +1,4 @@ +:. :js :deforest logNonAffine diff --git a/hkmc2/shared/src/test/mlscript/deforest/imperative.mls b/hkmc2/shared/src/test/mlscript/deforest/imperative.mls index b5d22bdb24..2dc85e5756 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/imperative.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/imperative.mls @@ -1,3 +1,4 @@ +:. :js :deforest diff --git a/hkmc2/shared/src/test/mlscript/deforest/listComprehension.mls b/hkmc2/shared/src/test/mlscript/deforest/listComprehension.mls index 254b119b14..b4ecfac5e5 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/listComprehension.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/listComprehension.mls @@ -1,3 +1,4 @@ +:. :js :deforest @@ -242,7 +243,7 @@ if A is //β”‚ deforest > Nil⁰ -> //β”‚ deforest > match: lsΒΉ //β”‚ deforest > A⁰ -> -//β”‚ deforest > match: A⁰ +//β”‚ deforest > match: scrut⁰ //β”‚ deforest > <<< fusing <<< //β”‚ = Cons(2, Cons(3, Cons(4, Nil))) diff --git a/hkmc2/shared/src/test/mlscript/deforest/module.mls b/hkmc2/shared/src/test/mlscript/deforest/module.mls index 319c8b6fd8..666e148f6e 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/module.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/module.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/deforest/multiArgLists.mls b/hkmc2/shared/src/test/mlscript/deforest/multiArgLists.mls index c99a124325..c8501f5134 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/multiArgLists.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/multiArgLists.mls @@ -1,3 +1,4 @@ +:. :js :deforest diff --git a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls index 1deb2c2326..ab7b536f4b 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls @@ -1,3 +1,4 @@ +:. :js :deforest diff --git a/hkmc2/shared/src/test/mlscript/deforest/recursive.mls b/hkmc2/shared/src/test/mlscript/deforest/recursive.mls index efb5813e30..9094facd9e 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/recursive.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/recursive.mls @@ -1,3 +1,4 @@ +:. :js :deforest @@ -742,17 +743,17 @@ fun g2() = f() f //β”‚ deforest > >>> fusing >>> //β”‚ deforest > B⁰ -> -//β”‚ deforest > match: B⁰ +//β”‚ deforest > match: scrut⁰ //β”‚ deforest > B⁰ -> -//β”‚ deforest > match: B⁰ +//β”‚ deforest > match: scrut⁰ //β”‚ deforest > B⁰ -> -//β”‚ deforest > match: B⁰ +//β”‚ deforest > match: scrut⁰ //β”‚ deforest > B⁰ -> -//β”‚ deforest > match: B⁰ +//β”‚ deforest > match: scrut⁰ //β”‚ deforest > B⁰ -> -//β”‚ deforest > match: B⁰ +//β”‚ deforest > match: scrut⁰ //β”‚ deforest > B⁰ -> -//β”‚ deforest > match: B⁰ +//β”‚ deforest > match: scrut⁰ //β”‚ deforest > <<< fusing <<< //β”‚ = fun f$f diff --git a/hkmc2/shared/src/test/mlscript/deforest/relaxedPrograms.mls b/hkmc2/shared/src/test/mlscript/deforest/relaxedPrograms.mls index 3c5c9ec64a..b0e67c4186 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/relaxedPrograms.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/relaxedPrograms.mls @@ -1,3 +1,4 @@ +:. :js @@ -44,7 +45,7 @@ store.foo(AA(4)) //β”‚ deforest > fields: scrutΒΉ.xΒΉ //β”‚ deforest > <<< fusing <<< //β”‚ ═══[INTERNAL ERROR] `this` not in scope: class:Store -//β”‚ > let AA3, Store3, store1, tmp2, tmp3, $scrut_AA;try { const n$accessorSymbol = globalThis.Symbol("n"); $scrut_AA = function $scrut_AA(AA_x) { runtime.checkArgs("$scrut_AA", 1, true, arguments.length); return (fv_x, fv_arg$AA$0$) => { return AA_x + β€ΉMISSING_THISβ€Ί[n$accessorSymbol] } }; AA3 = function AA(x) { return globalThis.Object.freeze(new AA.class(x)); }; globalThis.Object.freeze(class AA2 { static { AA3.class = this } constructor(x) { this.x = x; } toString() { return runtime.render(this); } static [definitionMetadata] = ["class", "AA", ["x"]]; }); Store3 = function Store(n) { return globalThis.Object.freeze(new Store.class(n)); }; globalThis.Object.freeze(class Store2 { static { Store3.class = this } constructor(n) { this.#n = n; } #n; get [n$accessorSymbol]() { return this.#n; } set [n$accessorSymbol](value) { this.#n = value; } get get$__checkNotMethod() { runtime.deboundMethod("get", "Store"); } get foo$__checkNotMethod() { runtime.deboundMethod("foo", "Store"); } get() { runtime.checkArgs("get", 0, true, arguments.length); let AA_x; AA_x = this.#n; return runtime.checkCall($scrut_AA(AA_x)(undefined, undefined)) } foo(x) { runtime.checkArgs("foo", 1, true, arguments.length); let arg$AA$0$; if (x instanceof AA3.class) { arg$AA$0$ = x.x; return arg$AA$0$ + this.#n } throw globalThis.Object.freeze(new globalThis.Error("match error")); } toString() { return runtime.render(this); } static [definitionMetadata] = ["class", "Store", [null]]; }); tmp2 = runtime.checkCall(Store3(1)); store1 = tmp2; tmp3 = runtime.checkCall(AA3(4)); block$res2 = runtime.safeCall(store1.foo(tmp3));; undefined } catch (e) { console.log('\u200B' + e + '\u200B'); } +//β”‚ > let AA3, Store3, store1, tmp2, tmp3, $scrut_AA;try { const n$accessorSymbol = globalThis.Symbol("n"); $scrut_AA = function $scrut_AA(AA_x) { runtime.checkArgs("$scrut_AA", 1, true, arguments.length); return (fv_x, fv_arg$AA$0$) => { return AA_x + β€ΉMISSING_THISβ€Ί[n$accessorSymbol] } }; AA3 = function AA(x) { return globalThis.Object.freeze(new AA.class(x)); }; globalThis.Object.freeze(class AA2 { static { AA3.class = this } constructor(x) { this.x = x; } toString() { return runtime.render(this); } static [definitionMetadata] = ["class", "AA", ["x"]]; }); Store3 = function Store(n) { return globalThis.Object.freeze(new Store.class(n)); }; globalThis.Object.freeze(class Store2 { static { Store3.class = this } constructor(n) { this.#n = n; } #n; get [n$accessorSymbol]() { return this.#n; } set [n$accessorSymbol](value) { this.#n = value; } get get$__checkNotMethod() { runtime.deboundMethod("get", "Store"); } get foo$__checkNotMethod() { runtime.deboundMethod("foo", "Store"); } get() { runtime.checkArgs("get", 0, true, arguments.length); let AA_x; AA_x = this.#n; return runtime.checkCall($scrut_AA(AA_x)(undefined, undefined)) } foo(x) { runtime.checkArgs("foo", 1, true, arguments.length); let arg$AA$0$; if (x instanceof AA3.class) { arg$AA$0$ = x.x; return arg$AA$0$ + this.#n } throw globalThis.Object.freeze(new globalThis.Error("match error")); } toString() { return runtime.render(this); } static [definitionMetadata] = ["class", "Store", [null]]; }); tmp2 = runtime.checkCall(Store3(1)); store1 = tmp2; tmp3 = runtime.checkCall(AA3(4)); block$res2 = runtime.checkCall(store1.foo(tmp3));; undefined } catch (e) { console.log('\u200B' + e + '\u200B'); } //β”‚ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Invalid or unexpected token //β”‚ ═══[RUNTIME ERROR] Expected: '5', got: 'undefined' //β”‚ ═══[RUNTIME ERROR] ReferenceError: store1 is not defined diff --git a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls index 8a47163fbc..a64c6ea021 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls @@ -1,3 +1,4 @@ +:. :js :deforest @@ -30,9 +31,9 @@ fun c(x) = if x is c(AA(2)) //β”‚ deforest > >>> fusing >>> //β”‚ deforest > A⁰ -> -//β”‚ deforest > match: A⁰ +//β”‚ deforest > match: scrut⁰ //β”‚ deforest > A⁰ -> -//β”‚ deforest > match: A⁰ +//β”‚ deforest > match: scrut⁰ //β”‚ deforest > AA⁰(2) -> //β”‚ deforest > match: x⁰ //β”‚ deforest > fields: x⁰.xΒΉ @@ -165,9 +166,9 @@ fun c(x, y) = c(AA(2), 10) //β”‚ deforest > >>> fusing >>> //β”‚ deforest > A⁰ -> -//β”‚ deforest > match: A⁰ +//β”‚ deforest > match: scrutΒΉ //β”‚ deforest > A⁰ -> -//β”‚ deforest > match: A⁰ +//β”‚ deforest > match: scrutΒΉ //β”‚ deforest > AA⁰(2) -> //β”‚ deforest > match: x⁹ //β”‚ deforest > fields: x⁹.xΒΉ diff --git a/hkmc2/shared/src/test/mlscript/deforest/simple.mls b/hkmc2/shared/src/test/mlscript/deforest/simple.mls index d43fcd646b..1ca71fbbfd 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/simple.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/simple.mls @@ -1,3 +1,4 @@ +:. :js :deforest @@ -295,11 +296,11 @@ fun p(a) = c1(a) + c2(a) p(AA(1)) //β”‚ deforest > >>> fusing >>> //β”‚ deforest > A⁰ -> -//β”‚ deforest > match: A⁰ +//β”‚ deforest > match: scrut⁰ //β”‚ deforest > A⁰ -> -//β”‚ deforest > match: A⁰ +//β”‚ deforest > match: scrut⁰ //β”‚ deforest > A⁰ -> -//β”‚ deforest > match: A⁰ +//β”‚ deforest > match: scrut⁰ //β”‚ deforest > <<< fusing <<< //β”‚ = 2 @@ -461,7 +462,7 @@ if A is k + 2 //β”‚ deforest > >>> fusing >>> //β”‚ deforest > A⁰ -> -//β”‚ deforest > match: A⁰ +//β”‚ deforest > match: scrutΒΉ //β”‚ deforest > <<< fusing <<< //β”‚ = 5 //β”‚ k = undefined @@ -657,13 +658,13 @@ print(c(p(true)) + c(p(false))) c(B) //β”‚ deforest > >>> fusing >>> //β”‚ deforest > A⁰ -> -//β”‚ deforest > match: A⁰ +//β”‚ deforest > match: scrutΒ² //β”‚ deforest > A⁰ -> -//β”‚ deforest > match: A⁰ +//β”‚ deforest > match: scrutΒ² //β”‚ deforest > B⁰ -> //β”‚ deforest > match: x²⁢ //β”‚ deforest > A⁰ -> -//β”‚ deforest > match: A⁰ +//β”‚ deforest > match: scrutΒ² //β”‚ deforest > new AAΒΉ(2) -> //β”‚ deforest > match: x²⁢ //β”‚ deforest > fields: x²⁢.aa⁰ @@ -671,7 +672,7 @@ c(B) //β”‚ deforest > match: x²⁢ //β”‚ deforest > fields: x²⁢.bb⁰ //β”‚ deforest > A⁰ -> -//β”‚ deforest > match: A⁰ +//β”‚ deforest > match: scrutΒ² //β”‚ deforest > new AAΒΉ(2) -> //β”‚ deforest > match: x²⁢ //β”‚ deforest > fields: x²⁢.aa⁰ @@ -856,25 +857,25 @@ fun g(b) = [f(3), g(true)] //β”‚ deforest > >>> fusing >>> //β”‚ deforest > BB⁰(0) -> -//β”‚ deforest > match: scrut⁰ +//β”‚ deforest > match: scrutΒ³ //β”‚ deforest > D⁰ -> -//β”‚ deforest > match: scrut⁰ +//β”‚ deforest > match: scrutΒ³ //β”‚ deforest > AA⁰(0) -> -//β”‚ deforest > match: scrutΒΉ +//β”‚ deforest > match: scrut⁴ //β”‚ deforest > AA⁰(1) -> -//β”‚ deforest > match: scrutΒΉ +//β”‚ deforest > match: scrut⁴ //β”‚ deforest > C⁰ -> -//β”‚ deforest > match: scrutΒΉ +//β”‚ deforest > match: scrut⁴ //β”‚ deforest > BB⁰(0) -> -//β”‚ deforest > match: scrut⁰ +//β”‚ deforest > match: scrutΒ³ //β”‚ deforest > D⁰ -> -//β”‚ deforest > match: scrut⁰ +//β”‚ deforest > match: scrutΒ³ //β”‚ deforest > AA⁰(0) -> -//β”‚ deforest > match: scrutΒΉ +//β”‚ deforest > match: scrut⁴ //β”‚ deforest > AA⁰(1) -> -//β”‚ deforest > match: scrutΒΉ +//β”‚ deforest > match: scrut⁴ //β”‚ deforest > C⁰ -> -//β”‚ deforest > match: scrutΒΉ +//β”‚ deforest > match: scrut⁴ //β”‚ deforest > <<< fusing <<< //β”‚ = [D, AA(1)] diff --git a/hkmc2/shared/src/test/mlscript/deforest/todos.mls b/hkmc2/shared/src/test/mlscript/deforest/todos.mls index 21f8f05f8a..edfb659c39 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/todos.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/todos.mls @@ -1,3 +1,4 @@ +:. :js :deforest @@ -195,10 +196,10 @@ test() //β”‚ deforest > fields: scrut⁰.xΒ² //β”‚ deforest > <<< fusing <<< //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'Local' -//β”‚ β•‘ l.187: Global(x) then Local(x + 1) +//β”‚ β•‘ l.188: Global(x) then Local(x + 1) //β”‚ β•‘ ^^^^^ //β”‚ β•Ÿβ”€β”€ which references the symbol introduced here -//β”‚ β•‘ l.185: data class Local(x) +//β”‚ β•‘ l.186: data class Local(x) //β”‚ ╙── ^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] ReferenceError: Local is not defined //β”‚ ═══[RUNTIME ERROR] Expected: 'Local(2)', got: 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/deforest/zipunzip.mls b/hkmc2/shared/src/test/mlscript/deforest/zipunzip.mls index d9bb2a716b..213fa2ee2d 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/zipunzip.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/zipunzip.mls @@ -1,3 +1,4 @@ +:. :js :deforest diff --git a/hkmc2/shared/src/test/mlscript/flows/BasicFlows.mls b/hkmc2/shared/src/test/mlscript/flows/BasicFlows.mls index 5966f72e06..01455eeeca 100644 --- a/hkmc2/shared/src/test/mlscript/flows/BasicFlows.mls +++ b/hkmc2/shared/src/test/mlscript/flows/BasicFlows.mls @@ -50,7 +50,7 @@ fun test(x) = x.m //β”‚ β•‘ l.48: fun test(x) = x.m //β”‚ ╙── ^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ fun test⁰::testΒΉ(xΒΉ) β€Ήflow:test⁰› = xΒΉ.mβ€Ή?β€Ί +//β”‚ fun test⁰::testΒΉ(xΒΉ) β€Ήflow:test⁰› = xΒΉ.mΛ€Λ€Λ€ //β”‚ where //β”‚ xΒΉ ~> {m: β‹…m⁰} //β”‚ flow:test⁰ <~ ((xΒΉ) -> β‹…m⁰) @@ -60,7 +60,7 @@ fun test(x) = x.m // :df fun test(x: Foo) = x.m //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ fun testΒ²::testΒ³(xΒ²: Foo⁰{ ~> Foo⁰ }) β€Ήflow:testΒΉβ€Ί = xΒ².mβ€Ή?β€Ί{ ~> xΒ².m⁰ } +//β”‚ fun testΒ²::testΒ³(xΒ²: Foo⁰{ ~> Foo⁰ }^testΒ³) β€Ήflow:testΒΉβ€Ί = xΒ².mΛ€Λ€Λ€{ ~> xΒ².m⁰ } //β”‚ where //β”‚ xΒ² <~ type Foo //β”‚ flow:testΒΉ <~ ((type Foo) -> flow:m⁰) @@ -69,7 +69,7 @@ fun test(x: Foo) = x.m let f = (x: Foo) => x.m //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let f⁰, -//β”‚ f⁰ = (xΒ³: Foo⁰{ ~> Foo⁰ }) => xΒ³.mβ€Ή?β€Ί{ ~> xΒ³.m⁰ } +//β”‚ f⁰ = (xΒ³: Foo⁰{ ~> Foo⁰ }) => xΒ³.mΛ€Λ€Λ€{ ~> xΒ³.m⁰ } //β”‚ where //β”‚ f⁰ <~ ((type Foo) -> flow:m⁰) //β”‚ xΒ³ <~ type Foo diff --git a/hkmc2/shared/src/test/mlscript/flows/LeadingDotAccesses.mls b/hkmc2/shared/src/test/mlscript/flows/LeadingDotAccesses.mls index e017b8826e..46093abd8a 100644 --- a/hkmc2/shared/src/test/mlscript/flows/LeadingDotAccesses.mls +++ b/hkmc2/shared/src/test/mlscript/flows/LeadingDotAccesses.mls @@ -88,7 +88,7 @@ g(d) //β”‚ β•‘ l.8: fun a = new A //β”‚ ╙── ^^^^^^^^^^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ fun g⁰::gΒΉ(x⁰: C⁰{ ~> C⁰ }) β€Ήflow:g⁰› = x⁰, +//β”‚ fun g⁰::gΒΉ(x⁰: C⁰{ ~> C⁰ }^gΒΉ) β€Ήflow:g⁰› = x⁰, //β”‚ let d⁰, //β”‚ d⁰ = _?_.a{ ~> β€Ήerrorβ€Ί }, //β”‚ f⁰{ ~> f⁰ }(d⁰)β€Ήapp⁹›, diff --git a/hkmc2/shared/src/test/mlscript/flows/SelExpansion.mls b/hkmc2/shared/src/test/mlscript/flows/SelExpansion.mls index 6b4f7ec7c4..0614ac66e5 100644 --- a/hkmc2/shared/src/test/mlscript/flows/SelExpansion.mls +++ b/hkmc2/shared/src/test/mlscript/flows/SelExpansion.mls @@ -15,7 +15,7 @@ module Foo with //β”‚ val a⁰::aΒΉ β€Ήflow:a⁰› = 123 //β”‚ }, //β”‚ module Foo⁰::FooΒ² { -//β”‚ fun foo⁰::fooΒΉ(x⁰: Foo⁰{ ~> Foo⁰ }) β€Ήflow:foo⁰› = x⁰.aβ€Ή?β€Ί{ ~> x⁰.a⁰ } +//β”‚ fun foo⁰::fooΒΉ(x⁰: Foo⁰{ ~> Foo⁰ }^fooΒΉ) β€Ήflow:foo⁰› = x⁰.aΛ€Λ€Λ€{ ~> x⁰.a⁰ } //β”‚ } //β”‚ where //β”‚ x⁰ <~ type Foo @@ -50,16 +50,16 @@ f.a.b //β”‚ β•‘ l.10: val a = 123 //β”‚ ╙── ^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ f⁰.a⁰.bβ€Ή?β€Ί +//β”‚ f⁰.a⁰.bΛ€Λ€Λ€ //β”‚ where //β”‚ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'b' yielded 'undefined' +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :sjs f.foo //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ f⁰.fooβ€Ή?β€Ί{ ~> Foo⁰.foo⁰(f⁰)β€Ήapp⁰› } +//β”‚ f⁰.Foo⁰.foo⁰{ ~> Foo⁰.foo⁰(f⁰)β€Ήapp⁰› } //β”‚ where //β”‚ f⁰ <~ Foo() //β”‚ f⁰ ~> {a: β‹…a⁰} {a: β‹…aΒΉ} {foo: β‹…foo⁰} @@ -78,7 +78,7 @@ fun id(x) = x id(f).foo //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ id⁰{ ~> id⁰ }(f⁰)β€ΉappΒΉβ€Ί.fooβ€Ή?β€Ί{ ~> Foo⁰.foo⁰(id⁰(f⁰)β€ΉappΒΉβ€Ί)β€ΉappΒ²β€Ί } +//β”‚ id⁰{ ~> id⁰ }(f⁰)β€ΉappΒΉβ€Ί.Foo⁰.foo⁰{ ~> Foo⁰.foo⁰(id⁰(f⁰)β€ΉappΒΉβ€Ί)β€ΉappΒ²β€Ί } //β”‚ where //β”‚ f⁰ <~ Foo() //β”‚ f⁰ ~> {a: β‹…a⁰} {a: β‹…aΒΉ} {foo: β‹…foo⁰} @@ -100,7 +100,7 @@ let id(x) = x id(f).foo //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ idΒ²(f⁰)β€ΉappΒ³β€Ί.fooβ€Ή?β€Ί{ ~> Foo⁰.foo⁰(idΒ²(f⁰)β€ΉappΒ³β€Ί)β€Ήapp⁴› } +//β”‚ idΒ²(f⁰)β€ΉappΒ³β€Ί.Foo⁰.foo⁰{ ~> Foo⁰.foo⁰(idΒ²(f⁰)β€ΉappΒ³β€Ί)β€Ήapp⁴› } //β”‚ where //β”‚ f⁰ <~ Foo() //β”‚ f⁰ ~> {a: β‹…a⁰} {a: β‹…aΒΉ} {foo: β‹…foo⁰} @@ -138,7 +138,7 @@ id(f).foo //β”‚ β•‘ l.123: id(0) //β”‚ ╙── ^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ idΒ³{ ~> idΒ³ }(f⁰)β€Ήapp⁢›.fooβ€Ή?β€Ί{ ~> Foo⁰.foo⁰(idΒ³(f⁰)β€Ήapp⁢›)β€Ήapp⁷› } +//β”‚ idΒ³{ ~> idΒ³ }(f⁰)β€Ήapp⁢›.Foo⁰.foo⁰{ ~> Foo⁰.foo⁰(idΒ³(f⁰)β€Ήapp⁢›)β€Ήapp⁷› } //β”‚ where //β”‚ f⁰ <~ Foo() //β”‚ f⁰ ~> {a: β‹…a⁰} {a: β‹…aΒΉ} {foo: β‹…foo⁰} @@ -155,7 +155,7 @@ fun test(g) = g.foo //β”‚ β•‘ l.153: fun test(g) = g.foo //β”‚ ╙── ^^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ fun test⁰::testΒΉ(g⁰) β€Ήflow:test⁰› = g⁰.fooβ€Ή?β€Ί +//β”‚ fun test⁰::testΒΉ(g⁰) β€Ήflow:test⁰› = g⁰.fooΛ€Λ€Λ€ //β”‚ where //β”‚ g⁰ ~> {foo: β‹…foo⁴} //β”‚ flow:test⁰ <~ ((g⁰) -> β‹…foo⁴) @@ -176,7 +176,7 @@ test(f) fun test(g) = g.foo test(f) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ fun testΒ²::testΒ³(gΒΉ) β€Ήflow:testΒΉβ€Ί = gΒΉ.fooβ€Ή?β€Ί{ ~> Foo⁰.foo⁰(gΒΉ)β€Ήapp⁹› }, +//β”‚ fun testΒ²::testΒ³(gΒΉ) β€Ήflow:testΒΉβ€Ί = gΒΉ.Foo⁰.foo⁰{ ~> Foo⁰.foo⁰(gΒΉ)β€Ήapp⁹› }, //β”‚ testΒ²{ ~> testΒ² }(f⁰)β€Ήapp¹⁰› //β”‚ where //β”‚ f⁰ <~ Foo() @@ -199,10 +199,10 @@ module AA with //β”‚ module AA⁰::AAΒΉ { //β”‚ module BB⁰::BBΒΉ { //β”‚ class CC⁰::CCΒΉ { -//β”‚ val x⁴::x⁡: Int⁰{ ~> Int⁰ } β€Ήflow:x⁰› = 1 +//β”‚ val x⁴::x⁡: Int⁰{ ~> Int⁰ }^CCΒΉ^x⁡ β€Ήflow:x⁰› = 1 //β”‚ }, //β”‚ module CC⁰::CCΒ² { -//β”‚ fun getX⁰::getXΒΉ(self⁰: ⟨BBΒΉ.⟩CC⁰) β€Ήflow:getX⁰› = self⁰.xβ€Ή?β€Ί{ ~> self⁰.x⁴ } +//β”‚ fun getX⁰::getXΒΉ(self⁰: ⟨BBΒΉ.⟩CC⁰^getXΒΉ) β€Ήflow:getX⁰› = self⁰.xΛ€Λ€Λ€{ ~> self⁰.x⁴ } //β”‚ } //β”‚ } //β”‚ } @@ -215,7 +215,7 @@ module AA with :sjs new AA.BB.CC().x //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ new AA⁰.BB⁰.CC⁰().xβ€Ή?β€Ί{ ~> new AA⁰.BB⁰.CC⁰().x⁴ } +//β”‚ new AA⁰.BB⁰.CC⁰().xΛ€Λ€Λ€{ ~> new AA⁰.BB⁰.CC⁰().x⁴ } //β”‚ where //β”‚ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” @@ -226,7 +226,7 @@ new AA.BB.CC().x :sjs new AA.BB.CC().getX //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ new AA⁰.BB⁰.CC⁰().getXβ€Ή?β€Ί{ ~> AA⁰.BB⁰.CC⁰.getX⁰(new AA⁰.BB⁰.CC⁰())β€ΉappΒΉΒΉβ€Ί } +//β”‚ new AA⁰.BB⁰.CC⁰().AA⁰.BB⁰.CC⁰.getX⁰{ ~> AA⁰.BB⁰.CC⁰.getX⁰(new AA⁰.BB⁰.CC⁰())β€ΉappΒΉΒΉβ€Ί } //β”‚ where //β”‚ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” @@ -266,12 +266,12 @@ foo.test //β”‚ β•‘ l.264: foo.test //β”‚ ╙── ^^^^^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ fooΒ².testβ€Ή?β€Ί +//β”‚ fooΒ².testΛ€Λ€Λ€ //β”‚ where //β”‚ fooΒ² <~ A //β”‚ fooΒ² ~> {test: β‹…test⁰} //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'test' yielded 'undefined' +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. let foo = @@ -288,7 +288,7 @@ let foo = //β”‚ module AΒ³::A⁡ { //β”‚ fun test⁢::test⁷(aΒ³) β€Ήflow:testΒ³β€Ί = new AΒ³ //β”‚ }, -//β”‚ new AΒ³.testβ€Ή?β€Ί{ ~> AΒ³.test⁢(new AΒ³)β€ΉappΒΉΒ²β€Ί } +//β”‚ new AΒ³.AΒ³.test⁢{ ~> AΒ³.test⁢(new AΒ³)β€ΉappΒΉΒ²β€Ί } //β”‚ } //β”‚ where //β”‚ fooΒ³ <~ A @@ -305,12 +305,12 @@ foo.test //β”‚ β•‘ l.303: foo.test //β”‚ ╙── ^^^^^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ fooΒ³.testβ€Ή?β€Ί +//β”‚ fooΒ³.testΛ€Λ€Λ€ //β”‚ where //β”‚ fooΒ³ <~ A //β”‚ fooΒ³ ~> {test: β‹…testΒ²} //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'test' yielded 'undefined' +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. class CC(val x: Int) @@ -318,11 +318,11 @@ module CC with fun getX(self: CC) = self.x //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ fun CCΒ³ -//β”‚ class CC⁴::CC⁡(val x⁢: Int⁰{ ~> Int⁰ }) { +//β”‚ class CC⁴::CC⁡(val x⁢: Int⁰{ ~> Int⁰ }^CC⁡) { //β”‚ val x⁷::x⁸ β€Ήflow:xΒΉβ€Ί = x⁢ //β”‚ }, //β”‚ module CC⁴::CC⁢ { -//β”‚ fun getXΒ²::getXΒ³(selfΒΉ: CC⁴{ ~> CC⁴ }) β€Ήflow:getXΒΉβ€Ί = selfΒΉ.xβ€Ή?β€Ί{ ~> selfΒΉ.x⁷ } +//β”‚ fun getXΒ²::getXΒ³(selfΒΉ: CC⁴{ ~> CC⁴ }^getXΒ³) β€Ήflow:getXΒΉβ€Ί = selfΒΉ.xΛ€Λ€Λ€{ ~> selfΒΉ.x⁷ } //β”‚ } //β”‚ where //β”‚ x⁢ <~ type Int @@ -335,7 +335,7 @@ module CC with :fixme // TODO: handle lifted class defs: the JS is missing the `.class` access to get the static member `CC2.class.getX` CC(123).getX //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ CC⁴(123)β€ΉappΒΉΒ³β€Ί.getXβ€Ή?β€Ί{ ~> CC⁴.getXΒ²(CC⁴(123)β€ΉappΒΉΒ³β€Ί)β€Ήapp¹⁴› } +//β”‚ CC⁴(123)β€ΉappΒΉΒ³β€Ί.CC⁴.getXΒ²{ ~> CC⁴.getXΒ²(CC⁴(123)β€ΉappΒΉΒ³β€Ί)β€Ήapp¹⁴› } //β”‚ where //β”‚ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” @@ -351,7 +351,7 @@ module CC with //β”‚ //β”‚ }, //β”‚ module CC⁷::CC⁹ { -//β”‚ fun oops⁰::oopsΒΉ(x⁹: CC⁷{ ~> CC⁷ }) β€Ήflow:oops⁰› = x⁹.oopsβ€Ή?β€Ί{ ~> CC⁷.oops⁰(x⁹)β€Ήapp¹⁡› } +//β”‚ fun oops⁰::oopsΒΉ(x⁹: CC⁷{ ~> CC⁷ }^oopsΒΉ) β€Ήflow:oops⁰› = x⁹.CC⁷.oops⁰{ ~> CC⁷.oops⁰(x⁹)β€Ήapp¹⁡› } //β”‚ } //β”‚ where //β”‚ x⁹ <~ type CC @@ -363,7 +363,7 @@ module CC with :re new CC().oops //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ new CC⁷().oopsβ€Ή?β€Ί{ ~> CC⁷.oops⁰(new CC⁷())β€Ήapp¹⁢› } +//β”‚ new CC⁷().CC⁷.oops⁰{ ~> CC⁷.oops⁰(new CC⁷())β€Ήapp¹⁢› } //β”‚ where //β”‚ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” @@ -379,7 +379,7 @@ module CC with //β”‚ val okay⁰::okayΒΉ β€Ήflow:okay⁰› = 123 //β”‚ }, //β”‚ module CC¹⁰::CCΒΉΒ² { -//β”‚ fun okayΒ²::okayΒ³(x¹⁰: CC¹⁰{ ~> CC¹⁰ }) β€Ήflow:okayΒΉβ€Ί = x¹⁰.okayβ€Ή?β€Ί{ ~> x¹⁰.okay⁰ } +//β”‚ fun okayΒ²::okayΒ³(x¹⁰: CC¹⁰{ ~> CC¹⁰ }^okayΒ³) β€Ήflow:okayΒΉβ€Ί = x¹⁰.okayΛ€Λ€Λ€{ ~> x¹⁰.okay⁰ } //β”‚ } //β”‚ where //β”‚ x¹⁰ <~ type CC @@ -389,7 +389,7 @@ module CC with new CC().okay //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ new CC¹⁰().okayβ€Ή?β€Ί{ ~> new CC¹⁰().okay⁰ } +//β”‚ new CC¹⁰().okayΛ€Λ€Λ€{ ~> new CC¹⁰().okay⁰ } //β”‚ where //β”‚ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” @@ -403,11 +403,11 @@ module Example with //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ module Example⁰::ExampleΒΉ { //β”‚ fun Test⁰ -//β”‚ class TestΒΉ::TestΒ²(val field⁰: Int⁰{ ~> Int⁰ }) { +//β”‚ class TestΒΉ::TestΒ²(val field⁰: Int⁰{ ~> Int⁰ }^TestΒ²) { //β”‚ val fieldΒΉ::fieldΒ² β€Ήflow:field⁰› = field⁰ //β”‚ }, //β”‚ module TestΒΉ::TestΒ³ { -//β”‚ fun next⁰::nextΒΉ(selfΒ²: ⟨ExampleΒΉ.⟩TestΒΉ) β€Ήflow:next⁰› = ⟨ExampleΒΉ.⟩TestΒΉ(+(selfΒ².fieldΒΉ, 1)β€Ήapp¹⁷›)β€Ήapp¹⁸› +//β”‚ fun next⁰::nextΒΉ(selfΒ²: ⟨ExampleΒΉ.⟩TestΒΉ^nextΒΉ) β€Ήflow:next⁰› = ⟨ExampleΒΉ.⟩TestΒΉ(+(selfΒ².fieldΒΉ, 1)β€Ήapp¹⁷›)β€Ήapp¹⁸› //β”‚ } //β”‚ } //β”‚ where @@ -430,7 +430,7 @@ let e = new Example.Test(123) :fixme // Same as test case above (missing `.class` access) e.next //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ e⁰.nextβ€Ή?β€Ί{ ~> Example⁰.TestΒΉ.next⁰(e⁰)β€Ήapp¹⁹› } +//β”‚ e⁰.Example⁰.TestΒΉ.next⁰{ ~> Example⁰.TestΒΉ.next⁰(e⁰)β€Ήapp¹⁹› } //β”‚ where //β”‚ e⁰ <~ Test(123) //β”‚ e⁰ ~> {next: β‹…next⁰} diff --git a/hkmc2/shared/src/test/mlscript/flows/SelExpansionImport.mls b/hkmc2/shared/src/test/mlscript/flows/SelExpansionImport.mls index 473d127f7a..0c425ef222 100644 --- a/hkmc2/shared/src/test/mlscript/flows/SelExpansionImport.mls +++ b/hkmc2/shared/src/test/mlscript/flows/SelExpansionImport.mls @@ -39,12 +39,12 @@ e.next //β”‚ β•‘ l.37: e.next //β”‚ ╙── ^^^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ e⁰.nextβ€Ή?β€Ί +//β”‚ e⁰.nextΛ€Λ€Λ€ //β”‚ where //β”‚ e⁰ <~ Test(123) //β”‚ e⁰ ~> {field: β‹…field⁰} {next: β‹…next⁰} //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'next' yielded 'undefined' +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. open Example @@ -71,12 +71,12 @@ e.next //β”‚ β•‘ l.69: e.next //β”‚ ╙── ^^^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ eΒΉ.nextβ€Ή?β€Ί +//β”‚ eΒΉ.nextΛ€Λ€Λ€ //β”‚ where //β”‚ eΒΉ <~ Test(123) //β”‚ eΒΉ ~> {next: β‹…nextΒΉ} //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'next' yielded 'undefined' +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -132,11 +132,11 @@ Example.t.field //β”‚ β•‘ ^^^^^^^^^^^^^^^ //β”‚ ╙── Type `type ⊀` does not contain member 'field' //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ Example⁰{ ~> Example⁰ }.t⁰{ ~> Example⁰.t⁰ }.fieldβ€Ή?β€Ί +//β”‚ Example⁰{ ~> Example⁰ }.t⁰{ ~> Example⁰.t⁰ }.fieldΛ€Λ€Λ€ //β”‚ where //β”‚ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ = 123 +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :fixme // Same Example.t.next @@ -148,10 +148,10 @@ Example.t.next //β”‚ β•‘ ^^^^^^^^^^^^^^ //β”‚ ╙── Type `type ⊀` does not contain member 'next' //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Flowed |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ Example⁰{ ~> Example⁰ }.t⁰{ ~> Example⁰.t⁰ }.nextβ€Ή?β€Ί +//β”‚ Example⁰{ ~> Example⁰ }.t⁰{ ~> Example⁰.t⁰ }.nextΛ€Λ€Λ€ //β”‚ where //β”‚ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'next' yielded 'undefined' +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/handlers/.mls b/hkmc2/shared/src/test/mlscript/handlers/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/handlers/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/handlers/Async.mls b/hkmc2/shared/src/test/mlscript/handlers/Async.mls index 383602c03f..a7c4f348f4 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Async.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Async.mls @@ -1,15 +1,23 @@ +:. :js :lift import "fs" +// Describe the foreign API at the import boundary; await preserves these payloads. +:silent +let fileSystem = fs as {promises: { + readFile: Str -> Promise[{toString: () -> Str}], + readdir: Str -> Promise[Array[Str]] +}} + open annotations open handlers @async -fun f(print_result) = +fun f(print_result: Bool) = print("Hello, world!") - let r = await(fs.promises.readFile("./README.md")) + let r = await(fileSystem.promises.readFile("./README.md")) let readme = r.toString() let first = readme.slice(0, readme.indexOf("\n")) if print_result do @@ -41,7 +49,7 @@ f() fun f() = await(Promise.resolve(1)) //β”‚ ╔══[COMPILATION ERROR] Only await inside of async bodies are allowed if effect handlers are not enabled. -//β”‚ β•‘ l.42: await(Promise.resolve(1)) +//β”‚ β•‘ l.50: await(Promise.resolve(1)) //β”‚ ╙── ^^^^^ :re @@ -75,11 +83,11 @@ f() fun f() = print(0) - let r = await(fs.promises.readFile("./README.md")) + let r = await(fileSystem.promises.readFile("./README.md")) let readme = r.toString() let first = readme.slice(0, readme.indexOf("\n")) print(first) - let r2 = await(fs.promises.readdir(".")) + let r2 = await(fileSystem.promises.readdir(".")) print(r2.indexOf("README.md") >= 0) @async @@ -104,18 +112,18 @@ fun g() = @async fun f1() = print("f1 started") - let work1 = new Promise((resolve, reject) => + let work1 = new Promise[undefined]((resolve, reject) => set work1resolve = resolve ) await(work1) print("f1 done") - work2resolve() + work2resolve(undefined) @async fun f2() = print("f2 started") - let work2 = new Promise((resolve, reject) => + let work2 = new Promise[undefined]((resolve, reject) => set work2resolve = resolve - work1resolve() + work1resolve(undefined) ) await(work2) print("f2 done") diff --git a/hkmc2/shared/src/test/mlscript/handlers/CurriedFunctionCalls.mls b/hkmc2/shared/src/test/mlscript/handlers/CurriedFunctionCalls.mls index a3875cb9c5..0437cadca1 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/CurriedFunctionCalls.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/CurriedFunctionCalls.mls @@ -1,3 +1,4 @@ +:. :js :effectHandlers @@ -60,7 +61,7 @@ fun g(a)(b) = f(a)(b) + f(b)(a) //β”‚ set pc = 2; //β”‚ end //β”‚ else -//β”‚ return runtime⁰.unwindοΉ–(gΒΉ, 2, "CurriedFunctionCalls.mls:32:15", null, null, 2, 1, a, 1, b, 1, tmp) +//β”‚ return runtime⁰.unwindοΉ–(gΒΉ, 2, "CurriedFunctionCalls.mls:33:15", null, null, 2, 1, a, 1, b, 1, tmp) //β”‚ end //β”‚ match pc //β”‚ 2 => @@ -73,7 +74,7 @@ fun g(a)(b) = f(a)(b) + f(b)(a) //β”‚ set pc = 1; //β”‚ end //β”‚ else -//β”‚ return runtime⁰.unwindοΉ–(gΒΉ, 1, "CurriedFunctionCalls.mls:32:25", null, null, 2, 1, a, 1, b, 1, tmp) +//β”‚ return runtime⁰.unwindοΉ–(gΒΉ, 1, "CurriedFunctionCalls.mls:33:25", null, null, 2, 1, a, 1, b, 1, tmp) //β”‚ end //β”‚ match pc //β”‚ 1 => diff --git a/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls b/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls index 6deaebf5da..eee514784c 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls @@ -129,7 +129,7 @@ res.resumeWith(42) -class Debugger() with +abstract class Debugger() with fun break(payload) module Test with diff --git a/hkmc2/shared/src/test/mlscript/handlers/EffectInHandler.mls b/hkmc2/shared/src/test/mlscript/handlers/EffectInHandler.mls index bed1bc53d9..24fabe0b83 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/EffectInHandler.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/EffectInHandler.mls @@ -2,7 +2,7 @@ :effectHandlers :lift -class PrintEffect with +abstract class PrintEffect with fun p(s: String): () fun aux(s: String): () diff --git a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls index be34806400..fe8ace8571 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls @@ -197,8 +197,8 @@ fun f() = case [...x, y, z] then x //β”‚ match caseScrut //β”‚ Array(2+) => //β”‚ let inlinedVal, tmp; -//β”‚ set tmp = -⁰(caseScrut.lengthοΉ–, 2); -//β”‚ set inlinedVal = caseScrut.sliceοΉ–(0, tmp); +//β”‚ set tmp = -⁰(caseScrut.length⁰, 2); +//β”‚ set inlinedVal = caseScrut.slice⁰(0, tmp); //β”‚ do runtime⁰.Tuple⁰.get⁰(caseScrut, -2); //β”‚ do runtime⁰.Tuple⁰.get⁰(caseScrut, -1); //β”‚ return inlinedVal @@ -311,7 +311,7 @@ if true do //β”‚ > 3 module A with - data class Effect(x) with + data abstract class Effect(x) with fun test() = x fun perform(arg) diff --git a/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls b/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls index 51f96b4eec..3b5685c0de 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls @@ -1,3 +1,4 @@ +:. :js :effectHandlers :lift @@ -16,16 +17,16 @@ fun foo(h): Object = module A A //β”‚ ╔══[WARNING] Modules are not yet lifted. -//β”‚ β•‘ l.16: module A +//β”‚ β•‘ l.17: module A //β”‚ ╙── ^ //β”‚ ╔══[WARNING] Modules are not yet lifted. -//β”‚ β•‘ l.13: module A +//β”‚ β•‘ l.14: module A //β”‚ ╙── ^ //β”‚ ╔══[INTERNAL ERROR] Unexpected nested class: lambdas may not function correctly. -//β”‚ β•‘ l.13: module A +//β”‚ β•‘ l.14: module A //β”‚ ╙── ^ //β”‚ ╔══[INTERNAL ERROR] Unexpected nested class: lambdas may not function correctly. -//β”‚ β•‘ l.16: module A +//β”‚ β•‘ l.17: module A //β”‚ ╙── ^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (sanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let foo; diff --git a/hkmc2/shared/src/test/mlscript/handlers/Fallthrough.mls b/hkmc2/shared/src/test/mlscript/handlers/Fallthrough.mls index 863dbaceac..e64fb4945e 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Fallthrough.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Fallthrough.mls @@ -1,3 +1,4 @@ +:. :js :effectHandlers :lift @@ -36,7 +37,7 @@ fun f() = //β”‚ if (runtime.curEffect === null) { //β”‚ pc = 6; //β”‚ } else { -//β”‚ return runtime.unwind(f, 6, "Fallthrough.mls:11:19", null, null, 1, 0, 1, tmp) +//β”‚ return runtime.unwind(f, 6, "Fallthrough.mls:12:19", null, null, 1, 0, 1, tmp) //β”‚ } //β”‚ case 6: //β”‚ runtime.resumeValue; @@ -46,7 +47,7 @@ fun f() = //β”‚ if (runtime.curEffect === null) { //β”‚ pc = 4; //β”‚ } else { -//β”‚ return runtime.unwind(f, 4, "Fallthrough.mls:12:11", null, null, 1, 0, 1, tmp) +//β”‚ return runtime.unwind(f, 4, "Fallthrough.mls:13:11", null, null, 1, 0, 1, tmp) //β”‚ } //β”‚ case 4: //β”‚ runtime.resumeValue; @@ -54,7 +55,7 @@ fun f() = //β”‚ if (runtime.curEffect === null) { //β”‚ pc = 3; //β”‚ } else { -//β”‚ return runtime.unwind(f, 3, "Fallthrough.mls:13:11", null, null, 1, 0, 1, tmp) +//β”‚ return runtime.unwind(f, 3, "Fallthrough.mls:14:11", null, null, 1, 0, 1, tmp) //β”‚ } //β”‚ case 3: //β”‚ runtime.resumeValue; @@ -62,7 +63,7 @@ fun f() = //β”‚ if (runtime.curEffect === null) { //β”‚ pc = 2; //β”‚ } else { -//β”‚ return runtime.unwind(f, 2, "Fallthrough.mls:14:11", null, null, 1, 0, 1, tmp) +//β”‚ return runtime.unwind(f, 2, "Fallthrough.mls:15:11", null, null, 1, 0, 1, tmp) //β”‚ } //β”‚ case 2: //β”‚ runtime.resumeValue; @@ -70,7 +71,7 @@ fun f() = //β”‚ if (runtime.curEffect === null) { //β”‚ pc = 1; //β”‚ } else { -//β”‚ return runtime.unwind(f, 1, "Fallthrough.mls:15:11", null, null, 1, 0, 1, tmp) +//β”‚ return runtime.unwind(f, 1, "Fallthrough.mls:16:11", null, null, 1, 0, 1, tmp) //β”‚ } //β”‚ case 1: //β”‚ return runtime.resumeValue; @@ -81,7 +82,7 @@ fun f() = //β”‚ pc = 7; //β”‚ continue main //β”‚ } -//β”‚ return runtime.unwind(f, 7, "Fallthrough.mls:11:13", null, null, 1, 0, 1, tmp); +//β”‚ return runtime.unwind(f, 7, "Fallthrough.mls:12:13", null, null, 1, 0, 1, tmp); //β”‚ } //β”‚ pc = 5; //β”‚ continue main; diff --git a/hkmc2/shared/src/test/mlscript/handlers/GeneratorStack.mls b/hkmc2/shared/src/test/mlscript/handlers/GeneratorStack.mls index cd01677764..1a34181221 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/GeneratorStack.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/GeneratorStack.mls @@ -5,7 +5,7 @@ import "../../mlscript-compile/Stack.mls" open Stack -class Generator with +abstract class Generator with fun produce(result: Stack[Int]): () fun (++) concat(l1, l2) = diff --git a/hkmc2/shared/src/test/mlscript/handlers/HandlerReset.mls b/hkmc2/shared/src/test/mlscript/handlers/HandlerReset.mls index 40ad2030da..c5514cb8cf 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/HandlerReset.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/HandlerReset.mls @@ -1,3 +1,4 @@ +:. :js :effectHandlers :lift diff --git a/hkmc2/shared/src/test/mlscript/handlers/HandlersScratch.mls b/hkmc2/shared/src/test/mlscript/handlers/HandlersScratch.mls index 67b143ed20..252ae4ede3 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/HandlersScratch.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/HandlersScratch.mls @@ -1,3 +1,4 @@ +:. :js :effectHandlers :lift diff --git a/hkmc2/shared/src/test/mlscript/handlers/Loops.mls b/hkmc2/shared/src/test/mlscript/handlers/Loops.mls index 542d3fc2b5..72d6492946 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Loops.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Loops.mls @@ -1,3 +1,4 @@ +:. :js :lift :effectHandlers diff --git a/hkmc2/shared/src/test/mlscript/handlers/MultiResumption.mls b/hkmc2/shared/src/test/mlscript/handlers/MultiResumption.mls index 43573d189b..6b5da74e54 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/MultiResumption.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/MultiResumption.mls @@ -2,7 +2,7 @@ :effectHandlers :lift -class Logger with +abstract class Logger with fun info(s: Str): () :re diff --git a/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls index 392d5fb2a6..0f57ae871d 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls @@ -6,7 +6,7 @@ let id = 0 //β”‚ id = 0 // FIXME: if the following 2 blocks are together, lifter causes class ordering issue -class MaybeStop with +abstract class MaybeStop with fun f(x: Bool): () fun handleEffects(g) = @@ -89,7 +89,7 @@ handleEffects(f) //β”‚ > h1 end 10 -class Eff with +abstract class Eff with fun perform(): () data class Box(n) let box = new mut Box(0) diff --git a/hkmc2/shared/src/test/mlscript/handlers/NoHandler.mls b/hkmc2/shared/src/test/mlscript/handlers/NoHandler.mls index f292f4cc6f..f07d713f9a 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/NoHandler.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/NoHandler.mls @@ -1,3 +1,4 @@ +:. :js abstract class Effect with @@ -8,10 +9,10 @@ handle h = Effect with fun perform(arg)(k) = k(arg) h.perform("k") //β”‚ ╔══[COMPILATION ERROR] Effect handlers are not enabled -//β”‚ β•‘ l.7: handle h = Effect with +//β”‚ β•‘ l.8: handle h = Effect with //β”‚ β•‘ ^^^^^^^^^^^ -//β”‚ β•‘ l.8: fun perform(arg)(k) = k(arg) +//β”‚ β•‘ l.9: fun perform(arg)(k) = k(arg) //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//β”‚ β•‘ l.9: h.perform("k") -//β”‚ ╙── ^^^^^^^^^^^^^^ +//β”‚ β•‘ l.10: h.perform("k") +//β”‚ ╙── ^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/handlers/NonLocalLabelControlFlow.mls b/hkmc2/shared/src/test/mlscript/handlers/NonLocalLabelControlFlow.mls index 7858130ddb..70af738d81 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/NonLocalLabelControlFlow.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/NonLocalLabelControlFlow.mls @@ -71,7 +71,7 @@ continueFromNestedFunctionWithEffects() //β”‚ let loop, handleBlock$, NonLocalLabelEffectbreak, NonLocalLabelEffectcontinue, NonLocalLabelEffect, Capture$scope0, handleBlock$$, NonLocalLabelEffectbreak$, tmp; //β”‚ do runtime⁰.resetEffectsοΉ–(); //β”‚ define NonLocalLabelEffectcontinue as fun NonLocalLabelEffectcontinue⁰(resume) { -//β”‚ return runtime⁰.ContinueοΉ– +//β”‚ return runtime⁰.Continue⁰ //β”‚ }; //β”‚ @inline //β”‚ define NonLocalLabelEffectbreak$ as fun NonLocalLabelEffectbreak$⁰(value)(resume) { diff --git a/hkmc2/shared/src/test/mlscript/handlers/SavedVars.mls b/hkmc2/shared/src/test/mlscript/handlers/SavedVars.mls index cb7981397e..375787ec9f 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/SavedVars.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/SavedVars.mls @@ -1,3 +1,4 @@ +:. :js :effectHandlers :lift @@ -38,7 +39,7 @@ fun g() = //β”‚ if (runtime.curEffect === null) { //β”‚ pc = 3; //β”‚ } else { -//β”‚ return runtime.unwind(g, 3, "SavedVars.mls:17:8", null, null, 1, 0, 0) +//β”‚ return runtime.unwind(g, 3, "SavedVars.mls:18:8", null, null, 1, 0, 0) //β”‚ } //β”‚ case 3: //β”‚ scrut = runtime.resumeValue; @@ -50,7 +51,7 @@ fun g() = //β”‚ pc = 2; //β”‚ continue main //β”‚ } -//β”‚ return runtime.unwind(g, 2, "SavedVars.mls:18:7", null, null, 1, 0, 0); +//β”‚ return runtime.unwind(g, 2, "SavedVars.mls:19:7", null, null, 1, 0, 0); //β”‚ } //β”‚ tmp = 2; //β”‚ pc = 1; diff --git a/hkmc2/shared/src/test/mlscript/handlers/UserThreadsSafe.mls b/hkmc2/shared/src/test/mlscript/handlers/UserThreadsSafe.mls index 3c99eb4ab1..4a9b99ec1e 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/UserThreadsSafe.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/UserThreadsSafe.mls @@ -3,7 +3,7 @@ :lift -class ThreadEffect with +abstract class ThreadEffect with // task queue val tasks = mut [] fun drain() = diff --git a/hkmc2/shared/src/test/mlscript/handlers/UserThreadsUnsafe.mls b/hkmc2/shared/src/test/mlscript/handlers/UserThreadsUnsafe.mls index 47a5e799e7..d412a735fc 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/UserThreadsUnsafe.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/UserThreadsUnsafe.mls @@ -3,7 +3,7 @@ :lift -class ThreadEffect with +abstract class ThreadEffect with fun fork(thread: () -> ()): () fun yld(): () diff --git a/hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls b/hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls index f1e674d953..ba8edcb1d6 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls @@ -1,3 +1,4 @@ +:. :js :effectHandlers :lift diff --git a/hkmc2/shared/src/test/mlscript/interop/.mls b/hkmc2/shared/src/test/mlscript/interop/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/interop/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/interop/Arrays.mls b/hkmc2/shared/src/test/mlscript/interop/Arrays.mls index 372a72581b..b6e512bdb9 100644 --- a/hkmc2/shared/src/test/mlscript/interop/Arrays.mls +++ b/hkmc2/shared/src/test/mlscript/interop/Arrays.mls @@ -1,3 +1,4 @@ +:. :js @@ -6,8 +7,16 @@ let arr = [true, false] // * Array methods annoyingly supply extra arguments, such as the index and the array itself +:e :re arr.map(_ === false) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in function type +//β”‚ β•‘ l.260: fun map[U](f: (T, Int, Array[T]) -> U): Array[U] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Callback parameter list does not match its declared function type. +//β”‚ β•‘ in Arrays.mls:12 +//β”‚ β•‘ l.12: arr.map(_ === false) +//β”‚ ╙── ^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: Function expected 1 argument but got 3 :sjs @@ -33,7 +42,7 @@ arr.map((_, ...) => 1) :e arr.map((..., ...) => 1) //β”‚ ╔══[COMPILATION ERROR] Spread parameters must be the last in the parameter list. -//β”‚ β•‘ l.34: arr.map((..., ...) => 1) +//β”‚ β•‘ l.43: arr.map((..., ...) => 1) //β”‚ ╙── ^^^ //β”‚ = [1, 1] @@ -41,8 +50,15 @@ arr.map((..., ...) => 1) :re arr.map((..., e) => e) //β”‚ ╔══[COMPILATION ERROR] Spread parameters must be the last in the parameter list. -//β”‚ β•‘ l.42: arr.map((..., e) => e) +//β”‚ β•‘ l.51: arr.map((..., e) => e) //β”‚ ╙── ^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in function type +//β”‚ β•‘ l.260: fun map[U](f: (T, Int, Array[T]) -> U): Array[U] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Callback parameter list does not match its declared function type. +//β”‚ β•‘ in Arrays.mls:51 +//β”‚ β•‘ l.51: arr.map((..., e) => e) +//β”‚ ╙── ^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: Function expected 2 arguments but got 3 arr.map(pass1(_ === false)) @@ -63,28 +79,49 @@ Array Array(1) //β”‚ = [<1 empty items>] -:breakme // TODO better report an error, here :e Array() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.83: Array() +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Array' expected 1 argument, but got 0 +//β”‚ β•‘ in ../decls/Prelude.mls +//β”‚ β•‘ l.240: declare class Array[T](val length: Int) { +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.241: fun join(...separators): Str +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.242: fun reverse(): Array[T] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.243: fun sort(...compare): Array[T] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.244: ... (more lines omitted) ... +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ = [] -:breakme // TODO better report an error, here :e Array(1, 2, 3) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.102: Array(1, 2, 3) +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Array' expected 1 argument, but got 3 +//β”‚ β•‘ in ../decls/Prelude.mls +//β”‚ β•‘ l.240: declare class Array[T](val length: Int) { +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.241: fun join(...separators): Str +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.242: fun reverse(): Array[T] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.243: fun sort(...compare): Array[T] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.244: ... (more lines omitted) ... +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ = [1, 2, 3] if arr is Array then "ok" //β”‚ = "ok" -:fixme // TODO handle? +:expect 2 if arr is Array(n) then n -//β”‚ ╔══[COMPILATION ERROR] Class Array does not have a parameter list -//β”‚ β•‘ l.81: if arr is Array(n) then n -//β”‚ β•‘ ^^^^^ -//β”‚ β•Ÿβ”€β”€ but the pattern has one sub-pattern -//β”‚ β•‘ l.81: if arr is Array(n) then n -//β”‚ ╙── ^ -//β”‚ ═══[RUNTIME ERROR] Error: match error - +//β”‚ = 2 diff --git a/hkmc2/shared/src/test/mlscript/interop/CtorBypass.mls b/hkmc2/shared/src/test/mlscript/interop/CtorBypass.mls index c3756f7c68..27c81a3d29 100644 --- a/hkmc2/shared/src/test/mlscript/interop/CtorBypass.mls +++ b/hkmc2/shared/src/test/mlscript/interop/CtorBypass.mls @@ -1,3 +1,4 @@ +:. :js @@ -13,11 +14,12 @@ let a = new A a.n //β”‚ = 123 -A.prototype +A!prototype //β”‚ = {} -// * This can be used for deserialization -let b = Object.create(A.prototype) +// * This can be used for deserialization. The constructor has not initialized its +// * fields, so use a dynamic receiver to retain runtime checks for missing fields. +let b = Object.create(A!prototype) as dyn //β”‚ b = A :sjs diff --git a/hkmc2/shared/src/test/mlscript/interop/Functions.mls b/hkmc2/shared/src/test/mlscript/interop/Functions.mls index c9d872691f..c5ab3451d0 100644 --- a/hkmc2/shared/src/test/mlscript/interop/Functions.mls +++ b/hkmc2/shared/src/test/mlscript/interop/Functions.mls @@ -1,23 +1,24 @@ +:. :js -(x => x).constructor == Function +(x => x)!constructor == Function //β”‚ = true fun foo(x) = x -foo.constructor == Function +foo!constructor == Function //β”‚ = true -foo.name +foo!name //β”‚ = "foo" -foo.constructor.name +foo!constructor!name //β”‚ = "Function" typeof(foo) //β”‚ = "function" -typeof(foo.constructor) +typeof(foo!constructor) //β”‚ = "function" diff --git a/hkmc2/shared/src/test/mlscript/interop/JSCollections.mls b/hkmc2/shared/src/test/mlscript/interop/JSCollections.mls index a4a6ec7b72..10c539a4c3 100644 --- a/hkmc2/shared/src/test/mlscript/interop/JSCollections.mls +++ b/hkmc2/shared/src/test/mlscript/interop/JSCollections.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/interop/JSDerp.mls b/hkmc2/shared/src/test/mlscript/interop/JSDerp.mls index 9149486604..25d3ec54f9 100644 --- a/hkmc2/shared/src/test/mlscript/interop/JSDerp.mls +++ b/hkmc2/shared/src/test/mlscript/interop/JSDerp.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/interop/Null.mls b/hkmc2/shared/src/test/mlscript/interop/Null.mls index 5e1f0c24b5..e0d6f73216 100644 --- a/hkmc2/shared/src/test/mlscript/interop/Null.mls +++ b/hkmc2/shared/src/test/mlscript/interop/Null.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/interop/Number.mls b/hkmc2/shared/src/test/mlscript/interop/Number.mls index 3773c0a80d..92045a2952 100644 --- a/hkmc2/shared/src/test/mlscript/interop/Number.mls +++ b/hkmc2/shared/src/test/mlscript/interop/Number.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/interop/Objects.mls b/hkmc2/shared/src/test/mlscript/interop/Objects.mls index 5fd07cbc93..9e9438fce1 100644 --- a/hkmc2/shared/src/test/mlscript/interop/Objects.mls +++ b/hkmc2/shared/src/test/mlscript/interop/Objects.mls @@ -1,13 +1,14 @@ +:. :js let rcd = { x: 0, y: undefined, z: null} //β”‚ rcd = {x: 0, y: undefined, z: null} -rcd.constructor +rcd!constructor //β”‚ = class Object -typeof(rcd.constructor) +typeof(rcd!constructor) //β”‚ = "function" Object.entries of rcd @@ -30,23 +31,23 @@ Object.entries of Foo Object.entries of foo //β”‚ = [["x", 1], ["y", 2], ["a", 3]] -Foo.name +Foo!name //β”‚ = "Foo" -Foo.class.name +Foo!class!name //β”‚ = "Foo" -foo.constructor +foo!constructor //β”‚ = class Foo // ! will be changed by minification -foo.constructor.name +foo!constructor!name //β”‚ = "Foo" typeof(foo) //β”‚ = "object" -typeof(foo.constructor) +typeof(foo!constructor) //β”‚ = "function" diff --git a/hkmc2/shared/src/test/mlscript/interop/Symbols.mls b/hkmc2/shared/src/test/mlscript/interop/Symbols.mls index aa5046983e..6401d565d7 100644 --- a/hkmc2/shared/src/test/mlscript/interop/Symbols.mls +++ b/hkmc2/shared/src/test/mlscript/interop/Symbols.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/interop/Virtualization.mls b/hkmc2/shared/src/test/mlscript/interop/Virtualization.mls index 915c2beed9..6432e7e63d 100644 --- a/hkmc2/shared/src/test/mlscript/interop/Virtualization.mls +++ b/hkmc2/shared/src/test/mlscript/interop/Virtualization.mls @@ -1,3 +1,4 @@ +:. :js @@ -6,13 +7,27 @@ js.try_catch(() => 123, id) //β”‚ = 123 +:e js.try_catch(() => 123(), id) -//β”‚ = TypeError: 123 is not a function - +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.11: js.try_catch(() => 123(), id) +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be called like a function. +//β”‚ β•‘ l.11: js.try_catch(() => 123(), id) +//β”‚ ╙── ^^^ +//β”‚ = "This code cannot be run as its compilation yielded an error." + +:e js.try_catch of () => 123() case { TypeError then 666, e then throw e } -//β”‚ = 666 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.22: () => 123() +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be called like a function. +//β”‚ β•‘ l.22: () => 123() +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :re js.try_catch of diff --git a/hkmc2/shared/src/test/mlscript/invalml/InvalMLCodeGen.mls b/hkmc2/shared/src/test/mlscript/invalml/InvalMLCodeGen.mls index f5a3a22735..e371ef0fce 100644 --- a/hkmc2/shared/src/test/mlscript/invalml/InvalMLCodeGen.mls +++ b/hkmc2/shared/src/test/mlscript/invalml/InvalMLCodeGen.mls @@ -192,7 +192,7 @@ fun nott = case //β”‚ case false: //β”‚ return true; //β”‚ } -//β”‚ throw globalThis.Object.freeze(new globalThis.Error.class("match error")) +//β”‚ throw globalThis.Object.freeze(new globalThis.Error("match error")) //β”‚ }); //β”‚ return lambda2 //β”‚ }; @@ -208,7 +208,7 @@ nott of false //β”‚ } else if (false === false) { //β”‚ return true //β”‚ } -//β”‚ throw globalThis.Object.freeze(new globalThis.Error.class("match error")); +//β”‚ throw globalThis.Object.freeze(new globalThis.Error("match error")); //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ = true //β”‚ Type: Bool diff --git a/hkmc2/shared/src/test/mlscript/invalml/InvalMLPrelude.mls b/hkmc2/shared/src/test/mlscript/invalml/InvalMLPrelude.mls index eb54d2013c..73aa842ea0 100644 --- a/hkmc2/shared/src/test/mlscript/invalml/InvalMLPrelude.mls +++ b/hkmc2/shared/src/test/mlscript/invalml/InvalMLPrelude.mls @@ -4,7 +4,7 @@ open annotations declare class Any declare class Anything declare class Nothing -declare class Object +declare open class Object declare class Unit declare class Bool diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls index 11dea82494..53c2ba0228 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls @@ -1,3 +1,4 @@ +:.. :. @@ -9,7 +10,7 @@ fun foo(x) = val empty = C(123) C.empty //β”‚ ╔══[WARNING] Modules are not yet lifted. -//β”‚ β•‘ l.8: module C with +//β”‚ β•‘ l.9: module C with //β”‚ ╙── ^ foo(10).get @@ -23,7 +24,7 @@ fun foo(x) = val empty = new C C.empty //β”‚ ╔══[WARNING] Modules are not yet lifted. -//β”‚ β•‘ l.22: module C with +//β”‚ β•‘ l.23: module C with //β”‚ ╙── ^ foo(10).get diff --git a/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls index 353c1d9b18..256f6f47bb 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls @@ -1,3 +1,4 @@ +:.. :. @@ -9,8 +10,8 @@ fun f(x) = module A fun g = new A //β”‚ ╔══[WARNING] Modules are not yet lifted. -//β”‚ β•‘ l.9: module A -//β”‚ ╙── ^ +//β”‚ β•‘ l.10: module A +//β”‚ ╙── ^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let g; //β”‚ define g as fun g⁰(A$) { diff --git a/hkmc2/shared/src/test/mlscript/lifter/CurriedClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/CurriedClassInFun.mls index 863e1f53eb..6d8e674fcc 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/CurriedClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/CurriedClassInFun.mls @@ -1,3 +1,4 @@ +:.. :. diff --git a/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls index 38b485aa7f..d6bbb7d6c7 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls @@ -1,3 +1,4 @@ +:.. :. // NOTE: due to conflict between flow analysis and lifter :flowBasedOpt off diff --git a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls index aa6ce6560d..3605ce18f8 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls @@ -1,3 +1,4 @@ +:.. :. @@ -130,12 +131,12 @@ x + y data class Tuple(a, b) -fun f(used1) = - fun g1(used2) = +fun f(used1: Int) = + fun g1(used2: Int) = fun h() = set used1 = 10 set used2 = 100 - fun i() = used1 + used2 + fun i(): Int = used1 + used2 Tuple(h, i) fun g22() = used1 Tuple(g1(10), g22) diff --git a/hkmc2/shared/src/test/mlscript/lifter/FunInMethod.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInMethod.mls index 4b9f5034e3..ebba796d18 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/FunInMethod.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInMethod.mls @@ -1,3 +1,4 @@ +:.. :. :expect 6 diff --git a/hkmc2/shared/src/test/mlscript/lifter/Imports.mls b/hkmc2/shared/src/test/mlscript/lifter/Imports.mls index e34c1d1402..320a87d14b 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/Imports.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/Imports.mls @@ -1,3 +1,4 @@ +:.. :. import "../../mlscript-compile/Option.mls" diff --git a/hkmc2/shared/src/test/mlscript/lifter/Labels.mls b/hkmc2/shared/src/test/mlscript/lifter/Labels.mls index b561387edc..6791ddddbd 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/Labels.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/Labels.mls @@ -1,3 +1,4 @@ +:.. :. diff --git a/hkmc2/shared/src/test/mlscript/lifter/Loops.mls b/hkmc2/shared/src/test/mlscript/lifter/Loops.mls index 8ed4e924a1..0a95fd658c 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/Loops.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/Loops.mls @@ -1,3 +1,4 @@ +:.. :. // :ignore // used to be ON with comment: Lifting should not require scope flattening (which doesn't handle loops) @@ -80,7 +81,9 @@ fun foo() = //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” :expect 2 -foo()() +foo() as Function +|> + _() //β”‚ = 2 diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls index eb9b30886d..fbda2542c9 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -1,3 +1,4 @@ +:.. :. // NOTE: due to conflict between flow analysis and lifter :flowBasedOpt off @@ -15,7 +16,7 @@ fun foo(x, y) = x + y + test M.foo() //β”‚ ╔══[WARNING] Objects are not yet lifted. -//β”‚ β•‘ l.11: object M with +//β”‚ β•‘ l.12: object M with //β”‚ ╙── ^ :expect 14 @@ -58,10 +59,11 @@ fun foo(y) = set y = 2 [new C, new mut C] +// Exercise writes to undeclared fields with checked dynamic access. let cc = foo(10) - c0 = cc.0 - c1 = cc.1 + c0 = cc.0 as dyn + c1 = cc.1 as dyn //β”‚ c0 = C //β”‚ c1 = C //β”‚ cc = [C, C] @@ -86,10 +88,10 @@ fun foo(y) = set y = 2 O //β”‚ ╔══[WARNING] Objects are not yet lifted. -//β”‚ β•‘ l.84: object O with +//β”‚ β•‘ l.86: object O with //β”‚ ╙── ^ -let o = foo(10) +let o = foo(10) as dyn //β”‚ o = O :re @@ -108,7 +110,7 @@ fun foo(x, y) = fun foo3 = M.foo2() foo3 //β”‚ ╔══[WARNING] Objects are not yet lifted. -//β”‚ β•‘ l.104: object M with +//β”‚ β•‘ l.106: object M with //β”‚ ╙── ^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let foo3; @@ -138,7 +140,7 @@ data class A(x) with fun bar = M.getB() bar //β”‚ ╔══[WARNING] Objects are not yet lifted. -//β”‚ β•‘ l.135: object M with +//β”‚ β•‘ l.137: object M with //β”‚ ╙── ^ :expect 2 @@ -195,7 +197,7 @@ fun f = foo f.a //β”‚ ╔══[WARNING] Objects are not yet lifted. -//β”‚ β•‘ l.191: object A with +//β”‚ β•‘ l.193: object A with //β”‚ ╙── ^ //β”‚ = 2 @@ -274,7 +276,7 @@ fun foo(x) = val baz = x [x === O.bar, set x += 1 in [x === O.bar, x === O.baz], x === O.bar] //β”‚ ╔══[WARNING] Objects are not yet lifted. -//β”‚ β•‘ l.272: object O with +//β”‚ β•‘ l.274: object O with //β”‚ ╙── ^ foo(123) @@ -323,13 +325,13 @@ fun f = A.B.C f //β”‚ ╔══[WARNING] Objects are not yet lifted. -//β”‚ β•‘ l.320: object A with +//β”‚ β•‘ l.322: object A with //β”‚ ╙── ^ //β”‚ ╔══[WARNING] Objects are not yet lifted. -//β”‚ β•‘ l.321: object B with +//β”‚ β•‘ l.323: object B with //β”‚ ╙── ^ //β”‚ ╔══[WARNING] Objects are not yet lifted. -//β”‚ β•‘ l.322: object C +//β”‚ β•‘ l.324: object C //β”‚ ╙── ^ //β”‚ = C @@ -342,10 +344,10 @@ fun f = A.B.C f //β”‚ ╔══[WARNING] Modules are not yet lifted. -//β”‚ β•‘ l.339: module A with +//β”‚ β•‘ l.341: module A with //β”‚ ╙── ^ //β”‚ ╔══[WARNING] Objects are not yet lifted. -//β”‚ β•‘ l.341: object C +//β”‚ β•‘ l.343: object C //β”‚ ╙── ^ //β”‚ = C @@ -358,7 +360,7 @@ fun f = A.B.C f //β”‚ ╔══[WARNING] Modules are not yet lifted. -//β”‚ β•‘ l.355: module A with +//β”‚ β•‘ l.357: module A with //β”‚ ╙── ^ //β”‚ = C diff --git a/hkmc2/shared/src/test/mlscript/lifter/Mutation.mls b/hkmc2/shared/src/test/mlscript/lifter/Mutation.mls index ed032aa929..170cae4b55 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/Mutation.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/Mutation.mls @@ -1,3 +1,4 @@ +:.. :. @@ -30,7 +31,7 @@ fun foo() = //β”‚ set scope0$cap = new mut Capture$scope0ΒΉ(x); //β”‚ set scope0$cap.x$0ΒΉ = 1; //β”‚ set bar$here = bar$⁰(scope0$cap); -//β”‚ do xs⁰.pushοΉ–(bar$here); +//β”‚ do xs⁰.push⁰(bar$here); //β”‚ set scope0$cap.x$0ΒΉ = 2; //β”‚ return runtime⁰.Unit⁰ //β”‚ }; diff --git a/hkmc2/shared/src/test/mlscript/lifter/PatternInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/PatternInFun.mls index 301fba91dc..39b6c4c753 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/PatternInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/PatternInFun.mls @@ -1,3 +1,4 @@ +:.. :. diff --git a/hkmc2/shared/src/test/mlscript/meta/.mls b/hkmc2/shared/src/test/mlscript/meta/.mls index f2133a669e..bccc957f6b 100644 --- a/hkmc2/shared/src/test/mlscript/meta/.mls +++ b/hkmc2/shared/src/test/mlscript/meta/.mls @@ -1,3 +1,4 @@ +:.. :js console.log("Imported from '" + source.file.slice(source.file.lastIndexOf("/src/") + 1) + "'") 40 + 2 diff --git a/hkmc2/shared/src/test/mlscript/meta/BlockDiffTesting.mls b/hkmc2/shared/src/test/mlscript/meta/BlockDiffTesting.mls index d6e24d540f..efd5cc618d 100644 --- a/hkmc2/shared/src/test/mlscript/meta/BlockDiffTesting.mls +++ b/hkmc2/shared/src/test/mlscript/meta/BlockDiffTesting.mls @@ -1,3 +1,4 @@ +:.. :js diff --git a/hkmc2/shared/src/test/mlscript/meta/CompilerFiction.mls b/hkmc2/shared/src/test/mlscript/meta/CompilerFiction.mls index c5c7391ddf..b6ba06eccb 100644 --- a/hkmc2/shared/src/test/mlscript/meta/CompilerFiction.mls +++ b/hkmc2/shared/src/test/mlscript/meta/CompilerFiction.mls @@ -1,10 +1,11 @@ +:.. :js :ge source //β”‚ ╔══[COMPILATION ERROR] Module 'source' is virtual (i.e., "compiler fiction"); cannot be used directly -//β”‚ β•‘ l.5: source +//β”‚ β•‘ l.6: source //β”‚ ╙── ^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -12,25 +13,26 @@ source :ge js //β”‚ ╔══[COMPILATION ERROR] Module 'js' is virtual (i.e., "compiler fiction"); cannot be used directly -//β”‚ β•‘ l.13: js +//β”‚ β•‘ l.14: js //β”‚ ╙── ^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e :ge js.oops -//β”‚ ╔══[COMPILATION ERROR] Module 'js' does not contain member 'oops' -//β”‚ β•‘ l.21: js.oops -//β”‚ ╙── ^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Module 'js' is virtual (i.e., "compiler fiction"); cannot be used directly -//β”‚ β•‘ l.21: js.oops -//β”‚ ╙── ^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.22: js.oops +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Module 'js' does not contain member 'oops' +//β”‚ β•‘ in ../decls/Prelude.mls +//β”‚ β•‘ l.437: declare module js { +//β”‚ ╙── ^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :ge js.try_catch //β”‚ ╔══[COMPILATION ERROR] Module 'js' is virtual (i.e., "compiler fiction"); cannot be used directly -//β”‚ β•‘ l.31: js.try_catch +//β”‚ β•‘ l.33: js.try_catch //β”‚ ╙── ^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/meta/ImportedTest.mls b/hkmc2/shared/src/test/mlscript/meta/ImportedTest.mls index f8704a4d09..ba9ab1df04 100644 --- a/hkmc2/shared/src/test/mlscript/meta/ImportedTest.mls +++ b/hkmc2/shared/src/test/mlscript/meta/ImportedTest.mls @@ -1,3 +1,4 @@ +:.. fun hello() = "Hello!" diff --git a/hkmc2/shared/src/test/mlscript/meta/ImporterTest.mls b/hkmc2/shared/src/test/mlscript/meta/ImporterTest.mls index 7a78953375..650b85d694 100644 --- a/hkmc2/shared/src/test/mlscript/meta/ImporterTest.mls +++ b/hkmc2/shared/src/test/mlscript/meta/ImporterTest.mls @@ -1,3 +1,4 @@ +:.. :js diff --git a/hkmc2/shared/src/test/mlscript/meta/LocDebugging.mls b/hkmc2/shared/src/test/mlscript/meta/LocDebugging.mls index ba30eecea3..8267ab6678 100644 --- a/hkmc2/shared/src/test/mlscript/meta/LocDebugging.mls +++ b/hkmc2/shared/src/test/mlscript/meta/LocDebugging.mls @@ -1,3 +1,4 @@ +:.. :js :loc diff --git a/hkmc2/shared/src/test/mlscript/newres/.mls b/hkmc2/shared/src/test/mlscript/newres/.mls new file mode 100644 index 0000000000..f246a84b72 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/.mls @@ -0,0 +1,4 @@ +:.. +:js +#lang(strictResolution: true) +#lang(eagerResolution: true) diff --git a/hkmc2/shared/src/test/mlscript/newres/AbstractClassInterface.mls b/hkmc2/shared/src/test/mlscript/newres/AbstractClassInterface.mls new file mode 100644 index 0000000000..d0ac000dac --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/AbstractClassInterface.mls @@ -0,0 +1,20 @@ +:. + +// The annotation names the interface that subclasses of the abstract class +// conform to. The following braces contain the class body, not a refinement +// of the interface; its methods must remain available to subclasses. +type Named = {name: Str} +abstract class NamedBase(prefix: Str): Named { + fun label(suffix: Str): Str = prefix + suffix +} +class NamedChild extends NamedBase("hello ") { + val name: Str = this.label("world") +} +fun readName(value: Named): Str = value.name +fun labelThroughBase(value: NamedBase): Str = value.label("base") + +:expect ["hello world", "hello base"] +let child = new NamedChild +[readName(child), labelThroughBase(child)] +//β”‚ = ["hello world", "hello base"] +//β”‚ child = NamedChild { name: "hello world" } diff --git a/hkmc2/shared/src/test/mlscript/newres/AnnotationContexts.mls b/hkmc2/shared/src/test/mlscript/newres/AnnotationContexts.mls new file mode 100644 index 0000000000..0272584819 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/AnnotationContexts.mls @@ -0,0 +1,57 @@ +:. + +// A captured alias combines its enclosing binder with a supplied local binder. +:expect [1, 2, 3, 2] +class First(val first: Int) +class Second(val second: Int) +private fun enclosing[A](x: A) = + type Both[B] = {left: A, right: B} + private fun wrap[B](y: B): Both[B] = {left: x, right: y} + wrap[Second](Second(2)) +let one = enclosing[First](First(1)) +let two = enclosing[Second](Second(3)) +[one.left.first, one.right.second, two.left.second, two.right.second] +//β”‚ = [1, 2, 3, 2] +//β”‚ one = {left: First(1), right: Second(2)} +//β”‚ two = {left: Second(3), right: Second(2)} + +// Ordinary value inference distinguishes the same two calls without an annotation. +:expect [1, 2, 1, 4] +class First(val first: Int) +class Second(val second: Int) +private fun enclosing[A](x: A) = + type Both[B] = {left: A, right: B} + private fun wrap(value) = value + [wrap({left: x, right: First(2)}), wrap({left: x, right: Second(4)})] +let result = enclosing[First](First(1)) +[result.0.left.first, result.0.right.first, result.1.left.first, result.1.right.second] +//β”‚ = [1, 2, 1, 4] +//β”‚ result = [{left: First(1), right: First(2)}, {left: First(1), right: Second(4)}] + +// A missing argument must retain its scope independently of the captured binder. +// TODO: rebasing the omitted B strips wrap's caller IDs before publishing its bounds. +:fixme +:expect [1, 2, 1, 4] +class First(val first: Int) +class Second(val second: Int) +private fun enclosing[A](x: A) = + type Both[B] = {left: A, right: B} + private fun wrap(value: Both) = value + [wrap({left: x, right: First(2)}), wrap({left: x, right: Second(4)})] +let result = enclosing[First](First(1)) +[result.0.left.first, result.0.right.first, result.1.left.first, result.1.right.second] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.42: [result.0.left.first, result.0.right.first, result.1.left.first, result.1.right.second] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Second' does not contain member 'first' +//β”‚ β•‘ l.40: [wrap({left: x, right: First(2)}), wrap({left: x, right: Second(4)})] +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.42: [result.0.left.first, result.0.right.first, result.1.left.first, result.1.right.second] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'First' does not contain member 'second' +//β”‚ β•‘ l.40: [wrap({left: x, right: First(2)}), wrap({left: x, right: Second(4)})] +//β”‚ ╙── ^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '[1, 2, 1, 4]', got: 'undefined' +//β”‚ result = [{left: First(1), right: First(2)}, {left: First(1), right: Second(4)}] diff --git a/hkmc2/shared/src/test/mlscript/newres/Annotations.mls b/hkmc2/shared/src/test/mlscript/newres/Annotations.mls new file mode 100644 index 0000000000..b334a084c4 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/Annotations.mls @@ -0,0 +1,147 @@ +:. + +// Qualified annotations must affect function kind before the body is elaborated. +:sjs +@annotations.generator +fun generated() = yield 1 +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let generated; generated = function* generated() { return yield 1 }; +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +:sjs +@annotations.async +fun asynchronous() = 2 +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let asynchronous, asynchronous1; +//β”‚ asynchronous1 = function asynchronous() { +//β”‚ return () => { +//β”‚ return 2 +//β”‚ } +//β”‚ }; +//β”‚ asynchronous = function asynchronous() { +//β”‚ let tmp; +//β”‚ tmp = asynchronous1(); +//β”‚ return runtime.toJsAsync(tmp) +//β”‚ }; +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +// Explicit and wildcard opens identify the same built-in annotation symbol. +:soir +fun explicitAnnotation(x) = + open annotations { noInline } + @noInline + fun inner(y) = y + 1 + inner(x) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Optimized IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ define explicitAnnotation⁰ as fun explicitAnnotationΒΉ(x) { +//β”‚ let inner; +//β”‚ @noInline +//β”‚ define inner as fun inner⁰(y) { return +⁰(y, 1) }; +//β”‚ return inner⁰(x) +//β”‚ }; +//β”‚ end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +:soir +fun wildcardAnnotation(x) = + open annotations + @noInline + fun inner(y) = y + 1 + inner(x) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Optimized IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ define wildcardAnnotation⁰ as fun wildcardAnnotationΒΉ(x) { +//β”‚ let inner; +//β”‚ @noInline +//β”‚ define inner as fun innerΒΉ(y) { return +⁰(y, 1) }; +//β”‚ return innerΒΉ(x) +//β”‚ }; +//β”‚ end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +// Spelling alone must not recognize a shadowing object as a built-in annotation. +:w +fun shadowed() = + object noInline + @noInline + fun inner(x) = x + inner(3) +//β”‚ ╔══[WARNING] This annotation has no effect. +//β”‚ β•‘ l.65: @noInline +//β”‚ ╙── ^^^^^^^^^ + +// A known application head is enough; its argument need not have a value shape. +:w +class Custom(value: Int) +fun customAnnotation(arg) = + @Custom(arg) + fun inner() = 4 + inner() +//β”‚ ╔══[WARNING] This annotation has no effect. +//β”‚ β•‘ l.76: @Custom(arg) +//β”‚ ╙── ^^^^^^^^^^^^ + +// A qualified annotation cannot wait for its receiver's forward definition. +:e +fun forwardQualified() = + @Later.noInline + fun inner(x) = x + inner(5) +module Later { object noInline } +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.86: @Later.noInline +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ ╙── An annotation's main symbol must be uniquely known when the annotation is elaborated. + +// Nor can an annotation wait for a forward wildcard source or an alias's value. +:e +fun forwardWildcard() = + open Future + @noInline + fun inner(x) = x + inner(6) +module Future { object noInline } +//β”‚ ╔══[COMPILATION ERROR] Resolution error in wildcard-open reference +//β”‚ β•‘ l.99: @noInline +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ ╙── An annotation's main symbol must be uniquely known when the annotation is elaborated. + +:e +fun forwardAlias() = + @alias.noInline + fun inner(x) = x + inner(7) +val alias = Later +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.110: @alias.noInline +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ ╙── An annotation's main symbol must be uniquely known when the annotation is elaborated. +//β”‚ alias = class Later { noInline: noInline } + +// Two immediately available heads are ambiguous. +:e +module First { object marker } +module Second { object marker } +fun ambiguousAnnotation() = + open First + open Second + @marker + fun inner(x) = x + inner(8) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in wildcard-open reference +//β”‚ β•‘ l.127: @marker +//β”‚ β•‘ ^^^^^^^ +//β”‚ ╙── An annotation's main symbol must be uniquely known when the annotation is elaborated. + +// A late candidate must not silently invalidate an already recognized annotation. +:e +fun lateAmbiguity() = + open annotations + open Last + @noInline + fun inner(x) = x + inner(9) +module Last { object noInline } +//β”‚ ╔══[COMPILATION ERROR] Resolution error in wildcard-open reference +//β”‚ β•‘ l.140: @noInline +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ ╙── An annotation's main symbol must be uniquely known when the annotation is elaborated. diff --git a/hkmc2/shared/src/test/mlscript/newres/ArrayMethods.mls b/hkmc2/shared/src/test/mlscript/newres/ArrayMethods.mls new file mode 100644 index 0000000000..3a6e83b3d6 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ArrayMethods.mls @@ -0,0 +1,33 @@ +:. + +// Exercise the Array interfaces used by Iter's forwarding methods, including +// typed iterator results and callbacks checked against their declared domains. +:expect "[[2,3,3],[0,1],0,2,1,3,2,[3,2,1],[1,2,3],[1,4,3],[1,4,3],6]" +fun inspect(xs: Array[Int]) = [ + (mut [1, 2, 3]).copyWithin(0, 1) + xs.entries().next().value + xs.keys().next().value + xs.find((x, i, a) => x > 1) + xs.findIndex((x, i, a) => x > 1) + xs.findLast((x, i, a) => x > 1) + xs.findLastIndex((x, i, a) => x > 1) + xs.toReversed() + xs.toSorted() + xs.toSpliced(1, 1, 4) + xs.with(1, 4) + xs.reduceRight((acc, x, i, a) => acc + x, 0) +] +JSON.stringify(inspect([1, 2, 3])) +//β”‚ = "[[2,3,3],[0,1],0,2,1,3,2,[3,2,1],[1,2,3],[1,4,3],[1,4,3],6]" + +:expect ["AB", "AB"] +fun flatten(xs: Array[Str]) = + let mapped = xs.flatMap((x, i, a) => [x.toUpperCase()]) + [mapped.join(""), [mapped].flat().join("")] +flatten(["a", "b"]) +//β”‚ = ["AB", "AB"] + +:expect "Array" +fun prototypeName(value) = Reflect.getPrototypeOf(value).constructor.name +prototypeName([1]) +//β”‚ = "Array" diff --git a/hkmc2/shared/src/test/mlscript/newres/ArraySpreadViews.mls b/hkmc2/shared/src/test/mlscript/newres/ArraySpreadViews.mls new file mode 100644 index 0000000000..0ec811f09c --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ArraySpreadViews.mls @@ -0,0 +1,86 @@ +:. + +// Unknown-length segments retain type arguments just like fixed tuple fields. +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +private fun copy[A](xs: Array[A]) = [...xs] +[copy([First(1)]).0.first, copy([Second(2)]).0.second] +//β”‚ = [1, 2] + +// Spreading a declared array keeps separate callers' interfaces distinct. +:expect [3, 4] +class First(val first: Int) +class Second(val second: Int) +fun first(): Array[First] = [First(3)] +fun second(): Array[Second] = [Second(4)] +private fun copy(xs) = [...xs] +[copy(first()).0.first, copy(second()).0.second] +//β”‚ = [3, 4] + +// Array patterns observe the same declared element type without expanding it early. +:expect [5, 6] +class First(val first: Int) +class Second(val second: Int) +private fun head[A](xs: Array[A]) = if xs is [x, ...rest] then x else throw "empty" +[head([First(5)]).first, head([Second(6)]).second] +//β”‚ = [5, 6] + +// A checked array index also sees substitutions through an enclosing generic call. +:expect [7, 8] +class First(val first: Int) +class Second(val second: Int) +private fun copy[A](xs: Array[A]) = [...xs] +private fun wrap[B](x: B) = copy([x]).[0] +[wrap(First(7)).first, wrap(Second(8)).second] +//β”‚ = [7, 8] + +// A spread can be empty: the explicit suffix can occupy index zero too. +:e +class First(val first: Int) +class Second(val second: Int) +fun pick(xs: Array[First]) = [...xs, Second(2)].0.first +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.42: fun pick(xs: Array[First]) = [...xs, Second(2)].0.first +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Second' does not contain member 'first' +//β”‚ β•‘ l.42: fun pick(xs: Array[First]) = [...xs, Second(2)].0.first +//β”‚ ╙── ^^^^^^^^^ + +// A fixed prefix remains precise even when a later spread has a different type. +:expect 9 +class First(val first: Int) +class Second(val second: Int) +private fun pick[A](x: A, xs: Array[Second]) = [x, ...xs].0 +pick(First(9), [Second(10)]).first +//β”‚ = 9 + +// Rest slicing and spreading into a call preserve the deferred element's view. +:expect [11, 12] +class First(val first: Int) +class Second(val second: Int) +private fun prepend[A](xs: Array[A]) = [0, ...xs] +private fun tail[B](x: B) = if prepend([x]) is [_, ...rest] then rest else throw "empty" +private fun head(x) = x +[head(...tail(First(11))).first, head(...tail(Second(12))).second] +//β”‚ = [11, 12] + +// Multiple unknown-length segments can move either an element or the suffix here. +:e +class First(val first: Int) +class Second(val second: Int) +fun pick(xs: Array[First], ys: Array[First]) = [0, ...xs, ...ys, Second(2)].1.first +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.72: fun pick(xs: Array[First], ys: Array[First]) = [0, ...xs, ...ys, Second(2)].1.first +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Second' does not contain member 'first' +//β”‚ β•‘ l.72: fun pick(xs: Array[First], ys: Array[First]) = [0, ...xs, ...ys, Second(2)].1.first +//β”‚ ╙── ^^^^^^^^^ + +// Fields whose minimum position is past the selected index cannot contribute. +:expect 13 +class First(val first: Int) +class Second(val second: Int) +private fun pick(xs: Array[First]) = [...xs, First(13), Second(14)].0.first +pick([]) +//β”‚ = 13 diff --git a/hkmc2/shared/src/test/mlscript/newres/Arrays.mls b/hkmc2/shared/src/test/mlscript/newres/Arrays.mls new file mode 100644 index 0000000000..d5284b4384 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/Arrays.mls @@ -0,0 +1,279 @@ +:. + +// Tuples retain indexed fields while inheriting the declared Array interface. +:expect [3, "1-2-3", 2] +let xs = [1, 2, 3] +[xs.length, xs.join("-"), xs.1] +//β”‚ = [3, "1-2-3", 2] +//β”‚ xs = [1, 2, 3] + +// A tuple is also an instance of the Array class for pattern transfer. +:expect 3 +let xs = [1, 2, 3] +if xs is Array then xs.length else 0 +//β”‚ = 3 +//β”‚ xs = [1, 2, 3] + +// Inherited callbacks receive the union of the tuple's element shapes. +:expect [2, 3] +class Item(val value: Int) +[Item(1), Item(2)].map((item, ...) => item.value + 1) +//β”‚ = [2, 3] + +// Declared tuple element types remain opaque through the Array interface. +:ge +class Base +class Child extends Base with + val extra = 1 +fun read(xs: [Base]) = xs.map((item, ...) => item.extra) +read([new Child]) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.28: fun read(xs: [Base]) = xs.map((item, ...) => item.extra) +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.28: fun read(xs: [Base]) = xs.map((item, ...) => item.extra) +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Mutable arrays expose methods without claiming their initial elements persist. +:expect [3, "2,1,3", 3] +let xs = mut [1, 2] +xs.reverse() +let size = xs.push(3) +[size, xs.join(), xs.2] +//β”‚ = [3, "2,1,3", 3] +//β”‚ size = 3 +//β”‚ xs = [2, 1, 3] + +// Array results retain their declared instance interface. +:expect "2,3" +[1, 2].map((x, ...) => x + 1).join() +//β”‚ = "2,3" + +// Tuple element inference must remain separate across function activations. +:expect [[1], [2]] +class First(val first: Int) +class Second(val second: Int) +fun singleton(x) = [x] +[singleton(First(1)).map((x, ...) => x.first), + singleton(Second(2)).map((x, ...) => x.second)] +//β”‚ = [[1], [2]] + +// Inheriting Array does not authorize arbitrary properties. +:ge +[1].nonexistent +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.64: [1].nonexistent +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Tuple literal does not contain member 'nonexistent' +//β”‚ β•‘ l.64: [1].nonexistent +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Nominal argument inference sees the tuple's Array supertype at the caller. +:expect [4] +class Item(val value: Int) +fun values[A](xs: Array[A]) = xs.map((item, ...) => item) +values([Item(4)]).map((item, ...) => item.value) +//β”‚ = [4] + +// Rest callback parameters retain the remaining argument tuple. +:expect [3, 4] +[10, 20].map((item, ...rest) => rest.0 + rest.1.length + 1) +//β”‚ = [3, 4] + +// Fixed callbacks must accept all three arguments supplied by Array.map. +:ge +[1].map(x => x) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in function type +//β”‚ β•‘ l.260: fun map[U](f: (T, Int, Array[T]) -> U): Array[U] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Callback parameter list does not match its declared function type. +//β”‚ β•‘ in Arrays.mls:87 +//β”‚ β•‘ l.87: [1].map(x => x) +//β”‚ ╙── ^^^^^^ +//β”‚ ═══[RUNTIME ERROR] Error: Function expected 1 argument but got 3 + +// Mutable arrays do not propagate stale initializer element shapes to callbacks. +:ge +class Original(val original: Int) +class Replacement(val replacement: Int) +let xs = mut [Original(1)] +set xs.0 = Replacement(2) +xs.map((x, ...) => x.original) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.103: xs.map((x, ...) => x.original) +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Replacement' does not contain member 'original' +//β”‚ β•‘ l.102: set xs.0 = Replacement(2) +//β”‚ ╙── ^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ xs = [Replacement(2)] + +// Declared constructor parameters do not introduce a generated `.class` binding. +:sjs +:expect 3 +let allocated = new Array[Int](3) +allocated.length +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let allocated; +//β”‚ allocated = globalThis.Object.freeze(new globalThis.Array(3)); +//β”‚ return allocated.length +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 3 +//β”‚ allocated = [<3 empty items>] + +// Both bare instance checks and constructor-field patterns use the native Array. +:expect [true, 3] +[allocated is Array, if allocated is Array(length) then length else 0] +//β”‚ = [true, 3] + +// Foreign classes with companion modules continue to use their native bindings. +:expect true +new String("hello") is String +//β”‚ = true + +// The length-only Array interface rejects element-list construction. +:ge +new Array[Int](1, 2) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.138: new Array[Int](1, 2) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Array' expected 1 argument, but got 2 +//β”‚ β•‘ in ../decls/Prelude.mls +//β”‚ β•‘ l.240: declare class Array[T](val length: Int) { +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.241: fun join(...separators): Str +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.242: fun reverse(): Array[T] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.243: fun sort(...compare): Array[T] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.244: ... (more lines omitted) ... +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ = [1, 2] + +:ge +new Array[Int]() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.157: new Array[Int]() +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Array' expected 1 argument, but got 0 +//β”‚ β•‘ in ../decls/Prelude.mls +//β”‚ β•‘ l.240: declare class Array[T](val length: Int) { +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.241: fun join(...separators): Str +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.242: fun reverse(): Array[T] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.243: fun sort(...compare): Array[T] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.244: ... (more lines omitted) ... +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ = [] + +// Calls select the callable overload even when the class has a companion module. +:expect 3 +Array[Int](3).length +//β”‚ = 3 + +// at supports negative indices; foreign undefined results normalize to unit. +:expect [3, 1, ()] +let xs = [1, 2, 3] +[xs.at(-1), xs.at(0), xs.at(3)] +//β”‚ = [3, 1, ()] +//β”‚ xs = [1, 2, 3] + +:expect ["7,7,3", "7", "7,9,3"] +let xs = mut [1, 2, 3] +let filled = xs.fill(7, 0, 2).join() +let removed = xs.splice(1, 1, 9).join() +[filled, removed, xs.join()] +//β”‚ = ["7,7,3", "7", "7,9,3"] +//β”‚ filled = "7,7,3" +//β”‚ removed = "7" +//β”‚ xs = [7, 9, 3] + +// reduce's accumulator receives both the initial value and the callback results. +:expect [3, 5] +class Item(val value: Int) +class Total(val total: Int) +[[Item(1), Item(2)].reduce((acc, item, ...) => Total(acc.total + item.value), Total(0)).total, + [Item(5)].reduce((acc, item, ...) => acc + item.value, 0)] +//β”‚ = [3, 5] + +// An accumulator shape that comes only from the initial value is still checked. +:e +class Item(val value: Int) +class Total(val total: Int) +[Item(1)].reduce((acc, item, ...) => acc.missing, Total(0)) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.209: [Item(1)].reduce((acc, item, ...) => acc.missing, Total(0)) +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Total' does not contain member 'missing' +//β”‚ β•‘ l.209: [Item(1)].reduce((acc, item, ...) => acc.missing, Total(0)) +//β”‚ ╙── ^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Without an initial value, the first element would be the accumulator, +// which the declared signature does not describe. +:e +[1, 2].reduce((acc, x, ...) => acc + x) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.221: [1, 2].reduce((acc, x, ...) => acc + x) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'reduce' expected 2 arguments, but got 1 +//β”‚ β•‘ in ../decls/Prelude.mls +//β”‚ β•‘ l.274: fun reduce[U](f: (U, T, Int, Array[T]) -> U, initial: U): U +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ = 3 + +// A tuple created in one activation of `mk` and passed to another has the path +// `β†˜mk β†—mk`, whereas `[1]` has the path `β†˜mk`. Both receivers share the result +// type parameter of the same `map` selection. The exit from the first activation +// is provenance: Array's members remain at its global definition, so both paths +// read that parameter from the same scope. +:expect [1] +private fun mk(xs) = xs.map((x, ...) => if x is Int then x else mk([x])) +mk([1]) +//β”‚ = [1] + +// The accumulator of reduce is fed back into the receiver in the same way. +:expect 0 +private fun mk(...args) = args.reduce((acc, x, ...) => if acc is Int then acc else mk(...acc), 0) +mk(1, 2) +//β”‚ = 0 + +// A checked index reads some element: its result has the element interface. +:expect [2, "b", 3, 4] +class Item(val value: Int) +fun get(xs: Array[Item], i: Int) = xs.[i].value +private fun last(xs) = xs.[xs.length - 1] +let items = mut [Item(4)] +[get([Item(1), Item(2)], 1), ["a", "b"].[1], last([Item(3)]).value, items.[0].value] +//β”‚ = [2, "b", 3, 4] +//β”‚ items = [Item(4)] + +// Checked indexing requires an array receiver. +:e +:re +class Item(val value: Int) +(new Item(1)).[0] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in array index +//β”‚ β•‘ l.261: (new Item(1)).[0] +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Item' does not support checked array indexing. +//β”‚ β•‘ l.261: (new Item(1)).[0] +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ ╙── Use '![...]' for a dynamic access. +//β”‚ ═══[RUNTIME ERROR] TypeError: tmp48.at is not a function + +// A dynamic access authorizes dynamic operations on its result; a checked index does not. +:e +fun first(xs: Array[Int]) = [xs![0].whatever, xs.[0].whatever] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.273: fun first(xs: Array[Int]) = [xs![0].whatever, xs.[0].whatever] +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Int' does not contain member 'whatever' +//β”‚ β•‘ l.273: fun first(xs: Array[Int]) = [xs![0].whatever, xs.[0].whatever] +//β”‚ ╙── ^^^ diff --git a/hkmc2/shared/src/test/mlscript/newres/Assignments.mls b/hkmc2/shared/src/test/mlscript/newres/Assignments.mls new file mode 100644 index 0000000000..a2087e4dc4 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/Assignments.mls @@ -0,0 +1,88 @@ +:. + + +// The target must resolve even when the module has not been elaborated yet. +fun writeLater(value) = { + set Later.Item = value +} +module Later { + class ItemClass { + fun answer() = 42 + } + mut val Item = 0 +} +writeLater(7) + +:expect 7 +Later.Item +//β”‚ = 7 + +:expect 42 +(new Later.ItemClass).answer() +//β”‚ = 42 + + +// A declared field has no initializer shape, but still has a definite target. +declare module External with + mut val value +fun writeExternal(value) = { + set External.value = value +} + + +// Both the temporary write and the synthesized restoration use term resolution. +:expect 8 +set Later.Item = 8 in Later.Item +//β”‚ = 8 + +:expect 7 +Later.Item +//β”‚ = 7 + + +// Resolving the assignment must preserve captures through the nested module. +fun replace(initial) = { + module Local { + class ItemClass + mut val Item = initial + } + set Local.Item = 9 + Local.Item +} + +:noInline +:expect 9 +replace(0) +//β”‚ = 9 + + +// Resolving an l-value must preserve receiver-before-RHS evaluation, once each. +module Order { + mut val trace = "" + mut val value = 0 +} +fun receiver() = { + set Order.trace = Order.trace + "receiver;" + Order +} +fun rhs() = { + set Order.trace = Order.trace + "rhs;" + 10 +} +set receiver().value = rhs() + +:expect ["receiver;rhs;", 10] +[Order.trace, Order.value] +//β”‚ = ["receiver;rhs;", 10] + + +// A class without a term definition is not a term-member assignment target. +:e +module Classes { + class Only +} +set Classes.Only = 0 +//β”‚ ╔══[COMPILATION ERROR] Assignment requires a term member +//β”‚ β•‘ l.84: set Classes.Only = 0 +//β”‚ ╙── ^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/newres/BadApps.mls b/hkmc2/shared/src/test/mlscript/newres/BadApps.mls new file mode 100644 index 0000000000..301aa3ff07 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/BadApps.mls @@ -0,0 +1,94 @@ +:. + + +// FIXME: dedup errors + +:e +42() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.7: 42() +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be called like a function. +//β”‚ β•‘ l.7: 42() +//β”‚ ╙── ^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + + +let x = 123 +//β”‚ x = 123 + +:e +x(456) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.21: x(456) +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be called like a function. +//β”‚ β•‘ l.17: let x = 123 +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + + +// Returning a non-callable value adds capture marks, not an argument list. +:e +fun result() = 1 +result()() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.34: result()() +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be called like a function. +//β”‚ β•‘ l.33: fun result() = 1 +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A captured module is still a non-callable value. +:e +module M +fun call(x) = x() +call(M) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.46: fun call(x) = x() +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Module 'M' cannot be called like a function. +//β”‚ β•‘ l.45: module M +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Plain class values require new, including when passed through a parameter. +:e +class Plain +fun instantiate(cls) = cls() +instantiate(Plain) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.59: fun instantiate(cls) = cls() +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Plain' must be instantiated with 'new'. +//β”‚ β•‘ l.58: class Plain +//β”‚ ╙── ^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A fully applied constructor really has received all its argument lists. +:e +class Curried(x)(y) +fun overapply(instance) = instance() +overapply(Curried(1)(2)) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.72: fun overapply(instance) = instance() +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Curried' cannot receive more argument lists. +//β”‚ β•‘ l.73: overapply(Curried(1)(2)) +//β”‚ ╙── ^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Explicitly constructed instances must not be told to add another new. +:e +class Plain +fun overapply(instance) = instance() +overapply(new Plain) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.85: fun overapply(instance) = instance() +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Plain' cannot receive more argument lists. +//β”‚ β•‘ l.86: overapply(new Plain) +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + diff --git a/hkmc2/shared/src/test/mlscript/newres/BareClasses.mls b/hkmc2/shared/src/test/mlscript/newres/BareClasses.mls new file mode 100644 index 0000000000..77d41a0050 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/BareClasses.mls @@ -0,0 +1,159 @@ +:. + + +:elt +class Foo { + val b = 1 +} +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Elaborated tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Blk: +//β”‚ stats = Ls of +//β”‚ Plain: +//β”‚ kind = Cls +//β”‚ sym = class:Foo⁰ +//β”‚ bsym = member:FooΒΉ +//β”‚ body = ObjBody of Blk: +//β”‚ stats = Ls of +//β”‚ TermDefinition: +//β”‚ k = ImmutVal +//β”‚ sym = member:b⁰ +//β”‚ tsym = term:Foo⁰/bΒΉ +//β”‚ body = S of Lit of IntLit of 1 +//β”‚ flags = (method) +//β”‚ modulefulness = Modulefulness of N +//β”‚ res = Lit of UnitLit of false +//β”‚ res = Lit of UnitLit of false +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +:sr +:sir +class Foo { + val b = 1 + this.b +} +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ class Foo⁰::FooΒΉ { val b⁰::bΒΉ β€Ήflow:b⁰› = 1, FooΒΉ.this.b⁰ } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ define FooΒ² as class FooΒ³ { val bΒ²; constructor() { define bΒ² as val bΒ³ = 1; end } }; end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + + +:sr +Foo +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Foo⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = class Foo + +:e +Foo.b +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.49: Foo.b +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Class value does not contain member 'b' +//β”‚ β•‘ l.30: class Foo { +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +let f = Foo +//β”‚ f = class Foo + +:e +f.b +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.62: f.b +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Class value does not contain member 'b' +//β”‚ β•‘ l.30: class Foo { +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + + +:sr +:sir +:sjs +let f = new Foo +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let f⁰, f⁰ = new Foo⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ set f⁰ = new FooΒ³(); end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let f1; f1 = globalThis.Object.freeze(new Foo3()); +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ f = Foo { b: 1 } + +let f = new Foo() +//β”‚ f = Foo { b: 1 } + +:sr +f.b +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fΒΉ.b⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 1 + + +:e +class Foo { + constructor(val a) { + val b = a + 1 + } +} +//β”‚ ╔══[COMPILATION ERROR] Unsupported constructor in this position. +//β”‚ β•‘ l.98: constructor(val a) { +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.99: val b = a + 1 +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.100: } +//β”‚ ╙── ^^^ + + +// :fixme +:breakme // `val` in ctor param list should be compiled or explicitly disallowed +:sr +class Foo { + constructor(val a) + val b = a + 1 +} +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fun FooΒ² class FooΒ³::Foo⁴(val a⁰) { let aΒΉ, aΒΉ = a⁰, val bΒ²::bΒ³ β€Ήflow:bΒΉβ€Ί = +(aΒΉ^bΒ³, 1)β€Ήapp⁰› } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + + +:e +let f = Foo(1) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.124: let f = Foo(1) +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Foo' must be instantiated with 'new'. +//β”‚ β•‘ l.114: class Foo { +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ f = undefined + + +let f = new Foo(1) +//β”‚ f = Foo { b: 2 } + +:fixme +:sr +f.a +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.140: f.a +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Foo' does not contain member 'a' +//β”‚ β•‘ l.135: let f = new Foo(1) +//β”‚ ╙── ^^^^^^^^^^ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fΒ².aΛ€Λ€Λ€ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:sr +f.b +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fΒ².bΒ² +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 2 + + diff --git a/hkmc2/shared/src/test/mlscript/newres/Basics.mls b/hkmc2/shared/src/test/mlscript/newres/Basics.mls new file mode 100644 index 0000000000..6db9d87e4f --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/Basics.mls @@ -0,0 +1,177 @@ +:. + + +:elt +:rt +:sir +id +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Elaborated tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Blk: +//β”‚ res = NewSel: +//β”‚ prefix = MemberRef of member:Predef⁰ +//β”‚ id = Ident of "id" +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Blk: +//β”‚ res = NewSel: +//β”‚ prefix = MemberRef of member:Predef⁰ +//β”‚ id = Ident of "id" +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ return PredefΒΉ.id⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = fun id + + +:de +:sr +:sir +Predef.id +//β”‚ Elab block List(Sel(Ident(Predef),Ident(id))) LocalScope(block:3) +//β”‚ | Elab term Sel(Ident(Predef),Ident(id)) +//β”‚ | | Elab subterm Sel(Ident(Predef),Ident(id)) +//β”‚ | | | Elab subterm Ident(Predef) +//β”‚ | | | | listen: trm = member:Predef⁰ +//β”‚ | | | | listenBMS: bms = module +//β”‚ | | | | listenedBMS: bms = module +//β”‚ | | | | fromBMS: bms = member:Predef⁰, sh = module:PredefΒΉ, flow = PredefΒ², markss = List() +//β”‚ | | | | PUSH module:PredefΒΉ +//β”‚ | | | ~> member:Predef⁰ +//β”‚ | | | newSel? sel = member:Predef⁰.id +//β”‚ | | | listen: trm = member:Predef⁰ +//β”‚ | | | listenBMS: bms = module +//β”‚ | | | listenedBMS: bms = module +//β”‚ | | | fromBMS: bms = member:Predef⁰, sh = module:PredefΒΉ, flow = PredefΒ², markss = List() +//β”‚ | | | PUSH module:PredefΒΉ +//β”‚ | | | newSel: sel = member:Predef⁰.id, shape = module:PredefΒΉ +//β”‚ | | | newSel member: bms = member:idΒΉ, mss = List() +//β”‚ | | | listenTerm: trm = member:Predef⁰.id +//β”‚ | | | listen: trm = member:Predef⁰.id +//β”‚ | | | listenBMS: bms = function +//β”‚ | | | listenedBMS: bms = function +//β”‚ | | | fromBMS: bms = member:idΒΉ, sh = member:idΒΉ, flow = β‹…id⁰, markss = List() +//β”‚ | | | .exit MarkedShape (member:idΒΉ, term:PredefΒΉ/id⁰, β‹…id⁰) +//β”‚ | | | = MarkedShape(member:idΒΉ, β†—βŸ¨term:PredefΒΉ/id⁰⟩%βŸ¨β‹…id⁰⟩ϡ) +//β”‚ | | | PUSH MarkedShape(member:idΒΉ, β†—βŸ¨term:PredefΒΉ/id⁰⟩%βŸ¨β‹…id⁰⟩ϡ) +//β”‚ | | ~> member:Predef⁰.id +//β”‚ | ~> member:Predef⁰.id +//β”‚ ~> { member:Predef⁰.id } +//β”‚ listenTerm: trm = member:Predef⁰.id +//β”‚ listen: trm = member:Predef⁰.id +//β”‚ listenBMS: bms = function +//β”‚ listenedBMS: bms = function +//β”‚ fromBMS: bms = member:idΒΉ, sh = member:idΒΉ, flow = β‹…id⁰, markss = List() +//β”‚ .exit MarkedShape (member:idΒΉ, term:PredefΒΉ/id⁰, β‹…id⁰) +//β”‚ = MarkedShape(member:idΒΉ, β†—βŸ¨term:PredefΒΉ/id⁰⟩%βŸ¨β‹…id⁰⟩ϡ) +//β”‚ PUSH MarkedShape(member:idΒΉ, β†—βŸ¨term:PredefΒΉ/id⁰⟩%βŸ¨β‹…id⁰⟩ϡ) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Predef⁰.id⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ return PredefΒΉ.id⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = fun id + +:elt +Predef.id(1) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Elaborated tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Blk: +//β”‚ res = App: +//β”‚ lhs = NewSel: +//β”‚ prefix = MemberRef of member:Predef⁰ +//β”‚ id = Ident of "id" +//β”‚ rhs = Tup of Ls of +//β”‚ Fld: +//β”‚ term = Lit of IntLit of 1 +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 1 + +:e +Predef.idd(1) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.87: Predef.idd(1) +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Module 'Predef' does not contain member 'idd' +//β”‚ β•‘ in ../../mlscript-compile/Predef.mls +//β”‚ β•‘ l.16: module Predef with ... +//β”‚ ╙── ^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + + +// :de +:silent +let p = Predef + +// :de +:rt +:sir +p.id +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Blk: +//β”‚ res = NewSel: +//β”‚ prefix = SimpleRef of p⁰ +//β”‚ id = Ident of "id" +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ return p⁰.id⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = fun id + +p.id +//β”‚ = fun id + +:sr +p.id +p.id +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ p⁰.id⁰, p⁰.id⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = fun id + + +:silent +val p = Predef + +:sr +p.id +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ pΒΉ.id⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = fun id + + +:e +(1).id +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.140: (1).id +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal does not contain member 'id' +//β”‚ β•‘ l.140: (1).id +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:fixme // parse +1.id +//β”‚ ╔══[LEXICAL ERROR] Expected at least one digit after the decimal point +//β”‚ β•‘ l.150: 1.id +//β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (identifier). +//β”‚ β•‘ l.150: 1.id +//β”‚ ╙── ^^ +//β”‚ = 1 + +:expect "ha ha " +"ha ".repeat(2) +//β”‚ = "ha ha " + + +print(1) +//β”‚ > 1 + + + +:e +().id +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.170: ().id +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Unit value does not contain member 'id' +//β”‚ β•‘ l.170: ().id +//β”‚ ╙── ^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/newres/BooleanConstraintApproximation.mls b/hkmc2/shared/src/test/mlscript/newres/BooleanConstraintApproximation.mls new file mode 100644 index 0000000000..fb4c699395 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/BooleanConstraintApproximation.mls @@ -0,0 +1,96 @@ +:. + +// An upper union sends argument constraints into both sets of type parameters. +// Observe the element's members, not just a wrapper that contains the element. +:expect 3 +fun structural[A, B](x: Indexable[A] | Indexable[B]) = x.at(0) +fun runStructural(x: Indexable[Array]) = structural(x).length +runStructural([[1, 2, 3]]) +//β”‚ = 3 + +fun nominal[A, B](x: Iterator[A] | Iterator[B]) = x.next() + +(x: Iterator[Array]) => nominal(x).value.length +//β”‚ = fun + +// Explicit arguments retain their interfaces while nested variables still infer. +fun identity[T](x: T): T = x +fun supplied[A, B](x: Indexable[A] | Indexable[B]) = + identity[Indexable[A] | Indexable[B]](x).at(0) + +:expect 2 +fun runSupplied(x: Indexable[Array]) = supplied(x).length +runSupplied([[1, 2]]) +//β”‚ = 2 + +// The same rule applies without record or nominal structure. +fun either[A, B](x: A | B) = x + +:expect 4 +either([1, 2, 3, 4]).length +//β”‚ = 4 + +// Constraint inspection treats a lower intersection as independent candidates. +// Ordinary member lookup still combines the two declared record interfaces, +// including when the constraint view of that type has already been observed. +:expect [3, "SECOND"] +type Cell[A] = {value: A} +type First = {first: Int} +type Second = {second: Str} +fun extract[A](x: Cell[A]) = x.value +fun direct(x: Cell[First] & Cell[Second]) = + extract(x) + [x.value.first, x.value.second.toUpperCase()] +direct(value: {first: 3, second: "second"}) +//β”‚ = [3, "SECOND"] + +// With eager diagnostics, the separate inferred candidates are observable: +// Second contributes a candidate without first, instead of merging into First. +:e +(x: Cell[First] & Cell[Second]) => extract(x).first +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.50: (x: Cell[First] & Cell[Second]) => extract(x).first +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Record type does not contain member 'first' +//β”‚ β•‘ l.39: type Second = {second: Str} +//β”‚ ╙── ^^^^^^^^^^^ +//β”‚ = fun + +// The default lenient mode accepts a member supplied by either candidate. +:expect [5, "six"] +#lang(eagerResolution: false) +fun projected(x: Cell[First] & Cell[Second]) = + let value = extract(x) + [value.first, value.second] +projected(value: {first: 5, second: "six"}) +//β”‚ = [5, "six"] + +// Nested generic calls retain the captures of each separate outer activation. +:expect [7, "eight"] +#lang(eagerResolution: true) +private fun enclosing[A](x: Cell[A]) = + fun inner[B](y: Cell[A] | Cell[B]) = y.value + inner(x) +[enclosing(value: {first: 7}).first, enclosing(value: {second: "eight"}).second] +//β”‚ = [7, "eight"] + +// Recursive unions reuse the same constraint edges rather than expanding paths. +:expect 3 +type Node[A, B] = {item: A, next: Chain[B, A]} +type Chain[A, B] = Node[A, B] | Node[B, A] +class Link(val item: Array) with + val next = this +fun recursive[A, B](x: Chain[A, B]) = x.next.next.item +recursive(Link([1, 2, 3])).length +//β”‚ = 3 + +// Nominal views discard unrelated substitutions but retain their own arguments +// and enclosing binders when union constraints cross generic calls. +:expect [1, "ABC", 2] +private fun captured[A](x: A) = + class Box[B](val second: B) with + fun first(): A = x + private fun pass[B, C](box: Box[B] | Box[C]) = box + pass(new Box[Array]([1, 2])) +[captured([1]).first().length, captured("abc").first().toUpperCase(), captured([1]).second.length] +//β”‚ = [1, "ABC", 2] diff --git a/hkmc2/shared/src/test/mlscript/newres/BuiltinFunction.mls b/hkmc2/shared/src/test/mlscript/newres/BuiltinFunction.mls new file mode 100644 index 0000000000..2524a80aea --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/BuiltinFunction.mls @@ -0,0 +1,254 @@ +:. + +// Function annotations expose callability without requiring an observed call. +fun unused(f: Function) = f(1) + +// Arity is unknown: zero, multiple, and spread arguments are all permitted. +:expect 42 +fun call0(f: Function) = f() +fun call2(f: Function) = f(20, 22) +fun spread(f: Function, args: Array[Int]) = f(...args) +call0(() => call2((x, y) => spread((a, b) => a + b, [x, y]))) +//β”‚ = 42 + +// Aliases, stored values, and nested scopes retain builtin identity. +:expect 42 +type Callback = Function +class Holder(val callback: Callback) +fun nested(holder: Holder) = + fun inner() = holder.callback(41) + inner() +nested(Holder((x) => x + 1)) +//β”‚ = 42 + +// A nominal subclass retains the callability of Function. +class CallbackClass extends Function +fun inherited(f: CallbackClass) = f(1) + +// A call does not expose the concrete callback's result members. +:ge +class Item(val value: Int) +fun read(f: Function) = f().value +read(() => Item(42)) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.31: fun read(f: Function) = f().value +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•‘ l.31: fun read(f: Function) = f().value +//β”‚ β•‘ ^^^^^^^^ +//β”‚ ╙── The 'Function' type does not specify a return type. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Nor does it authorize another call on the unknown result. +:ge +fun curried(f: Function) = f(1)(2) +curried((x) => (y) => x + y) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.44: fun curried(f: Function) = f(1)(2) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of unknown shape cannot be called like a function. +//β”‚ β•‘ l.44: fun curried(f: Function) = f(1)(2) +//β”‚ β•‘ ^^^^^^^^ +//β”‚ ╙── The 'Function' type does not specify a return type. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// The diagnostic points to the written alias annotation, not its definition or call. +:ge +type Callback = Function +fun aliased(f: Callback) = + val result = f() + result.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.60: result.value +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•‘ l.58: fun aliased(f: Callback) = +//β”‚ β•‘ ^^^^^^^^ +//β”‚ ╙── The 'Function' type does not specify a return type. + +// Storing a call result and capturing it preserves the outer Function annotation. +:ge +fun captured(f: Function) = + val result = f(1) + fun inner() = result(2) + inner() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.73: fun inner() = result(2) +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of unknown shape cannot be called like a function. +//β”‚ β•‘ l.71: fun captured(f: Function) = +//β”‚ β•‘ ^^^^^^^^ +//β”‚ ╙── The 'Function' type does not specify a return type. + +// Inherited callability retains the subclass annotation as its diagnostic witness. +:ge +fun inheritedResult(f: CallbackClass) = f().value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.85: fun inheritedResult(f: CallbackClass) = f().value +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•‘ l.85: fun inheritedResult(f: CallbackClass) = f().value +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ ╙── The 'Function' type does not specify a return type. + +// Knowing that a value is callable does not permit arbitrary member access. +:ge +fun missing(f: Function) = f.nonexistent +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.96: fun missing(f: Function) = f.nonexistent +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Function' does not contain member 'nonexistent' +//β”‚ β•‘ l.96: fun missing(f: Function) = f.nonexistent +//β”‚ ╙── ^^^^^^^^ + +// A user-defined class named Function is not the builtin. +:ge +module Shadowing with + class Function + fun shadowed(f: Function) = f(1) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.108: fun shadowed(f: Function) = f(1) +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Function' cannot be called like a function. +//β”‚ β•‘ l.108: fun shadowed(f: Function) = f(1) +//β”‚ ╙── ^^^^^^^^ + +:e +fun test(f: Function) = f(1, 2, 3) +test((x) => x.value) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.118: test((x) => x.value) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.118: test((x) => x.value) +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•‘ l.117: fun test(f: Function) = f(1, 2, 3) +//β”‚ β•‘ ^^^^^^^^ +//β”‚ ╙── The 'Function' type does not specify parameter types. +//β”‚ ═══[RUNTIME ERROR] Error: Function expected 1 argument but got 3 + +// Erasure checks callback inputs even when a private consumer never calls it. +:ge +private fun discard(f: Function) = () +discard(x => x.value) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.134: discard(x => x.value) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.134: discard(x => x.value) +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•‘ l.133: private fun discard(f: Function) = () +//β”‚ β•‘ ^^^^^^^^ +//β”‚ ╙── The 'Function' type does not specify parameter types. + +// Aliases retain the annotation witness, including for unknown rest elements. +:ge +type ErasedCallback = Function +private fun discardRest(f: ErasedCallback) = () +discardRest((...xs) => xs.0.value) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.150: discardRest((...xs) => xs.0.value) +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Rest parameter 'xs' admits elements of unknown shape. +//β”‚ β•‘ l.150: discardRest((...xs) => xs.0.value) +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•‘ l.149: private fun discardRest(f: ErasedCallback) = () +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ ╙── The 'Function' type does not specify parameter types. + +// Explicit callback annotations still provide an interface for their parameters. +:expect 42 +class Payload(val value: Int) +private fun invoke(f: Function) = f(Payload(42)) +invoke((x: Payload) => x.value) +//β”‚ = 42 + +// Exposure follows returned callbacks after forward definitions are available. +:ge +private fun discardReturned(f: Function) = () +discardReturned(() => makeCallback()) +private fun makeCallback() = laterCallback +private fun laterCallback(x) = x.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.174: private fun laterCallback(x) = x.value +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.174: private fun laterCallback(x) = x.value +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ This value is returned here. +//β”‚ β•‘ l.172: discardReturned(() => makeCallback()) +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•‘ l.171: private fun discardReturned(f: Function) = () +//β”‚ β•‘ ^^^^^^^^ +//β”‚ ╙── The 'Function' type does not specify parameter types. + +// A declared result erases a private implementation even without a caller. +:ge +private fun declared(): Function = x => x.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.191: private fun declared(): Function = x => x.value +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.191: private fun declared(): Function = x => x.value +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•‘ l.191: private fun declared(): Function = x => x.value +//β”‚ β•‘ ^^^^^^^^ +//β”‚ ╙── The 'Function' type does not specify parameter types. + +// Erasing an imported parameter's signature checks the consumer's callback. +import "../../mlscript-compile/MutMap.mls" + +:ge +MutMap.empty |> MutMap.updateWith("key") of (x => x.value) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.207: MutMap.empty |> MutMap.updateWith("key") of (x => x.value) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.207: MutMap.empty |> MutMap.updateWith("key") of (x => x.value) +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•‘ in ../../mlscript-compile/MutMap.mls +//β”‚ β•‘ l.30: fun updateWith(key)(op: Function)(m: MutMap) = +//β”‚ β•‘ ^^^^^^^^ +//β”‚ ╙── The 'Function' type does not specify parameter types. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Following a callback that returns itself must reach a fixed point. +:expect () +private fun recursive(x) = recursive +private fun discardRecursive(f: Function) = () +discardRecursive(recursive) + +// Application diagnostics follow the same parameter-to-annotation path as selections. +:ge +private fun discardCallable(f: Function) = () +discardCallable(x => x()) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.230: discardCallable(x => x()) +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.230: discardCallable(x => x()) +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Value of unknown shape cannot be called like a function. +//β”‚ β•‘ l.229: private fun discardCallable(f: Function) = () +//β”‚ β•‘ ^^^^^^^^ +//β”‚ ╙── The 'Function' type does not specify parameter types. + +// Propagating an unknown result through a spread retains the annotation's position. +:ge +private fun spreadResult(f: Function) = [...f()].0.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.244: private fun spreadResult(f: Function) = [...f()].0.value +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This spread has no known element shape. +//β”‚ β•‘ l.244: private fun spreadResult(f: Function) = [...f()].0.value +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•‘ l.244: private fun spreadResult(f: Function) = [...f()].0.value +//β”‚ β•‘ ^^^^^^^^ +//β”‚ ╙── The 'Function' type does not specify a return type. diff --git a/hkmc2/shared/src/test/mlscript/newres/Builtins.mls b/hkmc2/shared/src/test/mlscript/newres/Builtins.mls new file mode 100644 index 0000000000..97a49a86e7 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/Builtins.mls @@ -0,0 +1,34 @@ +:. + + +// :de +:sr +new Map +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ new Map⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = Map(0) {} + +// :de +:sr +new Map() +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ new Map⁰() +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = Map(0) {} + +:sr +new Map([]) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ new Map⁰([]) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = Map(0) {} + +:sr +new Map([[1, 2], [3, 4]]) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ new Map⁰([ [ 1, 2 ], [ 3, 4 ] ]) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = Map(2) {1 => 2, 3 => 4} + + diff --git a/hkmc2/shared/src/test/mlscript/newres/CallSiteShapes.mls b/hkmc2/shared/src/test/mlscript/newres/CallSiteShapes.mls new file mode 100644 index 0000000000..ca5897cc7f --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/CallSiteShapes.mls @@ -0,0 +1,86 @@ +:. + +// Call-site shape correlation is incomplete even without member projections. +:fixme +:expect 40 +class A { fun get() = 17 } +class B { fun get() = 23 } +fun identity(value) = value +fun forwarded(value) = identity(value) +forwarded(new A).get() + forwarded(new B).get() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.10: forwarded(new A).get() + forwarded(new B).get() +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'get' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'value' admits values of unknown shape. +//β”‚ β•‘ l.9: fun forwarded(value) = identity(value) +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ 'forwarded' is exposed by this compilation unit. +//β”‚ β•‘ l.9: fun forwarded(value) = identity(value) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.10: forwarded(new A).get() + forwarded(new B).get() +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'get' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'value' admits values of unknown shape. +//β”‚ β•‘ l.9: fun forwarded(value) = identity(value) +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ 'forwarded' is exposed by this compilation unit. +//β”‚ β•‘ l.9: fun forwarded(value) = identity(value) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '40', got: 'undefined' + +// Projected functions expose the same limitation after argument forwarding. +:fixme +:expect 40 +class Id { fun apply(value) = value } +let id = new Id +val apply = Id::apply +apply(id, new A).get() + apply(id, new B).get() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.40: apply(id, new A).get() + apply(id, new B).get() +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'get' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ The shape of this value is unknown. +//β”‚ β•‘ l.39: val apply = Id::apply +//β”‚ ╙── ^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.40: apply(id, new A).get() + apply(id, new B).get() +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'get' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ The shape of this value is unknown. +//β”‚ β•‘ l.39: val apply = Id::apply +//β”‚ ╙── ^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '40', got: 'undefined' +//β”‚ apply = fun +//β”‚ id = Id + +// Rest-argument forwarding has the same call-site correlation limitation. +:fixme +:expect 40 +fun restIdentity(ignored, ...args) = identity(...args) +restIdentity(0, new A).get() + restIdentity(0, new B).get() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.64: restIdentity(0, new A).get() + restIdentity(0, new B).get() +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'get' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Rest parameter 'args' admits elements of unknown shape. +//β”‚ β•‘ l.63: fun restIdentity(ignored, ...args) = identity(...args) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'restIdentity' is exposed by this compilation unit. +//β”‚ β•‘ l.63: fun restIdentity(ignored, ...args) = identity(...args) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.64: restIdentity(0, new A).get() + restIdentity(0, new B).get() +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'get' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Rest parameter 'args' admits elements of unknown shape. +//β”‚ β•‘ l.63: fun restIdentity(ignored, ...args) = identity(...args) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'restIdentity' is exposed by this compilation unit. +//β”‚ β•‘ l.63: fun restIdentity(ignored, ...args) = identity(...args) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '40', got: 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/newres/Captures.mls b/hkmc2/shared/src/test/mlscript/newres/Captures.mls new file mode 100644 index 0000000000..852d23447a --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/Captures.mls @@ -0,0 +1,115 @@ +:. + + +:sr +fun foo(x) = + print(x) + x +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fun foo⁰::fooΒΉ(x⁰) β€Ήflow:foo⁰› = { Predef⁰.print⁰^fooΒΉ(x⁰)β€Ήapp⁰›, x⁰ } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + + +// :de +:sr +foo(1) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ foo⁰(1)β€ΉappΒΉβ€Ί +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ > 1 +//β”‚ = 1 + +// :de +:sr +foo(Predef).id +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ foo⁰(Predef⁰)β€ΉappΒ²β€Ί.id⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ > class Predef { Symbols: Symbols { prettyPrint: Symbol("mlscript.prettyPrint"), definitionMetadata: Symbol("mlscript.definitionMetadata"), class: object Symbols }, Sub: class Sub, Eq: class Eq, Refl: Refl, js_assert: fun assert, foldl: fun fold, meta: class meta } +//β”‚ = fun id + +:sr +object Foo { + val id = 42 +} +foo(Foo).id +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ object Foo⁰::FooΒΉ { val idΒΉ::idΒ² β€Ήflow:id⁰› = 42 }, foo⁰(Foo⁰)β€ΉappΒ³β€Ί.idΒΉ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ > Foo { id: 42, class: object Foo } +//β”‚ = 42 + + +:sr +fun foo(x) = + if x > 0 do + print(x) + foo(x - 1) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fun fooΒ²::fooΒ³(xΒΉ) β€Ήflow:fooΒΉβ€Ί = if { +//β”‚ let scrut⁰ = >(xΒΉ, 0)β€Ήapp⁴›; +//β”‚ scrut⁰ is true do { Predef⁰.print⁰^fooΒ³(xΒΉ)β€Ήapp⁡›, fooΒ²^fooΒ³(-(xΒΉ, 1)β€Ήapp⁢›)β€Ήapp⁷› }; +//β”‚ else () +//β”‚ } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +foo(3) +//β”‚ > 3 +//β”‚ > 2 +//β”‚ > 1 + + +:sr +fun foo(x) = + print(x) + fun bar(y) = x + y + bar +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fun foo⁴::foo⁡(xΒ²) β€Ήflow:fooΒ²β€Ί = { +//β”‚ Predef⁰.print⁰^foo⁡(xΒ²)β€Ήapp⁸›, +//β”‚ fun bar⁰::barΒΉ(y⁰) β€Ήflow:bar⁰› = +(xΒ²^barΒΉ, y⁰)β€Ήapp⁹›, +//β”‚ bar⁰ +//β”‚ } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +foo(3)(4) +//β”‚ > 3 +//β”‚ = 7 + + +:sr +class foo(x) { + print(x) + fun bar(y) = x + y +} +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fun foo⁢ +//β”‚ class foo⁷::foo⁸(xΒ³) { +//β”‚ @private val x⁴::x⁡ β€Ήflow:x⁰› = xΒ³^x⁡, +//β”‚ Predef⁰.print⁰^foo⁸(foo⁸.this.x⁴)β€Ήapp¹⁰›, +//β”‚ fun barΒ²::barΒ³(yΒΉ) β€Ήflow:barΒΉβ€Ί = +(foo⁸.this.x⁴^barΒ³, yΒΉ)β€ΉappΒΉΒΉβ€Ί +//β”‚ } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +foo(3).bar(4) +//β”‚ > 3 +//β”‚ = 7 + + +:sr +fun foo(x) = y => + x + y +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fun foo⁹::foo¹⁰(x⁢) β€Ήflow:fooΒ³β€Ί = (yΒ²) => +(x⁢, yΒ²)β€ΉappΒΉΒ²β€Ί +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +let f = foo(3) +//β”‚ f = fun + +f(4) +//β”‚ = 7 + +foo(3)(4) +//β”‚ = 7 + + diff --git a/hkmc2/shared/src/test/mlscript/newres/ClassValues.mls b/hkmc2/shared/src/test/mlscript/newres/ClassValues.mls new file mode 100644 index 0000000000..e351e9ec97 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ClassValues.mls @@ -0,0 +1,455 @@ +:. +:js + +// Class objects are independent of constructor syntax and term overloads. +class Bare +class Empty() +class Primary(val x) +class Auxiliary { + constructor(val x) + fun value() = x +} +class Curried(val x)(val y) +class Overloaded { + fun answer() = 42 +} +// Selecting the class must not call its function companion. +fun Overloaded() = throw "must not call the companion function" +class Companion() +module Companion { + val value = 10 +} + +:expect class Bare +:sr +Bare.class +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Bare⁰.classβ€ΉBareΒΉβ€Ί +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = class Bare + +:expect class Empty +:sr +Empty.class +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Empty⁰.classβ€ΉEmptyΒΉβ€Ί +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = class Empty + +:expect class Primary +Primary.class +//β”‚ = class Primary + +:expect class Auxiliary +Auxiliary.class +//β”‚ = class Auxiliary + +:expect class Curried +Curried.class +//β”‚ = class Curried + +:expect class Overloaded +Overloaded.class +//β”‚ = class Overloaded + +:expect class Companion { value: 10 } +Companion.class +//β”‚ = class Companion { value: 10 } + +:expect 42 +(new Overloaded.class).answer() +//β”‚ = 42 + +:expect 12 +new Primary.class(12).x +//β”‚ = 12 + +:expect 13 +new Auxiliary.class(13).value() +//β”‚ = 13 + +:expect 15 +new Curried.class(14)(15).y +//β”‚ = 15 + +// Constructor functions and class values support the same class selection through bindings. +let raw = Primary.class +let ctor = Primary +val saved = Primary +//β”‚ ctor = fun Primary { class: class Primary } +//β”‚ raw = class Primary +//β”‚ saved = fun Primary { class: class Primary } + +:expect true +raw == Primary.class +//β”‚ = true + +:expect class Primary +raw.class +//β”‚ = class Primary + +:expect fun +new raw +//β”‚ = fun + +:expect fun +new ctor +//β”‚ = fun + +:expect class Primary +:sr +ctor.class +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ ctor⁰.classβ€ΉPrimary⁰› +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = class Primary + +:expect class Primary +saved.class +//β”‚ = class Primary + +:expect class Primary +Primary.class.class +//β”‚ = class Primary + +:expect class Primary +({make: Primary}).make.class +//β”‚ = class Primary + +// Qualified, opened, and captured classes must retain their runtime receiver. +module Namespace { + class Item(val value) + class Plain + fun Plain() = 100 +} + +:expect class Item +Namespace.Item.class +//β”‚ = class Item + +:expect class Plain +Namespace.Plain.class +//β”‚ = class Plain + +open Namespace + +:expect true +Item.class == Namespace.Item.class +//β”‚ = true + +fun captured(x) = { + class Local { + fun value() = x + } + fun Local() = 1 + (new Local.class).value() +} + +:expect 23 +captured(23) +//β”‚ = 23 + +// Selecting the runtime class does not instantiate it. +class Explosive() { + throw "must not run the constructor" +} + +:expect class Explosive +Explosive.class +//β”‚ = class Explosive + +:e +Empty().class +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.162: Empty().class +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Empty' does not contain member 'class' +//β”‚ β•‘ l.162: Empty().class +//β”‚ ╙── ^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:e +set Bare.class = 1 +//β”‚ ╔══[COMPILATION ERROR] Assignment requires a term member +//β”‚ β•‘ l.172: set Bare.class = 1 +//β”‚ ╙── ^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Specialization must survive class selection and subsequent construction. +class Generic[T](val value: T) +class Payload(val number: Int) + +:expect 31 +new Generic[Payload].class(Payload(31)).value.number +//β”‚ = 31 + +:expect 32 +new Generic.class[Payload](Payload(32)).value.number +//β”‚ = 32 + +:expect true +Generic[Payload].class == Generic.class +//β”‚ = true + +// Each outer instance owns a distinct nested class object. +class Outer(val value) { + class Inner { + fun result() = value + } +} +let first = Outer(41) +let second = Outer(42) +//β”‚ first = Outer(41) +//β”‚ second = Outer(42) + +:expect [41, 42, false] +[(new first.Inner.class).result(), (new second.Inner.class).result(), + first.Inner.class == second.Inner.class] +//β”‚ = [41, 42, false] + +// Calling through a closure retains the selected class, even with inlining off. +fun runtimeClass() = Primary.class + +:noInline +:expect true +runtimeClass() == Primary.class +//β”‚ = true + +// Forward references wait for the whole overload set to complete. +fun forwardClass() = Forward.class +class Forward +fun Forward() = false + +:expect class Forward +forwardClass() +//β”‚ = class Forward + +import "../../mlscript-compile/OverloadedClassLibrary.mls" + +:expect class Item +OverloadedClassLibrary.Item.class +//β”‚ = class Item + +:expect 52 +(new OverloadedClassLibrary.Item.class).answer() +//β”‚ = 52 + +// Ordinary fields and foreign dynamic selections keep their usual behavior. +let record = {"class": 5} +//β”‚ record = {class: 5} + +:expect 5 +record.class +//β”‚ = 5 + +// Class selection also recovers constructor functions passed as arguments. +:expect class Empty +private fun classOfValue(value) = value.class +classOfValue(Empty) +//β”‚ = class Empty + +// Foreign class declarations denote their native binding directly. +declare class String with + constructor(value: Str) + val length: Int +declare fun String(value: Str): Str + +:expect 5 +new String.class("hello").length +//β”‚ = 5 + +// Class values retain constructor arity and captured receivers through bindings. +:expect 16 +new raw(16).x +//β”‚ = 16 + +let rawCurried = Curried.class +//β”‚ rawCurried = class Curried + +:expect 18 +new rawCurried(17)(18).y +//β”‚ = 18 + +let partial = new rawCurried(19) +//β”‚ partial = fun partial + +:expect 20 +partial(20).y +//β”‚ = 20 + +val storedClass = Primary.class +let classRecord = {make: Primary.class} +//β”‚ classRecord = {make: class Primary} +//β”‚ storedClass = class Primary + +:expect [21, 22] +[new storedClass(21).x, new classRecord.make(22).x] +//β”‚ = [21, 22] + +let specializedClass = Generic[Payload].class +//β”‚ specializedClass = class Generic + +:expect 33 +new specializedClass(Payload(33)).value.number +//β”‚ = 33 + +let nestedClass = first.Inner.class +//β”‚ nestedClass = class Inner + +:expect 41 +new nestedClass().result() +//β”‚ = 41 + +// A partial construction captures the class object before its binding changes. +class Enclosing(val number) { + class Nested(val argument) { + fun result() = number + argument + } +} +let earlier = Enclosing(100) +let later = Enclosing(200) +let currentClass = earlier.Nested.class +let construct = new currentClass +set currentClass = later.Nested.class +//β”‚ construct = fun construct +//β”‚ currentClass = class Nested +//β”‚ earlier = Enclosing(100) +//β”‚ later = Enclosing(200) + +:noInline +:expect [101, 201] +[construct(1).result(), new currentClass(1).result()] +//β”‚ = [101, 201] + +// Class values can flow through function parameters and getter results. +:expect 34 +private fun instantiate(cls, x) = new cls(x) +instantiate(Primary.class, 34).x +//β”‚ = 34 + +fun storedGetter = Primary.class + +:expect 35 +new storedGetter(35).x +//β”‚ = 35 + +// Inheritance uses the same class value and constructor parameter lists. +class Derived extends rawCurried(36)(37) + +:expect 37 +new Derived().y +//β”‚ = 37 + +let bareClass = Bare +//β”‚ bareClass = class Bare + +:expect true +new bareClass is Bare +//β”‚ = true + +:e +let invalidClass = 0 +new invalidClass +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.352: new invalidClass +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be instantiated with keyword 'new'. +//β”‚ β•‘ l.351: let invalidClass = 0 +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ invalidClass = 0 + +:e +let ambiguousClass = Primary.class +set ambiguousClass = Curried.class +new ambiguousClass +//β”‚ ╔══[COMPILATION ERROR] The class interpretation here is ambiguous +//β”‚ β•‘ l.365: new ambiguousClass +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ class 'Primary' defined here +//β”‚ β•‘ l.7: class Primary(val x) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ class 'Curried' defined here +//β”‚ β•‘ l.12: class Curried(val x)(val y) +//β”‚ ╙── ^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ambiguousClass = class Curried + +// Storing a constructor function in a variable or a record preserves the same value. +:expect true +let constructors = {make: Primary} +ctor == constructors.make +//β”‚ = true +//β”‚ constructors = {make: fun Primary { class: class Primary }} + +:expect [35, 35] +[new ctor(35).x, new constructors.make(35).x] +//β”‚ = [35, 35] + +:expect true +ctor.class == constructors.make.class +//β”‚ = true + +let curriedCtor = Curried +//β”‚ curriedCtor = fun Curried { class: class Curried } + +:expect 44 +new curriedCtor(43)(44).y +//β”‚ = 44 + +let partialCtor = new curriedCtor(45) +//β”‚ partialCtor = fun partialCtor + +:expect 46 +partialCtor(46).y +//β”‚ = 46 + +let capturedCtor = earlier.Nested +let capturedRecord = {make: earlier.Nested} +//β”‚ capturedCtor = fun Nested { class: class Nested } +//β”‚ capturedRecord = {make: fun Nested { class: class Nested }} + +:noInline +:expect [101, 101, true] +[new capturedCtor(1).result(), new capturedRecord.make(1).result(), + capturedCtor.class == capturedRecord.make.class] +//β”‚ = [101, 101, true] + +// Partial construction reads the constructor's class before a later assignment. +let currentCtor = earlier.Nested +let ctorFactory = new currentCtor +set currentCtor = later.Nested +//β”‚ ctorFactory = fun ctorFactory +//β”‚ currentCtor = fun Nested { class: class Nested } + +:noInline +:expect [101, 201] +[ctorFactory(1).result(), new currentCtor(1).result()] +//β”‚ = [101, 201] + +:e +let mixedRepresentation = Primary +set mixedRepresentation = Primary.class +new mixedRepresentation +//β”‚ ╔══[COMPILATION ERROR] The class interpretation here is ambiguous +//β”‚ β•‘ l.433: new mixedRepresentation +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ function 'Primary' defined here +//β”‚ β•‘ l.7: class Primary(val x) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ class 'Primary' defined here +//β”‚ β•‘ l.7: class Primary(val x) +//β”‚ ╙── ^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ mixedRepresentation = class Primary + +// A class selection on a call result reads the class interpretation recorded on +// the selection, as for a local variable; recursion must reach a fixed point. +:expect 36 +private fun classThroughRecursion(value, n: Int) = + if n == 0 then value.class + else classThroughRecursion(value, n - 1).class +let recursiveClass = classThroughRecursion(Primary, 3) +new recursiveClass(36).x +//β”‚ = 36 +//β”‚ recursiveClass = class Primary diff --git a/hkmc2/shared/src/test/mlscript/newres/ClonedConstructors.mls b/hkmc2/shared/src/test/mlscript/newres/ClonedConstructors.mls new file mode 100644 index 0000000000..b8b0c45e73 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ClonedConstructors.mls @@ -0,0 +1,84 @@ +:. + +open annotations + +// Compiled patterns clone both direct and qualified constructor references. +data class Direct(val value) +module Nested with + data class Wrapped(val value) + +fun direct(x) = if x is @compile Direct(3) then true else false +fun qualified(x) = if x is @compile Nested.Wrapped(3) then true else false + +:expect [true, false, true, false] +[direct(Direct(3)), direct(Direct(4)), qualified(Nested.Wrapped(3)), qualified(Nested.Wrapped(4))] +//β”‚ = [true, false, true, false] + +// Wildcard candidates must keep the receiver selected by resolution. +open Nested +fun opened(x) = if x is @compile Wrapped(3) then true else false + +:expect [true, false] +[opened(Nested.Wrapped(3)), opened(Nested.Wrapped(4))] +//β”‚ = [true, false] + +// Preserve a captured module parameter in a cloned constructor path. +fun captured(module Inner: Nested, x) = + if x is @compile Inner.Wrapped(3) then true else false + +:expect [true, false] +[captured(Nested, Nested.Wrapped(3)), captured(Nested, Nested.Wrapped(4))] +//β”‚ = [true, false] + + +// @compile clones the constructor reference when generating the matcher. It must +// still read constructors.make, even though the class stored there is named Direct. +let constructors = {make: Direct} +fun throughRecord(x) = if x is @compile constructors.make(3) then true else false +//β”‚ constructors = {make: fun Direct { class: class Direct }} + +:expect [true, false] +[throughRecord(Direct(3)), throughRecord(Direct(4))] +//β”‚ = [true, false] + + +// The opened name `make` must compile to a read of constructors.make. +fun openedRecord(x) = + open constructors + if x is @compile make(3) then true else false + +:expect [true, false] +[openedRecord(Direct(3)), openedRecord(Direct(4))] +//β”‚ = [true, false] + +// Calling the constructor function stored in `make` creates a Direct instance. +:expect 35 +constructors.make(35).value +//β”‚ = 35 + +// Static `new` can also recover the known class through a constructor-valued field. +:expect 35 +(new constructors.make(35)).value +//β”‚ = 35 + +:expect 35 +// :sjs // * `Amazing` properly extends `constructors.make.class`, not the `constructors.make` function +object Amazing extends constructors.make(35) +Amazing.value +//β”‚ = 35 + +// Selecting the class from a let-bound constructor also survives pattern cloning. +let ctor = Direct +fun throughVariableClass(x) = if x is @compile ctor.class(3) then true else false +//β”‚ ctor = fun Direct { class: class Direct } + +:expect [true, false] +[throughVariableClass(Direct(3)), throughVariableClass(Direct(4))] +//β”‚ = [true, false] + +// The pattern instantiator mistakes a local constructor value for a pattern +// parameter and looks it up in the pattern-argument substitution. +:fixme +fun throughVariable(x) = if x is @compile ctor(3) then true else false +//β”‚ /!!!\ Uncaught error: java.util.NoSuchElementException: key not found: ctor + diff --git a/hkmc2/shared/src/test/mlscript/newres/ConstructorFieldRecovery.mls b/hkmc2/shared/src/test/mlscript/newres/ConstructorFieldRecovery.mls new file mode 100644 index 0000000000..eccbcaad1c --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ConstructorFieldRecovery.mls @@ -0,0 +1,95 @@ +:. + + +// A duplicate body member makes elaboration discard all generated parameter fields. +:e +class Broken(val missing, val clash) { + val clash = 0 +} +if Broken(1, 2) is Broken(x, _) then x else 0 +//β”‚ ╔══[COMPILATION ERROR] Duplicate definition of member named 'clash'. +//β”‚ β•Ÿβ”€β”€ Defined at: +//β”‚ β•‘ l.6: class Broken(val missing, val clash) { +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Defined at: +//β”‚ β•‘ l.7: val clash = 0 +//β”‚ ╙── ^^^^^^^^^^^^^ +//β”‚ = 0 + + +// A same-named body member is not the discarded constructor field either. +:e +class Replaced(val field) { + val field = {other: 0} +} +if Replaced({answer: 1}) is Replaced(x) then x.answer else 0 +//β”‚ ╔══[COMPILATION ERROR] Duplicate definition of member named 'field'. +//β”‚ β•Ÿβ”€β”€ Defined at: +//β”‚ β•‘ l.22: class Replaced(val field) { +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Defined at: +//β”‚ β•‘ l.23: val field = {other: 0} +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//β”‚ = 0 + + +// The same recovery applies to inherited matches and forward pattern resolution. +:e +fun recover(value) = if value is BadBase(x, _) then x else 0 +class BadBase(val missing, val clash) { + val clash = 0 +} +class Child extends BadBase(1, 2) +recover(new Child) +//β”‚ ╔══[COMPILATION ERROR] Duplicate definition of member named 'clash'. +//β”‚ β•Ÿβ”€β”€ Defined at: +//β”‚ β•‘ l.39: class BadBase(val missing, val clash) { +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Defined at: +//β”‚ β•‘ l.40: val clash = 0 +//β”‚ ╙── ^^^^^^^^^^^^^ +//β”‚ = 0 + + +// Valid inherited fields still propagate their value shapes to pattern bindings. +:expect 42 +class GoodBase(val field) +class GoodChild extends GoodBase({answer: 42}) +if new GoodChild is GoodBase(x) then x.answer else 0 +//β”‚ = 42 + + +// A bare subclass's class value is not an instance of its base class. +:expect 0 +if GoodChild is GoodBase(x) then 1 else 0 +//β”‚ = 0 + +// An object is an instance and exposes its inherited constructor field. +:expect 43 +class ObjectBase(val field) +object GoodObject extends ObjectBase(43) +if GoodObject is ObjectBase(x) then x else 0 +//β”‚ = 43 + + +// Class exits keep the two inherited constructor-field contexts distinct. +:expect [1, 2] +class SharedBase(val field) +class LeftChild extends SharedBase({left: 1}) +class RightChild extends SharedBase({right: 2}) +let left = if new LeftChild is SharedBase(x) then x.left else 0 +let right = if new RightChild is SharedBase(x) then x.right else 0 +[left, right] +//β”‚ = [1, 2] +//β”‚ left = 1 +//β”‚ right = 2 + +// Separate sites of one constructor retain distinct fields, including when nested. +:expect [3, 4] +class NestedField(val field) +let first = if new NestedField(new SharedBase({first: 3})) is NestedField(SharedBase(x)) then x.first else 0 +let second = if new NestedField(new SharedBase({second: 4})) is NestedField(SharedBase(x)) then x.second else 0 +[first, second] +//β”‚ = [3, 4] +//β”‚ first = 3 +//β”‚ second = 4 diff --git a/hkmc2/shared/src/test/mlscript/newres/ConstructorInstances.mls b/hkmc2/shared/src/test/mlscript/newres/ConstructorInstances.mls new file mode 100644 index 0000000000..aae1b3e6b4 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ConstructorInstances.mls @@ -0,0 +1,251 @@ +:. + +// Specializing one stored constructor must not mutate its original parameters. +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +class Box[T](val item: T) +private val shared = Box +private val first = shared[First] +private val second = shared[Second] +[first(First(1)).item.first, second(Second(2)).item.second] +//β”‚ = [1, 2] +//β”‚ first = fun Box { class: class Box } +//β”‚ second = fun Box { class: class Box } +//β”‚ shared = fun Box { class: class Box } + +// Explicit new uses the same specialization recipe through a stored alias. +:expect [3, 4] +class First(val first: Int) +class Second(val second: Int) +class Box[T](val item: T) +private val shared = Box +private val first = shared[First] +private val second = shared[Second] +[(new first(First(3))).item.first, (new second(Second(4))).item.second] +//β”‚ = [3, 4] +//β”‚ first = fun Box { class: class Box } +//β”‚ second = fun Box { class: class Box } +//β”‚ shared = fun Box { class: class Box } + +// Later constructor lists retain the instances chosen by the first list. +:expect [5, 6, 7, 8] +class First(val first: Int) +class Second(val second: Int) +class Pair[T](val item: T)(val other) +private val shared = Pair +private val left = shared[First](First(5)) +private val right = shared[Second](Second(7)) +[left(Second(6)).item.first, left(Second(6)).other.second, + right(First(8)).item.second, right(First(8)).other.first] +//β”‚ = [5, 6, 7, 8] +//β”‚ left = fun +//β”‚ right = fun +//β”‚ shared = fun Pair { class: class Pair } + +// An unapplied new expression keeps supplied types until its first application. +:expect [9, 10] +class First(val first: Int) +class Second(val second: Int) +class Box[T](val item: T) +private val first = new Box[First] +private val second = new Box[Second] +[first(First(9)).item.first, second(Second(10)).item.second] +//β”‚ = [9, 10] +//β”‚ first = fun +//β”‚ second = fun + +// Class and method binders retain independent views of the receiver and argument. +:expect [11, 12] +class First(val first: Int) +class Second(val second: Int) +class Box[T](val item: T) with + fun pair[U](other: U) = [item, other] +let box = new Box[First](First(11)) +[box.pair(Second(12)).0.first, box.pair(Second(12)).1.second] +//β”‚ = [11, 12] +//β”‚ box = Box(First(11)) + +// The receiver's Array parameter must not specialize a literal in a call argument. +:expect 14 +class First(val first: Int) +class Second(val second: Int) +let xs = new mut Array[First](0) +xs.push(First(13)) +xs.map((item, ...) => (mut [Second(14)]).0.second).0 +//β”‚ = 14 +//β”‚ xs = [First(13)] + +// A shared allocation site retains the enclosing function's distinct activations. +:expect [15, 16] +class First(val first: Int) +class Second(val second: Int) +class Box[T](val item: T) +private fun boxed[A](value: A) = new Box[A](value) +[boxed(First(15)).item.first, boxed(Second(16)).item.second] +//β”‚ = [15, 16] + +// Reconstructing a receiver's class retains the supplied argument's outer view. +:fixme +:expect [17, 18] +class First(val first: Int) +class Second(val second: Int) +class Box[T](val item: T) with + fun copy() = new Box[T](item) +let first = new Box[First](First(17)) +let second = new Box[Second](Second(18)) +[first.copy().item.first, second.copy().item.second] +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'first' has no resolved target +//β”‚ β•‘ l.97: [first.copy().item.first, second.copy().item.second] +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '[17, 18]', got: 'undefined' +//β”‚ first = Box(First(17)) +//β”‚ second = Box(Second(18)) + +// Specialization checks its arity without waiting for a constructor application. +:e +:ge +class Box[T](val item: T) +private val invalid = Box[Int, Str] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.110: private val invalid = Box[Int, Str] +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Box' expected 1 type argument, but got 2 +//β”‚ β•‘ l.109: class Box[T](val item: T) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ invalid = fun Box { class: class Box } + +// Supplied member types receive callback input obligations too. +:expect 19 +class Item(val value: Int) +let callbacks = new mut Array[Item -> Int](0) +callbacks.push((x) => x.value) +private fun first(callback, ...tail) = callback +first(...callbacks)(Item(19)) +//β”‚ = 19 +//β”‚ callbacks = [fun] + +// Type application cannot turn a constructed object back into its constructor. +:e +:re +class Box[T](val item: T) +let box = new Box[Int](1) +box[Str]("bad") +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.134: box[Str]("bad") +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Box' expected 0 type arguments, but got 1 +//β”‚ β•‘ l.132: class Box[T](val item: T) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.134: box[Str]("bad") +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Box' cannot receive more argument lists. +//β”‚ β•‘ l.133: let box = new Box[Int](1) +//β”‚ ╙── ^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ box = Box(1) + +// A partial constructor has already bound its type parameters at its first list. +:e +:ge +class Pair[T](val item: T)(val other: T) +private val partial = Pair[Int](1) +private val invalid = partial[Str] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.155: private val invalid = partial[Str] +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Pair' expected 0 type arguments, but got 1 +//β”‚ β•‘ l.153: class Pair[T](val item: T)(val other: T) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ invalid = fun +//β”‚ partial = fun + +// The supplied callback input exposes only its written nominal interface. +:e +:ge +class Base +class Child(val extra: Int) extends Base +let callbacks = new mut Array[Base -> Int](0) +callbacks.push(x => x.extra) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.171: callbacks.push(x => x.extra) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.170: let callbacks = new mut Array[Base -> Int](0) +//β”‚ ╙── ^^^^ +//β”‚ = 1 +//β”‚ callbacks = [fun] + +// The method input constrains the callback even if the array is never invoked. +:expect 1 +class Item(val value: Int) +let callbacks = new mut Array[Item -> Int](0) +callbacks.push(x => x.value) +//β”‚ = 1 +//β”‚ callbacks = [fun] + +// A more precise implementation must not refine the supplied callback result. +:e +:ge +class Base +class Child(val extra: Int) extends Base +let callbacks = new mut Array[Base -> Base](0) +callbacks.push(x => new Child(20)) +private fun first(callback, ...tail) = callback +first(...callbacks)(new Base).extra +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.197: first(...callbacks)(new Base).extra +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.194: let callbacks = new mut Array[Base -> Base](0) +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ callbacks = [fun] + +// Ordinary value flow through reconstruction also needs receiver separation. +// This control contains no type binders or annotations on the Box field. +:fixme +:expect [21, 22] +class First(val first: Int) +class Second(val second: Int) +class Box(val item) with + fun copy() = new Box(item) +let first = new Box(First(21)) +let second = new Box(Second(22)) +[first.copy().item.first, second.copy().item.second] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.217: [first.copy().item.first, second.copy().item.second] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'First' does not contain member 'second' +//β”‚ β•‘ l.215: let first = new Box(First(21)) +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'second' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'item' admits values of unknown shape. +//β”‚ β•‘ l.213: class Box(val item) with +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'Box' is exposed by this compilation unit. +//β”‚ β•‘ l.213: class Box(val item) with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.214: fun copy() = new Box(item) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.217: [first.copy().item.first, second.copy().item.second] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Second' does not contain member 'first' +//β”‚ β•‘ l.216: let second = new Box(Second(22)) +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'first' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'item' admits values of unknown shape. +//β”‚ β•‘ l.213: class Box(val item) with +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'Box' is exposed by this compilation unit. +//β”‚ β•‘ l.213: class Box(val item) with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.214: fun copy() = new Box(item) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '[21, 22]', got: 'undefined' +//β”‚ first = Box(First(21)) +//β”‚ second = Box(Second(22)) diff --git a/hkmc2/shared/src/test/mlscript/newres/ConstructorResolution.mls b/hkmc2/shared/src/test/mlscript/newres/ConstructorResolution.mls new file mode 100644 index 0000000000..a3510dbf90 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ConstructorResolution.mls @@ -0,0 +1,227 @@ +:. + + +// Constructor lookup waits for forward definitions and ignores term overloads. +fun forward(x) = { + open Constructors + if x is Token then 1 else 0 +} +module Constructors { + class Token + fun Token() = 41 + class Box(val value) + object Singleton +} + +:expect [1, 0, 41] +[forward(new Constructors.Token), forward(0), Constructors.Token()] +//β”‚ = [1, 0, 41] + + +fun named(x) = { + open Constructors {Token} + if x is Token then 1 else 0 +} +fun selected(x) = if x is Constructors.Token then 1 else 0 +fun boxed(x) = { + open Constructors + if x is Box(value) then value else 0 +} +fun singleton(x) = { + open Constructors + if x is Singleton then 1 else 0 +} + +:expect [1, 0, 1, 0, 42, 0, 1, 0] +[named(new Constructors.Token), named(0), selected(new Constructors.Token), selected(0), + boxed(Constructors.Box(42)), boxed(0), singleton(Constructors.Singleton), singleton(0)] +//β”‚ = [1, 0, 1, 0, 42, 0, 1, 0] + + +// Lowercase `as` names bind even when an open contains the same name. +// Uppercase `as` names match constructors, including deferred open candidates. +:expect [1, 0, 43] +fun chained(x) = { + open Constructors + if x is _ as Token then 1 else 0 +} +module Aliases { val saved = 99 } +fun alias(x) = { + open Aliases + if x is Constructors.Box(_) as saved then saved.value else 0 +} +[chained(new Constructors.Token), chained(0), alias(Constructors.Box(43))] +//β”‚ = [1, 0, 43] + + +// A constructor declared later in the same scope is also visible in a pattern. +fun localForward(x) = if x is Later then 1 else 0 +class Later +fun Later() = new Later + +:expect [1, 0] +[localForward(Later()), localForward(0)] +//β”‚ = [1, 0] + + +// An explicit binding wins over every wildcard candidate. +module Other { class Token } +fun explicit(x) = { + open Other + open Constructors {Token} + if x is Token then 1 else 0 +} + +:expect [1, 0] +[explicit(new Constructors.Token), explicit(new Other.Token)] +//β”‚ = [1, 0] + + +:e +fun ambiguous(x) = { + open Constructors + open Other + if x is Token then 1 else 0 +} +ambiguous(new Constructors.Token) +//β”‚ ╔══[COMPILATION ERROR] Constructor pattern is ambiguous. +//β”‚ β•‘ l.84: if x is Token then 1 else 0 +//β”‚ ╙── ^^^^^ +//β”‚ = 0 + + +:e +fun ambiguousReversed(x) = { + open Other + open Constructors + if x is Token then 1 else 0 +} +ambiguousReversed(new Constructors.Token) +//β”‚ ╔══[COMPILATION ERROR] Constructor pattern is ambiguous. +//β”‚ β•‘ l.97: if x is Token then 1 else 0 +//β”‚ ╙── ^^^^^ +//β”‚ = 0 + + +// Unknown uppercase names in `as` never silently become bindings. +:e +fun unknown(x) = if x is _ as MissingConstructor then 1 else 0 +unknown(0) +//β”‚ ╔══[COMPILATION ERROR] Name not found: MissingConstructor +//β”‚ β•‘ l.108: fun unknown(x) = if x is _ as MissingConstructor then 1 else 0 +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^ +//β”‚ = 0 + + +:expect [44, 45] +module Pairs { class (><) Pair(val first, val second) } +fun infix(x) = { + open Pairs + if x is a >< b then a else 0 +} +fun appliedSelection(x) = if x is Pairs.Pair(a, b) then b else 0 +[infix(Pairs.Pair(44, 45)), appliedSelection(Pairs.Pair(44, 45))] +//β”‚ = [44, 45] + + +// Repeating one source is unambiguous. +:expect 1 +fun repeated(x) = { + open Constructors + open Constructors + if x is Token then 1 else 0 +} +repeated(new Constructors.Token) +//β”‚ = 1 + + +// Distinct receivers can expose the same member symbol without denoting the +// same constructor value. Candidate identity includes the receiver. +fun factory() = { + module Local { class Token } + Local +} +val first = factory() +val second = factory() +//β”‚ first = class Local { Token: class Token } +//β”‚ second = class Local { Token: class Token } + +:e +fun sameMember(x) = { + open first + open second + if x is Token then 1 else 0 +} +sameMember(new first.Token) +//β”‚ ╔══[COMPILATION ERROR] Constructor pattern is ambiguous. +//β”‚ β•‘ l.153: if x is Token then 1 else 0 +//β”‚ ╙── ^^^^^ +//β”‚ = 0 + + +// A non-pattern value is diagnosed rather than treated as a constructor. +:e +val NotAPattern = 1 +fun invalid(x) = if x is NotAPattern then 1 else 0 +invalid(0) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in constructor +//β”‚ β•‘ l.165: fun invalid(x) = if x is NotAPattern then 1 else 0 +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be used as a constructor pattern. +//β”‚ β•‘ l.164: val NotAPattern = 1 +//β”‚ ╙── ^ +//β”‚ = 0 +//β”‚ NotAPattern = 1 + + +// Pattern definitions use the same deferred references as direct matches. +:expect [1, 0] +module PatternNames { + open Constructors + pattern Named = Token +} +fun namedDefinition(x) = { + open PatternNames + if x is Named then 1 else 0 +} +[namedDefinition(new Constructors.Token), namedDefinition(0)] +//β”‚ = [1, 0] + + +// Nominal filtering follows inheritance before propagating an alias's shape. +:expect [46, 0] +class Base { val field = 46 } +class Derived extends Base +fun inherited(x) = if x is Base as instance then instance.field else 0 +[inherited(new Derived), inherited(0)] +//β”‚ = [46, 0] + + +:e +fun ambiguousNew() = { + open Constructors + open Other + new Token +} +ambiguousNew() +//β”‚ ╔══[COMPILATION ERROR] The class instantiated here is ambiguous +//β”‚ β•‘ l.204: new Token +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ target: class 'Token' +//β”‚ β•‘ l.10: class Token +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ target: class 'Token' +//β”‚ β•‘ l.68: module Other { class Token } +//β”‚ ╙── ^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + + +// Pattern parameters preserve the caller's selected constructor interpretation. +:expect [1, 0] +pattern Identity(pattern P) = P +fun parameterized(x) = { + open Constructors + if x is Identity(Token) then 1 else 0 +} +[parameterized(new Constructors.Token), parameterized(0)] +//β”‚ = [1, 0] diff --git a/hkmc2/shared/src/test/mlscript/newres/ContextualInference.mls b/hkmc2/shared/src/test/mlscript/newres/ContextualInference.mls new file mode 100644 index 0000000000..7206dedfa5 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ContextualInference.mls @@ -0,0 +1,73 @@ +:. + +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +class Box[T](val value: T) +class Foo[X](val f: Box[X]) +private fun test[A](x: Foo[A]) = x.f +[test(new Foo(new Box(First(1)))).value.first, + test(new Foo(new Box(Second(2)))).value.second] +//β”‚ = [1, 2] + +// Both the explicit binder and the unannotated parameter belong to each activation. +:expect [3, 4, 5, 6] +class First(val first: Int) +class Second(val second: Int) +private fun pair[A](x: A, y) = [x, y] +private val shared = pair +let left = shared(First(3), Second(4)) +let right = shared(Second(5), First(6)) +[left.0.first, left.1.second, right.0.second, right.1.first] +//β”‚ = [3, 4, 5, 6] +//β”‚ left = [First(3), Second(4)] +//β”‚ right = [Second(5), First(6)] +//β”‚ shared = fun pair + +// Deferred record fields retain the same view as deferred tuple fields. +:expect [7, 8] +class First(val first: Int) +class Second(val second: Int) +private fun delayed[A](x: A) = {get: () => x} +[delayed(First(7)).get().first, delayed(Second(8)).get().second] +//β”‚ = [7, 8] + +// Both deferred record fields must retain their activation after the call returns. +:expect [9, 10, 11, 12] +class First(val first: Int) +class Second(val second: Int) +private fun pair[A](x: A, y) = {annotated: () => x, inferred: () => y} +private val shared = pair +let left = shared(First(9), Second(10)) +let right = shared(Second(11), First(12)) +[left.annotated().first, left.inferred().second, right.annotated().second, right.inferred().first] +//β”‚ = [9, 10, 11, 12] +//β”‚ left = {annotated: fun annotated, inferred: fun inferred} +//β”‚ right = {annotated: fun annotated, inferred: fun inferred} +//β”‚ shared = fun pair + +// A callback call inside the shared body uses the enclosing call's binder view. +:expect [13, 14] +class First(val first: Int) +class Second(val second: Int) +private fun apply[A](x: A, f) = f(x) +private val shared = apply +[shared(First(13), x => x.first), shared(Second(14), x => x.second)] +//β”‚ = [13, 14] +//β”‚ shared = fun apply + +// Recursive visits change the compound argument without expanding its type graph. +:expect [[[1]]] +private fun grow[A](value: A, n: Int) = + if n > 0 then grow(mut [value], n - 1) else value +grow(1, 3) +//β”‚ = [[[1]]] + +// Mutual recursion also reuses each definition/site's binder group. +:expect [[2]] +private fun left[A](value: A, n: Int) = + if n > 0 then right(mut [value], n - 1) else value +private fun right[B](value: B, n: Int) = + if n > 0 then left(value, n - 1) else value +left(2, 3) +//β”‚ = [[2]] diff --git a/hkmc2/shared/src/test/mlscript/newres/ControlFlowResults.mls b/hkmc2/shared/src/test/mlscript/newres/ControlFlowResults.mls new file mode 100644 index 0000000000..07f74d026f --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ControlFlowResults.mls @@ -0,0 +1,204 @@ +:. + +// Backtracking assignment returns its body value and restores the previous value. +:expect [42, 0] +let saved = 0 +let result = set saved = 1 in {answer: 42} +[result.answer, saved] +//β”‚ = [42, 0] +//β”‚ result = {answer: 42} +//β”‚ saved = 0 + +// The finally clause restores storage even when the body calls another function. +:expect [43, 0] +fun answer() = {answer: 43} +let saved = 0 +let result = set saved = 1 in answer() +[result.answer, saved] +//β”‚ = [43, 0] +//β”‚ result = {answer: 43} +//β”‚ saved = 0 + +// Imperative conditionals discard their branch results. +:e +fun answer() = {answer: 42} +let result = if true do answer() +result.answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.26: result.answer +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ ╙── Unit value does not contain member 'answer' +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ result = () + +// Loops also return unit, including when their bodies never execute. +:e +fun answer() = {answer: 42} +let result = while false do answer() +result.answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.38: result.answer +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ ╙── Unit value does not contain member 'answer' +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ result = () + +// Assignment returns unit, independently of the assigned value. +:e +let saved = 0 +let result = set saved = 1 +result.answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.50: result.answer +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ ╙── Unit value does not contain member 'answer' +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ result = () +//β”‚ saved = 1 + +// Discarding a value does not expose its members through the result. +:e +let result = drop {answer: 42} +result.answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.62: result.answer +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ ╙── Unit value does not contain member 'answer' +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ result = () + +// Cleanup preserves call-context marks for independently inferred results. +:expect [42, 43, 0] +let saved = 0 +private fun protectedValue(value) = set saved = 1 in value +[protectedValue({first: 42}).first, protectedValue({second: 43}).second, saved] +//β”‚ = [42, 43, 0] +//β”‚ saved = 0 + +// A throwing branch contributes no normal result shape. +:expect 42 +private fun maybeAnswer(fail) = + if fail then throw new Error("no answer") else {answer: 42} +maybeAnswer(false).answer +//β”‚ = 42 + +// Explicit returns supply function results, including returns in statements. +:expect [42, 43] +fun returned() = return {answer: 42} +fun early() = + return {answer: 43} + () +[returned().answer, early().answer] +//β”‚ = [42, 43] + +// Conditional returns and fall-through results preserve the calling context. +:expect [42, 43] +private fun choose(flag, value) = + if flag do return value + value +[choose(true, {first: 42}).first, choose(false, {second: 43}).second] +//β”‚ = [42, 43] + +// Returns in nested definitions belong to those definitions only. +:expect 42 +fun enclosing() = + fun nested() = return {other: 0} + {answer: 42} +enclosing().answer +//β”‚ = 42 + +// Abrupt cleanup replaces the pending return. +:expect 43 +fun cleanupReturn() = + try return {other: 42} + finally return {answer: 43} +cleanupReturn().answer +//β”‚ = 43 + +// A return's operand never becomes an assignment's normal value. +:expect 42 +fun noAssignmentResult() = + let value = return {answer: 42} + value.other +noAssignmentResult().answer +//β”‚ = 42 + +// Labels collect their own break values, not a nested label's values. +:expect [42, 43] +let first = do outerLabel: + do innerLabel: innerLabel.break({other: 0}) + outerLabel.break({answer: 42}) +let second = do exitLabel: {answer: 43} +[first.answer, second.answer] +//β”‚ = [42, 43] +//β”‚ first = {answer: 42} +//β”‚ second = {answer: 43} + +// A break without a value yields unit; statements after it are unreachable. +:e +let stopped = do exitLabel: + exitLabel.break + {answer: 42} +stopped.answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.142: stopped.answer +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ ╙── Unit value does not contain member 'answer' +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ stopped = undefined + +// Transparent annotations retain the target's value interface. +:expect 42 +let annotated = @annotations.untyped {answer: 42} +annotated.answer +//β”‚ = 42 +//β”‚ annotated = {answer: 42} + +// Explicit contextual arguments have the same tuple layout as ordinary arguments. +:expect 42 +private fun contextual(using value: {answer: Int}) = value +contextual(using {answer: 42}).answer +//β”‚ = 42 + +// Cleanup can replace a pending labeled break with a different value. +:expect 43 +let cleaned = do exitLabel: + try exitLabel.break({other: 42}) + finally exitLabel.break({answer: 43}) +cleaned.answer +//β”‚ = 43 +//β”‚ cleaned = {answer: 43} + +// Result annotations check a closure returned by an explicit return. +:expect 42 +class ReturnedAnswer(val answer: Int) +fun returnedClosure(): Int -> ReturnedAnswer = return (value => new ReturnedAnswer(value)) +returnedClosure()(42).answer +//β”‚ = 42 + +// Publicly returned closures must not infer their interface only from local calls. +:e +fun exposedClosure() = return value => value.answer +exposedClosure()({answer: 42}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.181: fun exposedClosure() = return value => value.answer +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'answer' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'value' admits values of unknown shape. +//β”‚ β•‘ l.181: fun exposedClosure() = return value => value.answer +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ This value is returned here. +//β”‚ β•‘ l.181: fun exposedClosure() = return value => value.answer +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'exposedClosure' is exposed by this compilation unit. +//β”‚ β•‘ l.181: fun exposedClosure() = return value => value.answer +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Pattern guards execute in the enclosing function's return scope. +:expect 42 +fun guardedReturn() = + if 0 is (_ where (return {answer: 42})) then throw new Error("unreachable") + else throw new Error("unreachable") +guardedReturn().answer +//β”‚ = 42 diff --git a/hkmc2/shared/src/test/mlscript/newres/CtxSens.mls b/hkmc2/shared/src/test/mlscript/newres/CtxSens.mls new file mode 100644 index 0000000000..00fc62fffe --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/CtxSens.mls @@ -0,0 +1,123 @@ +:. + + +:sr +module A { + val u = 1 +} +module B { + val u = 2 +} +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ module A⁰::AΒΉ { val u⁰::uΒΉ β€Ήflow:u⁰› = 1 }, module B⁰::BΒΉ { val uΒ²::uΒ³ β€Ήflow:uΒΉβ€Ί = 2 } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + + +fun foo(x) = x + +// :de +:sr +foo(A).u +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ foo⁰(A⁰)β€Ήapp⁰›.u⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 1 + +foo(A).u +//β”‚ = 1 + +// * We now preserve context sensitivity +:sr +foo(B).u +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ foo⁰(B⁰)β€ΉappΒΉβ€Ί.uΒ² +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 2 + + +// :de +:sr +fun bar(y) = + foo(y) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fun bar⁰::barΒΉ(y⁰) β€Ήflow:bar⁰› = foo⁰^barΒΉ(y⁰)β€ΉappΒ²β€Ί +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +// :de +// FIXME: Forwarding through a captured function loses the caller activation. +:fixme +:sr +bar(A).u +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.50: bar(A).u +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'u' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'y' admits values of unknown shape. +//β”‚ β•‘ l.40: fun bar(y) = +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'bar' is exposed by this compilation unit. +//β”‚ β•‘ l.40: fun bar(y) = +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•‘ l.41: foo(y) +//β”‚ ╙── ^^^^^^^^ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ bar⁰(A⁰)β€ΉappΒ³β€Ί.u⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// * As expected, we currently lose context sensitivity when going through the call of a captured function (foo) +:todo +:sr +bar(B).u +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.71: bar(B).u +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'u' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'y' admits values of unknown shape. +//β”‚ β•‘ l.40: fun bar(y) = +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'bar' is exposed by this compilation unit. +//β”‚ β•‘ l.40: fun bar(y) = +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•‘ l.41: foo(y) +//β”‚ ╙── ^^^^^^^^ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ bar⁰(B⁰)β€Ήapp⁴›.uβ€ΉuΒ², u⁰› +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + + +:sr +class Foo(val a) { + val b = A +} +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fun Foo⁰ +//β”‚ class FooΒΉ::FooΒ²(val a⁰) { val aΒΉ::aΒ² β€Ήflow:a⁰› = a⁰^aΒ², val b⁰::bΒΉ β€Ήflow:b⁰› = A⁰^FooΒ²^bΒΉ } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +let x = Foo(A) +//β”‚ x = Foo(class A { u: 1 }) + +:sr +x.a.u +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ x⁰.aΒΉ.u⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 1 + +:sr +x.b.u +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ x⁰.b⁰.u⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 1 + +:sr +Foo(B).a.u +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ FooΒΉ(B⁰)β€Ήapp⁡›.aΒΉ.uΒ² +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 2 + + diff --git a/hkmc2/shared/src/test/mlscript/newres/CurriedFunctions.mls b/hkmc2/shared/src/test/mlscript/newres/CurriedFunctions.mls new file mode 100644 index 0000000000..8a81488f3b --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/CurriedFunctions.mls @@ -0,0 +1,57 @@ +:. + + +:sr +fun foo(x)(y) = + x + y +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fun foo⁰::fooΒΉ(x⁰)(y⁰) β€Ήflow:foo⁰› = +(x⁰, y⁰)β€Ήapp⁰› +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + + +:sr +let f = foo(1)(2) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let f⁰, f⁰ = foo⁰(1)β€ΉappΒΉβ€Ί(2)β€ΉappΒ²β€Ί +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ f = 3 + +:breakme // builtins like `+` do not yet yield a shape +:e +f(3) +//β”‚ ═══[RUNTIME ERROR] TypeError: f is not a function + +:sr +let f = foo(1) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let fΒΉ, fΒΉ = foo⁰(1)β€ΉappΒ³β€Ί +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ f = fun + +:sr +let g = f(2) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let g⁰, g⁰ = fΒΉ(2)β€Ήapp⁴› +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ g = 3 + +:breakme // builtins like `+` do not yet yield a shape +:e +g(3) +//β”‚ ═══[RUNTIME ERROR] TypeError: g is not a function + + +let f = x => y => x + y +//β”‚ f = fun f + +f(1) +//β”‚ = fun + +:sr +f(1)(2) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fΒ²(1)β€Ήapp⁡›(2)β€Ήapp⁢› +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 3 + + diff --git a/hkmc2/shared/src/test/mlscript/newres/DeclarationLocations.mls b/hkmc2/shared/src/test/mlscript/newres/DeclarationLocations.mls new file mode 100644 index 0000000000..de1cdffb91 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/DeclarationLocations.mls @@ -0,0 +1,95 @@ +:. + +// An empty declaration still has a source span. +:e +module Empty +Empty.missing +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.6: Empty.missing +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Module 'Empty' does not contain member 'missing' +//β”‚ β•‘ l.5: module Empty +//β”‚ ╙── ^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A member annotation must not become the enclosing module's location. +:e +module Container with + declare fun text(): Str +Container.missing +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.19: Container.missing +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Module 'Container' does not contain member 'missing' +//β”‚ β•‘ l.17: module Container with +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Nested declarations retain their own spans. +:e +module Outer with + module Inner with + declare fun text(): Str +Outer.Inner.missing +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.33: Outer.Inner.missing +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Module 'Inner' does not contain member 'missing' +//β”‚ β•‘ l.31: module Inner with +//β”‚ ╙── ^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A nominal annotation continues to point at the use of the type. +:e +class Base with + declare fun text(): Str +fun hidden(value: Base) = value.missing +hidden(new Base) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.46: fun hidden(value: Base) = value.missing +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'missing' +//β”‚ β•‘ l.46: fun hidden(value: Base) = value.missing +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// The receiver inside a class uses the declaration, including its name and body. +:e +class Receiver with + declare fun text(): Str + fun inspect() = this.missing +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.60: fun inspect() = this.missing +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class definition 'Receiver' does not contain member 'missing' +//β”‚ β•‘ l.58: class Receiver with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.59: declare fun text(): Str +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.60: fun inspect() = this.missing +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// Parameterized classes likewise use the written declaration, not only its fields. +:e +class Parameterized(value: Int) with + fun inspect() = this.missing +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.75: fun inspect() = this.missing +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class definition 'Parameterized' does not contain member 'missing' +//β”‚ β•‘ l.74: class Parameterized(value: Int) with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.75: fun inspect() = this.missing +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// A class without fields has a declaration location even in pattern diagnostics. +:e +class Empty +if new Empty is Empty(_) then 1 else 0 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in constructor +//β”‚ β•‘ l.88: if new Empty is Empty(_) then 1 else 0 +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class definition 'Empty' does not take pattern arguments. +//β”‚ β•‘ l.87: class Empty +//β”‚ ╙── ^^^^^^^^^^^ +//β”‚ = 0 diff --git a/hkmc2/shared/src/test/mlscript/newres/DeclaredTypes.mls b/hkmc2/shared/src/test/mlscript/newres/DeclaredTypes.mls new file mode 100644 index 0000000000..494c00574d --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/DeclaredTypes.mls @@ -0,0 +1,725 @@ +:. + +// No calls are needed to resolve an annotated receiver's members. +class Box(val value) +fun unused(box: Box) = box.value + +// Reading an unannotated field crosses no constructor argument-flow boundary. +:expect 42 +class Box(val value) +fun read(box: Box) = box.value +read(Box(42)) +//β”‚ = 42 + +// An annotation hides subclass-only members, even at an observed call. +:ge +class Base +class Child extends Base with + val extra = 42 +fun hidden(base: Base) = base.extra +hidden(new Child) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.19: fun hidden(base: Base) = base.extra +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.19: fun hidden(base: Base) = base.extra +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// The declared interface selects the method, while dispatch uses the actual receiver. +:expect 42 +class Base with + open fun value(): Int = 0 +class Child extends Base with + override fun value(): Int = 42 +fun read(base: Base) = base.value() +read(new Child) +//β”‚ = 42 + +// A declared result is opaque even when its body constructs a known subclass. +:ge +class Base +class Child extends Base with + val extra = 42 +fun make(): Base = new Child +make().extra +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.45: make().extra +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.44: fun make(): Base = new Child +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Likewise for a getter/value annotation and for explicit ascription. +:ge +class Base +class Child extends Base with + val extra = 42 +val box: Base = new Child +box.extra +(new Child as Base).extra +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.61: (new Child as Base).extra +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.61: (new Child as Base).extra +//β”‚ ╙── ^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.60: box.extra +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.59: val box: Base = new Child +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ box = Child { extra: 42 } + +// TODO: infer the selected declaration's missing field type in the receiver's context. +:fixme +:expect 42 +class Item with + val extra = 42 +class Box(val item) +fun read(box: Box) = box.item.extra +read(Box(new Item)) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.83: fun read(box: Box) = box.item.extra +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'extra' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Constructor parameter 'item' has no type annotation. +//β”‚ β•‘ l.82: class Box(val item) +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This type annotation does not provide a type for field 'item'. +//β”‚ β•‘ l.83: fun read(box: Box) = box.item.extra +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '42', got: 'undefined' + +// TODO: infer the selected method's missing result through a nominal annotation. +:fixme +:expect 42 +class Item with + val extra = 42 +class Box with + fun item() = new Item +fun read(box: Box) = box.item().extra +read(new Box) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.105: fun read(box: Box) = box.item().extra +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'extra' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ The shape of this value is unknown. +//β”‚ β•‘ l.105: fun read(box: Box) = box.item().extra +//β”‚ ╙── ^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '42', got: 'undefined' + +// Inherited declared fields retain their type through curried constructors. +:expect 42 +class Item(val value: Int) +class Parent(val item: Item)(val ignored) +class Child(input)(ignored) extends Parent(input)(ignored) +fun read(child: Child) = child.item.value +read(Child(Item(42))(0)) +//β”‚ = 42 + +// Nested captures do not bring implementation argument marks through the annotation. +:expect 42 +class Item(val value: Int) +class Box(val item: Item) +fun enclosing(box: Box) = + fun inner() = box.item.value + inner() +enclosing(Box(Item(42))) +//β”‚ = 42 + +// Generic aliases and inherited interfaces substitute declared type arguments. +:expect 42 +class Item(val value: Int) +class Box[T](val item: T) +type Wrapped[T] = Box[T] +class Child[T](item: T) extends Box[T](item) +fun read(box: Wrapped[Item]) = box.item.value +fun inherited(box: Child[Item]) = box.item.value +read(Box[Item](Item(20))) + inherited(Child[Item](Item(22))) +//β”‚ = 42 + +// Function type annotations supply the result interface without inspecting a lambda. +:expect 42 +class Item(val value: Int) +type Factory[T] = () -> T +fun read(factory: Factory[Item]) = factory().value +read(() => Item(42)) +//β”‚ = 42 + +// Function type arity is checked even when the annotated function is unused. +:ge +fun unused(factory: () -> Int) = factory(1) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.157: fun unused(factory: () -> Int) = factory(1) +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function with a declared signature expected 0 arguments, but got 1 +//β”‚ β•‘ l.157: fun unused(factory: () -> Int) = factory(1) +//β”‚ ╙── ^^^^^^^^^ + +// A callable annotation also hides extra members of the concrete result. +:ge +class Base +class Child extends Base with + val extra = 42 +fun read(factory: () -> Base) = factory().extra +read(() => new Child) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.170: fun read(factory: () -> Base) = factory().extra +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.170: fun read(factory: () -> Base) = factory().extra +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Separate signatures annotate implementation parameters before any observed call. +:ge +class Base +class Child extends Base with + val extra = 42 +fun hidden: (Base) -> Int +fun hidden(base) = base.extra +hidden(new Child) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.186: fun hidden(base) = base.extra +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.185: fun hidden: (Base) -> Int +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Separate signatures supply an opaque result as well. +:ge +class Base +class Child extends Base with + val extra = 42 +fun make: () -> Base +fun make() = new Child +make().extra +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.203: make().extra +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.201: fun make: () -> Base +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Alias substitution keeps nested arguments in the scope where they were written. +:expect 42 +class Item(val value: Int) +type Identity[T] = T +type Twice[T] = Identity[Identity[T]] +fun read(item: Twice[Item]) = item.value +read(Item(42)) +//β”‚ = 42 + +// Recursive nominal signatures unfold only when a member is selected. +class Node(val next: Link) +type Link = Node +fun unused(node: Link) = node.next.next + +// An unproductive alias must not loop, even when each step grows its arguments. +:ge +class Box[T] +type Loop[T] = Loop[Box[T]] +fun unused(value: Loop[Int]) = value.field +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.229: type Loop[T] = Loop[Box[T]] +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Loop'. +//β”‚ β•‘ l.229: type Loop[T] = Loop[Box[T]] +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.229: type Loop[T] = Loop[Box[T]] +//β”‚ ╙── ^^^^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'field' has no resolved target +//β”‚ β•‘ l.230: fun unused(value: Loop[Int]) = value.field +//β”‚ ╙── ^^^^^^^^^^^ + +// A concrete instantiation cannot authorize calls through an opaque type parameter. +:e +private fun invoke[T](value: T) = value() +invoke[() -> Int](() => 42) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.246: private fun invoke[T](value: T) = value() +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of unknown shape cannot be called like a function. +//β”‚ β•‘ l.246: private fun invoke[T](value: T) = value() +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'T' does not specify a member interface. +//β”‚ β•‘ l.246: private fun invoke[T](value: T) = value() +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// TODO: class qualification preserves inference for a selected constructor field. +:fixme +:expect 42 +class Item with + val extra = 42 +class Box(val item) +fun read(box: Box) = box.Box#item.extra +read(Box(new Item)) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.265: fun read(box: Box) = box.Box#item.extra +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'extra' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Constructor parameter 'item' has no type annotation. +//β”‚ β•‘ l.264: class Box(val item) +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This type annotation does not provide a type for field 'item'. +//β”‚ β•‘ l.265: fun read(box: Box) = box.Box#item.extra +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '42', got: 'undefined' + +// A signature alias supplies the interface to both the body and callers. +:expect 42 +class Item(val value: Int) +type Reader = (Item) -> Int +fun read: Reader +fun read(item) = item.value +read(Item(42)) +//β”‚ = 42 + +// Explicit and inferred type arguments share parameter flow, with distinct call marks. +:expect [42, 7, 42, 7] +class Item(val value: Int) +class Other(val different: Int) +fun identity[T](x: T): T = x +[identity[Item](Item(42)).value, identity[Other](Other(7)).different, + identity(Item(42)).value, identity(Other(7)).different] +//β”‚ = [42, 7, 42, 7] + +// Explicit type arguments prevent the value argument from refining the interface. +:ge +class Base +class Child extends Base with + val extra = 42 +fun identity[T](x: T): T = x +identity[Base](new Child).extra +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.304: identity[Base](new Child).extra +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.304: identity[Base](new Child).extra +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Type-parameter uses in nested signatures retain their capture path. +:expect 42 +class Item(val value: Int) +fun identity[T](x: T): T = + fun nested(): T = x + nested() +identity(Item(42)).value +//β”‚ = 42 + +// Infer through nested nominal type arguments, preserving each constructor's marks. +:expect 42 +class Box[X](val value: X) +class Foo[X](val f: Box[X]) +fun test[A](x: Foo[A]) = x.f +test(new Foo(new Box(Predef))).value.id(42) +//β”‚ = 42 + +// Distinct nested instantiations must not leak their member sets into each other. +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +class Box[X](val value: X) +class Foo[X](val f: Box[X]) +fun test[A](x: Foo[A]) = x.f +[test(new Foo(new Box(First(1)))).value.first, + test(new Foo(new Box(Second(2)))).value.second] +//β”‚ = [1, 2] + +// An explicit constructor type argument is also an opaque boundary. +:ge +class Base +class Child extends Base with + val extra = 42 +class Box[T](val value: T) +(new Box[Base](new Child)).value.extra +Box[Base](new Child).value.extra +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.347: (new Box[Base](new Child)).value.extra +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.347: (new Box[Base](new Child)).value.extra +//β”‚ ╙── ^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.348: Box[Base](new Child).value.extra +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.348: Box[Base](new Child).value.extra +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A generic method's result carries the receiver's type argument to the caller. +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +class Box[X](val value: X) with + fun get(): X = value +fun identity[T](x: T): T = x +[identity[Box[First]](Box[First](First(1))).get().first, + identity[Box[Second]](Box[Second](Second(2))).get().second] +//β”‚ = [1, 2] + +// Function constraints propagate tuple shapes through a type argument. +:expect [2, 4] +class Foo[X](val f: X -> X) +fun test[A](x: Foo[[A, Int]]) = x.f +test(new Foo(arg => [arg.0 + 1, arg.1 + 2]))([1, 2]) +//β”‚ = [2, 4] + +// Dependent method signatures also keep distinct inferred instantiations separate. +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +class Box[X](val value: X) with + fun get(): X = value +class Foo[X](val f: Box[X]) +fun test[A](x: Foo[A]) = x.f +[test(new Foo(new Box(First(1)))).get().first, + test(new Foo(new Box(Second(2)))).get().second] +//β”‚ = [1, 2] + +// TODO: a class-qualified selection also infers the selected value member's type. +:fixme +:expect 42 +class Item with + val extra = 42 +class Box with + val item = new Item +(new Box as Box).Box#item.extra +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.400: (new Box as Box).Box#item.extra +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'extra' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ The shape of this value is unknown. +//β”‚ β•‘ l.400: (new Box as Box).Box#item.extra +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '42', got: 'undefined' + +// TODO: infer an unannotated field selected through a module parameter. +:fixme +:expect 42 +class Secret with + val extra = 42 +module Types with + class ItemClass + val Item = new Secret +fun hidden(module types: Types) = types.Item.extra +hidden(Types) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.419: fun hidden(module types: Types) = types.Item.extra +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'extra' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ The shape of this value is unknown. +//β”‚ β•‘ l.419: fun hidden(module types: Types) = types.Item.extra +//β”‚ ╙── ^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '42', got: 'undefined' + +// Callback inputs and outputs share inferred type arguments at the application. +:expect 43 +class Item(val value: Int) +fun apply[A](f: A -> A, x: A): A = f(x) +apply(arg => Item(arg.value + 1), Item(42)).value +//β”‚ = 43 + +// Expected callback parameters retain explicit abstraction boundaries. +:ge +class Base +class Child extends Base with + val extra = 1 +fun apply[A](f: A -> A, x: A): A = f(x) +apply[Base](arg => (arg.extra; arg), new Child) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.444: apply[Base](arg => (arg.extra; arg), new Child) +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.444: apply[Base](arg => (arg.extra; arg), new Child) +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Independent callback instantiations must not mix their tuple element shapes. +:expect [2, 4] +class First(val first: Int) +class Second(val second: Int) +class Foo[X](val f: X -> X) +fun test[A](x: Foo[[A, Int]]) = x.f +[test(new Foo(arg => [First(arg.0.first + 1), arg.1]))([First(1), 0]).0.first, + test(new Foo(arg => [Second(arg.0.second + 2), arg.1]))([Second(2), 0]).0.second] +//β”‚ = [2, 4] + +// Declared tuple fields expose their types, not their initializer's subclasses. +:ge +class Base +class Child extends Base with + val extra = 1 +fun read(xs: [Base, Int]) = xs.0.extra +read([new Child, 0]) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.468: fun read(xs: [Base, Int]) = xs.0.extra +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.468: fun read(xs: [Base, Int]) = xs.0.extra +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Tuple projections retain contexts through spreads and function returns. +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +fun wrap(x) = [...[x]] +[wrap(First(1)).0.first, wrap(Second(2)).0.second] +//β”‚ = [1, 2] + +// Mutation invalidates initializer shapes, including through a tuple spread. +:ge +class First(val first: Int) +class Second(val second: Int) +let xs = mut [First(1)] +set xs.0 = Second(2) +[...xs].0.first +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.492: [...xs].0.first +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Second' does not contain member 'first' +//β”‚ β•‘ l.491: set xs.0 = Second(2) +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ xs = [Second(2)] + +// Out-of-bounds static projections are rejected. +:ge +[1, 2].2 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.504: [1, 2].2 +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Tuple literal does not contain member '2' +//β”‚ β•‘ l.504: [1, 2].2 +//β”‚ ╙── ^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A callback supplied through an annotation exposes its declared result shape. +:expect 42 +class Item(val value: Int) +fun run[A](f: Int -> A): A = f(0) +fun callback: Int -> Item = ignored => Item(42) +run(callback).value +//β”‚ = 42 + +// Tuple elements can be used directly as callees. +:expect 42 +[x => x + 1].0(41) +//β”‚ = 42 + +// Indexed assignments must retain indexed lowering. +:expect 42 +let xs = mut [1, 2] +set xs.1 = 42 +xs.1 +//β”‚ = 42 +//β”‚ xs = [1, 42] + +// TODO: preserve inferred inherited field types through aliases and value forwarding. +:fixme +:expect 42 +class Item with + val extra = 42 +class Parent(val item) +class Child(input) extends Parent(input) +type Annotated = Child +private fun identity(value) = value +fun read(box: Annotated) = identity(box.item).extra +read(Child(new Item)) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.543: fun read(box: Annotated) = identity(box.item).extra +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'extra' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Constructor parameter 'item' has no type annotation. +//β”‚ β•‘ l.539: class Parent(val item) +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This type annotation does not provide a type for field 'item'. +//β”‚ β•‘ l.543: fun read(box: Annotated) = identity(box.item).extra +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '42', got: 'undefined' + +// TODO: infer a selected callback field's callable interface from its receiver. +:fixme +:expect 42 +class Box(val callback) +fun invoke(box: Box) = box.callback() +invoke(Box(() => 42)) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.562: fun invoke(box: Box) = box.callback() +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of unknown shape cannot be called like a function. +//β”‚ β•‘ l.562: fun invoke(box: Box) = box.callback() +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Constructor parameter 'callback' has no type annotation. +//β”‚ β•‘ l.561: class Box(val callback) +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This type annotation does not provide a type for field 'callback'. +//β”‚ β•‘ l.562: fun invoke(box: Box) = box.callback() +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '42', got: 'undefined' + +// Abstract tuple elements retain the use of the type parameter in the receiver annotation. +:ge +class Box[X](val value: [X, X]) +fun foo[A](x: Box[A]) = x.value.0.oops +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.582: fun foo[A](x: Box[A]) = x.value.0.oops +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'oops' of a value with unknown shape. +//β”‚ β•‘ l.582: fun foo[A](x: Box[A]) = x.value.0.oops +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' does not specify a member interface. +//β”‚ β•‘ l.582: fun foo[A](x: Box[A]) = x.value.0.oops +//β”‚ ╙── ^ + +// Concrete callers cannot hide the annotation responsible for an abstract tuple element. +:ge +class Item with + val oops = 42 +class Box[X](val value: [X, X]) +private fun foo[A](x: Box[A]) = x.value.0.oops +foo[Item](Box[Item]([new Item, new Item])) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.598: private fun foo[A](x: Box[A]) = x.value.0.oops +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'oops' of a value with unknown shape. +//β”‚ β•‘ l.598: private fun foo[A](x: Box[A]) = x.value.0.oops +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' does not specify a member interface. +//β”‚ β•‘ l.598: private fun foo[A](x: Box[A]) = x.value.0.oops +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Alias substitution and a function result still lead back to the written type argument. +:ge +class Box[X](val value: [X, X]) +type Wrapped[T] = Box[T] +fun foo[A](factory: () -> Wrapped[A]) = factory().value.1.oops +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.615: fun foo[A](factory: () -> Wrapped[A]) = factory().value.1.oops +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'oops' of a value with unknown shape. +//β”‚ β•‘ l.615: fun foo[A](factory: () -> Wrapped[A]) = factory().value.1.oops +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' does not specify a member interface. +//β”‚ β•‘ l.615: fun foo[A](factory: () -> Wrapped[A]) = factory().value.1.oops +//β”‚ ╙── ^ + +// Tuple annotations do not need a nominal receiver to produce an abstract element. +:ge +fun foo[A](x: [A, A]) = x.0.oops +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.628: fun foo[A](x: [A, A]) = x.0.oops +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'oops' of a value with unknown shape. +//β”‚ β•‘ l.628: fun foo[A](x: [A, A]) = x.0.oops +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' does not specify a member interface. +//β”‚ β•‘ l.628: fun foo[A](x: [A, A]) = x.0.oops +//β”‚ ╙── ^ + +// An unannotated field can be reached through a function result annotation and an alias. +:ge +class Box(val value) +type Wrapped = Box +fun foo(factory: () -> Wrapped) = factory().value.oops +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.643: fun foo(factory: () -> Wrapped) = factory().value.oops +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'oops' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Constructor parameter 'value' has no type annotation. +//β”‚ β•‘ l.641: class Box(val value) +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This type annotation does not provide a type for field 'value'. +//β”‚ β•‘ l.643: fun foo(factory: () -> Wrapped) = factory().value.oops +//β”‚ ╙── ^^^^^^^ + +// TODO: ascription preserves receiver flow for the selected field's missing type. +:fixme +:expect 42 +class Item with + val oops = 42 +class Box(val value) +(Box(new Item) as Box).value.oops +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.661: (Box(new Item) as Box).value.oops +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'oops' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Constructor parameter 'value' has no type annotation. +//β”‚ β•‘ l.660: class Box(val value) +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This type annotation does not provide a type for field 'value'. +//β”‚ β•‘ l.661: (Box(new Item) as Box).value.oops +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '42', got: 'undefined' + +// An abstract function result remains unknown after a nested tuple projection. +:ge +fun foo[A](factory: () -> [A, A]) = factory().0.oops +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.677: fun foo[A](factory: () -> [A, A]) = factory().0.oops +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'oops' of a value with unknown shape. +//β”‚ β•‘ l.677: fun foo[A](factory: () -> [A, A]) = factory().0.oops +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' does not specify a member interface. +//β”‚ β•‘ l.677: fun foo[A](factory: () -> [A, A]) = factory().0.oops +//β”‚ ╙── ^ + +// A concrete annotation supplies the nested member interface without losing tuple precision. +:expect 42 +class Item with + val oops = 42 +class Box[X](val value: [X, X]) +fun foo(x: Box[Item]) = x.value.0.oops +foo(Box[Item]([new Item, new Item])) +//β”‚ = 42 + +// Without supporting flow, an omitted argument cannot justify an arbitrary member. +:ge +class Pair[A, B](val value: [A, B]) +fun omittedSecond(x: Pair[Int]) = x.value.1.oops +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.700: fun omittedSecond(x: Pair[Int]) = x.value.1.oops +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'oops' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.700: fun omittedSecond(x: Pair[Int]) = x.value.1.oops +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'omittedSecond' is exposed by this compilation unit. +//β”‚ β•‘ l.700: fun omittedSecond(x: Pair[Int]) = x.value.1.oops +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// An unfilled omitted alias argument likewise cannot justify an arbitrary member. +:ge +type HalfPair[T] = Pair[Int, T] +fun omittedAlias(x: HalfPair) = x.value.1.oops +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.715: fun omittedAlias(x: HalfPair) = x.value.1.oops +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'oops' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.715: fun omittedAlias(x: HalfPair) = x.value.1.oops +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'omittedAlias' is exposed by this compilation unit. +//β”‚ β•‘ l.715: fun omittedAlias(x: HalfPair) = x.value.1.oops +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/newres/DeferredCalls.mls b/hkmc2/shared/src/test/mlscript/newres/DeferredCalls.mls new file mode 100644 index 0000000000..50552d70f7 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/DeferredCalls.mls @@ -0,0 +1,39 @@ +:. + +// The callee's definition becomes available after the call is elaborated. +:expect Box(1) +class Box(val value) +fun forward(x) = later(x) +fun later(x) = Box(x) +forward(1) +//β”‚ = Box(1) + +// A returned lambda must publish its result shape to the next application. +:expect 2 +fun makeBox() = x => Box(x) +makeBox()(2).value +//β”‚ = 2 + +// Curried constructor applications retain both parameter lists and field shapes. +:expect [3, 4, 5, 6] +class Pair(val left)(val right) +fun buildPair(x)(y) = Pair(x)(y) +let partial = buildPair(3) +let built = partial(4) +let direct = new Pair(5)(6) +[built.left, built.right, direct.left, direct.right] +//β”‚ = [3, 4, 5, 6] +//β”‚ built = Pair(3) +//β”‚ direct = Pair(5) +//β”‚ partial = fun + +// Validation waits for a forward callee, but still diagnoses a proven mismatch. +:e +fun bad(x) = laterArity(x) +fun laterArity(x, y) = x +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.32: fun bad(x) = laterArity(x) +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'laterArity' expected 2 arguments, but got 1 +//β”‚ β•‘ l.33: fun laterArity(x, y) = x +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/newres/Dynamic.mls b/hkmc2/shared/src/test/mlscript/newres/Dynamic.mls new file mode 100644 index 0000000000..c20e16729d --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/Dynamic.mls @@ -0,0 +1,161 @@ +:. + +// JavaScript imports allow ordinary selection and calls, even in strict mode. +:expect () +import "../../js/Test.mjs" as js +js.greet() +//β”‚ > Hello from Test.mjs! + +:expect "/a/b" +import "node:path" as path +path.posix.join("/a", "b") +//β”‚ = "/a/b" + +// An ascription forgets the initializer's shape; successive selections and calls +// remain dynamic rather than acquiring a fabricated member definition. +:expect 3 +let d = ({nested: {get(): 3}} as dyn) +d.nested.get() +//β”‚ = 3 +//β”‚ d = {nested: {get: fun get}} + +// Notice: no resolution error for dynamic `d` +:re +d.nasted.get() +//β”‚ ═══[RUNTIME ERROR] Error: Access to required field 'nasted' yielded 'undefined' + +:expect 4 +fun readDynamic(x: dyn) = x.value +readDynamic({value: 4}) +//β”‚ = 4 + +:expect 5 +type Dynamic = dyn +fun readAlias(x: Dynamic) = x.value +readAlias({value: 5}) +//β”‚ = 5 + +:expect 6 +let writable = mut {value: 0} as dyn +set writable.value = 6 +writable.value +//β”‚ = 6 +//β”‚ writable = {value: 6} + +:expect 7 +fun identity(x) = x +identity({value: 7} as dyn).value +//β”‚ = 7 + +// Dynamic spreads retain dynamic element values through parameter propagation. +:expect 8 +fun readSpread(x: dyn) = x.value +readSpread(...([{value: 8}] as dyn)) +//β”‚ = 8 + +:expect 9 +let copied = {...({value: 9} as dyn)} +copied.value +//β”‚ = 9 +//β”‚ copied = {value: 9} + +:expect 10 +fun fromOpen() = + let opened = ({value: 10} as dyn) + open opened + value +fromOpen() +//β”‚ = 10 + +// Explicit dynamic selections are also JS boundaries. +:expect 11 +let explicit = {nested: {value: 11}} +explicit."nested".value +//β”‚ = 11 +//β”‚ explicit = {nested: {value: 11}} + +// Known invalid operations still fail; dynamicity is not inferred from failure. +:e +{value: 1}.missing +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.79: {value: 1}.missing +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Record literal does not contain member 'missing' +//β”‚ β•‘ l.79: {value: 1}.missing +//β”‚ ╙── ^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:e +1() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.89: 1() +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be called like a function. +//β”‚ β•‘ l.89: 1() +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:e +dyn +//β”‚ ╔══[COMPILATION ERROR] Resolution error in dynamic type +//β”‚ β•‘ l.99: dyn +//β”‚ β•‘ ^^^ +//β”‚ ╙── A type cannot be used as a runtime value. +//β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (dynamic type) +//β”‚ β•‘ l.99: dyn +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Dynamic result ascriptions apply after evaluating the call. +:expect 12 +fun produce() = {value: 12} +(produce() as dyn).value +//β”‚ = 12 + +:expect 13 +globalThis.JSON.parse("{\"value\":13}").value +//β”‚ = 13 + +// A dyn parameter supplies a shape without needing a call site. +fun notCalled(x: dyn) = x.anything(1).result + +// A dynamic candidate must not disguise another candidate's known missing member. +:e +let mixed = if hide(true) then ({value: 1} as dyn) else {other: 2} +mixed.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.125: mixed.value +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Record literal does not contain member 'value' +//β”‚ β•‘ l.124: let mixed = if hide(true) then ({value: 1} as dyn) else {other: 2} +//β”‚ ╙── ^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ mixed = {value: 1} + +// Calling a selected JS method must preserve its receiver (`Map.get` uses this). +:expect 14 +let map = new! globalThis.Map() +map.set("key", 14) +map.get("key") +//β”‚ = 14 +//β”‚ map = Map(1) {"key" => 14} + +:expect 15 +fun skipFirst(first, ...tail) = readSpread(...tail) +skipFirst(...([0, {value: 15}] as dyn)) +//β”‚ = 15 + +// A known and a dynamic alternative for one open still name one runtime receiver. +:expect 16 +fun mixedOpen() = + let source = if hide(true) then ({value: 16} as dyn) else {value: 17} + open source + value +mixedOpen() +//β”‚ = 16 + +// Dynamicity permits compilation, while the runtime still checks the operation. +:re +(1 as dyn)() +//β”‚ ═══[RUNTIME ERROR] TypeError: 1 is not a function + diff --git a/hkmc2/shared/src/test/mlscript/newres/DynamicInstantiation.mls b/hkmc2/shared/src/test/mlscript/newres/DynamicInstantiation.mls new file mode 100644 index 0000000000..9c5eb33627 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/DynamicInstantiation.mls @@ -0,0 +1,11 @@ +:. + +class First +class Second + +fun construct(c) = new! c +fun constructMutable(c) = new! mut c + +:expect [First, Second, First, Second] +[construct(First), construct(Second), constructMutable(First), constructMutable(Second)] +//β”‚ = [First, Second, First, Second] diff --git a/hkmc2/shared/src/test/mlscript/newres/Erasure.mls b/hkmc2/shared/src/test/mlscript/newres/Erasure.mls new file mode 100644 index 0000000000..2eb594d9c9 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/Erasure.mls @@ -0,0 +1,54 @@ +:. + + +// The call is lowered before its callee and the alias declarations. +:expect 42 +fun caller(): Number = later(42) +fun later(value: Number): Number = value +type Number = NextNumber +type NextNumber = Int +caller() +//β”‚ = 42 + + +// A forward signature supplies both constructor parameters and generated fields. +:expect 42 +class Box(val value: Number) with + fun get(): Number = value +type Number = Int +Box(42).get() +//β”‚ = 42 + + +// Lambda parameter annotations must be prepared even inside a record field. +:expect 42 +type Number = Int +let record = {apply: (value: Number) => value} +record.apply(42) +//β”‚ = 42 +//β”‚ record = {apply: fun apply} + + +// Both wildcard sources arrive after the annotation; the unused signature must still be checked. +:ge +open Left +open Right +fun unused(value: Types.Number) = value +module Left with + module Types with + type Number = Int +module Right with + module Types with + type Number = Str +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.36: fun unused(value: Types.Number) = value +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This type is ambiguous, as it has multiple resolved targets +//β”‚ β•‘ l.36: fun unused(value: Types.Number) = value +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ candidate: type alias +//β”‚ β•‘ l.39: type Number = Int +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ candidate: type alias +//β”‚ β•‘ l.42: type Number = Str +//β”‚ ╙── ^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/newres/ForeignConstructors.mls b/hkmc2/shared/src/test/mlscript/newres/ForeignConstructors.mls new file mode 100644 index 0000000000..5fe0405e3d --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ForeignConstructors.mls @@ -0,0 +1,29 @@ +:. + +// Explicit foreign function overloads and class constructors share the native binding. +declare class String with + constructor(value: Str) + val length: Int +declare fun String(value: Str): Str + +:sjs +:expect "hello" +String("hello") +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ return runtime.safeCall(globalThis.String("hello")) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = "hello" + +:sjs +:expect 5 +let boxed = new String("hello") +boxed.length +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let boxed; boxed = globalThis.Object.freeze(new globalThis.String("hello")); return boxed.length +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 5 +//β”‚ boxed = String { "0": "h", "1": "e", "2": "l", "3": "l", "4": "o", length: 5 } + +:expect true +boxed is String +//β”‚ = true diff --git a/hkmc2/shared/src/test/mlscript/newres/FunctionIntersectionConstraints.mls b/hkmc2/shared/src/test/mlscript/newres/FunctionIntersectionConstraints.mls new file mode 100644 index 0000000000..04cdf0bf67 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/FunctionIntersectionConstraints.mls @@ -0,0 +1,40 @@ +:. +#lang(eagerResolution: false) + +// Both arrows constrain A -> B: A <: Int, Int <: B, A <: Str, Str <: B. +// Each result member witnesses a different arrow; choosing just one loses it. +:expect ["12.0", "ABC"] +fun adapt[A, B](f: A -> B): A -> B = f +fun results(f: (Int -> Int) & (Str -> Str)) = + let g = adapt(f) + [g(12).toFixed(1), g("abc").toUpperCase()] +results(x => x) +//β”‚ = ["12.0", "ABC"] + +// The same two constraints must propagate contravariantly into both domains. +// The only declared source of these fields is the argument passed to invoke. +// Leave domains uncalled so argument values cannot supply the missing fields. +fun invoke[A, B](f: A -> B, value: A): B = f(value) +private fun domains[X, Y](f: (X -> Int) & (Y -> Str), x: X, y: Y) = + invoke(f, {first: 1, second: "two"}) + [x.first, y.second] + +// Eager checking sees both concrete result candidates, including the one that +// lacks each member. The approximation does not turn the result into dyn. +:e +#lang(eagerResolution: true) +fun allResults(f: (Int -> Int) & (Str -> Str)) = + let g = adapt(f) + [g(0).toUpperCase(), g("").toFixed(0)] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.28: [g(0).toUpperCase(), g("").toFixed(0)] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Int' does not contain member 'toUpperCase' +//β”‚ β•‘ l.26: fun allResults(f: (Int -> Int) & (Str -> Str)) = +//β”‚ ╙── ^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.28: [g(0).toUpperCase(), g("").toFixed(0)] +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Str' does not contain member 'toFixed' +//β”‚ β•‘ l.26: fun allResults(f: (Int -> Int) & (Str -> Str)) = +//β”‚ ╙── ^^^ diff --git a/hkmc2/shared/src/test/mlscript/newres/Functions.mls b/hkmc2/shared/src/test/mlscript/newres/Functions.mls new file mode 100644 index 0000000000..83d75245a8 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/Functions.mls @@ -0,0 +1,254 @@ +:. + + +private fun foo(x) = x + 1 + +:e +foo() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.7: foo() +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'foo' expected 1 argument, but got 0 +//β”‚ β•‘ l.4: private fun foo(x) = x + 1 +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] Error: Function 'foo' expected 1 argument but got 0 + +:e +foo(1, 2) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.17: foo(1, 2) +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'foo' expected 1 argument, but got 2 +//β”‚ β•‘ l.4: private fun foo(x) = x + 1 +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] Error: Function 'foo' expected 1 argument but got 2 + + +:sr +id +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Predef⁰.id⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = fun id + + +:e +fun callid0(p) = p.id(1) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.36: fun callid0(p) = p.id(1) +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'id' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'p' admits values of unknown shape. +//β”‚ β•‘ l.36: fun callid0(p) = p.id(1) +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'callid0' is exposed by this compilation unit. +//β”‚ β•‘ l.36: fun callid0(p) = p.id(1) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ + + +:de +:sr +private fun callid1(p) = p.id(1) +callid1(Predef) +//β”‚ Elab block List(Annotated(Keywrd(keyword 'private'),TermDef(Fun,App(Ident(callid1),Tup(List(Ident(p)))),Some(Ap[...] LocalScope(block:7) +//β”‚ | Processing term definition App(Ident(callid1),Tup(List(Ident(p)))) +//β”‚ | Elaborating ParamTree: ParamTree(β€Ήβ€Ί,Ident(p),None,None,Set()) +//β”‚ | Elab term App(Sel(Ident(p),Ident(id)),Tup(List(IntLit(1)))) +//β”‚ | | Elab subterm App(Sel(Ident(p),Ident(id)),Tup(List(IntLit(1)))) +//β”‚ | | | Elab subterm Sel(Ident(p),Ident(id)) +//β”‚ | | | | Elab subterm Ident(p) +//β”‚ | | | | | listen: trm = p⁰ +//β”‚ | | | | ~> p⁰ +//β”‚ | | | | newSel? sel = p⁰.id +//β”‚ | | | | listen: trm = p⁰ +//β”‚ | | | | listenTerm: trm = p⁰.id +//β”‚ | | | | listen: trm = p⁰.id +//β”‚ | | | ~> p⁰.id +//β”‚ | | | Elab subterm Tup(List(IntLit(1))) +//β”‚ | | | | Elab term IntLit(1) +//β”‚ | | | | | Elab subterm IntLit(1) +//β”‚ | | | | | ~> 1 +//β”‚ | | | | ~> 1 +//β”‚ | | | ~> [1] +//β”‚ | | | listenTerm: trm = p⁰.id +//β”‚ | | | listen: trm = p⁰.id +//β”‚ | | ~> p⁰.id(1) +//β”‚ | ~> p⁰.id(1) +//β”‚ | Elab term App(Ident(callid1),Tup(List(Ident(Predef)))) +//β”‚ | | Elab subterm App(Ident(callid1),Tup(List(Ident(Predef)))) +//β”‚ | | | Elab subterm Ident(callid1) +//β”‚ | | | | listenTerm: trm = member:callid1⁰ +//β”‚ | | | | listen: trm = member:callid1⁰ +//β”‚ | | | | listenBMS: bms = function +//β”‚ | | | ~> member:callid1⁰ +//β”‚ | | | Elab subterm Tup(List(Ident(Predef))) +//β”‚ | | | | Elab term Ident(Predef) +//β”‚ | | | | | Elab subterm Ident(Predef) +//β”‚ | | | | | | listenTerm: trm = member:Predef⁰ +//β”‚ | | | | | | listen: trm = member:Predef⁰ +//β”‚ | | | | | | listenBMS: bms = module +//β”‚ | | | | | | listenedBMS: bms = module +//β”‚ | | | | | | fromBMS: bms = member:Predef⁰, sh = module:PredefΒΉ, flow = PredefΒ², markss = List() +//β”‚ | | | | | | PUSH module:PredefΒΉ +//β”‚ | | | | | ~> member:Predef⁰ +//β”‚ | | | | ~> member:Predef⁰ +//β”‚ | | | ~> [member:Predef⁰] +//β”‚ | | | listenTerm: trm = member:callid1⁰ +//β”‚ | | | listen: trm = member:callid1⁰ +//β”‚ | | | listenBMS: bms = function +//β”‚ | | ~> member:callid1⁰(member:Predef⁰) +//β”‚ | ~> member:callid1⁰(member:Predef⁰) +//β”‚ | listenedBMS: bms = function +//β”‚ | fromBMS: bms = member:callid1⁰, sh = member:callid1⁰, flow = callid1ΒΉ, markss = List() +//β”‚ | .exit MarkedShape (member:callid1⁰, term:callid1Β², callid1ΒΉ) +//β”‚ | = MarkedShape(member:callid1⁰, β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | PUSH MarkedShape(member:callid1⁰, β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | listenedBMS: bms = function +//β”‚ | fromBMS: bms = member:callid1⁰, sh = member:callid1⁰, flow = callid1ΒΉ, markss = List() +//β”‚ | .exit MarkedShape (member:callid1⁰, term:callid1Β², callid1ΒΉ) +//β”‚ | = MarkedShape(member:callid1⁰, β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | PUSH MarkedShape(member:callid1⁰, β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | appShape: lhs = MarkedShape(member:callid1⁰, β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ), args = [member:Predef⁰], res = member:callid1⁰(member:Predef⁰) +//β”‚ | listenTerm: trm = [member:Predef⁰] +//β”‚ | listen: trm = [member:Predef⁰] +//β”‚ | listenTerm: trm = member:Predef⁰ +//β”‚ | listen: trm = member:Predef⁰ +//β”‚ | listenBMS: bms = module +//β”‚ | listenedBMS: bms = module +//β”‚ | fromBMS: bms = member:Predef⁰, sh = module:PredefΒΉ, flow = PredefΒ², markss = List() +//β”‚ | PUSH module:PredefΒΉ +//β”‚ | .exit module:PredefΒΉ (Ο΅) +//β”‚ | = module:PredefΒΉ +//β”‚ | .enter module:PredefΒΉ (β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | | .enter MarkedShape(module:PredefΒΉ, β†˜βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) (Ο΅) +//β”‚ | | = MarkedShape(module:PredefΒΉ, β†˜βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | = MarkedShape(module:PredefΒΉ, β†˜βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | newSel: sel = p⁰.id, shape = MarkedShape(module:PredefΒΉ, β†˜βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | newSel member: bms = member:id⁰, mss = List(β†˜βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | listenBMS: bms = function +//β”‚ | listenedBMS: bms = function +//β”‚ | fromBMS: bms = member:id⁰, sh = member:id⁰, flow = β‹…id⁰, markss = List(β†˜βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | .exit MarkedShape (member:id⁰, term:PredefΒΉ/idΒΉ, β‹…id⁰) +//β”‚ | = MarkedShape(member:id⁰, β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ) +//β”‚ | .exit MarkedShape(member:id⁰, β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ) (β†˜βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | | .exit MarkedShape(member:id⁰, β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ) (Ο΅) +//β”‚ | | = MarkedShape(member:id⁰, β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ) +//β”‚ | = MarkedShape(member:id⁰, β†˜βŸ¨term:callid1²⟩%⟨callid1ΒΉβŸ©β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ) +//β”‚ | PUSH MarkedShape(member:id⁰, β†˜βŸ¨term:callid1²⟩%⟨callid1ΒΉβŸ©β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ) +//β”‚ | listenBMS: bms = function +//β”‚ | listenedBMS: bms = function +//β”‚ | fromBMS: bms = member:id⁰, sh = member:id⁰, flow = β‹…id⁰, markss = List(β†˜βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | .exit MarkedShape (member:id⁰, term:PredefΒΉ/idΒΉ, β‹…id⁰) +//β”‚ | = MarkedShape(member:id⁰, β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ) +//β”‚ | .exit MarkedShape(member:id⁰, β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ) (β†˜βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | | .exit MarkedShape(member:id⁰, β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ) (Ο΅) +//β”‚ | | = MarkedShape(member:id⁰, β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ) +//β”‚ | = MarkedShape(member:id⁰, β†˜βŸ¨term:callid1²⟩%⟨callid1ΒΉβŸ©β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ) +//β”‚ | PUSH MarkedShape(member:id⁰, β†˜βŸ¨term:callid1²⟩%⟨callid1ΒΉβŸ©β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ) +//β”‚ | appShape: lhs = MarkedShape(member:id⁰, β†˜βŸ¨term:callid1²⟩%⟨callid1ΒΉβŸ©β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ), args = [1], res = p⁰.id(1) +//β”‚ | listenTerm: trm = [1] +//β”‚ | listen: trm = [1] +//β”‚ | listenTerm: trm = 1 +//β”‚ | listen: trm = 1 +//β”‚ | listen: trm = member:Num⁰ +//β”‚ | .exit InstanceShape(member:Num⁰) (β†˜βŸ¨class:Int⁰⟩ϡ) +//β”‚ | | .exit InstanceShape(member:Num⁰) (Ο΅) +//β”‚ | | = InstanceShape(member:Num⁰) +//β”‚ | = MarkedShape(InstanceShape(member:Num⁰), β†˜βŸ¨class:Int⁰⟩ϡ) +//β”‚ | .enter InstanceShape(missing) (β†˜βŸ¨class:Int⁰⟩ϡ) +//β”‚ | | .exit MarkedShape (InstanceShape(missing), class:Int⁰, ) +//β”‚ | | = MarkedShape(InstanceShape(missing), β†—βŸ¨class:Int⁰⟩ϡ) +//β”‚ | | .enter MarkedShape(InstanceShape(missing), β†—βŸ¨class:Int⁰⟩ϡ) (Ο΅) +//β”‚ | | = MarkedShape(InstanceShape(missing), β†—βŸ¨class:Int⁰⟩ϡ) +//β”‚ | = MarkedShape(InstanceShape(missing), β†—βŸ¨class:Int⁰⟩ϡ) +//β”‚ | .exit NominalInstanceView(class:NumΒΉ) (β†˜βŸ¨class:Int⁰⟩ϡ) +//β”‚ | | .exit NominalInstanceView(class:NumΒΉ) (Ο΅) +//β”‚ | | = NominalInstanceView(class:NumΒΉ) +//β”‚ | = MarkedShape(NominalInstanceView(class:NumΒΉ), β†˜βŸ¨class:Int⁰⟩ϡ) +//β”‚ | introShape: intro = Lit(IntLit(1)) +//β”‚ | .exit IntroShape(1) (Ο΅) +//β”‚ | = IntroShape(1) +//β”‚ | .enter IntroShape(1) (β†˜βŸ¨term:callid1²⟩%⟨callid1ΒΉβŸ©β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ) +//β”‚ | | .exit MarkedShape (IntroShape(1), term:callid1Β², callid1ΒΉ) +//β”‚ | | = MarkedShape(IntroShape(1), β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | | .enter MarkedShape(IntroShape(1), β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) (β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ) +//β”‚ | | | .enter MarkedShape(IntroShape(1), β†˜βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…idβ°βŸ©β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) (Ο΅) +//β”‚ | | | = MarkedShape(IntroShape(1), β†˜βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…idβ°βŸ©β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | | = MarkedShape(IntroShape(1), β†˜βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…idβ°βŸ©β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | = MarkedShape(IntroShape(1), β†˜βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…idβ°βŸ©β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | appShape isSaturated? true; head? (DefnShape(term definition 'id'),List(EntryMark(term:callid1,Some(callid1),ExitMark(term:Predef/id,Some(β‹…id),NoMarks)))) +//β”‚ | lhs false List(((x⁰),List(β†˜βŸ¨term:callid1²⟩%⟨callid1ΒΉβŸ©β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ))) +//β”‚ | appShape: td.body = Some(SimpleRef(x)) +//β”‚ | listenTerm: trm = x⁰ +//β”‚ | listen: trm = x⁰ +//β”‚ | .exit MarkedShape(UnknownValueShape(x⁰), β†˜βŸ¨term:PredefΒΉ/id¹⟩%⟨exposed⁰⟩ϡ) (β†˜βŸ¨term:callid1²⟩%⟨callid1ΒΉβŸ©β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ) +//β”‚ | | .exit MarkedShape(UnknownValueShape(x⁰), β†˜βŸ¨term:PredefΒΉ/id¹⟩%⟨exposed⁰⟩ϡ) (β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ) +//β”‚ | | | .exit MarkedShape(UnknownValueShape(x⁰), β†˜βŸ¨term:PredefΒΉ/id¹⟩%⟨exposed⁰⟩ϡ) (Ο΅) +//β”‚ | | | = MarkedShape(UnknownValueShape(x⁰), β†˜βŸ¨term:PredefΒΉ/id¹⟩%⟨exposed⁰⟩ϡ) +//β”‚ | | | .exit MarkedShape (MarkedShape(UnknownValueShape(x⁰), β†˜βŸ¨term:PredefΒΉ/id¹⟩%⟨exposed⁰⟩ϡ), term:PredefΒΉ/idΒΉ, β‹…id⁰) +//β”‚ | | | = NoShape +//β”‚ | | = NoShape +//β”‚ | = NoShape +//β”‚ | .exit MarkedShape(IntroShape(1), β†˜βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…idβ°βŸ©β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) (β†˜βŸ¨term:callid1²⟩%⟨callid1ΒΉβŸ©β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ) +//β”‚ | | .exit MarkedShape(IntroShape(1), β†˜βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…idβ°βŸ©β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) (β†—βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…id⁰⟩ϡ) +//β”‚ | | | .exit MarkedShape(IntroShape(1), β†˜βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…idβ°βŸ©β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) (Ο΅) +//β”‚ | | | = MarkedShape(IntroShape(1), β†˜βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…idβ°βŸ©β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | | | .exit MarkedShape (MarkedShape(IntroShape(1), β†˜βŸ¨term:PredefΒΉ/id¹⟩%βŸ¨β‹…idβ°βŸ©β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ), term:PredefΒΉ/idΒΉ, β‹…id⁰) +//β”‚ | | | = MarkedShape(IntroShape(1), β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | | = MarkedShape(IntroShape(1), β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | = MarkedShape(IntroShape(1), β†˜βŸ¨term:callid1²⟩%⟨callid1ΒΉβŸ©β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | appShape isSaturated? true; head? (DefnShape(term definition 'callid1'),List(ExitMark(term:callid1,Some(callid1),NoMarks))) +//β”‚ | lhs false List(((p⁰),List(β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ))) +//β”‚ | appShape: td.body = Some(App(NewSel(SimpleRef(p),Ident(id),None),Tup(List(Fld(β€Ήβ€Ί,Lit(IntLit(1)),None))))) +//β”‚ | listenTerm: trm = p⁰.id(1) +//β”‚ | listen: trm = p⁰.id(1) +//β”‚ | .exit MarkedShape(IntroShape(1), β†˜βŸ¨term:callid1²⟩%⟨callid1ΒΉβŸ©β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) (β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | | .exit MarkedShape(IntroShape(1), β†˜βŸ¨term:callid1²⟩%⟨callid1ΒΉβŸ©β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) (Ο΅) +//β”‚ | | = MarkedShape(IntroShape(1), β†˜βŸ¨term:callid1²⟩%⟨callid1ΒΉβŸ©β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | | .exit MarkedShape (MarkedShape(IntroShape(1), β†˜βŸ¨term:callid1²⟩%⟨callid1ΒΉβŸ©β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ), term:callid1Β², callid1ΒΉ) +//β”‚ | | = MarkedShape(IntroShape(1), β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ | = MarkedShape(IntroShape(1), β†—βŸ¨term:callid1²⟩%⟨callid1¹⟩ϡ) +//β”‚ ~> { fun member:callid1(p⁰) = p⁰.id(1); member:callid1⁰(member:Predef⁰) } +//β”‚ listenTerm: trm = member:callid1⁰(member:Predef⁰) +//β”‚ listen: trm = member:callid1⁰(member:Predef⁰) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ @private fun callid1⁰::callid1ΒΉ(p⁰) β€Ήflow:callid1⁰› = p⁰.id⁰(1)β€Ήapp⁰›, callid1⁰(Predef⁰)β€ΉappΒΉβ€Ί +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 1 + + +:sr +callid2(Predef) +private fun callid2(p) = p.id(1) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ callid2⁰(Predef⁰)β€ΉappΒ²β€Ί, @private fun callid2⁰::callid2ΒΉ(pΒΉ) β€Ήflow:callid2⁰› = pΒΉ.id⁰(1)β€ΉappΒ³β€Ί +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + + +fun getPredef() = Predef + +:sr +getPredef().id +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ getPredef⁰()β€Ήapp⁴›.id⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = fun id + + +// :de +:sr +private fun foo(pred) = + let p = pred + val r = p.id(1) +foo(Predef) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ @private fun foo⁰::fooΒΉ(pred⁰) β€Ήflow:foo⁰› = { +//β”‚ let pΒ², +//β”‚ pΒ² = pred⁰, +//β”‚ val r⁰::rΒΉ β€Ήflow:r⁰› = pΒ²^rΒΉ.id⁰(1)β€Ήapp⁡›, +//β”‚ () +//β”‚ }, +//β”‚ foo⁰(Predef⁰)β€Ήapp⁢› +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + + diff --git a/hkmc2/shared/src/test/mlscript/newres/GeneratorResults.mls b/hkmc2/shared/src/test/mlscript/newres/GeneratorResults.mls new file mode 100644 index 0000000000..10020889ba --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/GeneratorResults.mls @@ -0,0 +1,18 @@ +:. + +// A generator call returns an iterator even when its body returns a literal. +// Resolution currently propagates that literal instead of the iterator shape. +@annotations.generator +fun generated() = 1 + +:fixme +:expect {value: 1, done: true} +generated().next() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.10: generated().next() +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal does not contain member 'next' +//β”‚ β•‘ l.6: fun generated() = 1 +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '{value: 1, done: true}', got: 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/newres/GenericMethods.mls b/hkmc2/shared/src/test/mlscript/newres/GenericMethods.mls new file mode 100644 index 0000000000..81185141b2 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/GenericMethods.mls @@ -0,0 +1,162 @@ +:. + +// Generic methods use the same inferred and explicit arguments as free functions. +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +class Methods with + fun identity[A](x: A): A = x +fun test(m: Methods) = + [m.identity(First(1)).first, m.identity[Second](Second(2)).second] +test(new Methods) +//β”‚ = [1, 2] + +// Class and method type arguments retain their separate scopes. +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +class Box[T](val value: T) with + fun map[U](f: T -> U): Box[U] = Box(f(value)) +fun test[T](box: Box[T], f: T -> Second) = box.map(f).value.second +[ (Box(First(1)) as Box[First]).map(x => x).value.first, + test(Box(First(1)), x => Second(x.first + 1)) ] +//β”‚ = [1, 2] + +// Imported generic methods infer result arguments from callback results. +:expect [2] +class Item(val value: Int) +class Box[T](val value: T) +[Item(1)].map((item, ...) => Box(Item(item.value + 1))) + .map((box, ...) => box.value.value) +//β”‚ = [2] + +// Instantiations of the same imported method must not contaminate each other. +:expect [[1], [2]] +class First(val first: Int) +class Second(val second: Int) +fun first() = [0].map((x, ...) => First(1)) +fun second() = [0].map((x, ...) => Second(2)) +[first().map((x, ...) => x.first), second().map((x, ...) => x.second)] +//β”‚ = [[1], [2]] + +// Explicit type arguments preserve the requested opaque result interface. +:ge +class Base +class Child extends Base with + val extra = 1 +[new Child].map[Base]((x, ...) => x).map((x, ...) => x.extra) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.47: [new Child].map[Base]((x, ...) => x).map((x, ...) => x.extra) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.47: [new Child].map[Base]((x, ...) => x).map((x, ...) => x.extra) +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Imported methods validate explicit type-argument counts too. +:ge +[1].map[Int, Int]((x, ...) => x) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.58: [1].map[Int, Int]((x, ...) => x) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'map' expected 1 type argument, but got 2 +//β”‚ β•‘ in ../decls/Prelude.mls +//β”‚ β•‘ l.260: fun map[U](f: (T, Int, Array[T]) -> U): Array[U] +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ = [1] + +// Imported free functions and methods use the same inference as prelude methods. +import "../../mlscript-compile/GenericLibrary.mls" + +:expect [1, 2, 3] +class First(val first: Int) +class Second(val second: Int) +fun test(m: GenericLibrary.Methods) = m.identity[Second](Second(2)).second +[GenericLibrary.identity(First(1)).first, + test(new GenericLibrary.Methods), GenericLibrary.known.known] +//β”‚ = [1, 2, 3] + +// Explicit method arguments also remain opaque for local declared interfaces. +:ge +class Base +class Child extends Base with + val extra = 1 +class Methods with + fun identity[A](x: A): A = x +fun test(m: Methods) = m.identity[Base](new Child).extra +test(new Methods) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.86: fun test(m: Methods) = m.identity[Base](new Child).extra +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.86: fun test(m: Methods) = m.identity[Base](new Child).extra +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Curried declared methods preserve their type arguments across parameter lists. +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +class Methods with + fun pick[A](x: A)(ignored: Int): A = x +fun test(m: Methods) = + [m.pick(First(1))(0).first, m.pick[Second](Second(2))(0).second] +test(new Methods) +//β”‚ = [1, 2] + +// Separate signatures preserve the method's generic parameter list. +:expect 4 +class Item(val value: Int) +class Methods with + fun identity[A]: A -> A = x => x +fun test(m: Methods) = m.identity[Item](Item(4)).value +test(new Methods) +//β”‚ = 4 + +// Imported values retain the dependencies of their inferred Array arguments. +:expect [5] +GenericLibrary.mapped.map((item, ...) => item.known) +//β”‚ = [5] + +// Imported parameter-to-result listeners run in the consuming resolver state. +:expect 6 +class Item(val value: Int) +GenericLibrary.inferred(Item(6)).value +//β”‚ = 6 + +// Inferred generic method results use the same imported listener graph. +:expect [7, 8] +class Item(val value: Int) +let methods = new GenericLibrary.Methods +[methods.inferred(Item(7)).value, methods.inferred[Item](Item(8)).value] +//β”‚ = [7, 8] +//β”‚ methods = Methods + +:expect [5] +module Local with + class Known(val known: Int) + val mapped = [Known(4)].map((item, ...) => Known(item.known + 1)) +Local.mapped.map((item, ...) => item.known) +//β”‚ = [5] + +// A local class's members are located inside its enclosing activation. Receivers +// annotated in a nested function add an exit from that function, which is only +// the receiver's provenance: it must not move the members of the local class. +:expect [1, 2, 3] +class First(val first: Int) +class Second(val second: Int) +class Item(val value: Int) +private fun wrap[A](value: A) = + class Local(val item: A) with + fun get[U](f: A -> U): U = f(item) + fun inner() = (new Local(value) as Local) + [inner().item, inner().get(x => x)] +private fun wrapped[A](value: A) = + class Local(val item: A) with + fun get[U](f: A -> U): U = f(item) + (new Local(value) as Local) +private fun viaNested(x) = + fun nested() = wrapped[Item](x) + nested().get(i => i.value) +[wrap[First](First(1)).0.first, wrap[Second](Second(2)).1.second, viaNested(Item(3))] +//β”‚ = [1, 2, 3] diff --git a/hkmc2/shared/src/test/mlscript/newres/GetterReadEffects.mls b/hkmc2/shared/src/test/mlscript/newres/GetterReadEffects.mls new file mode 100644 index 0000000000..ce9d3df4e7 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/GetterReadEffects.mls @@ -0,0 +1,114 @@ +:. + +// A source-level getter is effectful even when its result is discarded. +:expect 1 +fun test() = + let calls = 0 + class Box with + fun value: Int = + set calls += 1 + 42 + let box = new Box + box.value + calls +test() +//β”‚ = 1 + +// Replacing a data field with a JS getter does not change its declared val +// contract. Reads through that nominal interface are pure and may be eliminated. +:expect 0 +fun test() = + let calls = 0 + class Box(val value: Int) + let box = new mut Box(42) + Object.defineProperty(box, "value", (get: () => + set calls += 1 + 42)) + box.value + calls +test() +//β”‚ = 0 + +// Dynamic reads make no field-purity assumption and must retain getter effects. +:expect 1 +fun test() = + let calls = 0 + class Box(val value: Int) + val box: dyn = new mut Box(42) + Object.defineProperty(box, "value", (get: () => + set calls += 1 + 42)) + box.value + calls +test() +//β”‚ = 1 + +// An explicit dynamic selection also retains effects on a nominal receiver. +:expect 1 +fun test() = + let calls = 0 + class Box(val value: Int) + let box = new mut Box(42) + Object.defineProperty(box, "value", (get: () => + set calls += 1 + 42)) + box!value + calls +test() +//β”‚ = 1 + +// Structural field interfaces likewise describe pure reads. Passing a host +// accessor through this interface does not preserve its undeclared effects. +:expect 0 +fun test() = + let calls = 0 + val box: {value: Int} = Object.defineProperty(mut {}, "value", (get: () => + set calls += 1 + 42)) + box.value + calls +test() +//β”‚ = 0 + +// An abstract val promises a pure read, so implementing it with an effectful +// getter is rejected by override validation. +:e +fun test() = + let calls = 0 + abstract class Base with + val value: Int + class Child extends Base with + fun value: Int = + set calls += 1 + 42 + val box: Base = new Child + box.value + calls +test() +//β”‚ ╔══[COMPILATION ERROR] Overriding member 'value' must preserve whether the member is a field, getter, or method +//β”‚ β•‘ l.81: fun value: Int = +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.82: set calls += 1 +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.83: 42 +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Inherited member 'value' is defined here +//β”‚ β•‘ l.79: val value: Int +//β”‚ ╙── ^^^^^ +//β”‚ = 0 + +// An abstract getter interface preserves effects when dispatching to its child. +:expect 1 +fun test() = + let calls = 0 + abstract class Base with + fun value: Int + class Child extends Base with + fun value: Int = + set calls += 1 + 42 + val box: Base = new Child + box.value + calls +test() +//β”‚ = 1 diff --git a/hkmc2/shared/src/test/mlscript/newres/HandlerResults.mls b/hkmc2/shared/src/test/mlscript/newres/HandlerResults.mls new file mode 100644 index 0000000000..009cc4bd43 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/HandlerResults.mls @@ -0,0 +1,34 @@ +:. +:lift +:effectHandlers + +// FIXME: The handler receiver and its result need shape propagation. This +// handler aborts: its record, not the rest of the handled body, is the result. +:fixme +:expect 42 +abstract class AnswerEffect with + fun answer() +fun handled() = + handle h = AnswerEffect with + fun answer()(resume) = {answer: 42} + h.answer() + {answer: 0} +handled().answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.16: handled().answer +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'answer' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ An effect handler can replace the handled body's result. +//β”‚ β•‘ l.12: handle h = AnswerEffect with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.13: fun answer()(resume) = {answer: 42} +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.14: h.answer() +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•‘ l.15: {answer: 0} +//β”‚ ╙── ^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Member reference 'AnswerEffect' has no resolved target +//β”‚ β•‘ l.12: handle h = AnswerEffect with +//β”‚ ╙── ^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '42', got: 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/newres/HostInterfaces.mls b/hkmc2/shared/src/test/mlscript/newres/HostInterfaces.mls new file mode 100644 index 0000000000..3a3322a5b0 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/HostInterfaces.mls @@ -0,0 +1,14 @@ +:. + +// These host declarations are checked without requiring a DOM in the test VM. +:!js +fun readFile(path: Str) = fs.readFileSync(path).toString() +fun query(selector: Str) = document.querySelector(selector).classList.contains("active") +fun register(name: Str, ctor: dyn) = customElements.define(name, ctor) +class Element extends HTMLElement with + fun render(text: Str) = { + set this.textContent = text + this.appendChild(document.createElement("span")) + this.attachShadow({mode: "open"}) + set this.shadowRoot.innerHTML = text + } diff --git a/hkmc2/shared/src/test/mlscript/newres/IRClassHeaders.mls b/hkmc2/shared/src/test/mlscript/newres/IRClassHeaders.mls new file mode 100644 index 0000000000..3c1b986655 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/IRClassHeaders.mls @@ -0,0 +1,19 @@ +:. + +// Functions are lowered before class bodies. Their pattern specialization must +// already know the inheritance chain, including nested class declarations. +:expect 1 +fun classify(x) = + if x is Classes.Child and x is Classes.Parent then 1 else 2 +module Classes with + class Parent + class Child extends Parent +classify(new Classes.Child) +//β”‚ = 1 + +// Prelude classes have no executable definitions to supply their parent links. +:expect 1 +fun classifyNumber(x) = + if x is Int and x is Num then 1 else 2 +classifyNumber(42) +//β”‚ = 1 diff --git a/hkmc2/shared/src/test/mlscript/newres/ImportedNamedFields.mls b/hkmc2/shared/src/test/mlscript/newres/ImportedNamedFields.mls new file mode 100644 index 0000000000..db2ce6db27 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ImportedNamedFields.mls @@ -0,0 +1,19 @@ +:. + +import "../../mlscript-compile/NamedFieldLibrary.mls" + +:expect ["A", 2, 3] +let a = NamedFieldLibrary.pack("a") +let b = NamedFieldLibrary.pack([1, 2]) +[a.0.value.toUpperCase(), b.0.value.length, NamedFieldLibrary.select(value: 3)] +//β”‚ = ["A", 2, 3] +//β”‚ a = [{value: "a"}] +//β”‚ b = [{value: [1, 2]}] + +:expect [4, 5] +[NamedFieldLibrary.select(value: 4), NamedFieldLibrary.pack(5).0.value] +//β”‚ = [4, 5] + +:expect [36, 36] +[NamedFieldLibrary.fields.0.value, NamedFieldLibrary.observed] +//β”‚ = [36, 36] diff --git a/hkmc2/shared/src/test/mlscript/newres/ImportedRecords.mls b/hkmc2/shared/src/test/mlscript/newres/ImportedRecords.mls new file mode 100644 index 0000000000..0cde56684a --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ImportedRecords.mls @@ -0,0 +1,16 @@ +:. + +import "../../mlscript-compile/RecordProducers.mls" + +// Resolving RecordProducers.observed in the imported file already discovered the +// `value` field. Our first read of fields.value must receive that cached information. +:expect [36, 36] +[RecordProducers.fields.value, RecordProducers.observed] +//β”‚ = [36, 36] + +// Spreading the imported record must also expose its `value` field on the copy. +:expect 36 +let importedFields = {...RecordProducers.fields} +importedFields.value +//β”‚ = 36 +//β”‚ importedFields = {value: 36} diff --git a/hkmc2/shared/src/test/mlscript/newres/InferenceHoleScopes.mls b/hkmc2/shared/src/test/mlscript/newres/InferenceHoleScopes.mls new file mode 100644 index 0000000000..d790b5b214 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/InferenceHoleScopes.mls @@ -0,0 +1,99 @@ +:. + +// An invariant omitted argument receives constraints from both functions. +// Its bounds must keep their source scope when the result flows to another call. +:expect 2 +class Box[T](val item: T) +fun make(x): Box = Box(x) +fun read(box: Box) = box.item +fun call(x) = read(make(x)) +call(2) +//β”‚ = 2 + +// Iterator annotations retain their declared members while inferring elements. +:expect 3 +fun getIterator(xs): Iterator = xs.(Symbol.iterator)() +fun foldingImpl(iterator: Iterator, acc, op: Function) = + let next = iterator.next() + while next.done is false do + set acc = op(acc, next.value) + set next = iterator.next() + acc +fun folded(xs, z, op: Function) = + let iterator = getIterator(xs) + foldingImpl(iterator, z, op) +folded([1, 2], 0, (x, y) => x + y) +//β”‚ = 3 + +// A nested caller must observe the same hole through its own lexical path. +:expect [1, 2] +fun getIterator(xs): Iterator = xs.(Symbol.iterator)() +fun foldingImpl(iterator: Iterator) = iterator.next().value +fun folded(xs) = foldingImpl(getIterator(xs)) +fun flattened(xss) = + let iterableIterator = getIterator(xss) + fun skipEmptyIterables() = + let nextIterableResult = iterableIterator.next() + let nextIterator = getIterator(nextIterableResult.value) + let nextResult = nextIterator.next() + [nextIterator, nextResult.value] + skipEmptyIterables().1 +[flattened([[1]]), folded([2])] +//β”‚ = [1, 2] + +// Partially supplied arguments move with the application; the omitted position +// remains anchored to its source reference, including inside a nested function. +:expect [1, 2] +class Pair[A, B](val first: A, val second: B) +fun make(x): Pair[Int] = Pair(1, x) +fun read(pair: Pair[Int]) = [pair.first, pair.second] +fun call(x) = + fun nested() = read(make(x)) + nested() +call(2) +//β”‚ = [1, 2] + +// Exposure still contributes unknowns to the hole. Keeping scope paths correct +// must not let one local caller narrow the public interface. +:ge +class Item(val value: Int) +class Box[T](val item: T) +fun make(x): Box = Box(x) +fun read(box: Box) = box.item.value +fun call(x) = read(make(x)) +call(Item(2)) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.62: fun read(box: Box) = box.item.value +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.61: fun make(x): Box = Box(x) +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'make' is exposed by this compilation unit. +//β”‚ β•‘ l.61: fun make(x): Box = Box(x) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•‘ l.60: class Box[T](val item: T) +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'box' admits values of unknown shape. +//β”‚ β•‘ l.62: fun read(box: Box) = box.item.value +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'read' is exposed by this compilation unit. +//β”‚ β•‘ l.62: fun read(box: Box) = box.item.value +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.63: fun call(x) = read(make(x)) +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'call' is exposed by this compilation unit. +//β”‚ β•‘ l.63: fun call(x) = read(make(x)) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Inference through an explicit callback signature also transports bounds back +// from a generic callee to the caller's omitted argument (as in nofib/cse). +:expect [1, 2] +class Box[T](val item: T) +fun fold[A, B](f: (A, B) -> B, z: B, xs: Box[A]): B = f(xs.item, z) +fun call(xs: Box) = fold((x, z) => [x, z], 2, xs) +call(Box(1)) +//β”‚ = [1, 2] diff --git a/hkmc2/shared/src/test/mlscript/newres/InferenceHoles.mls b/hkmc2/shared/src/test/mlscript/newres/InferenceHoles.mls new file mode 100644 index 0000000000..0193f874e9 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/InferenceHoles.mls @@ -0,0 +1,187 @@ +:. + +// Each missing formal position and each annotation occurrence has its own hole. +:expect [1, 2, 3, 4] +class First(val first: Int) +class Second(val second: Int) +class Pair[A, B](val value: [A, B]) +private fun both(left: Pair, right: Pair) = + [left.value.0.first, left.value.1.second, right.value.0.second, right.value.1.first] +both(Pair([First(1), Second(2)]), Pair([Second(3), First(4)])) +//β”‚ = [1, 2, 3, 4] + +// TODO: preserve distinct callers through a recursive hole reference. +// This hole must use ordinary inference, not fresh call-site parameter symbols. +// Recursive hole references currently conflate bounds from distinct callers. +:fixme +:expect [5, 6] +class First(val first: Int) +class Second(val second: Int) +private fun append(xs: Array, value, n: Int) = + if n > 0 then append(xs, value, n - 1) else xs.push(value) +let xs = mut [] +let ys = mut [] +append(xs, First(5), 2) +append(ys, Second(6), 2) +[xs.0.first, ys.0.second] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.26: [xs.0.first, ys.0.second] +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'First' does not contain member 'second' +//β”‚ β•‘ l.24: append(xs, First(5), 2) +//β”‚ ╙── ^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.26: [xs.0.first, ys.0.second] +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Second' does not contain member 'first' +//β”‚ β•‘ l.25: append(ys, Second(6), 2) +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '[5, 6]', got: 'undefined' +//β”‚ xs = [First(5)] +//β”‚ ys = [Second(6)] + +// A fixed fragment still restricts the interface next to an inferred argument. +:ge +class Base +class Child(val extra: Int) extends Base +class Pair[A, B](val value: [A, B]) +private fun first(pair: Pair[Base]) = pair.value.0.extra +first(Pair([Child(7), 0])) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.49: private fun first(pair: Pair[Base]) = pair.value.0.extra +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.49: private fun first(pair: Pair[Base]) = pair.value.0.extra +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// External inputs remain unknown even when local callers supply useful shapes. +:ge +class Item(val value: Int) +class Box[T](val item: T) +fun read(box: Box) = box.item.value +read(Box(Item(8))) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.63: fun read(box: Box) = box.item.value +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'box' admits values of unknown shape. +//β”‚ β•‘ l.63: fun read(box: Box) = box.item.value +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'read' is exposed by this compilation unit. +//β”‚ β•‘ l.63: fun read(box: Box) = box.item.value +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Incomplete result annotations infer their holes from the implementation. +:expect 9 +class Item(val value: Int) +class Box[T](val item: T) +private fun make(): Box = Box(Item(9)) +make().item.value +//β”‚ = 9 + +// An ascription preserves its written structure and infers its missing argument. +:expect 10 +class Item(val value: Int) +class Box[T](val item: T) +let box = Box(Item(10)) as Box +box.item.value +//β”‚ = 10 +//β”‚ box = Box(Item(10)) + +// TODO: omissions in an alias body need the alias use's contextual graph view. +:fixme +:expect [11, 12] +class First(val first: Int) +class Second(val second: Int) +class Box[T](val item: T) +type SomeBox = Box +private fun both(left: SomeBox, right: SomeBox) = [left.item.first, right.item.second] +both(Box(First(11)), Box(Second(12))) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.101: private fun both(left: SomeBox, right: SomeBox) = [left.item.first, right.item.second] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'First' does not contain member 'second' +//β”‚ β•‘ l.102: both(Box(First(11)), Box(Second(12))) +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.101: private fun both(left: SomeBox, right: SomeBox) = [left.item.first, right.item.second] +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Second' does not contain member 'first' +//β”‚ β•‘ l.102: both(Box(First(11)), Box(Second(12))) +//β”‚ ╙── ^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '[11, 12]', got: 'undefined' + +// Only missing arguments are inferred; excess arguments remain invalid. +:ge +class Box[T](val item: T) +private fun excess(value: Box[Int, Str]) = () +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.121: private fun excess(value: Box[Int, Str]) = () +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Type 'Box' accepts at most 1 type argument, but got 2. +//β”‚ β•‘ l.121: private fun excess(value: Box[Int, Str]) = () +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ The type parameters are declared here. +//β”‚ β•‘ l.120: class Box[T](val item: T) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ + +// A type alias checks its own argument list before interpreting the body. +:ge +class Box[T](val item: T) +type Alias[A] = Box[A] +private fun excess(value: Alias[Int, Str]) = () +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.136: private fun excess(value: Alias[Int, Str]) = () +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Type 'Alias' accepts at most 1 type argument, but got 2. +//β”‚ β•‘ l.136: private fun excess(value: Alias[Int, Str]) = () +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ The type parameters are declared here. +//β”‚ β•‘ l.135: type Alias[A] = Box[A] +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^ + +// Annotated results do not hide missing input types from exposure checking. +:ge +class Item(val value: Int) +class Box[T](val item: T) +fun read(box: Box): Int = box.item.value +read(Box(Item(13))) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.151: fun read(box: Box): Int = box.item.value +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ 'read' is exposed by this compilation unit. +//β”‚ β•‘ l.151: fun read(box: Box): Int = box.item.value +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// An external callback may supply an unknown value for its result's hole. +:ge +class Item(val value: Int) +class Box[T](val item: T) +fun read(callback: () -> Box) = callback().item.value +read(() => Box(Item(14))) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.166: fun read(callback: () -> Box) = callback().item.value +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'callback' admits values of unknown shape. +//β”‚ β•‘ l.166: fun read(callback: () -> Box) = callback().item.value +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'read' is exposed by this compilation unit. +//β”‚ β•‘ l.166: fun read(callback: () -> Box) = callback().item.value +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A result hole retains the call's explicit-binder view while observing its body. +:expect [15, 16] +class First(val first: Int) +class Second(val second: Int) +class Box[T](val item: T) +private fun wrap[A](value: A): Box = Box(value) +[wrap(First(15)).item.first, wrap(Second(16)).item.second] +//β”‚ = [15, 16] diff --git a/hkmc2/shared/src/test/mlscript/newres/Inheritance.mls b/hkmc2/shared/src/test/mlscript/newres/Inheritance.mls new file mode 100644 index 0000000000..bae7e7bdfe --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/Inheritance.mls @@ -0,0 +1,141 @@ +:. + + +:sr +class Foo(val x) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fun Foo⁰ class FooΒΉ::FooΒ²(val x⁰) { val xΒΉ::xΒ² β€Ήflow:x⁰› = x⁰^xΒ² } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + + +// :elt +:sr +:sjs +class Bar(y) extends Foo(y + 1) { + val z = this.x + 1 +} +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fun Bar⁰ +//β”‚ class BarΒΉ::BarΒ²(y⁰) extends new FooΒΉ^BarΒ²(+(y⁰, 1)β€Ήapp⁰›) { +//β”‚ @private val yΒΉ::yΒ² β€Ήflow:y⁰› = y⁰^yΒ², +//β”‚ val z⁰::zΒΉ β€Ήflow:z⁰› = +(BarΒ².this^zΒΉ.xΒΉ, 1)β€ΉappΒΉβ€Ί +//β”‚ } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let Bar1; +//β”‚ Bar1 = function Bar(y) { +//β”‚ return globalThis.Object.freeze(new Bar.class(y)); +//β”‚ }; +//β”‚ (class Bar extends Foo1.class { +//β”‚ static { +//β”‚ Bar1.class = this +//β”‚ } +//β”‚ constructor(y) { +//β”‚ let tmp; +//β”‚ tmp = y + 1; +//β”‚ super(tmp); +//β”‚ let tmp1; +//β”‚ tmp1 = this.x + 1; +//β”‚ this.z = tmp1; +//β”‚ } +//β”‚ toString() { return runtime.render(this); } +//β”‚ static [definitionMetadata] = ["class", "Bar", [null]]; +//β”‚ }); +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +let b = Bar(123) +//β”‚ b = Bar(_) + +// :de +:sr +b.x +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ b⁰.xΒΉ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 124 + +:breakme +:e +b.y + +b.z +//β”‚ = 125 + + +let b = new Bar(123) +//β”‚ b = Bar(_) + +:breakme +:e +b.y + +:expect 124 +b.x +//β”‚ = 124 + +b.z +//β”‚ = 125 + + +:expect [44, 43] +class Baz extends Bar(42) { + val a = [this.z, this.x] +} +(new Baz).a +//β”‚ = [44, 43] + + + + +module Outer { + class Inner1 { + constructor(val x) + } + class Inner2(val x) +} + +:e +object Derived extends Outer.Inner1 +//β”‚ ╔══[COMPILATION ERROR] Extending a partially applied class is not supported +//β”‚ β•‘ l.97: object Derived extends Outer.Inner1 +//β”‚ ╙── ^^^^^^^^^^^^ + +:sr +:sir +object Derived extends Outer.Inner1(11) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ object Derived⁰::DerivedΒΉ extends new Outer⁰.Inner1⁰(11) { } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ define Derived⁰ as object DerivedΒΉ extends Outer⁰.Inner1⁰ {}; end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +:fixme +Derived.x +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.112: Derived.x +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Object 'Derived' does not contain member 'x' +//β”‚ β•‘ l.104: object Derived extends Outer.Inner1(11) +//β”‚ ╙── ^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +Derived."x" + +:e +object Derived extends Outer.Inner2 +//β”‚ ╔══[COMPILATION ERROR] Extending a partially applied class is not supported +//β”‚ β•‘ l.124: object Derived extends Outer.Inner2 +//β”‚ ╙── ^^^^^^^^^^^^ + +:sr +:sir +object Derived extends Outer.Inner2(12) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ object DerivedΒ²::DerivedΒ³ extends new Outer⁰.Inner2⁰(12) { } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ define DerivedΒ² as object DerivedΒ³ extends Outer⁰.Inner2⁰ {}; end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +Derived.x +//β”‚ = 12 + + diff --git a/hkmc2/shared/src/test/mlscript/newres/InheritedTypeArguments.mls b/hkmc2/shared/src/test/mlscript/newres/InheritedTypeArguments.mls new file mode 100644 index 0000000000..96c3493bd3 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/InheritedTypeArguments.mls @@ -0,0 +1,154 @@ +:. +:flowBasedOpt off +// ^ FIXME: DPE causes problems + +// Control: an exact nominal match transfers the supplied argument to read's A. +:expect 1 +class Item(val value: Int) +class Parent[A](val item: A) +private fun read[A](parent: Parent[A]) = parent.item +read(new Parent[Item](Item(1))).value +//β”‚ = 1 + +// A constructed subclass must transfer its instantiated Parent argument. +// The field signature is complete, so this does not require inferring members. +:expect 2 +class Item(val value: Int) +class Parent[A](val item: A) +class Child[A](item: A) extends Parent[A](item) +private fun read[A](parent: Parent[A]) = parent.item +read(new Child[Item](Item(2))).value +//β”‚ = 2 + +// A declared subclass interface must likewise follow its parent view. +:expect 3 +class Item(val value: Int) +class Parent[A](val item: A) +class Child[A](item: A) extends Parent[A](item) +private fun read[A](parent: Parent[A]) = parent.item +private fun child(): Child[Item] = new Child[Item](Item(3)) +read(child()).value +//β”‚ = 3 + +// Multiple parent steps compose substitutions, including permuted arguments. +:expect [4, 5] +class First(val first: Int) +class Second(val second: Int) +class Parent[A, B](val left: A, val right: B) +class Middle[X, Y](x: X, y: Y) extends Parent[Y, X](y, x) +class Child[P, Q](p: P, q: Q) extends Middle[P, Q](p, q) +private fun read[A, B](parent: Parent[A, B]) = [parent.left, parent.right] +let result = read(new Child[Second, First](Second(5), First(4))) +[result.0.first, result.1.second] +//β”‚ = [4, 5] +//β”‚ result = [First(4), Second(5)] + +// A declared parent chain retains the same permutation and both call contexts. +:expect [6, 7] +class First(val first: Int) +class Second(val second: Int) +class Parent[A](val item: A) +class Middle[X](x: X) extends Parent[X](x) +class Child[Y](y: Y) extends Middle[Y](y) +private fun read[A](parent: Parent[A]) = parent.item +private fun first(): Child[First] = new Child[First](First(6)) +private fun second(): Child[Second] = new Child[Second](Second(7)) +[read(first()).first, read(second()).second] +//β”‚ = [6, 7] + +// An enclosing function's parameter remains captured while following a parent. +:expect [8, 9] +class First(val first: Int) +class Second(val second: Int) +class Parent[A](val item: A) +private fun read[A](parent: Parent[A]) = parent.item +private fun make[T](item: T) = + class Child extends Parent[T](item) + read(new Child) +[make(First(8)).first, make(Second(9)).second] +//β”‚ = [8, 9] + +// Qualified subclass annotations retain the parent's capture into the class, +// even when the annotated value travels through a tuple and nested calls. +:expect 3 +module Tokens with + class Parent with + fun value: Int = 3 + class Child() extends Parent +private fun run() = + fun make(): [Int, Tokens.Child] = [0, Tokens.Child()] + fun read(pair: [Int, Tokens.Parent]) = pair.1.value + read(make()) +run() +//β”‚ = 3 + +// Parent type arguments must survive more than one ancestry step and keep +// separate calls distinct. +:expect [4, 5] +module Types with + class First(val first: Int) + class Second(val second: Int) + class Parent[A](val item: A) + class Middle[A](item: A) extends Parent[A](item) + class Child[A](item: A) extends Middle[A](item) +private fun make[A](item: A): Types.Child[A] = new Types.Child[A](item) +private fun read[A](parent: Types.Parent[A]) = parent.item +[read(make(Types.First(4))).first, read(make(Types.Second(5))).second] +//β”‚ = [4, 5] + +// A qualified parent under an enclosing generic activation retains the outer +// binder, rather than treating it as one of the child's own type parameters. +:expect [6, 7] +class First(val first: Int) +class Second(val second: Int) +private fun make[A](item: A) = + module Types with + class Parent(val item: A) + class Child(item: A) extends Parent(item) + fun child(): Types.Child = new Types.Child(item) + fun read(parent: Types.Parent) = parent.item + read(child()) +[make(First(6)).first, make(Second(7)).second] +//β”‚ = [6, 7] + +// Constraints on a callable parent argument must also propagate its input. +:expect 8 +module Types with + class Item(val value: Int) + class Parent[A](val run: A -> Int) + class Child[A](run: A -> Int) extends Parent[A](run) +private fun make[A](run: A -> Int): Types.Child[A] = new Types.Child[A](run) +private fun call[A](parent: Types.Parent[A], item: A) = parent.run(item) +call(make(x => x.value), Types.Item(8)) +//β”‚ = 8 + +// Distinct annotated subclass calls must not mix their ancestor arguments. +:e +module Types with + class First(val first: Int) + class Second(val second: Int) + class Parent[A](val item: A) + class Child[A](item: A) extends Parent[A](item) +private fun make[A](item: A): Types.Child[A] = new Types.Child[A](item) +private fun read[A](parent: Types.Parent[A]) = parent.item +read(make(Types.Second(9))).second +read(make(Types.First(8))).second +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.135: read(make(Types.First(8))).second +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'First' does not contain member 'second' +//β”‚ β•‘ l.135: read(make(Types.First(8))).second +//β”‚ ╙── ^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// The imported recursive Stack type supplies an argument to Cons, but Cons's +// Base parent does not use it. Interpreting Base must not capture that argument +// again while a nested pattern is already observing it inside Cons. +:expect 2 +import "../../mlscript-compile/Stack.mls" +open Stack +private fun second(xs: Stack[Int]) = if xs is + Cons(_, Cons(value, _)) then value + else 0 +second(Cons(1, Cons(2, Nil))) +//β”‚ = 2 diff --git a/hkmc2/shared/src/test/mlscript/newres/InstantiationSites.mls b/hkmc2/shared/src/test/mlscript/newres/InstantiationSites.mls new file mode 100644 index 0000000000..ef684fa4cc --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/InstantiationSites.mls @@ -0,0 +1,129 @@ +:. + +// A by-name invocation allocates storage before the returned closure is called. +// Later calls through one stored result share that storage and its type instance. +:expect [1, 2] +private fun make[A] = + let cells = mut [] + (value: A) => + cells.push(value) + cells.length +private val shared = make +[shared(1), shared(2)] +//β”‚ = [1, 2] +//β”‚ shared = fun + +// Distinct by-name invocation sites create distinct storage, even in one class. +:expect [1, 1, 2] +class Factory with + fun make[A] = + let cells = mut [] + (value: A) => + cells.push(value) + cells.length +private val factory = new Factory +private val left = factory.make +private val right = factory.make +[left(1), right(2), left(3)] +//β”‚ = [1, 1, 2] +//β”‚ factory = Factory +//β”‚ left = fun +//β”‚ right = fun + +// One stored result cannot acquire independent element types on its later calls. +// The second invocation returns the first inserted value, which has no .second. +:ge +class First(val first: Int) +class Second(val second: Int) +private fun make[A] = + let cells = mut [] + (value: A) => + cells.push(value) + cells.0 +private val shared = make +shared(First(1)) +shared(Second(2)).second +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.45: shared(Second(2)).second +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'First' does not contain member 'second' +//β”‚ β•‘ l.44: shared(First(1)) +//β”‚ ╙── ^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ shared = fun + +// An annotated result must not re-generalize the by-name definition's binder. +// Both calls use one allocated array; the result of the second is still First. +:e +class First(val first: Int) +class Second(val second: Int) +private fun make[A]: A -> A = + let cells = mut [] + (value: A) => + cells.push(value) + cells.0 +private val shared = make +shared(First(3)) +shared(Second(4)).second +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.67: shared(Second(4)).second +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'First' does not contain member 'second' +//β”‚ β•‘ l.66: shared(First(3)) +//β”‚ ╙── ^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ shared = fun + +// Explicit arguments instantiate the invocation, before the returned closure is used. +:expect [5, 6] +class First(val first: Int) +class Second(val second: Int) +private fun make[A]: A -> A = (value: A) => value +private val left = make[First] +private val right = make[Second] +[left(First(5)).first, right(Second(6)).second] +//β”‚ = [5, 6] +//β”‚ left = fun +//β”‚ right = fun + +// A selected by-name method consumes its own binders while retaining its receiver. +:expect [7, 8] +class First(val first: Int) +class Second(val second: Int) +class Factory with + fun make[A]: A -> A = (value: A) => value +private val factory = new Factory +private val left = factory.make[First] +private val right = factory.make[Second] +[left(First(7)).first, right(Second(8)).second] +//β”‚ = [7, 8] +//β”‚ factory = Factory +//β”‚ left = fun +//β”‚ right = fun + +// An explicit invocation inside a generic body retains its enclosing captures. +:expect [9, 10] +class First(val first: Int) +class Second(val second: Int) +private fun make[A]: A -> A = (value: A) => value +private fun wrap[T](value: T) = make[T](value) +[wrap[First](First(9)).first, wrap[Second](Second(10)).second] +//β”‚ = [9, 10] + +// Separate signatures bind the by-name definition, not its returned closure. +:e +class First(val first: Int) +class Second(val second: Int) +private fun make: [A] -> A -> A +private fun make = value => value +private val shared = make +shared(First(11)) +shared(Second(12)).second +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.121: shared(Second(12)).second +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'First' does not contain member 'second' +//β”‚ β•‘ l.120: shared(First(11)) +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ shared = fun diff --git a/hkmc2/shared/src/test/mlscript/newres/InterfaceExposure.mls b/hkmc2/shared/src/test/mlscript/newres/InterfaceExposure.mls new file mode 100644 index 0000000000..ae10c5c7fb --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/InterfaceExposure.mls @@ -0,0 +1,405 @@ +:. + +// Observed calls cannot close an exposed unannotated parameter. +:e +fun foo(x) = x.a +foo({a: 1}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.5: fun foo(x) = x.a +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.5: fun foo(x) = x.a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'foo' is exposed by this compilation unit. +//β”‚ β•‘ l.5: fun foo(x) = x.a +//β”‚ ╙── ^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Nor can a type parameter acquire an interface from local instantiations. +:e +fun foo[A](x: A) = x.a +foo({a: 1}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.21: fun foo[A](x: A) = x.a +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' of a value with unknown shape. +//β”‚ β•‘ l.21: fun foo[A](x: A) = x.a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' does not specify a member interface. +//β”‚ β•‘ l.21: fun foo[A](x: A) = x.a +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// The abstraction boundary applies to private functions too. +:e +private fun privateGeneric[A](x: A) = x.a +privateGeneric({a: 1}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.36: private fun privateGeneric[A](x: A) = x.a +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' of a value with unknown shape. +//β”‚ β•‘ l.36: private fun privateGeneric[A](x: A) = x.a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' does not specify a member interface. +//β”‚ β•‘ l.36: private fun privateGeneric[A](x: A) = x.a +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// It also applies through captures and generic class members. +:e +private class GenericClass[A](value: A) with + fun read() = (() => value.a)() +(new GenericClass({a: 1})).read() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.52: fun read() = (() => value.a)() +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' of a value with unknown shape. +//β”‚ β•‘ l.51: private class GenericClass[A](value: A) with +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' does not specify a member interface. +//β”‚ β•‘ l.51: private class GenericClass[A](value: A) with +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Concrete type arguments still substitute into generic results at call sites. +:expect [3, 4] +class IdentityItem(val a: Int) +private fun identity[A](x: A): A = x +[identity(IdentityItem(3)).a, identity[IdentityItem](IdentityItem(4)).a] +//β”‚ = [3, 4] + +:expect 1 +private fun foo(x) = x.a +foo({a: 1}) +//β”‚ = 1 + +:expect 1 +class A(val a: Int) +fun foo(x: A) = x.a +foo(new A(1)) +//β”‚ = 1 + +// An unknown callable alternative is an error despite a known local call. +:e +fun apply(f) = f(1) +apply(x => x) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.85: fun apply(f) = f(1) +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Value of unknown shape cannot be called like a function. +//β”‚ β•‘ l.85: fun apply(f) = f(1) +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Parameter 'f' admits values of unknown shape. +//β”‚ β•‘ l.85: fun apply(f) = f(1) +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'apply' is exposed by this compilation unit. +//β”‚ β•‘ l.85: fun apply(f) = f(1) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A declared callable interface admits calls. +:expect 1 +fun apply(f: Int -> Int) = f(1) +apply(x => x) +//β”‚ = 1 + +// Exposure follows returned closures and aggregate elements. +:e +fun escaped() = x => x.a +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.109: fun escaped() = x => x.a +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.109: fun escaped() = x => x.a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ This value is returned here. +//β”‚ β•‘ l.109: fun escaped() = x => x.a +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'escaped' is exposed by this compilation unit. +//β”‚ β•‘ l.109: fun escaped() = x => x.a +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ + +:e +private fun hidden(x) = x.a +val escaped = [hidden] +hidden({a: 1}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.125: private fun hidden(x) = x.a +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.125: private fun hidden(x) = x.a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ This value is stored in this tuple. +//β”‚ β•‘ l.126: val escaped = [hidden] +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'escaped' is exposed by this compilation unit. +//β”‚ β•‘ l.126: val escaped = [hidden] +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ escaped = [fun hidden] + +:e +private fun hidden(x) = x.a +val escaped = {f: hidden} +hidden({a: 1}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.145: private fun hidden(x) = x.a +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.145: private fun hidden(x) = x.a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ This value is stored in this record field. +//β”‚ β•‘ l.146: val escaped = {f: hidden} +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'escaped' is exposed by this compilation unit. +//β”‚ β•‘ l.146: val escaped = {f: hidden} +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ escaped = {f: fun hidden} + +// Calling a private helper does not expose arbitrary inputs to it. +:expect 1 +private fun hidden(x) = x.a +fun closed() = hidden({a: 1}) +closed() +//β”‚ = 1 + +// An annotated returned callback is checked under its declared domain. +:expect 1 +class A(val a: Int) +fun typed(): A -> Int = x => x.a +typed()(new A(1)) +//β”‚ = 1 + +// The diagnostic retains the parameter, field storage, and returned record. +:e +private fun hiddenRecord(x) = x.a +fun exportRecord() = {callback: hiddenRecord} +hiddenRecord({a: 1}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.180: private fun hiddenRecord(x) = x.a +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.180: private fun hiddenRecord(x) = x.a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ This value is stored in this record field. +//β”‚ β•‘ l.181: fun exportRecord() = {callback: hiddenRecord} +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This value is returned here. +//β”‚ β•‘ l.181: fun exportRecord() = {callback: hiddenRecord} +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'exportRecord' is exposed by this compilation unit. +//β”‚ β•‘ l.181: fun exportRecord() = {callback: hiddenRecord} +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Exposing a constructor must not pollute separately constructed instances. +// FIXME: Rebuilding the same class loses the original constructor activation. +:fixme +:expect 42 +class CopyBox(val value) with + fun copy() = CopyBox(value) +CopyBox({answer: 42}).copy().value.answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.207: CopyBox({answer: 42}).copy().value.answer +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'answer' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'value' admits values of unknown shape. +//β”‚ β•‘ l.205: class CopyBox(val value) with +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'CopyBox' is exposed by this compilation unit. +//β”‚ β•‘ l.205: class CopyBox(val value) with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.206: fun copy() = CopyBox(value) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '42', got: 'undefined' + +// A previously compiled private definition cannot acquire an open interface later. +private fun oldHidden(x) = x.a +oldHidden({a: 1}) +//β”‚ = 1 + +:e +val oldEscape = {callback: oldHidden} +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.224: private fun oldHidden(x) = x.a +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.224: private fun oldHidden(x) = x.a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ This value is stored in this record field. +//β”‚ β•‘ l.229: val oldEscape = {callback: oldHidden} +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'oldEscape' is exposed by this compilation unit. +//β”‚ β•‘ l.229: val oldEscape = {callback: oldHidden} +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ oldEscape = {callback: fun oldHidden} + +// A spread retains the generic input that made its member interface unknown. +:e +fun spreadRecord[A](x: A) = ({...x}).a +spreadRecord({a: 1}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.247: fun spreadRecord[A](x: A) = ({...x}).a +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This spread has no known element shape. +//β”‚ β•‘ l.247: fun spreadRecord[A](x: A) = ({...x}).a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' across a computed key or unknown record spread. +//β”‚ β•‘ l.247: fun spreadRecord[A](x: A) = ({...x}).a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' does not specify a member interface. +//β”‚ β•‘ l.247: fun spreadRecord[A](x: A) = ({...x}).a +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// The same provenance survives indexing into an unresolved tuple spread. +:e +fun spreadTuple[A](x: A) = [...x].0.a +spreadTuple([{a: 1}]) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.265: fun spreadTuple[A](x: A) = [...x].0.a +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This spread has no known element shape. +//β”‚ β•‘ l.265: fun spreadTuple[A](x: A) = [...x].0.a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' of a value with unknown shape. +//β”‚ β•‘ l.265: fun spreadTuple[A](x: A) = [...x].0.a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' does not specify a member interface. +//β”‚ β•‘ l.265: fun spreadTuple[A](x: A) = [...x].0.a +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Explicit dynamic spreads still permit dynamic element operations. +:expect 3 +[...([{a: 3}] as dyn)].0.a +//β”‚ = 3 + +// Recursive discovery observes later shapes without repeatedly scanning the graph. +:e +private fun recursiveHidden(x) = x.a +fun recursiveExport(n) = if n == 0 then {callback: recursiveHidden} else recursiveExport(n - 1) +recursiveHidden({a: 1}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.288: private fun recursiveHidden(x) = x.a +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.288: private fun recursiveHidden(x) = x.a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ This value is stored in this record field. +//β”‚ β•‘ l.289: fun recursiveExport(n) = if n == 0 then {callback: recursiveHidden} else recursiveExport(n - 1) +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This value is returned here. +//β”‚ β•‘ l.289: fun recursiveExport(n) = if n == 0 then {callback: recursiveHidden} else recursiveExport(n - 1) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'recursiveExport' is exposed by this compilation unit. +//β”‚ β•‘ l.289: fun recursiveExport(n) = if n == 0 then {callback: recursiveHidden} else recursiveExport(n - 1) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A term overload cannot hide an exposed class interface from this check. +:e +class Shadowed { + fun bad(x) = x.a +} +fun Shadowed() = 0 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.312: fun bad(x) = x.a +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.312: fun bad(x) = x.a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Member 'bad' is accessible here. +//β”‚ β•‘ l.312: fun bad(x) = x.a +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'Shadowed' is exposed by this compilation unit. +//β”‚ β•‘ l.311: class Shadowed { +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.312: fun bad(x) = x.a +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.313: } +//β”‚ β•‘ ^ +//β”‚ β•‘ l.314: fun Shadowed() = 0 +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^ + +// A constructor test narrows the class, but cannot close its unannotated fields. +:e +class Holder(val value) +fun inspect(x) = if x is Holder(y) then y.a else 0 +inspect(Holder({a: 1})) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.338: fun inspect(x) = if x is Holder(y) then y.a else 0 +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ This constructor field may contain a value of unknown shape. +//β”‚ β•‘ l.337: class Holder(val value) +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.338: fun inspect(x) = if x is Holder(y) then y.a else 0 +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'inspect' is exposed by this compilation unit. +//β”‚ β•‘ l.338: fun inspect(x) = if x is Holder(y) then y.a else 0 +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Declared fields remain usable after narrowing an unknown input. +:expect 2 +class KnownField(val a: Int) +class TypedHolder(val value: KnownField) +fun inspectTyped(x) = if x is TypedHolder(y) then y.a else 0 +inspectTyped(TypedHolder(KnownField(2))) +//β”‚ = 2 + +// A class pattern also admits arbitrary instantiations of its type parameters. +:e +class GenericHolder[A](val value: A) +fun inspectGeneric(x) = if x is GenericHolder(y) then y.a else 0 +inspectGeneric(GenericHolder({a: 1})) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.366: fun inspectGeneric(x) = if x is GenericHolder(y) then y.a else 0 +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ This constructor field may contain a value of unknown shape. +//β”‚ β•‘ l.365: class GenericHolder[A](val value: A) +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.366: fun inspectGeneric(x) = if x is GenericHolder(y) then y.a else 0 +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'inspectGeneric' is exposed by this compilation unit. +//β”‚ β•‘ l.366: fun inspectGeneric(x) = if x is GenericHolder(y) then y.a else 0 +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Narrowing does not mix the external activation into concrete calls. +:expect 3 +val unwrap = case Holder(y) then y +unwrap(Holder({a: 3})).a +//β”‚ = 3 +//β”‚ unwrap = fun + +// Abstract annotations still admit a class test and its declared field interface. +:expect 4 +fun inspectAbstract(x: Any) = if x is TypedHolder(y) then y.a else 0 +inspectAbstract(TypedHolder(KnownField(4))) +//β”‚ = 4 + +// A result annotation does not hide callable methods of a returned private class. +// FIXME: Follow the declared member interface to its implementation without +// exposing initializer shapes hidden by the nominal annotation. +:breakme +:e +private class EscapedClass with + fun inspect(x) = x.a +fun annotatedInstance(): EscapedClass = new EscapedClass +(new EscapedClass).inspect({a: 1}) +//β”‚ = 1 diff --git a/hkmc2/shared/src/test/mlscript/newres/Interpretations.mls b/hkmc2/shared/src/test/mlscript/newres/Interpretations.mls new file mode 100644 index 0000000000..0b4eb8e8d0 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/Interpretations.mls @@ -0,0 +1,98 @@ +:. + + +// Ordinary references must resolve even when no caller asks for their shapes. +fun forwardValue() = { + open Later + value +} +module Later { val value = 23 } + +:expect 23 +forwardValue() +//β”‚ = 23 + + +// Parentheses and locally opened blocks preserve a class target's interpretation. +module Choices { + class Item { fun answer() = 24 } + fun Item() = 25 +} +fun choices() = { + open Choices + [Item(), (new (Item)).answer(), (new (Choices.{Item})).answer()] +} + +:expect [25, 24, 24] +choices() +//β”‚ = [25, 24, 24] + + +// Constructor arguments are terms even though the constructor target is a class. +module Arguments { + class Box(val value) + class Item + fun Item() = 26 +} +fun construct() = { + open Arguments + (new Box(Item())).value +} + +:expect 26 +construct() +//β”‚ = 26 + + +// Pattern targets retain the class interpretation; guards and results are terms. +fun matchBox(box) = { + open Arguments + if box is Box(value) and value > 0 then Item() else 0 +} + +:expect [26, 0] +[matchBox(Arguments.Box(1)), matchBox(Arguments.Box(0))] +//β”‚ = [26, 0] + + +// Type-only members must not be interpreted as runtime values in signatures, +// aliases, type arguments, or ascriptions. +module Types { type Alias = Int } +fun annotated() = { + open Types + type Local = (Alias) + type Pair = [Alias, Alias] + type Fn = Alias -> Alias + type Record = { field: Alias } + type Excluded = ~Alias + type Combined = Alias | (Alias & Alias) + fun echo[A](x: Alias): Alias = x + (echo[Alias](27) as Alias) +} + +:expect 27 +annotated() +//β”‚ = 27 + + +// The same type-only name in an expression must still be rejected. +:e +fun invalidValue() = { + open Types + Alias +} +invalidValue() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in wildcard-open reference +//β”‚ β•‘ l.82: Alias +//β”‚ β•‘ ^^^^^ +//β”‚ ╙── Expected a term; got type alias 'Alias' +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + + +// The prefix of a class selection is a term, even if that name also has a class. +class Carrier +fun Carrier() = Arguments + +:expect 26 +(new (Carrier().Box)(Carrier().Item())).value +//β”‚ = 26 diff --git a/hkmc2/shared/src/test/mlscript/newres/InvalidClassCompanions.mls b/hkmc2/shared/src/test/mlscript/newres/InvalidClassCompanions.mls new file mode 100644 index 0000000000..3d4e694b91 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/InvalidClassCompanions.mls @@ -0,0 +1,189 @@ +:. +:!js + + +// Companions are definitions of fresh functions, never values to be augmented. +:e +class Primitive +val Primitive = 7 +//β”‚ ╔══[COMPILATION ERROR] A class companion must be a function with a parameter list +//β”‚ β•‘ l.8: val Primitive = 7 +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Arbitrary values cannot be overloaded with class 'Primitive' +//β”‚ β•‘ l.7: class Primitive +//β”‚ ╙── ^^^^^^^^^^^^^^^ + +:e +val Reverse = 13 +class Reverse +//β”‚ ╔══[COMPILATION ERROR] A class companion must be a function with a parameter list +//β”‚ β•‘ l.17: val Reverse = 13 +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Arbitrary values cannot be overloaded with class 'Reverse' +//β”‚ β•‘ l.18: class Reverse +//β”‚ ╙── ^^^^^^^^^^^^^ + +:e +class Getter +fun Getter = 14 +//β”‚ ╔══[COMPILATION ERROR] A class companion must be a function with a parameter list +//β”‚ β•‘ l.28: fun Getter = 14 +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Arbitrary values cannot be overloaded with class 'Getter' +//β”‚ β•‘ l.27: class Getter +//β”‚ ╙── ^^^^^^^^^^^^ + +:e +module MutableMembers { + class Mutable + mut val Mutable = 17 +} +//β”‚ ╔══[COMPILATION ERROR] A class companion must be a function with a parameter list +//β”‚ β•‘ l.39: mut val Mutable = 17 +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Arbitrary values cannot be overloaded with class 'Mutable' +//β”‚ β•‘ l.38: class Mutable +//β”‚ ╙── ^^^^^^^^^^^^^ + + +// Object-valued initializers are rejected too: they may be frozen or aliased. +:e +class Instance +val Instance = new Instance +//β”‚ ╔══[COMPILATION ERROR] A class companion must be a function with a parameter list +//β”‚ β•‘ l.52: val Instance = new Instance +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Arbitrary values cannot be overloaded with class 'Instance' +//β”‚ β•‘ l.51: class Instance +//β”‚ ╙── ^^^^^^^^^^^^^^ + +val existing = { answer: 42 } + +:e +class Aliased +val Aliased = existing +//β”‚ ╔══[COMPILATION ERROR] A class companion must be a function with a parameter list +//β”‚ β•‘ l.64: val Aliased = existing +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Arbitrary values cannot be overloaded with class 'Aliased' +//β”‚ β•‘ l.63: class Aliased +//β”‚ ╙── ^^^^^^^^^^^^^ + +:e +class FreshRecord +val FreshRecord = { answer: 42 } +//β”‚ ╔══[COMPILATION ERROR] A class companion must be a function with a parameter list +//β”‚ β•‘ l.74: val FreshRecord = { answer: 42 } +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Arbitrary values cannot be overloaded with class 'FreshRecord' +//β”‚ β•‘ l.73: class FreshRecord +//β”‚ ╙── ^^^^^^^^^^^^^^^^^ + +:e +class FunctionValue +val FunctionValue = x => x +//β”‚ ╔══[COMPILATION ERROR] A class companion must be a function with a parameter list +//β”‚ β•‘ l.84: val FunctionValue = x => x +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Arbitrary values cannot be overloaded with class 'FunctionValue' +//β”‚ β•‘ l.83: class FunctionValue +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^ + + +// The same rule applies inside modules and local scopes. +:e +module Members { + class Item + val Item = existing +} +//β”‚ ╔══[COMPILATION ERROR] A class companion must be a function with a parameter list +//β”‚ β•‘ l.97: val Item = existing +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Arbitrary values cannot be overloaded with class 'Item' +//β”‚ β•‘ l.96: class Item +//β”‚ ╙── ^^^^^^^^^^ + +:e +fun local() = { + class Item + val Item = 0 + Item +} +//β”‚ ╔══[COMPILATION ERROR] A class companion must be a function with a parameter list +//β”‚ β•‘ l.109: val Item = 0 +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Arbitrary values cannot be overloaded with class 'Item' +//β”‚ β•‘ l.108: class Item +//β”‚ ╙── ^^^^^^^^^^ + + +// A prototype method cannot hold a class that differs between instances. +:e +class Container(val value: Int) { + class Item { fun answer() = value } + fun Item() = new Item +} +//β”‚ ╔══[COMPILATION ERROR] Not yet supported: class companion 'Item' defined as an instance method +//β”‚ β•‘ l.124: fun Item() = new Item +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ An instance method is shared by all instances, but its companion is created per instance +//β”‚ β•‘ l.123: class Item { fun answer() = value } +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +// Parameterless functions cannot own a module companion either. +:e +fun GetterModule = existing +module GetterModule { val answer = 42 } +//β”‚ ╔══[COMPILATION ERROR] A module companion must be a function with a parameter list +//β”‚ β•‘ l.136: fun GetterModule = existing +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Arbitrary values cannot be overloaded with module 'GetterModule' +//β”‚ β•‘ l.137: module GetterModule { val answer = 42 } +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +// Declarations do not allocate companion functions either. +:e +class Declared +fun Declared(): Int +//β”‚ ╔══[COMPILATION ERROR] A term declaration cannot have an implemented class companion +//β”‚ β•‘ l.149: fun Declared(): Int +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Companion 'Declared' is defined here +//β”‚ β•‘ l.148: class Declared +//β”‚ ╙── ^^^^^^^^^^^^^^ + +:e +class ForeignFunction +declare fun ForeignFunction(): Int +//β”‚ ╔══[COMPILATION ERROR] A term declaration cannot have an implemented class companion +//β”‚ β•‘ l.159: declare fun ForeignFunction(): Int +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Companion 'ForeignFunction' is defined here +//β”‚ β•‘ l.158: class ForeignFunction +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^ + +:e +class ModuleContainer(val value: Int) { + module Item { val answer = value } + fun Item() = value +} +//β”‚ ╔══[COMPILATION ERROR] Not yet supported: module companion 'Item' defined as an instance method +//β”‚ β•‘ l.170: fun Item() = value +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ An instance method is shared by all instances, but its companion is created per instance +//β”‚ β•‘ l.169: module Item { val answer = value } +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +// A bare declaration is rejected even when it precedes its companion. +:e +fun Bare +class Bare +//β”‚ ╔══[COMPILATION ERROR] A term declaration cannot have an implemented class companion +//β”‚ β•‘ l.182: fun Bare +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Companion 'Bare' is defined here +//β”‚ β•‘ l.183: class Bare +//β”‚ ╙── ^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/newres/LegacyGenericMethods.mls b/hkmc2/shared/src/test/mlscript/newres/LegacyGenericMethods.mls new file mode 100644 index 0000000000..31495f0c40 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/LegacyGenericMethods.mls @@ -0,0 +1,19 @@ +:. + +import "../../mlscript-compile/FingerTreeList.mls" + +// Calling a generic method imported from a legacy fixture must preserve the +// class's type argument when interpreting the method's annotated result. +:expect 42 +FingerTreeList.mk(42).at(0) +//β”‚ = 42 + +import "../../mlscript-compile/LegacyGenericLibrary.mls" + +:expect [7, "other"] +class NumberBox(val number: Int) +class TextBox(val text: Str) +fun readNumber(cell: LegacyGenericLibrary.Cell[NumberBox]) = cell.get.number +fun readText(cell: LegacyGenericLibrary.Cell[TextBox]) = cell.getAt(0).text +[readNumber(LegacyGenericLibrary.Cell(NumberBox(7))), readText(LegacyGenericLibrary.Cell(TextBox("other")))] +//β”‚ = [7, "other"] diff --git a/hkmc2/shared/src/test/mlscript/newres/LexerMigration.mls b/hkmc2/shared/src/test/mlscript/newres/LexerMigration.mls new file mode 100644 index 0000000000..2aaca2631b --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/LexerMigration.mls @@ -0,0 +1,20 @@ +:. + +// Explicit imports override the builtin unary ~ under the new open semantics. +:expect "ab" +module Strings with + fun (~) concat(left: Str, right: Str): Str = left + right +open Strings { concat, ~ } +"a" ~ "b" +//β”‚ = "ab" + +// Lexer supplies a LineLookupTable instance, but new resolution does not yet +// insert the trailing contextual argument at calls to Token's factory methods. +:fixme +:expect 5 +class Context(val value: Int) +private fun make(value: Int)(using context: Context): Int = value + context.value +using Context = Context(2) +make(3) +//β”‚ ═══[RUNTIME ERROR] Expected: '5', got: 'fun' +//β”‚ = fun diff --git a/hkmc2/shared/src/test/mlscript/newres/MapOptionFlow.mls b/hkmc2/shared/src/test/mlscript/newres/MapOptionFlow.mls new file mode 100644 index 0000000000..a62d821be9 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/MapOptionFlow.mls @@ -0,0 +1,31 @@ +:. + +import "../../mlscript-compile/MutMap.mls" +import "../../mlscript-compile/Option.mls" +open Option + +// A fresh map retains its inserted values through the curried get API and +// the higher-order pipe helper. Some itself needs no nested class pattern. +:expect 42 +class Box(val value: Int) +val m = MutMap.empty +m |> MutMap.insert("x", Box(42)) +private fun read() = if m |> MutMap.get("x") is Some(b) then b.value else 0 +read() +//β”‚ = 42 +//β”‚ m = MutMap(Map(1) {"x" => Box(42)}) + +// An explicitly exposed map supplies the same element interface. +:expect [42, 42, 0] +class Box(val value: Int) +val m: MutMap.MutMap[Str, Box] = MutMap.empty +m |> MutMap.insert("x", Box(42)) +val piped = if m |> MutMap.get("x") is Some(b) then b.value else 0 +val direct = if MutMap.get("x")(m) is Some(b) then b.value else 0 +val absent = if m |> MutMap.get("absent") is Some(b) then b.value else 0 +[piped, direct, absent] +//β”‚ = [42, 42, 0] +//β”‚ absent = 0 +//β”‚ direct = 42 +//β”‚ m = MutMap(Map(1) {"x" => Box(42)}) +//β”‚ piped = 42 diff --git a/hkmc2/shared/src/test/mlscript/newres/MemberAssignments.mls b/hkmc2/shared/src/test/mlscript/newres/MemberAssignments.mls new file mode 100644 index 0000000000..17bd031c7d --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/MemberAssignments.mls @@ -0,0 +1,22 @@ +:. + +class Counter with + let count = 0 + fun increment() = { + set count = count + 1 + count + } + +:expect [1, 2] +let counter = new mut Counter +[counter.increment(), counter.increment()] +//β”‚ = [1, 2] +//β”‚ counter = Counter + +:e +module Fixed with + fun value() = 0 + fun change() = set value = 1 +//β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Int' at an unrelated type 'Function' +//β”‚ β•‘ l.19: fun change() = set value = 1 +//β”‚ ╙── ^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/newres/MemberVariables.mls b/hkmc2/shared/src/test/mlscript/newres/MemberVariables.mls new file mode 100644 index 0000000000..d739513c95 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/MemberVariables.mls @@ -0,0 +1,39 @@ +:. + +// Module let initializers feed reads in nested functions. +:expect [3, 5] +module TextCell with + let text = "abc" + fun setText(value: Str) = set text = value + fun length() = text.length +val first = TextCell.length() +TextCell.setText("hello") +[first, TextCell.length()] +//β”‚ = [3, 5] +//β”‚ first = 3 + +// Reading a let initialized from a parameter refers to the let's storage, +// not the parameter's symbol, and preserves each receiver's type argument. +:expect [7, "other"] +class Cell[A](value: A) with + let stored = value + fun get() = stored +class NumberBox(val number: Int) +class TextBox(val text: Str) +[Cell(NumberBox(7)).get().number, Cell(TextBox("other")).get().text] +//β”‚ = [7, "other"] + +// All assignments contribute, including those inside exposed functions. +:e +module Changing with + let value = "before" + fun replace() = set value = 42 + fun length() = value.length +Changing.length() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.31: fun length() = value.length +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal does not contain member 'length' +//β”‚ β•‘ l.30: fun replace() = set value = 42 +//β”‚ ╙── ^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/newres/MigratedFixtureInterfaces.mls b/hkmc2/shared/src/test/mlscript/newres/MigratedFixtureInterfaces.mls new file mode 100644 index 0000000000..6cde48ae71 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/MigratedFixtureInterfaces.mls @@ -0,0 +1,93 @@ +:. +:js + +import "../../mlscript-compile/Rendering.mls" +import "../../mlscript-compile/Runtime.mls" +import "../../mlscript-compile/LazyFingerTree.mls" + +// Callback adapters retain their result interfaces through an imported module. +:expect [4, 5] +class Box(val value: Int) +[3, 4].map(Rendering.pass1((n: Int) => Box(n + 1))).map((box, ...) => box.value) +//β”‚ = [4, 5] + +:expect 6 +Rendering.render([1, 2]).length +//β”‚ = 6 + +// Lazy splices expose indexing and iteration before and after materialization. +:expect [0, 1, 2, 3] +[...LazyFingerTree.concat(LazyFingerTree.mk(0, 1), [2, 3])] +//β”‚ = [0, 1, 2, 3] + +:expect 4 +LazyFingerTree.concat([1, 2], [3, 4]).materialize().length +//β”‚ = 4 + +:expect [2, 3] +Runtime.Tuple.slice([1, 2, 3, 4], 1, 1) +//β”‚ = [2, 3] + +// Host collections retain typed values when used through declared interfaces. +:expect "value" +fun lookup(map: WeakMap[Object, Str], key: Object) = if map.get(key) is + Str as value then value + else "missing" +val key = new Object +val map = new WeakMap +map.set(key, "value") +lookup(map, key) +//β”‚ = "value" +//β”‚ key = {} +//β”‚ map = WeakMap { } + +// This runtime primitive preserves undefined; user-call instrumentation forbids returning it. +:noSanityCheck +:expect true +Runtime.Tuple.get([undefined], 0) is undefined +//β”‚ = true + +// Tuple runtime operations also support lazy views. +:expect 2 +Runtime.Tuple.get(LazyFingerTree.mk(1, 2), -1) +//β”‚ = 2 + +import "../../mlscript-compile/Term.mls" + +let termContext = Term.Context(new Map(), new Map(), new Set(), true) +let termSymbol = Term.Symbol("x") +//β”‚ termContext = Context(Map(0) {}, Map(0) {}, Set(0) {}, true) +//β”‚ termSymbol = Symbol("x") + +:expect "(x_0) =>\n x_0" +Term.show(Term.Lam([termSymbol], Term.Ref(termSymbol)), termContext) +//β”‚ = "(x_0) =>\n x_0" + +:expect ["null", "undefined", "true", "42"] +[null, undefined, true, 42].map((lit, ...rest) => Term.show(Term.Lit(lit), termContext)) +//β”‚ = ["null", "undefined", "true", "42"] + +:expect "undefined" +Term.showPattern(Term.LitPattern(undefined), termContext) +//β”‚ = "undefined" + +// Shared read interfaces retain element types without invariant Array[Any] annotations. +:expect [4, 5, 6] +class Item(val value: Int) +fun firstValue(xs: Indexable[Item]) = xs.at(0).value +fun iteratedValue(xs: IterableIndexable[Item]) = xs.iterator().next().value.value +[firstValue([Item(4)]), firstValue(LazyFingerTree.mk(Item(5))), + iteratedValue(LazyFingerTree.concat([Item(6)]).materialize())] +//β”‚ = [4, 5, 6] + +// Iterator is covariant: a more specific iterator remains usable through its base interface. +:expect 7 +class SpecialItem extends Item(7) +fun iteratorValue(xs: Iterator[Item]) = xs.next().value.value +val items: Set[SpecialItem] = new Set +items.add(new SpecialItem) +val specialized: Iterator[SpecialItem] = items.values() +iteratorValue(specialized) +//β”‚ = 7 +//β”‚ items = Set(1) {SpecialItem { value: 7 }} +//β”‚ specialized = Iterator {} diff --git a/hkmc2/shared/src/test/mlscript/newres/ModuleCaptures.mls b/hkmc2/shared/src/test/mlscript/newres/ModuleCaptures.mls new file mode 100644 index 0000000000..d4f6835df5 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ModuleCaptures.mls @@ -0,0 +1,105 @@ +:. + +// Module members can capture an enclosing function parameter. +fun get(initial) = { + module Local { + val item = initial + } + Local.item +} + +:expect [0, 1] +[get(0), get(1)] +//β”‚ = [0, 1] + +// Each additional module remains transparent to capture marks. +fun nested(initial) = { + module Outer { + module Inner { + val item = initial + } + } + Outer.Inner.item +} + +:expect [2, 3] +[nested(2), nested(3)] +//β”‚ = [2, 3] + +// Objects follow the same rule; class capture behavior stays unchanged. +fun obj(initial) = { + object Local { + val item = initial + } + Local.item +} + +:expect [4, 5] +[obj(4), obj(5)] +//β”‚ = [4, 5] + +fun cls(initial) = { + class Local { + val item = initial + } + (new Local).item +} + +:expect [6, 7] +[cls(6), cls(7)] +//β”‚ = [6, 7] + +// Member functions retain their own boundaries across the module. +fun getters(initial) = { + module Local { + fun item = initial + fun method() = initial + } + [Local.item, Local.method()] +} + +:expect [[8, 8], [9, 9]] +[getters(8), getters(9)] +//β”‚ = [[8, 8], [9, 9]] + +// Distinct member names expose accidental mixing of shapes from different calls. +module Left { val left = 11 } +module Right { val right = 12 } + +:expect [11, 12] +[get(Left).left, get(Right).right] +//β”‚ = [11, 12] + +// Returning a module must retain its enclosing call context for later selections. +fun escape(initial) = { + module Local { val item = initial } + Local +} + +:expect [11, 12] +[escape(Left).item.left, escape(Right).item.right] +//β”‚ = [11, 12] + +fun escapedNested(initial) = { + module Outer { + module Inner { val item = initial } + } + Outer +} + +:expect [11, 12] +[escapedNested(Left).Inner.item.left, escapedNested(Right).Inner.item.right] +//β”‚ = [11, 12] + +// A returned module's methods retain both their own and their enclosing contexts. +fun escapedMethod(initial) = { + module Local { + fun read() = initial + } + Local +} + +:noInline +:expect [11, 12] +[escapedMethod(Left).read().left, escapedMethod(Right).read().right] +//β”‚ = [11, 12] diff --git a/hkmc2/shared/src/test/mlscript/newres/MultiParamClasses.mls b/hkmc2/shared/src/test/mlscript/newres/MultiParamClasses.mls new file mode 100644 index 0000000000..c2dcfa45a5 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/MultiParamClasses.mls @@ -0,0 +1,83 @@ +:. + + +:sr +class Foo(val a)(val b) { + val c = a + b +} +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fun Foo⁰ +//β”‚ class FooΒΉ::FooΒ²(val a⁰)(val b⁰) { +//β”‚ val aΒΉ::aΒ² β€Ήflow:a⁰› = a⁰^aΒ², +//β”‚ val bΒΉ::bΒ² β€Ήflow:b⁰› = b⁰^bΒ², +//β”‚ val c⁰::cΒΉ β€Ήflow:c⁰› = +(FooΒ².this.aΒΉ^cΒΉ, FooΒ².this.bΒΉ^cΒΉ)β€Ήapp⁰› +//β”‚ } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +:sr +let f = new Foo +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let f⁰, f⁰ = new FooΒΉ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ f = fun f + +let mk = f(1) +//β”‚ mk = fun + +:sr +mk(2) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ mk⁰(2)β€ΉappΒΉβ€Ί +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = Foo(1) + +:sr +f(1)(2) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ f⁰(1)β€ΉappΒ²β€Ί(2)β€ΉappΒ³β€Ί +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = Foo(1) + +:sr +let f = new Foo(1) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let fΒΉ, fΒΉ = new FooΒΉ(1) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ f = fun f + +:sr +f(2) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fΒΉ(2)β€Ήapp⁴› +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = Foo(1) + +:sr +let f = new Foo(1)(2) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let fΒ², fΒ² = new FooΒΉ(1)(2) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ f = Foo(1) + +:sr +f.a +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fΒ².aΒΉ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 1 + +:sr +f.b +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fΒ².bΒΉ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 2 + +:sr +f.c +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fΒ².c⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 3 + + diff --git a/hkmc2/shared/src/test/mlscript/newres/MutableArrays.mls b/hkmc2/shared/src/test/mlscript/newres/MutableArrays.mls new file mode 100644 index 0000000000..5dd3c4412d --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/MutableArrays.mls @@ -0,0 +1,270 @@ +:. + +// Mutable arrays expose their element union without retaining positions or length. +:expect [1, [1, 2]] +class Item(val value: Int) +let xs = mut [Item(1)] +xs.push(Item(2)) +[xs.0.value, xs.map((x, ...) => x.value)] +//β”‚ = [1, [1, 2]] +//β”‚ xs = [Item(1), Item(2)] + +// Empty literals acquire their element interface from writes through aliases. +:expect [3, 4] +class Item(val value: Int) +let xs = mut [] +let alias = xs +alias.push(Item(4)) +xs.unshift(Item(3)) +[xs.0.value, xs.1.value] +//β”‚ = [3, 4] +//β”‚ alias = [Item(3), Item(4)] +//β”‚ xs = [Item(3), Item(4)] + +// Indexed writes contribute elements, including computed indices. +:expect [2, 3] +class Item(val value: Int) +let xs = mut [Item(1)] +set xs.0 = Item(2) +let index = 1 +set xs.[index] = Item(3) +[xs.0.value, xs.1.value] +//β”‚ = [2, 3] +//β”‚ index = 1 +//β”‚ xs = [Item(2), Item(3)] + +// Separate allocations do not share inferred element interfaces. +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +let xs = mut [First(1)] +let ys = mut [] +ys.push(Second(2)) +[xs.0.first, ys.0.second] +//β”‚ = [1, 2] +//β”‚ xs = [First(1)] +//β”‚ ys = [Second(2)] + +// The same literal retains distinct element flow in different function calls. +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +fun singleton(x) = mut [x] +let xs = singleton(First(1)) +let ys = singleton(Second(2)) +[xs.0.first, ys.0.second] +//β”‚ = [1, 2] +//β”‚ xs = [First(1)] +//β”‚ ys = [Second(2)] + +// Every possible element is checked, including values inserted by methods. +:ge +class First(val first: Int) +class Second(val second: Int) +let xs = mut [First(1)] +xs.push(Second(2)) +xs.0.first +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.66: xs.0.first +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Second' does not contain member 'first' +//β”‚ β•‘ l.65: xs.push(Second(2)) +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ xs = [First(1), Second(2)] + +// Nested arrays listen to later inner and outer writes. +:expect [[1], [2]] +class Item(val value: Int) +let rows = mut [mut []] +rows.0.push(Item(1)) +rows.push(mut [Item(2)]) +rows.map((row, ...) => row.map((item, ...) => item.value)) +//β”‚ = [[1], [2]] +//β”‚ rows = [[Item(1)], [Item(2)]] + +// Writes through a function parameter return to the same array context. +:expect 3 +class Item(val value: Int) +private fun append(xs, value) = xs.push(value) +let xs = mut [] +append(xs, Item(3)) +xs.0.value +//β”‚ = 3 +//β”‚ xs = [Item(3)] + +// Calls producing an empty literal stay independent after later writes. +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +fun empty() = mut [] +let xs = empty() +let ys = empty() +xs.push(First(1)) +ys.push(Second(2)) +[xs.0.first, ys.0.second] +//β”‚ = [1, 2] +//β”‚ xs = [First(1)] +//β”‚ ys = [Second(2)] + +// Already compiled blocks retain the live element flow for later reads. +let retained = mut [] +//β”‚ retained = [] + +:expect 5 +class Item(val value: Int) +retained.push(Item(5)) +retained.0.value +//β”‚ = 5 + +// Concrete annotations do not acquire the extra members of inserted values. +:ge +class Base +class Child extends Base with + val extra = 1 +let xs = (mut [] as Array[Base]) +xs.push(new Child) +xs.0.extra +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.127: xs.0.extra +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.125: let xs = (mut [] as Array[Base]) +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ xs = [Child { extra: 1 }] + +// Indexed and fill writes must invalidate initializer-only member assumptions. +:ge +class First(val first: Int) +class Second(val second: Int) +let xs = mut [First(1)] +set xs.[0] = Second(2) +xs.0.first +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.143: xs.0.first +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Second' does not contain member 'first' +//β”‚ β•‘ l.142: set xs.[0] = Second(2) +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ xs = [Second(2)] + +:ge +class First(val first: Int) +class Second(val second: Int) +let xs = mut [First(1)] +xs.fill(Second(2)) +xs.0.first +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.158: xs.0.first +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Second' does not contain member 'first' +//β”‚ β•‘ l.157: xs.fill(Second(2)) +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ xs = [Second(2)] + +// A mutable array does not hide a closure exposed by a public result. +:ge +module Exposed with + fun make() = mut [x => x.missing] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.171: fun make() = mut [x => x.missing] +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'missing' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.171: fun make() = mut [x => x.missing] +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ This value is stored in this array. +//β”‚ β•‘ l.171: fun make() = mut [x => x.missing] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This value is returned here. +//β”‚ β•‘ l.171: fun make() = mut [x => x.missing] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Member 'make' is accessible here. +//β”‚ β•‘ l.171: fun make() = mut [x => x.missing] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'Exposed' is exposed by this compilation unit. +//β”‚ β•‘ l.170: module Exposed with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.171: fun make() = mut [x => x.missing] +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// Explicit constructor type arguments remain opaque when methods receive subtypes. +:ge +class Base +class Child extends Base with + val extra = 1 +let xs = new mut Array[Base](0) +xs.push(new Child) +xs.map((x, ...) => x.extra) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.201: xs.map((x, ...) => x.extra) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.199: let xs = new mut Array[Base](0) +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ xs = [Child { extra: 1 }] + +// Recursive element flow terminates without expanding a positional layout. +:expect true +let recursive = mut [] +recursive.push(recursive) +recursive.0.0 === recursive +//β”‚ = true +//β”‚ recursive = [ref'1] as ref'1 + +// TODO: splice's optional deletion count needs a separate interface from inserted elements. +:fixme +:expect 2 +class Item(val value: Int) +let xs = mut [] +xs.splice(0, 0, Item(2)) +xs.0.value +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'value' has no resolved target +//β”‚ β•‘ l.225: xs.0.value +//β”‚ ╙── ^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' +//β”‚ xs = [Item(2)] + +// Invariant Array[A] arguments retain the element reference in both directions. +:expect 3 +class Item(val value: Int) +fun appendTyped[A](xs: Array[A], value: A) = xs.push(value) +let xs = mut [] +appendTyped(xs, Item(3)) +xs.0.value +//β”‚ = 3 +//β”‚ xs = [Item(3)] + +// Recursive input/output constraints preserve caller activations. +:expect 5 +class Item(val value: Int) +fun append[A](xs: Array[A], value: A, n: Int) = + if n > 0 then append(xs, value, n - 1) else xs.push(value) +let xs = mut [] +append(xs, Item(5), 2) +xs.0.value +//β”‚ = 5 +//β”‚ xs = [Item(5)] + +// Distinct applications through an alias instantiate A separately. +// The recursive site is shared; enclosing marks still separate its callers. +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +fun append[A](xs: Array[A], value: A, n: Int) = + if n > 0 then append(xs, value, n - 1) else xs.push(value) +let g = append +let xs = mut [] +let ys = mut [] +g(xs, First(1), 2) +g(ys, Second(2), 2) +[xs.0.first, ys.0.second] +//β”‚ = [1, 2] +//β”‚ g = fun append +//β”‚ xs = [First(1)] +//β”‚ ys = [Second(2)] diff --git a/hkmc2/shared/src/test/mlscript/newres/MutationFlow.mls b/hkmc2/shared/src/test/mlscript/newres/MutationFlow.mls new file mode 100644 index 0000000000..36d087154d --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/MutationFlow.mls @@ -0,0 +1,18 @@ +:. + +// Assignment leaves the variable's initializer shape in place, so the +// later read is checked against a one-element tuple instead of the stored array. +:e +:expect 1 +let values = [0] +set values = values.concat([1]) +values.1 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.9: values.1 +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Tuple literal does not contain member '1' +//β”‚ β•‘ l.7: let values = [0] +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '1', got: 'undefined' +//β”‚ values = [0, 1] diff --git a/hkmc2/shared/src/test/mlscript/newres/NamedFields.mls b/hkmc2/shared/src/test/mlscript/newres/NamedFields.mls new file mode 100644 index 0000000000..864c1e128e --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/NamedFields.mls @@ -0,0 +1,92 @@ +:. + +// Named values form one record after all positional values, even when interleaved. +:expect [3, 5, 7] +private fun named(fields) = [fields.a, fields.b, fields.c] +named(a: 3, b: 5, c: 7) +//β”‚ = [3, 5, 7] + +:expect [1, 2, 3, 4] +private fun mixed(x, y, fields) = [x, y, fields.a, fields.b] +mixed(a: 3, 1, b: 4, 2) +//β”‚ = [1, 2, 3, 4] + +:expect [2, 4, 6] +let inner = [2, a: 4] +let combined = [b: 6, ...inner] +[combined.0, combined.1.a, combined.2.b] +//β”‚ = [2, 4, 6] +//β”‚ combined = [2, {a: 4}, {b: 6}] +//β”‚ inner = [2, {a: 4}] + +:expect [8, 9] +private fun forward(...xs) = named(...xs) +let result = forward(a: 7, b: 8, c: 9) +[result.1, result.2] +//β”‚ = [8, 9] +//β”‚ result = [7, 8, 9] + +// Field values retain function-call marks through returned tuples and record spreads. +:expect ["LEFT", 3] +fun pack(x) = [value: x] +let left = pack("left") +let right = pack([1, 2, 3]) +[({...left.0}).value.toUpperCase(), ({...right.0}).value.length] +//β”‚ = ["LEFT", 3] +//β”‚ left = [{value: "left"}] +//β”‚ right = [{value: [1, 2, 3]}] + +:expect 12 +let overwritten = [a: {old: 0}, a: {answer: 12}] +overwritten.0.a.answer +//β”‚ = 12 +//β”‚ overwritten = [{a: {answer: 12}}] + +// Packing must not reorder or repeat evaluation of values or computed keys. +:expect ["abcde", 2, 4, 1, 3, 5] +let log = "" +fun tick(tag: Str, value) = { + set log += tag + value +} +let evaluated = [a: tick("a", 1), tick("b", 2), (tick("c", "c")): 3, + tick("d", 4), e: tick("e", 5)] +[log, evaluated.0, evaluated.1, evaluated.2.("a"), evaluated.2.("c"), evaluated.2.e] +//β”‚ = ["abcde", 2, 4, 1, 3, 5] +//β”‚ evaluated = [2, 4, {a: 1, c: 3, e: 5}] +//β”‚ log = "abcde" + +:e +fun one(fields: {a: Int}) = fields.a +one(1, a: 2) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.61: one(1, a: 2) +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'one' expected 1 argument, but got 2 +//β”‚ β•‘ l.60: fun one(fields: {a: Int}) = fields.a +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:e +let key = "a" +[a: {answer: 1}, (key): {other: 2}].0.a.answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.71: [a: {answer: 1}, (key): {other: 2}].0.a.answer +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' across a computed key or unknown record spread. +//β”‚ β•Ÿβ”€β”€ This computed key can overwrite the selected member. +//β”‚ β•‘ l.71: [a: {answer: 1}, (key): {other: 2}].0.a.answer +//β”‚ ╙── ^^^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'answer' has no resolved target +//β”‚ β•‘ l.71: [a: {answer: 1}, (key): {other: 2}].0.a.answer +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ key = "a" + +:expect 13 +[(key): 0, a: 13].0.a +//β”‚ = 13 + +:expect [42, 43] +class Box(val fields) +[Box(answer: 42).fields.answer, (new Box(answer: 43)).fields.answer] +//β”‚ = [42, 43] diff --git a/hkmc2/shared/src/test/mlscript/newres/NegatedPatterns.mls b/hkmc2/shared/src/test/mlscript/newres/NegatedPatterns.mls new file mode 100644 index 0000000000..85980eda74 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/NegatedPatterns.mls @@ -0,0 +1,171 @@ +:. +:js + +// Negation removes only the excluded value, and emits a null test rather than instanceof. +:sjs +fun nullableLength(value: Str | null) = + if value is ~null as text then text.length else 0 +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let nullableLength; +//β”‚ nullableLength = function nullableLength(value) { +//β”‚ if (value === null) { return 0 } +//β”‚ return value.length; +//β”‚ }; +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +:expect [3, 0] +[nullableLength("abc"), nullableLength(null)] +//β”‚ = [3, 0] + +// Other singleton literals use the same refinement rule. +:expect [3, 0] +fun definedLength(value: Str | undefined) = + if value is ~undefined as text then text.length else 0 +[definedLength("abc"), definedLength(undefined)] +//β”‚ = [3, 0] + +:expect [3, 0] +fun truthyLength(value: Str | false) = + if value is ~false as text then text.length else 0 +[truthyLength("abc"), truthyLength(false)] +//β”‚ = [3, 0] + +// Excluding a class keeps the other union members and their declared fields. +class TextBox(val text: Str) +class Empty + +:expect [3, 0] +fun boxLength(value: TextBox | Empty) = + if value is ~Empty as box then box.text.length else 0 +[boxLength(TextBox("abc")), boxLength(new Empty)] +//β”‚ = [3, 0] + +// De Morgan's laws apply to both alternatives and conjunctions. +:expect [3, 0, 0] +fun presentLength(value: Str | null | undefined) = + if value is ~(null | undefined) as text then text.length else 0 +[presentLength("abc"), presentLength(null), presentLength(undefined)] +//β”‚ = [3, 0, 0] + +:expect [3, 0] +fun nonNullLength(value: Str | null) = + if value is ~(null & _) as text then text.length else 0 +[nonNullLength("abc"), nonNullLength(null)] +//β”‚ = [3, 0] + +:expect [3, 0] +fun stringLength(value: Str | Int) = + if value is ~(~Str) as text then text.length else 0 +[stringLength("abc"), stringLength(42)] +//β”‚ = [3, 0] + +// A negative test of a subclass must retain possible instances of its base class. +class Base(val text: Str) +class Child extends Base("child") + +:expect [3, 0] +fun baseLength(value: Base) = + if value is ~Child as base then base.text.length else 0 +[baseLength(Base("abc")), baseLength(new Child)] +//β”‚ = [3, 0] + +// Field tests and guards can fail even when the constructor matches. +:expect [3, 0] +fun nonEmptyLength(value: TextBox) = + if value is ~TextBox("") as box then box.text.length else 0 +[nonEmptyLength(TextBox("abc")), nonEmptyLength(TextBox(""))] +//β”‚ = [3, 0] + +:expect 3 +fun guardedLength(value: TextBox) = + if value is ~(TextBox where false) as box then box.text.length else 0 +guardedLength(TextBox("abc")) +//β”‚ = 3 + +// A named extractor is not an unconditional nominal test. +pattern Blank = TextBox("") + +:expect 3 +fun extractedLength(value: TextBox) = + if value is ~Blank as box then box.text.length else 0 +extractedLength(TextBox("abc")) +//β”‚ = 3 + +// Excluding null does not remove undefined or manufacture an interface for Any. +:ge +fun stillUndefined(value: Str | null | undefined) = + if value is ~null as text then text.length else 0 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.97: if value is ~null as text then text.length else 0 +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Undefined literal does not contain member 'length' +//β”‚ β•‘ l.96: fun stillUndefined(value: Str | null | undefined) = +//β”‚ ╙── ^^^^^^^^^ + +:ge +fun stillUnknown(value: Any) = + if value is ~TextBox as other then other.text else "" +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.107: if value is ~TextBox as other then other.text else "" +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'text' of a value with unknown shape. +//β”‚ β•‘ l.106: fun stillUnknown(value: Any) = +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Type 'Any' is declared without a member interface. +//β”‚ β•‘ in ../decls/Prelude.mls +//β”‚ β•‘ l.2: declare type Any +//β”‚ ╙── ^^^ + +// An impossible conjunction must not exclude either of its alternatives. +:ge +fun stillNull(value: Str | null) = + if value is ~(null & Str) as text then text.length else 0 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.122: if value is ~(null & Str) as text then text.length else 0 +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Null literal does not contain member 'length' +//β”‚ β•‘ l.121: fun stillNull(value: Str | null) = +//β”‚ ╙── ^^^^ + +// A negated loop condition is checked again after assigning the next link. +:expect [0, 2] +class Link(val next: Link | null) +fun linkCount(head: Link | null) = + let cur = head + let count = 0 + while cur is ~null as node do + set count += 1 + set cur = node.next + count +[linkCount(null), linkCount(Link(Link(null)))] +//β”‚ = [0, 2] + +// Rewritten loops used by effect handlers must also reach the failure exit. +:lift +:effectHandlers +:expect [0, 2] +fun countLinks(head: Link | null) = + let cur = head + let count = 0 + while cur is ~null as node do + set count += 1 + set cur = node.next + count +[countLinks(null), countLinks(Link(Link(null)))] +//β”‚ = [0, 2] + +// A failed negation in an imperative conditional skips its body. +:expect [0, 1] +fun mark(value: TextBox | Empty) = + let count = 0 + if value is ~Empty do set count = 1 + count +[mark(new Empty), mark(TextBox("abc"))] +//β”‚ = [0, 1] + +// A nominal constructor with unrestricted fields can be excluded as a whole. +:expect [0, 3] +fun unboxedLength(value: TextBox | Str) = + if value is ~TextBox(_) as text then text.length else 0 +[unboxedLength(TextBox("abc")), unboxedLength("abc")] +//β”‚ = [0, 3] diff --git a/hkmc2/shared/src/test/mlscript/newres/NestedClasses.mls b/hkmc2/shared/src/test/mlscript/newres/NestedClasses.mls new file mode 100644 index 0000000000..67c18dd5a6 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/NestedClasses.mls @@ -0,0 +1,46 @@ +:. + + +:sr +:sir +module Foo { + class Bar { + fun id(x) = x + } +} +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ module Foo⁰::FooΒΉ { class Bar⁰::BarΒΉ { fun id⁰::idΒΉ(x⁰) β€Ήflow:id⁰› = x⁰ } } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ define Foo⁰ as class FooΒΉ +//β”‚ module FooΒ² { +//β”‚ constructor { define Bar⁰ as class BarΒΉ { method id⁰ = fun idΒΉ(x) { return x } }; end } +//β”‚ }; +//β”‚ end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +:sr +:sir +:lot +let x = new Foo.Bar +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let xΒΉ, xΒΉ = new Foo⁰.Bar⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR Tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Program: +//β”‚ main = Assign: +//β”‚ lhs = x⁰ +//β”‚ rhs = Instantiate: +//β”‚ mut = false +//β”‚ cls = Select{sym=class:BarΒΉ}: +//β”‚ qual = MemberRef{disamb=module:FooΒ²}: +//β”‚ bms = member:Foo⁰ +//β”‚ disamb = module:FooΒ² +//β”‚ name = Ident of "Bar" +//β”‚ argss = Ls of +//β”‚ Nil +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ set x⁰ = new FooΒ².BarΒΉ(); end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ x = Bar + + + diff --git a/hkmc2/shared/src/test/mlscript/newres/NestedModules.mls b/hkmc2/shared/src/test/mlscript/newres/NestedModules.mls new file mode 100644 index 0000000000..005cfd9fbf --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/NestedModules.mls @@ -0,0 +1,115 @@ +:. + + +:sr +:sir +module Foo { + module Bar { + fun id(x) = x + } +} +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ module Foo⁰::FooΒΉ { module Bar⁰::BarΒΉ { fun id⁰::idΒΉ(x⁰) β€Ήflow:id⁰› = x⁰ } } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ define Foo⁰ as class FooΒΉ +//β”‚ module FooΒ² { +//β”‚ constructor { +//β”‚ define Bar⁰ as class BarΒΉ +//β”‚ module BarΒ² { method id⁰ = fun idΒΉ(x) { return x } }; +//β”‚ end +//β”‚ } +//β”‚ }; +//β”‚ end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +// :de +:sr +:sir +:lot +Foo.Bar +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Foo⁰.Bar⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR Tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Program: +//β”‚ main = Return of Select{sym=module:BarΒ²}: +//β”‚ qual = MemberRef{disamb=module:FooΒ²}: +//β”‚ bms = member:Foo⁰ +//β”‚ disamb = module:FooΒ² +//β”‚ name = Ident of "Bar" +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ return FooΒ².BarΒ² +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = class Bar + +:sr +Foo.Bar.id +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Foo⁰.Bar⁰.id⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = fun id + + +let + f = Foo + b = f.Bar +//β”‚ b = class Bar +//β”‚ f = class Foo { Bar: class Bar } + +:elt +b.id +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Elaborated tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Blk: +//β”‚ res = NewSel: +//β”‚ prefix = SimpleRef of b⁰ +//β”‚ id = Ident of "id" +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = fun id + + +// :de +:sr +let m = if maybe then Foo.Bar else Predef +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let m⁰, m⁰ = if { let scrut⁰ = Predef⁰.maybe⁰; scrut⁰ is true then Foo⁰.Bar⁰; else Predef⁰ } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ m = class Bar + +:sr +:e +m.id +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ m⁰.idβ€ΉidΒ², id⁰› +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'id' is ambiguous, as it has multiple resolved targets +//β”‚ β•‘ l.79: m.id +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ target: function 'id' in module 'Predef' +//β”‚ β•‘ in ../../mlscript-compile/Predef.mls +//β”‚ β•‘ l.19: fun id(x) = x +//β”‚ β•‘ ^^ +//β”‚ β•Ÿβ”€β”€ target: function 'id' in module 'Bar' +//β”‚ β•‘ in NestedModules.mls:6 +//β”‚ β•‘ l.8: fun id(x) = x +//β”‚ ╙── ^^ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + + +let x = id(Foo.Bar) +//β”‚ x = class Bar + +// External identity functions preserve their argument's inferred interface. +:expect fun id +x.id +//β”‚ = fun id + + +fun localid(x) = x + +localid(Foo.Bar).id +//β”‚ = fun id + +// * We preserve context sensitivity +localid(Predef).id +//β”‚ = fun id + + diff --git a/hkmc2/shared/src/test/mlscript/newres/NominalMemberPaths.mls b/hkmc2/shared/src/test/mlscript/newres/NominalMemberPaths.mls new file mode 100644 index 0000000000..8f4e1c6852 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/NominalMemberPaths.mls @@ -0,0 +1,65 @@ +:. + +// Inherited fields and generic methods retain the two callers' arguments after +// an annotated receiver leaves its allocation scope and crosses another call. +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +class Parent[A](val item: A) with + fun map[B](f: A -> B): B = f(item) +class Child[A](item: A) extends Parent[A](item) +private fun make[A](value: A): Child[A] = new Child[A](value) +private fun forward(x) = x +[forward(make[First](First(1))).item.first, + forward(make[Second](Second(2))).map(x => x).second] +//β”‚ = [1, 2] + +// The superclass and subclass are both local to a generic activation. Their +// inherited signatures must keep that activation when receivers escape. +:expect [3, 4] +class First(val first: Int) +class Second(val second: Int) +private fun make[A](value: A) = + class Parent(val item: A) with + fun map[B](f: A -> B): B = f(item) + class Child(item: A) extends Parent(item) + fun nested() = new Child(value) as Child + nested() +private fun forward(x) = x +[forward(make[First](First(3))).item.first, + forward(make[Second](Second(4))).map(x => x).second] +//β”‚ = [3, 4] + +// Rest slicing through a generic body preserves the field substitutions even +// when both ends are removed in successive views. +:expect [5, 6] +class First(val first: Int) +class Second(val second: Int) +private fun trim[A](value: A) = + let values = [0, value, 9] + if values is [_, ...tail] and tail is [...middle, _] then middle +[trim[First](First(5)).0.first, trim[Second](Second(6)).0.second] +//β”‚ = [5, 6] + +// reduce feeds the callback result into the next callback invocation, even +// when it has a different shape from the initial accumulator. +:e +class Initial(val start: Int) +class Later(val result: Int) +[1, 2].reduce((acc, value, ...) => Later(acc.start + value), Initial(0)) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.49: [1, 2].reduce((acc, value, ...) => Later(acc.start + value), Initial(0)) +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Later' does not contain member 'start' +//β”‚ β•‘ l.49: [1, 2].reduce((acc, value, ...) => Later(acc.start + value), Initial(0)) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// The remaining callback arguments retain their declared positions and types. +:expect [7, 3] +class Total(val total: Int) +val empty: Array[Int] = mut [] +[empty.reduce((acc, value, ...) => Total(acc.total + value), Total(7)).total, + [4, 5].reduce((acc, value, index, array) => acc + index + array.length, -2)] +//β”‚ = [7, 3] +//β”‚ empty = [] diff --git a/hkmc2/shared/src/test/mlscript/newres/ObjectMethods.mls b/hkmc2/shared/src/test/mlscript/newres/ObjectMethods.mls new file mode 100644 index 0000000000..f90fcfcf3d --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ObjectMethods.mls @@ -0,0 +1,88 @@ +:. + +:expect "Empty" +object Empty +Empty.toString() +//β”‚ = "Empty" + +:expect "Box(1)" +data class Box(value: Int) +Box(1).toString() +//β”‚ = "Box(1)" + +:expect "BOX(2)" +fun show(box: Box): Str = box.toString().toUpperCase() +show(Box(2)) +//β”‚ = "BOX(2)" + +:expect "custom" +class Parent with + fun toString(): Str = "custom" +new Parent().toString() +//β”‚ = "custom" + +:e +Empty.toString(1) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.25: Empty.toString(1) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'toString' expected 0 arguments, but got 1 +//β”‚ β•‘ in ../decls/Prelude.mls +//β”‚ β•‘ l.7: fun toString(): Str +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^ +//β”‚ = "Empty" + +:expect "empty" +fun lower(value: Empty): Str = value.toString().toLowerCase() +lower(Empty) +//β”‚ = "empty" + +:expect "empty" +data class Wrapped(kind: Empty) +fun lowerWrapped(value: Wrapped): Str = if value is Wrapped(kind) then kind.toString().toLowerCase() +lowerWrapped(Wrapped(Empty)) +//β”‚ = "empty" + +module Kind with + object Red + object Blue +type Kind = Kind.{Red | Blue} +data class Colored(kind: Kind) + +:expect "red" +fun lowerColored(value: Colored): Str = if value is Colored(kind) then kind.toString().toLowerCase() +lowerColored(Colored(Kind.Red)) +//β”‚ = "red" + +abstract class Base +data class Derived(value: Str) extends Base + +:expect "VALUE" +fun derivedValue(value: Base): Str = if value is Derived(text) then text.toUpperCase() +derivedValue(new Derived("value")) +//β”‚ = "VALUE" + +:expect "Box(3)" +fun renderObject(value: Object): Str = value.toString() +renderObject(Box(3)) +//β”‚ = "Box(3)" + +:expect "child" +class HasMethod with + open fun label(): Str = "parent" +class Child extends HasMethod with + override fun label(): Str = "child" +new Child().label() +//β”‚ = "child" + +// Recursive narrowing must reuse its abstract subclass instantiation. +abstract class Link[A] +data class Cell[A](value: A, next: Link[A]) extends Link[A] +object End extends Link[Int] +fun depth(xs: Link[Int]): Int = if xs is + Cell(_, next) then 1 + depth(next) + else 0 + +:expect 2 +depth(Cell(1, Cell(2, End))) +//β”‚ = 2 diff --git a/hkmc2/shared/src/test/mlscript/newres/Opens.mls b/hkmc2/shared/src/test/mlscript/newres/Opens.mls new file mode 100644 index 0000000000..f1a9fd4aa9 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/Opens.mls @@ -0,0 +1,397 @@ +:. + + +// Explicit bindings across the complete context chain precede wildcard sources. +val explicit = 1 +module Values { + val explicit = 2 + val extra = 3 +} +fun outerBinding() = { + open Values + explicit + extra +} +//β”‚ explicit = 1 + +:expect 4 +outerBinding() +//β”‚ = 4 + + +// Neither order of a wildcard open lets it shadow a member definition. +fun before() = { + open Values + val extra = 4 + extra +} +fun after() = { + val extra = 5 + open Values + extra +} + +:expect [4, 5] +[before(), after()] +//β”‚ = [4, 5] + + +module Selected { val extra = 6 } +fun named() = { + open Selected { extra } + fun nested() = { + open Values + extra + } + nested() +} + +:expect 6 +named() +//β”‚ = 6 + + +// Both open forms work before the module definition is elaborated. +fun forwardWildcard() = { + open Later + answer +} +fun forwardNamed() = { + open Later { answer } + answer +} +module Later { val answer = 7 } + +:expect [7, 7] +[forwardWildcard(), forwardNamed()] +//β”‚ = [7, 7] + + +// A nonmatching wildcard source does not suppress a matching one. +module Other { val unrelated = 8 } +fun several() = { + open Other + open Later + answer +} + +:expect 7 +several() +//β”‚ = 7 + + +// Reopening the same source does not introduce a second candidate. +fun repeated() = { + open Later + open Later + answer +} + +:expect 7 +repeated() +//β”‚ = 7 + + +// Two distinct sources make a name ambiguous, independently of open order. +:e +fun ambiguous() = { + open Values + open Selected + extra +} +ambiguous() +//β”‚ ╔══[COMPILATION ERROR] Wildcard-open reference 'extra' is ambiguous +//β”‚ β•‘ l.99: extra +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ candidate: value 'extra' defined here +//β”‚ β•‘ l.8: val extra = 3 +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Opened here +//β”‚ β•‘ l.97: open Values +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ candidate: value 'extra' defined here +//β”‚ β•‘ l.38: module Selected { val extra = 6 } +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Opened here +//β”‚ β•‘ l.98: open Selected +//β”‚ ╙── ^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:e +fun missing() = { + open Later + absent +} +missing() +//β”‚ ╔══[COMPILATION ERROR] Name not found in wildcard opens: absent +//β”‚ β•‘ l.122: absent +//β”‚ ╙── ^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:e +open Later { absent } +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.131: open Later { absent } +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Module 'Later' does not contain member 'absent' +//β”‚ β•‘ l.62: module Later { val answer = 7 } +//β”‚ ╙── ^^^^^ + + +// Wildcard lookup retains overload sets until the use chooses a definition. +module Overloaded { + class Item { fun answer() = 9 } + fun Item() = 11 + mut val value = 11 +} +fun overload() = { + open Overloaded + // The call and constructor choose different definitions of Item. + [Item(), (new Item).answer()] +} + +:expect [11, 9] +overload() +//β”‚ = [11, 9] + + +// Captured module contexts survive deferred lookup and nested functions. +module Left { val left = 12 } +module Right { val right = 13 } +fun captured(initial) = { + module Local { val item = initial } + open Local + fun read() = item + read() +} + +:expect [12, 13] +[captured(Left).left, captured(Right).right] +//β”‚ = [12, 13] + + +// An opened module can itself come from a wildcard source. +module Outer { + module Inner { val innerValue = 14 } +} +fun chained() = { + open Outer + open Inner + innerValue +} + +:expect 14 +chained() +//β”‚ = 14 + + +// Imported aliases expose the imported module's shapes. +import "../../mlscript-compile/OverloadedClassLibrary.mls" as Library +fun imported() = { + open Library + [Item(), (new Item).answer()] +} +fun importedNamed() = { + open Library { Item } + [Item(), (new Item).answer()] +} + +:expect [[16, 52], [16, 52]] +[imported(), importedNamed()] +//β”‚ = [[16, 52], [16, 52]] + + +// Indented module bodies use the same completed shapes as braced bodies. +fun indented() = { + open Indented + value +} +module Indented with + val value = 15 + +:expect 15 +indented() +//β”‚ = 15 + + +// Two distinct receivers must not silently select the same member on the wrong instance. +fun instance(n) = { + module M { val value = n } + M +} + +:e +let first = instance(16) +let second = instance(17) +fun twoInstances() = { + open first + open second + value +} +twoInstances() +//β”‚ ╔══[COMPILATION ERROR] Wildcard-open reference 'value' is ambiguous +//β”‚ β•‘ l.228: value +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ candidate: value 'value' defined here +//β”‚ β•‘ l.218: module M { val value = n } +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Opened here +//β”‚ β•‘ l.226: open first +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ candidate: value 'value' defined here +//β”‚ β•‘ l.218: module M { val value = n } +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Opened here +//β”‚ β•‘ l.227: open second +//β”‚ ╙── ^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ first = class M { value: 16 } +//β”‚ second = class M { value: 17 } + + +// Parameters are explicit bindings even when an open provides the same name. +fun parameter(extra) = { + open Values + extra +} + +:expect 18 +parameter(18) +//β”‚ = 18 + + +// Applied constructor patterns demand the class interpretation, not a term value. +module Constructors { class Box(val value) } +fun unbox(box) = { + open Constructors + if box is Box(value) then value else 0 +} + +:expect 19 +unbox(Constructors.Box(19)) +//β”‚ = 19 + + +// A binding brought in explicitly prevents wildcard class lookup too. +:e +module ClassSource { class Shadowed } +val Shadowed = 20 +open ClassSource +new Shadowed +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.279: new Shadowed +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be instantiated with keyword 'new'. +//β”‚ β•‘ l.277: val Shadowed = 20 +//β”‚ ╙── ^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ Shadowed = 20 + + +// Explicit selective opens win regardless of their order relative to wildcard opens. +fun namedAfter() = { + open Values + open Selected { extra } + extra +} +fun namedBefore() = { + open Selected { extra } + open Values + extra +} + +:expect [6, 6] +[namedAfter(), namedBefore()] +//β”‚ = [6, 6] + +:e +fun ambiguousReversed() = { + open Selected + open Values + extra +} +ambiguousReversed() +//β”‚ ╔══[COMPILATION ERROR] Wildcard-open reference 'extra' is ambiguous +//β”‚ β•‘ l.310: extra +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ candidate: value 'extra' defined here +//β”‚ β•‘ l.38: module Selected { val extra = 6 } +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Opened here +//β”‚ β•‘ l.308: open Selected +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ candidate: value 'extra' defined here +//β”‚ β•‘ l.8: val extra = 3 +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Opened here +//β”‚ β•‘ l.309: open Values +//β”‚ ╙── ^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + + +// Both the temporary assignment and restoration use the selected open member. +fun temporary() = { + open Overloaded + let during = set value = 21 in value + [during, value] +} + +:expect [21, 11] +temporary() +//β”‚ = [21, 11] + + +module Callable { + class Make { fun answer() = 22 } + fun Make() = new Make +} +fun callable() = { + open Callable + [Make().answer(), (new Make).answer()] +} + +:expect [22, 22] +callable() +//β”‚ = [22, 22] + + +// Bare pattern names resolve through both forms of open. +:expect [1, 0, 1, 0] +module BarePatterns { class Token } +fun bareWildcard(x) = { + open BarePatterns + if x is Token then 1 else 0 +} +fun bareNamed(x) = { + open BarePatterns { Token } + if x is Token then 1 else 0 +} +[bareWildcard(new BarePatterns.Token), bareWildcard(0), + bareNamed(new BarePatterns.Token), bareNamed(0)] +//β”‚ = [1, 0, 1, 0] + + +// Opening sites survive capture by a nested function, independently of definitions. +:e +fun inheritedAmbiguity() = { + open Values + open Selected + fun nested() = extra + nested() +} +inheritedAmbiguity() +//β”‚ ╔══[COMPILATION ERROR] Wildcard-open reference 'extra' is ambiguous +//β”‚ β•‘ l.378: fun nested() = extra +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ candidate: value 'extra' defined here +//β”‚ β•‘ l.8: val extra = 3 +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Opened here +//β”‚ β•‘ l.376: open Values +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ candidate: value 'extra' defined here +//β”‚ β•‘ l.38: module Selected { val extra = 6 } +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Opened here +//β”‚ β•‘ l.377: open Selected +//β”‚ ╙── ^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/newres/OptionImports.mls b/hkmc2/shared/src/test/mlscript/newres/OptionImports.mls new file mode 100644 index 0000000000..a747c351da --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/OptionImports.mls @@ -0,0 +1,20 @@ +:. + +import "../../mlscript-compile/Option.mls" +open Option { Some, None } + +:expect [true, false, 42, 7, 42, 43, 7] +[Option.isDefined(Some(42)), Option.isDefined(None), + Option.getOrElse(Some(42), 7), Option.getOrElse(None, 7), + Option.unsafe.get(Some(42)), + Option.getOrElse(Option.flatMap(Some(42), x => Some(x + 1)), 7), + Option.getOrElse(Option.flatMap(None, x => Some(x + 1)), 7)] +//β”‚ = [true, false, 42, 7, 42, 43, 7] + +:expect "AB" +Option.getOrElse[Str](Some("ab"), "missing").toUpperCase() +//β”‚ = "AB" + +:re +Option.unsafe.get(None) +//β”‚ ═══[RUNTIME ERROR] Error: None.get diff --git a/hkmc2/shared/src/test/mlscript/newres/OverloadedCalls.mls b/hkmc2/shared/src/test/mlscript/newres/OverloadedCalls.mls new file mode 100644 index 0000000000..8314a40c78 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/OverloadedCalls.mls @@ -0,0 +1,67 @@ +:. + +// Calls and stored function values select the function overload. Selections +// retain the companion module, including inside nested function captures. +fun Compute[A](x: A) = x +module Compute with + fun next(x: Int) = Compute(x + 1) + +:expect [2, 3, 4] +let f = Compute +[Compute(2), f(3), Compute.next(3)] +//β”‚ = [2, 3, 4] +//β”‚ f = class Compute + +class Item(val value: Int) +module Item with + fun next(x: Item) = Item(x.value + 1) + +:expect [2, 3] +[Item(2).value, Item.next(Item(2)).value] +//β”‚ = [2, 3] + +module Library with + fun Convert[A](x: A) = x + module Convert with + fun twice(x: Int) = Convert(x + x) + +:expect [4, 6] +open Library +[Convert(4), Convert.twice(3)] +//β”‚ = [4, 6] + +:expect 5 +fun nested() = { + fun inner() = Item.next(Item(4)).value + inner() +} +nested() +//β”‚ = 5 + +:expect [7, 8] +[Compute[Int](7), Library.Convert[Int](8)] +//β”‚ = [7, 8] + +// Taking a function value does not also take its companion module. +:e +fun plain(x: Int) = x +plain.missing +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.48: plain.missing +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'plain' does not contain member 'missing' +//β”‚ β•‘ l.47: fun plain(x: Int) = x +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Captured constructor patterns retain the class target through every scope. +:expect 9 +fun inspect(x) = { + fun nested() = { + fun deeper() = if x is Item(v) then v else 0 + deeper() + } + nested() +} +inspect(Item(9)) +//β”‚ = 9 diff --git a/hkmc2/shared/src/test/mlscript/newres/OverloadedClassImports.mls b/hkmc2/shared/src/test/mlscript/newres/OverloadedClassImports.mls new file mode 100644 index 0000000000..754288762d --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/OverloadedClassImports.mls @@ -0,0 +1,67 @@ +:. + + +import "../../mlscript-compile/OverloadedClassLibrary.mls" + +// Inlining imports one companion binding for both calls and class references. +:sjs +:expect 51 +OverloadedClassLibrary.fresh().answer() +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ import { _$_modulePrivate_$_Hidden as Hidden } from "./../../mlscript-compile/OverloadedClassLibrary.mjs"; +//β”‚ let inlinedVal; +//β”‚ inlinedVal = globalThis.Object.freeze(new Hidden.class()); +//β”‚ return inlinedVal.answer() +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 51 + +:noInline +:expect 51 +OverloadedClassLibrary.fresh().answer() +//β”‚ = 51 + +:expect 15 +OverloadedClassLibrary.value() +//β”‚ = 15 + +:expect 16 +OverloadedClassLibrary.Item() +//β”‚ = 16 + +:expect 52 +(new OverloadedClassLibrary.Item).answer() +//β”‚ = 52 + +open OverloadedClassLibrary { Item } + +:expect 52 +(new Item).answer() +//β”‚ = 52 + +:expect 16 +Item() +//β”‚ = 16 + + +// The default export also carries its class on its function companion. +import "../../mlscript-compile/OverloadedDefault.mls" + +:expect 19 +OverloadedDefault() +//β”‚ = 19 + +:expect 54 +(new OverloadedDefault).answer() +//β”‚ = 54 + + +// Imported overloads retain their class interpretation in bare patterns. +:expect [1, 0, 1, 0] +fun importedPattern(x) = { + open OverloadedClassLibrary + if x is Item then 1 else 0 +} +fun namedPattern(x) = if x is Item then 1 else 0 +[importedPattern(new OverloadedClassLibrary.Item), importedPattern(0), + namedPattern(new Item), namedPattern(0)] +//β”‚ = [1, 0, 1, 0] diff --git a/hkmc2/shared/src/test/mlscript/newres/OverloadedClassValues.mls b/hkmc2/shared/src/test/mlscript/newres/OverloadedClassValues.mls new file mode 100644 index 0000000000..0ac9b3cda1 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/OverloadedClassValues.mls @@ -0,0 +1,228 @@ +:. + + +// A function definition creates a fresh companion that can own its class. +class Primitive { + fun answer() = 42 +} +fun Primitive() = 7 + +:expect 7 +Primitive() +//β”‚ = 7 + +:expect 42 +(new Primitive).answer() +//β”‚ = 42 + + +// Declaration order must not change class selection. +fun Reverse() = 13 +class Reverse { + fun answer() = 43 +} + +:expect 43 +(new Reverse()).answer() +//β”‚ = 43 + +class Getter { + fun answer() = 44 +} +fun Getter() = 14 + +:expect 14 +Getter() +//β”‚ = 14 + +:expect 44 +(new Getter).answer() +//β”‚ = 44 + + +// Both meanings remain available inside closures and local scopes. +fun local(n) = { + class Item { + fun answer() = n + } + fun Item(x) = (new Item).answer() + x + Item(2) +} + +:expect 42 +local(40) +//β”‚ = 42 + + +module Members { + class Item { + fun answer() = 45 + } + fun Item() = new Item + fun fresh() = new Item + class Factory { + fun answer() = 46 + } + fun Factory(x) = (new Factory).answer() + x +} + +:expect 45 +Members.Item().answer() +//β”‚ = 45 + +:expect 45 +Members.fresh().answer() +//β”‚ = 45 + +:expect 45 +(new Members.Item).answer() +//β”‚ = 45 + +:expect 48 +Members.Factory(2) +//β”‚ = 48 + +:expect 46 +(new Members.Factory()).answer() +//β”‚ = 46 + + +// An explicit constructor list does not make a bare class a callable class. +class Aux { + constructor(val x) + fun answer() = x +} +fun Aux(x) = new Aux(x + 1) + +// Constructor fields retain the selected constructor and its capture path. +:expect 42 +Aux(41).answer() +//β”‚ = 42 + +:expect 41 +(new Aux(41)).answer() +//β”‚ = 41 + + +class Parent { + fun answer() = 47 +} +fun Parent() = false +class Child extends Parent + +// Inherited members retain their class interpretation despite the term overload. +:expect 47 +(new Child).answer() +//β”‚ = 47 + + +class Factory { + fun answer() = 49 +} +fun Factory() = new Factory + +:noInline +:expect 49 +Factory().answer() +//β”‚ = 49 + + +// A class instance can expose a nested class alongside an ordinary field. +class Container { + class Inner { + fun answer() = 50 + } + val value = 5 + fun fresh() = new Inner +} +let container = new Container +//β”‚ container = Container { Inner: class Inner, value: 5 } + +:expect 5 +container.value +//β”‚ = 5 + +:expect 50 +(new container.Inner).answer() +//β”‚ = 50 + +:expect 50 +container.fresh().answer() +//β”‚ = 50 + + +module MutableMembers { + class Item { + fun answer() = 53 + } + mut val value = 17 +} +set MutableMembers.value = 18 + +:expect 18 +MutableMembers.value +//β”‚ = 18 + +:expect 53 +(new MutableMembers.Item).answer() +//β”‚ = 53 + + +// Direct parameter lists already define a constructor function with this name. +:e +class Parameterized() +fun Parameterized() = 0 +//β”‚ ╔══[COMPILATION ERROR] Not yet supported: overloading of function 'Parameterized' +//β”‚ β•‘ l.174: fun Parameterized() = 0 +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ with class of the same name +//β”‚ β•‘ l.173: class Parameterized() +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^ + +:e +class ParameterizedValue(x) +val ParameterizedValue = 0 +//β”‚ ╔══[COMPILATION ERROR] A class companion must be a function with a parameter list +//β”‚ β•‘ l.184: val ParameterizedValue = 0 +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Arbitrary values cannot be overloaded with class 'ParameterizedValue' +//β”‚ β•‘ l.183: class ParameterizedValue(x) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ParameterizedValue = 0 + +// Distinct term definitions are still duplicate definitions, even beside a class. +:e +class Duplicate +fun Duplicate() = 0 +val Duplicate = 1 +//β”‚ ╔══[COMPILATION ERROR] A class companion must be a function with a parameter list +//β”‚ β•‘ l.197: val Duplicate = 1 +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Arbitrary values cannot be overloaded with class 'Duplicate' +//β”‚ β•‘ l.195: class Duplicate +//β”‚ ╙── ^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Illegal overloading of value 'Duplicate' +//β”‚ β•‘ l.197: val Duplicate = 1 +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ with function of the same name +//β”‚ β•‘ l.196: fun Duplicate() = 0 +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^ + + +// Inlining the same local definitions twice must keep their class identities separate. +:inlineThreshold 10000 +:expect [42, 43] +[local(40), local(41)] +//β”‚ = [42, 43] + + +fun unusedClass() = { + class Unused + fun Unused() = 55 + Unused() +} + +:inlineThreshold 10000 +:expect 55 +unusedClass() +//β”‚ = 55 diff --git a/hkmc2/shared/src/test/mlscript/newres/OverloadedClasses.mls b/hkmc2/shared/src/test/mlscript/newres/OverloadedClasses.mls new file mode 100644 index 0000000000..839d9cc1bc --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/OverloadedClasses.mls @@ -0,0 +1,151 @@ +:. + + +class Bar { + fun id(x) = x +} +fun Bar() = new Bar + +:sr +:sir +:lot +let x = new Bar +let y = new Bar() +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let x⁰, x⁰ = new Bar⁰, let y⁰, y⁰ = new Bar⁰() +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR Tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Program: +//β”‚ main = Assign: +//β”‚ lhs = x⁰ +//β”‚ rhs = Instantiate: +//β”‚ mut = false +//β”‚ cls = MemberRef{disamb=class:Bar⁰}: +//β”‚ bms = member:BarΒΉ +//β”‚ disamb = class:Bar⁰ +//β”‚ argss = Ls of +//β”‚ Nil +//β”‚ rest = Assign: \ +//β”‚ lhs = y⁰ +//β”‚ rhs = Instantiate: +//β”‚ mut = false +//β”‚ cls = MemberRef{disamb=class:Bar⁰}: +//β”‚ bms = member:BarΒΉ +//β”‚ disamb = class:Bar⁰ +//β”‚ argss = Ls of +//β”‚ Nil +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ set x⁰ = new Bar⁰(); set y⁰ = new Bar⁰(); end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ x = Bar +//β”‚ y = Bar + + +class Bar { + fun id(x) = x +} +fun Bar() = new Bar + +:sr +:sir +:lot +let x = new Bar +let y = new Bar() +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let xΒΉ, xΒΉ = new BarΒΉ, let yΒΉ, yΒΉ = new BarΒΉ() +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR Tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Program: +//β”‚ main = Assign: +//β”‚ lhs = xΒΉ +//β”‚ rhs = Instantiate: +//β”‚ mut = false +//β”‚ cls = MemberRef{disamb=class:BarΒ²}: +//β”‚ bms = member:BarΒ³ +//β”‚ disamb = class:BarΒ² +//β”‚ argss = Ls of +//β”‚ Nil +//β”‚ rest = Assign: \ +//β”‚ lhs = yΒΉ +//β”‚ rhs = Instantiate: +//β”‚ mut = false +//β”‚ cls = MemberRef{disamb=class:BarΒ²}: +//β”‚ bms = member:BarΒ³ +//β”‚ disamb = class:BarΒ² +//β”‚ argss = Ls of +//β”‚ Nil +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ set xΒΉ = new BarΒ²(); set yΒΉ = new BarΒ²(); end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ x = Bar +//β”‚ y = Bar + + +:sr +:sir +module Foo { + class Bar { + fun id(x) = x + } + fun Bar() = new Bar +}//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ module Foo⁰::FooΒΉ { +//β”‚ class BarΒ²::BarΒ³ { +//β”‚ fun id⁰::idΒΉ(xΒ²) β€Ήflow:id⁰› = xΒ² +//β”‚ }, +//β”‚ fun BarΒ²::Bar⁴() β€Ήflow:Bar⁰› = new FooΒΉ.this.BarΒ²^Bar⁴ +//β”‚ } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ define Foo⁰ as class FooΒΉ +//β”‚ module FooΒ² { +//β”‚ constructor { +//β”‚ define Bar⁴ as class Bar⁡ { +//β”‚ method id⁰ = fun idΒΉ(x) { +//β”‚ return x +//β”‚ } +//β”‚ }; +//β”‚ end +//β”‚ } +//β”‚ method Bar⁴ = fun Bar⁢() { return new FooΒ².this.Bar⁡() } +//β”‚ }; +//β”‚ end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +:sr +:sir +:lot +let x = new Foo.Bar +let x = new Foo.Bar() +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let xΒ³, xΒ³ = new Foo⁰.BarΒ², let x⁴, x⁴ = new Foo⁰.BarΒ²() +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR Tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Program: +//β”‚ main = Scoped(syms = {xΒ²}): +//β”‚ body = Assign: +//β”‚ lhs = xΒ² +//β”‚ rhs = Instantiate: +//β”‚ mut = false +//β”‚ cls = Select{sym=class:Bar⁡}: +//β”‚ qual = MemberRef{disamb=module:FooΒ²}: +//β”‚ bms = member:Foo⁰ +//β”‚ disamb = module:FooΒ² +//β”‚ name = Ident of "Bar" +//β”‚ argss = Ls of +//β”‚ Nil +//β”‚ rest = Assign: \ +//β”‚ lhs = xΒ³ +//β”‚ rhs = Instantiate: +//β”‚ mut = false +//β”‚ cls = Select{sym=class:Bar⁡}: +//β”‚ qual = MemberRef{disamb=module:FooΒ²}: +//β”‚ bms = member:Foo⁰ +//β”‚ disamb = module:FooΒ² +//β”‚ name = Ident of "Bar" +//β”‚ argss = Ls of +//β”‚ Nil +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let x; set x = new FooΒ².Bar⁡(); set xΒ³ = new FooΒ².Bar⁡(); end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ x = Bar + + + diff --git a/hkmc2/shared/src/test/mlscript/newres/Overrides.mls b/hkmc2/shared/src/test/mlscript/newres/Overrides.mls new file mode 100644 index 0000000000..58093d269f --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/Overrides.mls @@ -0,0 +1,456 @@ +:. + +class Base { open fun foo() = 1 } +class Left extends Base { override fun foo() = 2 } +class Right extends Base { override fun foo() = 3 } +class Inherited extends Base + +:sir +:expect [2, 3, 1] +private fun read(x) = x.foo() +[read(new Left), read(new Right), read(new Inherited)] +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let tmp, tmp1, tmp2, tmp3, tmp4, tmp5; +//β”‚ @private +//β”‚ define read⁰ as fun readΒΉ(x) { +//β”‚ return x.foo⁰() +//β”‚ }; +//β”‚ set tmp = new Left⁰(); +//β”‚ set tmp1 = readΒΉ(tmp); +//β”‚ set tmp2 = new Right⁰(); +//β”‚ set tmp3 = readΒΉ(tmp2); +//β”‚ set tmp4 = new Inherited⁰(); +//β”‚ set tmp5 = readΒΉ(tmp4); +//β”‚ return [tmp1, tmp3, tmp5] +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = [2, 3, 1] + +:e +class Closed { fun foo() = 1 } +class Invalid extends Closed { override fun foo() = 2 } +//β”‚ ╔══[COMPILATION ERROR] Cannot override non-open member 'foo' +//β”‚ β•‘ l.30: class Invalid extends Closed { override fun foo() = 2 } +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Inherited member 'foo' is defined here +//β”‚ β•‘ l.29: class Closed { fun foo() = 1 } +//β”‚ ╙── ^^^ + +:e +class MissingOverride extends Base { fun foo() = 2 } +//β”‚ ╔══[COMPILATION ERROR] Overriding member 'foo' requires the 'override' modifier +//β”‚ β•‘ l.39: class MissingOverride extends Base { fun foo() = 2 } +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Inherited member 'foo' is defined here +//β”‚ β•‘ l.3: class Base { open fun foo() = 1 } +//β”‚ ╙── ^^^ + +:e +class SpuriousOverride { override fun missing() = 2 } +//β”‚ ╔══[COMPILATION ERROR] Member 'missing' is marked 'override' but overrides no inherited member +//β”‚ β•‘ l.48: class SpuriousOverride { override fun missing() = 2 } +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +abstract class Abstract { fun foo() } +class Implemented extends Abstract { fun foo() = 4 } + +:expect 4 +(new Implemented).foo() +//β”‚ = 4 + +:e +class Incomplete extends Abstract +//β”‚ ╔══[COMPILATION ERROR] Class 'Incomplete' has unimplemented members +//β”‚ β•‘ l.61: class Incomplete extends Abstract +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Member 'foo' requires an implementation +//β”‚ β•‘ l.53: abstract class Abstract { fun foo() } +//β”‚ ╙── ^^^ + +:e +new Abstract +//β”‚ ╔══[COMPILATION ERROR] Cannot instantiate abstract class 'Abstract' +//β”‚ β•‘ l.70: new Abstract +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Abstract class defined here +//β”‚ β•‘ l.53: abstract class Abstract { fun foo() } +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:sir +:expect 10 +class SuperBase { open fun foo(x) = x + 1 } +class SuperChild extends SuperBase { override fun foo(x) = super.foo(x + 1) * 2 } +(new SuperChild).foo(3) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let tmp; +//β”‚ define SuperBase⁰ as class SuperBaseΒΉ { +//β”‚ method fooΒΉ = fun fooΒ²(x) { +//β”‚ return +⁰(x, 1) +//β”‚ } +//β”‚ }; +//β”‚ define SuperChild⁰ as class SuperChildΒΉ extends SuperBaseΒΉ { +//β”‚ method fooΒ³ = fun foo⁴(x) { +//β”‚ let tmp1, tmp2; +//β”‚ set tmp1 = +⁰(x, 1); +//β”‚ set tmp2 = super[SuperBaseΒΉ](SuperChildΒΉ.this).fooΒ²(tmp1); +//β”‚ return *⁰(tmp2, 2) +//β”‚ } +//β”‚ }; +//β”‚ set tmp = new SuperChildΒΉ(); +//β”‚ return tmp.foo⁴(3) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 10 + +:expect 11 +class SuperInherited extends SuperChild { fun bar(x) = super.foo(x) + 1 } +(new SuperInherited).bar(3) +//β”‚ = 11 + +:expect 10 +class SuperClosure extends SuperBase { + override fun foo(x) = (() => super.foo(x + 1) * 2)() +} +(new SuperClosure).foo(3) +//β”‚ = 10 + +:expect 13 +class GetterBase(val n) { open fun value = n + 1 } +class GetterChild(n) extends GetterBase(n) { + override fun value = super.value + super.n +} +(new GetterChild(6)).value +//β”‚ = 13 + +:expect 12 +class ReceiverBase { + open fun value() = 1 + open fun run() = this.value() + 2 +} +class ReceiverChild extends ReceiverBase { + override fun value() = 10 + override fun run() = super.run() +} +(new ReceiverChild).run() +//β”‚ = 12 + +:expect 7 +class GenericBase[A](val value: A) { open fun get(): A = value } +class GenericChild[A](x: A) extends GenericBase[A](x) { + override fun get(): A = super.get() +} +(new GenericChild[Int](7)).get() +//β”‚ = 7 + +// Forward parent references resolve, but JS still initializes classes in source order. +:fixme +:expect 8 +class ForwardChild extends ForwardBase { override fun f(x) = super.f(x) + 1 } +class ForwardBase { open fun f(x) = x + 2 } +(new ForwardChild).f(5) +//β”‚ ═══[RUNTIME ERROR] TypeError: Class extends value undefined is not a constructor or null +//β”‚ ═══[RUNTIME ERROR] Expected: '8', got: 'undefined' + +:e +super.foo() +//β”‚ ╔══[COMPILATION ERROR] Cannot use 'super' outside of an object scope +//β”‚ β•‘ l.154: super.foo() +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:e +class NoParent { fun f() = super.missing() } +//β”‚ ╔══[COMPILATION ERROR] Resolution error in super reference +//β”‚ β•‘ l.161: class NoParent { fun f() = super.missing() } +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ No inherited member 'missing' is available through 'super' +//β”‚ β•‘ l.161: class NoParent { fun f() = super.missing() } +//β”‚ ╙── ^^^^^^^^^^^^^ + +:e +class AbstractSuper extends Abstract { fun foo() = super.foo() } +//β”‚ ╔══[COMPILATION ERROR] Super reference 'foo' requires one inherited implementation +//β”‚ β•‘ l.170: class AbstractSuper extends Abstract { fun foo() = super.foo() } +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Candidate 'foo' is declared here +//β”‚ β•‘ l.53: abstract class Abstract { fun foo() } +//β”‚ ╙── ^^^ + +:e +class PrivateBase { private fun secret() = 1 } +class PrivateSuper extends PrivateBase { fun f() = super.secret() } +//β”‚ ╔══[COMPILATION ERROR] Super reference 'secret' requires one inherited implementation +//β”‚ β•‘ l.180: class PrivateSuper extends PrivateBase { fun f() = super.secret() } +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Candidate 'secret' is declared here +//β”‚ β•‘ l.179: class PrivateBase { private fun secret() = 1 } +//β”‚ ╙── ^^^^^^ + +:e +class ConcreteMissing { fun foo() } +//β”‚ ╔══[COMPILATION ERROR] Class 'ConcreteMissing' has unimplemented members +//β”‚ β•‘ l.189: class ConcreteMissing { fun foo() } +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Member 'foo' requires an implementation +//β”‚ β•‘ l.189: class ConcreteMissing { fun foo() } +//β”‚ ╙── ^^^ + +:e +object MissingObject extends Abstract +//β”‚ ╔══[COMPILATION ERROR] Object 'MissingObject' has unimplemented members +//β”‚ β•‘ l.198: object MissingObject extends Abstract +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Member 'foo' requires an implementation +//β”‚ β•‘ l.53: abstract class Abstract { fun foo() } +//β”‚ ╙── ^^^ + +:e +class BadKind extends Base { override fun foo = 1 } +//β”‚ ╔══[COMPILATION ERROR] Overriding member 'foo' must preserve whether the member is a field, getter, or method +//β”‚ β•‘ l.207: class BadKind extends Base { override fun foo = 1 } +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Inherited member 'foo' is defined here +//β”‚ β•‘ l.3: class Base { open fun foo() = 1 } +//β”‚ ╙── ^^^ + +:e +class AbstractCall() { fun f() } +//β”‚ ╔══[COMPILATION ERROR] Class 'AbstractCall' has unimplemented members +//β”‚ β•‘ l.216: class AbstractCall() { fun f() } +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Member 'f' requires an implementation +//β”‚ β•‘ l.216: class AbstractCall() { fun f() } +//β”‚ ╙── ^ + +:e +abstract class AbstractCallable() { fun f() } +AbstractCallable() +//β”‚ ╔══[COMPILATION ERROR] Cannot instantiate abstract class 'AbstractCallable' +//β”‚ β•‘ l.226: AbstractCallable() +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Abstract class defined here +//β”‚ β•‘ l.225: abstract class AbstractCallable() { fun f() } +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:expect 6 +(new SuperBase with { + override fun foo(x) = super.foo(x) * 2 +}).foo(2) +//β”‚ = 6 + +:expect 9 +(new Abstract with { fun foo() = 9 }).foo() +//β”‚ = 9 + +:e +new Abstract with { fun other() = 9 } +//β”‚ ╔══[COMPILATION ERROR] Class '$anon' has unimplemented members +//β”‚ β•‘ l.246: new Abstract with { fun other() = 9 } +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Member 'foo' requires an implementation +//β”‚ β•‘ l.53: abstract class Abstract { fun foo() } +//β”‚ ╙── ^^^ +//β”‚ = $anon + +:e +class PrivateAbstract { private fun hidden() } +//β”‚ ╔══[COMPILATION ERROR] Class 'PrivateAbstract' has unimplemented members +//β”‚ β•‘ l.256: class PrivateAbstract { private fun hidden() } +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Member 'hidden' requires an implementation +//β”‚ β•‘ l.256: class PrivateAbstract { private fun hidden() } +//β”‚ ╙── ^^^^^^ + +// The nearest common ancestor is used even when a deeper ancestor is also common. +:expect [5, 6] +class MiddleBase extends Base { open override fun foo() = 4 } +class DeepLeft extends MiddleBase { override fun foo() = 5 } +class DeepRight extends MiddleBase { override fun foo() = 6 } +private fun readDeep(x) = x.foo() +[readDeep(new DeepLeft), readDeep(new DeepRight)] +//β”‚ = [5, 6] + +// Local classes and their super calls can be lifted while retaining captured arguments. +:lift +:inlineThreshold 10000 +:expect [6, 14] +fun local(n) = { + class LocalBase { open fun foo(x) = n + x } + class LocalChild extends LocalBase { override fun foo(x) = super.foo(x) * 2 } + (new LocalChild).foo(1) +} +[local(2), local(6)] +//β”‚ = [6, 14] + +:e +class Unrelated { fun foo() = 0 } +private fun ambiguous(x) = x.foo() +[ambiguous(new Base), ambiguous(new Unrelated)] +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'foo' is ambiguous, as it has multiple resolved targets +//β”‚ β•‘ l.287: private fun ambiguous(x) = x.foo() +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ target: function 'foo' in class 'Unrelated' +//β”‚ β•‘ l.286: class Unrelated { fun foo() = 0 } +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ target: function 'foo' in class 'Base' +//β”‚ β•‘ l.3: class Base { open fun foo() = 1 } +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:e +class ClosedAgain extends Left { override fun foo() = 4 } +//β”‚ ╔══[COMPILATION ERROR] Cannot override non-open member 'foo' +//β”‚ β•‘ l.301: class ClosedAgain extends Left { override fun foo() = 4 } +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Inherited member 'foo' is defined here +//β”‚ β•‘ l.4: class Left extends Base { override fun foo() = 2 } +//β”‚ ╙── ^^^ + +:e +abstract class MissingInherited extends Abstract +class StillMissing extends MissingInherited +//β”‚ ╔══[COMPILATION ERROR] Class 'StillMissing' has unimplemented members +//β”‚ β•‘ l.311: class StillMissing extends MissingInherited +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Member 'foo' requires an implementation +//β”‚ β•‘ l.53: abstract class Abstract { fun foo() } +//β”‚ ╙── ^^^ + +:expect 4 +class InheritsImplementation extends Implemented +(new InheritsImplementation).foo() +//β”‚ = 4 + +:expect 4 +object ImplementedObject extends Abstract { fun foo() = 4 } +ImplementedObject.foo() +//β”‚ = 4 + +// Evaluating a class value must not re-evaluate the parent at every super call. +:expect 8 +fun makeParent(n) = { + class Parent { open fun f() = n } + Parent +} +let parent = makeParent(4) +class StoredParent extends parent { override fun f() = super.f() * 2 } +(new StoredParent).f() +//β”‚ = 8 +//β”‚ parent = class Parent + +:e +let abstractValue = Abstract +new abstractValue +//β”‚ ╔══[COMPILATION ERROR] Cannot instantiate abstract class 'Abstract' +//β”‚ β•‘ l.343: new abstractValue +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Abstract class defined here +//β”‚ β•‘ l.53: abstract class Abstract { fun foo() } +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ abstractValue = class Abstract + +:e +let abstractCtor = AbstractCallable +abstractCtor() +//β”‚ ╔══[COMPILATION ERROR] Cannot instantiate abstract class 'AbstractCallable' +//β”‚ β•‘ l.355: abstractCtor() +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Abstract class defined here +//β”‚ β•‘ l.225: abstract class AbstractCallable() { fun f() } +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ abstractCtor = fun AbstractCallable { class: class AbstractCallable } + +:e +let abstractParent = Abstract +class StoredIncomplete extends abstractParent +//β”‚ ╔══[COMPILATION ERROR] Class 'StoredIncomplete' has unimplemented members +//β”‚ β•‘ l.367: class StoredIncomplete extends abstractParent +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Member 'foo' requires an implementation +//β”‚ β•‘ l.53: abstract class Abstract { fun foo() } +//β”‚ ╙── ^^^ +//β”‚ abstractParent = class Abstract + +:expect 5 +let abstractParent = Abstract +class StoredComplete extends abstractParent { fun foo() = 5 } +(new StoredComplete).foo() +//β”‚ = 5 +//β”‚ abstractParent = class Abstract + +// A lifted subclass can call a top-level parent and retain its captured argument. +:lift +:inlineThreshold 10000 +:expect [8, 10] +fun lifted(n) = { + class Child extends SuperBase { override fun foo(x) = super.foo(x) + n } + (new Child).foo(2) +} +[lifted(5), lifted(7)] +//β”‚ = [8, 10] + +:expect [9, 9] +class FieldBase { open val x: Int = 1 } +class FieldChild extends FieldBase { override val x: Int = 9 } +private fun field(x) = x.x +[field(new FieldChild), (new FieldChild).x] +//β”‚ = [9, 9] + +:expect 7 +abstract class AbstractField { val x: Int } +class ConcreteField extends AbstractField { val x: Int = 7 } +(new ConcreteField).x +//β”‚ = 7 + +abstract class PrivateRequirement { private fun hidden() } + +:e +class InheritedPrivateRequirement extends PrivateRequirement { fun hidden() = 1 } +//β”‚ ╔══[COMPILATION ERROR] Class 'InheritedPrivateRequirement' has unimplemented members +//β”‚ β•‘ l.410: class InheritedPrivateRequirement extends PrivateRequirement { fun hidden() = 1 } +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Member 'hidden' requires an implementation +//β”‚ β•‘ l.407: abstract class PrivateRequirement { private fun hidden() } +//β”‚ ╙── ^^^^^^ + +// Different receiver classes can inherit the same declaration without overriding it. +:expect [1, 1] +class OtherInherited extends Base +private fun readInherited(flag) = { + let receiver = if flag then new Inherited else new OtherInherited + receiver.foo() +} +[readInherited(true), readInherited(false)] +//β”‚ = [1, 1] + +// Reassigning a stored class cannot change a previously defined superclass. +// The resolver still assumes one contextual parent shape per class; reassignment violates +// its constructor-shape cache assertion before lowering. The runtime result remains correct. +:fixme +:expect 4 +let changingParent = makeParent(4) +class SavedParent extends changingParent { override fun f() = super.f() } +set changingParent = makeParent(9) +(new SavedParent).f() +//β”‚ ╔══[INTERNAL ERROR] Compiler reached an unexpected state at 'NewResolver.scala:2988' +//β”‚ β•Ÿβ”€β”€ The compilation result may be incorrect. +//β”‚ ╙── This is a compiler bug; please report it to the maintainers. +//β”‚ = 4 +//β”‚ changingParent = class Parent + +// For a nested class, its receiver must likewise be saved at the extends clause. +// This also encounters the constructor-shape cache limitation above. +:fixme +:expect 5 +class Holder(n) { class Parent { open fun f() = n } } +let changingHolder = Holder(5) +class SavedNestedParent extends changingHolder.Parent { override fun f() = super.f() } +set changingHolder = Holder(11) +(new SavedNestedParent).f() +//β”‚ ╔══[INTERNAL ERROR] Compiler reached an unexpected state at 'NewResolver.scala:2988' +//β”‚ β•Ÿβ”€β”€ The compilation result may be incorrect. +//β”‚ ╙── This is a compiler bug; please report it to the maintainers. +//β”‚ = 5 +//β”‚ changingHolder = Holder(_) diff --git a/hkmc2/shared/src/test/mlscript/newres/ParamClasses.mls b/hkmc2/shared/src/test/mlscript/newres/ParamClasses.mls new file mode 100644 index 0000000000..05729ecd67 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ParamClasses.mls @@ -0,0 +1,343 @@ +:. + + +// :elt +:sr +class Foo(val a) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fun Foo⁰ class FooΒΉ::FooΒ²(val a⁰) { val aΒΉ::aΒ² β€Ήflow:a⁰› = a⁰^aΒ² } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + + +// let f = Foo(123) + + +:sr +:silent +val f = Foo(Predef) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ val f⁰::fΒΉ β€Ήflow:f⁰› = FooΒΉ^fΒΉ(Predef⁰^fΒΉ)β€Ήapp⁰› +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +// :de +:sr +let tmp = f.a +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let tmp⁰, tmp⁰ = f⁰.aΒΉ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ tmp = class Predef { +//β”‚ Symbols: Symbols { +//β”‚ prettyPrint: Symbol("mlscript.prettyPrint"), +//β”‚ definitionMetadata: Symbol("mlscript.definitionMetadata"), +//β”‚ class: object Symbols +//β”‚ }, +//β”‚ Sub: class Sub, +//β”‚ Eq: class Eq, +//β”‚ Refl: Refl, +//β”‚ js_assert: fun assert, +//β”‚ foldl: fun fold, +//β”‚ meta: class meta +//β”‚ } + +// :de +tmp.id +//β”‚ = fun id + +// :de +:sr +f.a.id +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ f⁰.aΒΉ.id⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = fun id + + +// :de +:silent +:sr +let p = Foo(Predef).a +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let p⁰, p⁰ = FooΒΉ(Predef⁰)β€ΉappΒΉβ€Ί.aΒΉ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +// :de +:sr +p.id +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ p⁰.id⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = fun id + +:sr +Foo(Predef).a.id +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ FooΒΉ(Predef⁰)β€ΉappΒ²β€Ί.aΒΉ.id⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = fun id + + +:sr +// TODO +// class Foo(val a) { +// val b = a + 1 +// } +class Foo(val a) { + val b = a + 1 +} +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fun FooΒ³ +//β”‚ class Foo⁴::Foo⁡(val aΒ³) { +//β”‚ val a⁴::a⁡ β€Ήflow:aΒΉβ€Ί = aΒ³^a⁡, +//β”‚ val b⁰::bΒΉ β€Ήflow:b⁰› = +(Foo⁡.this.a⁴^bΒΉ, 1)β€ΉappΒ³β€Ί +//β”‚ } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +// :de +:sr +let f = Foo(1) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let fΒ², fΒ² = Foo⁴(1)β€Ήapp⁴› +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ f = Foo(1) + +// :fixme +// :de +:sr +f.a +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fΒ².a⁴ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 1 + +// :fixme +// :de +:sr +f.b +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fΒ².b⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 2 + + +val fv = Foo(1) +//β”‚ fv = Foo(1) + +:sr +fv.a +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fv⁰.a⁴ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 1 + +// :de +:sr +:sir +fun get = fv.a +val fv = Foo(1) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fun get⁰::getΒΉ β€Ήflow:get⁰› = fvΒΉ^getΒΉ.a⁴, val fvΒΉ::fvΒ² β€Ήflow:fv⁰› = Foo⁴^fvΒ²(1)β€Ήapp⁡› +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let tmp; +//β”‚ define get⁰ as fun getΒΉ() { return fv⁰.a⁰ }; +//β”‚ set tmp = Foo⁰(1); +//β”‚ define fvΒΉ as val fv⁰ = tmp; +//β”‚ end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fv = Foo(1) + +// :fixme +:sr +:sir +:expect 1 +get +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ get⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ return getΒΉ() +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 1 + + +:sr +private fun foo(x) = + x.a + x.b +foo(f) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ @private fun foo⁰::fooΒΉ(x⁰) β€Ήflow:foo⁰› = +(x⁰.a⁴, x⁰.b⁰)β€Ήapp⁢›, foo⁰(fΒ²)β€Ήapp⁷› +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 3 + + +// :elt +:sr +class Bar(b) with + val foo = Foo(b) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fun Bar⁰ +//β”‚ class BarΒΉ::BarΒ²(bΒ²) { +//β”‚ @private val bΒ³::b⁴ β€Ήflow:bΒΉβ€Ί = bΒ²^b⁴, +//β”‚ val fooΒ²::fooΒ³ β€Ήflow:fooΒΉβ€Ί = Foo⁴^BarΒ²^fooΒ³(BarΒ².this.bΒ³^fooΒ³)β€Ήapp⁸› +//β”‚ } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +// :de +let y = Bar(123) +//β”‚ y = Bar(_) + +:sr +y.foo +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ y⁰.fooΒ² +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = Foo(123) + +// :de +:sr +y.foo.a +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ y⁰.fooΒ².a⁴ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 123 + + +:sjs +:sr +:rt +// :de +let f = new Foo(1) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let fΒ³, fΒ³ = new Foo⁴(1) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ Blk: +//β”‚ stats = Ls of +//β”‚ LetDecl: +//β”‚ sym = f⁰ +//β”‚ DefineVar: +//β”‚ sym = f⁰ +//β”‚ rhs = New: +//β”‚ cls = MemberRef of member:FooΒΉ +//β”‚ args = Ls of +//β”‚ Tup of Ls of +//β”‚ Fld: +//β”‚ term = Lit of IntLit of 1 +//β”‚ res = Lit of UnitLit of false +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let f2; f2 = globalThis.Object.freeze(new Foo3.class(1)); +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ f = Foo(1) + + +let f = new Foo +//β”‚ f = fun f + +let g = f(1) +//β”‚ g = Foo(1) + +:e +g(2) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.237: g(2) +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Foo' cannot receive more argument lists. +//β”‚ β•‘ l.233: let g = f(1) +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:e +let f = new Foo() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.247: let f = new Foo() +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Foo' expected 1 argument, but got 0 +//β”‚ β•‘ l.84: class Foo(val a) { +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.85: val b = a + 1 +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.86: } +//β”‚ ╙── ^ +//β”‚ f = Foo(undefined) + + +:e +new 42 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.262: new 42 +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Invalid class expression: integer literal +//β”‚ β•‘ l.262: new 42 +//β”‚ ╙── ^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + + +// :de +:sr +:sjs +// private class Foo(pred) { // TODO +private class Foo(pred) with { + // val r = pred.id(42) + pred.id(42) +} +let f = new Foo(Predef) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ @private fun Foo⁢ +//β”‚ class Foo⁷::Foo⁸(pred⁰) { +//β”‚ @private val predΒΉ::predΒ² β€Ήflow:pred⁰› = pred⁰^predΒ², +//β”‚ Foo⁸.this.predΒΉ.id⁰(42)β€Ήapp⁹› +//β”‚ }, +//β”‚ let f⁴, +//β”‚ f⁴ = new Foo⁷(Predef⁰) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let Foo5, f6; +//β”‚ Foo5 = function Foo(pred) { +//β”‚ return globalThis.Object.freeze(new Foo.class(pred)); +//β”‚ }; +//β”‚ (class Foo4 { +//β”‚ static { +//β”‚ Foo5.class = this +//β”‚ } +//β”‚ toString() { return runtime.render(this); } +//β”‚ static [definitionMetadata] = ["class", "Foo", [null]]; +//β”‚ }); +//β”‚ f6 = globalThis.Object.freeze(new Foo5.class(Predef)); +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ f = Foo(_) + + +:sr +private class Foo(pred) { + pred.id(1) + pred.id(2) +} +let f = Foo(Predef) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ @private fun Foo⁹ +//β”‚ class Foo¹⁰::FooΒΉΒΉ(predΒ³) { +//β”‚ @private val pred⁴::pred⁡ β€Ήflow:predΒΉβ€Ί = predΒ³^pred⁡, +//β”‚ FooΒΉΒΉ.this.pred⁴.id⁰(1)β€Ήapp¹⁰›, +//β”‚ FooΒΉΒΉ.this.pred⁴.id⁰(2)β€ΉappΒΉΒΉβ€Ί +//β”‚ }, +//β”‚ let f⁡, +//β”‚ f⁡ = Foo¹⁰(Predef⁰)β€ΉappΒΉΒ²β€Ί +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ f = Foo(_) + +:sr +private class Foo(pred) { + val r = pred.id(42) + fun callId(x) = pred.id(x) +} +let f = new Foo(Predef) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ @private fun FooΒΉΒ² +//β”‚ class FooΒΉΒ³::Foo¹⁴(pred⁢) { +//β”‚ @private val pred⁷::pred⁸ β€Ήflow:predΒ²β€Ί = pred⁢^pred⁸, +//β”‚ val r⁰::rΒΉ β€Ήflow:r⁰› = Foo¹⁴.this.pred⁷^rΒΉ.id⁰(42)β€ΉappΒΉΒ³β€Ί, +//β”‚ fun callId⁰::callIdΒΉ(xΒΉ) β€Ήflow:callId⁰› = Foo¹⁴.this.pred⁷^callIdΒΉ.id⁰(xΒΉ)β€Ήapp¹⁴› +//β”‚ }, +//β”‚ let f⁢, +//β”‚ f⁢ = new FooΒΉΒ³(Predef⁰) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ f = Foo(_) + + + diff --git a/hkmc2/shared/src/test/mlscript/newres/PartialSignatures.mls b/hkmc2/shared/src/test/mlscript/newres/PartialSignatures.mls new file mode 100644 index 0000000000..e25849b859 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/PartialSignatures.mls @@ -0,0 +1,246 @@ +:. + +// An inferred result combines a declared type parameter and ordinary value flow. +// Both calls must preserve their own annotations and unannotated argument shapes. +:expect [1, 2, 3, 4] +class First(val first: Int) +class Second(val second: Int) +private fun pair[A](x: A, y) = [x, y] +let left = pair(First(1), Second(2)) +let right = pair(Second(3), First(4)) +[left.0.first, left.1.second, right.0.second, right.1.first] +//β”‚ = [1, 2, 3, 4] +//β”‚ left = [First(1), Second(2)] +//β”‚ right = [Second(3), First(4)] + +// An unannotated callback and inferred result retain their dependence on A. +:expect [5, 6] +class First(val first: Int) +class Second(val second: Int) +private fun apply[A](x: A, f) = f(x) +[apply(First(5), x => x.first), apply(Second(6), x => x.second)] +//β”‚ = [5, 6] + +// An omitted trailing type argument is a hole inferred from each caller. +// The written Int argument and the Pair interface remain in place. +// TODO: template argument rebasing must not introduce a lossy wildcard round trip. +:fixme +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +class Pair[A, B](val value: [A, B]) +private fun second(pair: Pair[Int]) = pair.value.1 +[second(Pair([0, First(1)])).first, second(Pair([0, Second(2)])).second] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.33: [second(Pair([0, First(1)])).first, second(Pair([0, Second(2)])).second] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Second' does not contain member 'first' +//β”‚ β•‘ l.33: [second(Pair([0, First(1)])).first, second(Pair([0, Second(2)])).second] +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.33: [second(Pair([0, First(1)])).first, second(Pair([0, Second(2)])).second] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'First' does not contain member 'second' +//β”‚ β•‘ l.33: [second(Pair([0, First(1)])).first, second(Pair([0, Second(2)])).second] +//β”‚ ╙── ^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '[1, 2]', got: 'undefined' + +// Bare alias uses infer their omitted argument without losing the alias body. +// TODO: the same template argument rebasing loses caller IDs through the alias. +:fixme +:expect [3, 4] +class First(val first: Int) +class Second(val second: Int) +class Pair[A, B](val value: [A, B]) +type HalfPair[T] = Pair[Int, T] +private fun second(pair: HalfPair) = pair.value.1 +[second(Pair([0, First(3)])).first, second(Pair([0, Second(4)])).second] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.58: [second(Pair([0, First(3)])).first, second(Pair([0, Second(4)])).second] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Second' does not contain member 'first' +//β”‚ β•‘ l.58: [second(Pair([0, First(3)])).first, second(Pair([0, Second(4)])).second] +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.58: [second(Pair([0, First(3)])).first, second(Pair([0, Second(4)])).second] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'First' does not contain member 'second' +//β”‚ β•‘ l.58: [second(Pair([0, First(3)])).first, second(Pair([0, Second(4)])).second] +//β”‚ ╙── ^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '[3, 4]', got: 'undefined' + +// A bare Array annotation has an inferred element hole in both positions. +// This case has no declared type binder to instantiate at either call. +// TODO: template argument rebasing currently mixes the two callers' hole bounds. +:fixme +:expect [5, 6] +class First(val first: Int) +class Second(val second: Int) +private fun append(xs: Array, value) = xs.push(value) +let xs = mut [] +let ys = mut [] +append(xs, First(5)) +append(ys, Second(6)) +[xs.0.first, ys.0.second] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.86: [xs.0.first, ys.0.second] +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'First' does not contain member 'second' +//β”‚ β•‘ l.84: append(xs, First(5)) +//β”‚ ╙── ^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.86: [xs.0.first, ys.0.second] +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Second' does not contain member 'first' +//β”‚ β•‘ l.85: append(ys, Second(6)) +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '[5, 6]', got: 'undefined' +//β”‚ xs = [First(5)] +//β”‚ ys = [Second(6)] + +// A returned closure observes annotated and inferred fields after the outer call. +:expect [7, 8, 9, 10] +class First(val first: Int) +class Second(val second: Int) +private fun delayed[A](x: A, y) = () => [x, y] +let left = delayed(First(7), Second(8)) +let right = delayed(Second(9), First(10)) +[left().0.first, left().1.second, right().0.second, right().1.first] +//β”‚ = [7, 8, 9, 10] +//β”‚ left = fun +//β”‚ right = fun + +// Inferring a base member's result must not assume every override returns that shape. +// Either an incompatible override or the unsupported selection must be rejected. +:ge +class Item(val value: Int) +class Other(val other: Int) +class Base with + open fun item() = Item(1) +class Child extends Base with + override fun item() = Other(2) +private fun read(x: Base) = x.item().value +read(new Child) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.125: private fun read(x: Base) = x.item().value +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ The shape of this value is unknown. +//β”‚ β•‘ l.125: private fun read(x: Base) = x.item().value +//β”‚ ╙── ^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A separate polymorphic signature retains binders for explicit type application. +:expect [11, 12] +class First(val first: Int) +class Second(val second: Int) +private fun identity: [A] -> A -> A +private fun identity(x) = x +[identity[First](First(11)).first, identity[Second](Second(12)).second] +//β”‚ = [11, 12] + +// A polymorphic value annotation also retains its own binders. +:expect 13 +class Item(val value: Int) +let identity = (x => x) as [A] -> A -> A +identity[Item](Item(13)).value +//β”‚ = 13 +//β”‚ identity = fun identity + +// Explicit arguments of an annotated polymorphic value restrict its result interface. +:ge +class Base +class Child extends Base with + val extra = 1 +let identity = (x => x) as [A] -> A -> A +identity[Base](new Child).extra +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.159: identity[Base](new Child).extra +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.159: identity[Base](new Child).extra +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ identity = fun identity + +// Type application checks the retained quantified binder count. +:ge +let identity = (x => x) as [A] -> A -> A +identity[Int, Int](1) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.172: identity[Int, Int](1) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function with a declared signature expected 1 type argument, but got 2 +//β”‚ β•‘ l.171: let identity = (x => x) as [A] -> A -> A +//β”‚ ╙── ^^^^^^ +//β”‚ = 1 +//β”‚ identity = fun identity + +// One stored polymorphic value has separate inferred arguments at each call site. +:expect [14, 15] +class First(val first: Int) +class Second(val second: Int) +let identity = (x => x) as [A] -> A -> A +[identity(First(14)).first, identity(Second(15)).second] +//β”‚ = [14, 15] +//β”‚ identity = fun identity + +// Specialization is retained by the value; it does not specialize the shared source. +:expect [16, 17] +class First(val first: Int) +class Second(val second: Int) +let identity = (x => x) as [A] -> A -> A +let first = identity[First] +let second = identity[Second] +[first(First(16)).first, second(Second(17)).second] +//β”‚ = [16, 17] +//β”‚ first = fun identity +//β”‚ identity = fun identity +//β”‚ second = fun identity + +// Later parameter lists retain the first application's type-parameter instance. +:expect [18, 19] +class First(val first: Int) +class Second(val second: Int) +let keep = (x => y => x) as [A] -> A -> A -> A +let first = keep(First(18)) +let second = keep(Second(19)) +[first(First(0)).first, second(Second(0)).second] +//β”‚ = [18, 19] +//β”‚ first = fun +//β”‚ keep = fun keep +//β”‚ second = fun + +// A stored reference to a fully annotated declaration uses the application's site. +:expect [20, 21] +class First(val first: Int) +class Second(val second: Int) +private fun identity[A](x: A): A = x +let shared = identity +[shared(First(20)).first, shared(Second(21)).second] +//β”‚ = [20, 21] +//β”‚ shared = fun identity + +// A returned scheme binds B at its own call while retaining the captured A. +:expect [22, 23, 24, 25] +class First(val first: Int) +class Second(val second: Int) +let pair = (x => y => [x, y]) as [A] -> A -> ([B] -> B -> [A, B]) +let first = pair(First(22)) +let second = pair(Second(23)) +let left = first(Second(24)) +let right = second(First(25)) +[left.0.first, right.0.second, left.1.second, right.1.first] +//β”‚ = [22, 23, 24, 25] +//β”‚ first = fun +//β”‚ left = [First(22), Second(24)] +//β”‚ pair = fun pair +//β”‚ right = [Second(23), First(25)] +//β”‚ second = fun + +// A private returned closure must be checked from its annotation before any call. +class Item(val value: Int) +private fun get(): Item -> Int = x => x.value diff --git a/hkmc2/shared/src/test/mlscript/newres/PatMat.mls b/hkmc2/shared/src/test/mlscript/newres/PatMat.mls new file mode 100644 index 0000000000..c827c4e6a5 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/PatMat.mls @@ -0,0 +1,193 @@ +:. + + +:sr +class Foo(val a) +object Bar { val b = 123 } +class Baz(c) { fun b = c } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fun Foo⁰ +//β”‚ class FooΒΉ::FooΒ²(val a⁰) { +//β”‚ val aΒΉ::aΒ² β€Ήflow:a⁰› = a⁰^aΒ² +//β”‚ }, +//β”‚ object Bar⁰::BarΒΉ { +//β”‚ val b⁰::bΒΉ β€Ήflow:b⁰› = 123 +//β”‚ }, +//β”‚ fun Baz⁰ +//β”‚ class BazΒΉ::BazΒ²(c⁰) { +//β”‚ @private val cΒΉ::cΒ² β€Ήflow:c⁰› = c⁰^cΒ², +//β”‚ fun bΒ²::bΒ³ β€Ήflow:bΒΉβ€Ί = BazΒ².this.cΒΉ^bΒ³ +//β”‚ } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + + +let x = Foo(Bar) +//β”‚ x = Foo(Bar { b: 123, class: object Bar }) + +// TODO: avoid the `let scrut⁰` like in old resl. + +// :dl +// :de +:sr +// :elt +// :sir +// :sjs +if x is + Foo(y) then y.b +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ if { x⁰ is FooΒΉ(_ as y⁰) then y⁰.b⁰ } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 123 + +:e +if x is + Foo(y) then y.id +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.44: Foo(y) then y.id +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Object 'Bar' does not contain member 'id' +//β”‚ β•‘ l.6: object Bar { val b = 123 } +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + + +let bz = Baz(Bar) +//β”‚ bz = Baz(_) + +let x2 = Foo(bz) +//β”‚ x2 = Foo(Baz(_)) + +:sr +if x2 is + Foo(y) then y.b +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ if { x2⁰ is FooΒΉ(_ as yΒΉ) then yΒΉ.bΒ² } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = Bar { b: 123, class: object Bar } + + +// :de +:sr +fun test = + case + Foo(y) then y +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fun test⁰::testΒΉ β€Ήflow:test⁰› = (caseScrut⁰) => if { caseScrut⁰ is FooΒΉ^testΒΉ(_ as yΒ²) then yΒ² } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +:sr +test(x).b +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ test⁰(x⁰)β€Ήapp⁰›.b⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 123 + +// :de +:sr +test(x2).b +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ test⁰(x2⁰)β€ΉappΒΉβ€Ί.bΒ² +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = Bar { b: 123, class: object Bar } + + +let bz = Baz(Bar) +//β”‚ bz = Baz(_) + +:todo +fun test(x) = + if x is + Foo(y) then y.b +test(bz) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.100: Foo(y) then y.b +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'b' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ This constructor field may contain a value of unknown shape. +//β”‚ β•‘ l.5: class Foo(val a) +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.98: fun test(x) = +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'test' is exposed by this compilation unit. +//β”‚ β•‘ l.98: fun test(x) = +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•‘ l.99: if x is +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•‘ l.100: Foo(y) then y.b +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] Error: match error + + +set x = bz + +:breakme // Note: if compiled together with it, this would make the previous code ambiguous +x = bz + + +let x = if maybe then Bar else bz +//β”‚ x = Bar { b: 123, class: object Bar } + +:e +x.b +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'b' is ambiguous, as it has multiple resolved targets +//β”‚ β•‘ l.132: x.b +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ target: function 'b' in overloaded function and class 'Baz' +//β”‚ β•‘ l.7: class Baz(c) { fun b = c } +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ target: value 'b' in object 'Bar' +//β”‚ β•‘ l.6: object Bar { val b = 123 } +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:re +if x is + Baz as y then y.b +//β”‚ ═══[RUNTIME ERROR] Error: match error + +:expect 123 +if x is + Baz as y then y.b + Bar as z then z.b +//β”‚ = 123 + + + + +:silent +let x2 = Foo(Predef) + +if x2 is + Foo(y2) then y2.id +//β”‚ = fun id + + +:expect fun id +if Foo(Predef) is + Foo(y) then y.id +//β”‚ = fun id + + +:todo +val f = case + Foo(y) then y.b +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.174: Foo(y) then y.b +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'b' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ This constructor field may contain a value of unknown shape. +//β”‚ β•‘ l.5: class Foo(val a) +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Parameter 'caseScrut' admits values of unknown shape. +//β”‚ β•Ÿβ”€β”€ 'f' is exposed by this compilation unit. +//β”‚ β•‘ l.173: val f = case +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•‘ l.174: Foo(y) then y.b +//β”‚ ╙── ^^^^^^^^^^^^^^^^^ +//β”‚ f = fun + + + + + diff --git a/hkmc2/shared/src/test/mlscript/newres/PatternDefinitionBindings.mls b/hkmc2/shared/src/test/mlscript/newres/PatternDefinitionBindings.mls new file mode 100644 index 0000000000..e7c7f0d58e --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/PatternDefinitionBindings.mls @@ -0,0 +1,145 @@ +:. + +// A definition's guard sees the same narrowed alias as a direct match. +:expect ["a", "no", "no"] +pattern AnyChar = (Str as s where s.length is 1) => s +fun read(value) = if value is AnyChar(s) then s else "no" +[read("a"), read("ab"), read(1)] +//β”‚ = ["a", "no", "no"] + +// The transformation has its own parameters, which receive the alias's shapes. +:expect "AB" +pattern Upper = Str as s => s.toUpperCase() +fun read(value) = if value is Upper(s) then s else "no" +read("ab") +//β”‚ = "AB" + +// Class declarations supply an interface even without local pattern callers. +:expect 3 +class Box(val value: Str) +pattern Length = Box(Str as text) => text.length +fun read(value) = if value is Length(n) then n else 0 +read(Box("abc")) +//β”‚ = 3 + +// Constructor-field signatures also flow to plain binding patterns. +:expect 3 +class Box(val value: Str) +pattern Length = Box(text) => text.length +fun read(value) = if value is Length(n) then n else 0 +read(Box("abc")) +//β”‚ = 3 + +// Exported extraction parameters and guards must share the binding's flow. +:expect "ab" +pattern Text(s) = Str as s where s.length > 0 +fun read(value) = if value is Text(s) then s else "no" +read("ab") +//β”‚ = "ab" + +// An untested input remains unknown, even when every local call is a string. +:ge +pattern Bad = s => s.length +fun read(value) = if value is Bad(n) then n else 0 +read("abc") +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.42: pattern Bad = s => s.length +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'length' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Pattern 'Bad' accepts values of unknown shape. +//β”‚ β•‘ l.42: pattern Bad = s => s.length +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A wrong member is rejected on the narrowed type, rather than left unresolved. +:ge +pattern Bad = Str as s => s.missingMember() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.56: pattern Bad = Str as s => s.missingMember() +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Str' does not contain member 'missingMember' +//β”‚ β•‘ l.56: pattern Bad = Str as s => s.missingMember() +//β”‚ ╙── ^^^ + +// Deferred constructor resolution must still reach guards and transformations. +:expect 4 +pattern Length = Later(Str as s where s.length > 0) => s.length +class Later(val value: Str) +fun read(value) = if value is Length(n) then n else 0 +read(Later("abcd")) +//β”‚ = 4 + +// Each alternative supplies the same extraction symbol to its own guard. +:expect ["a", "ab", "no"] +pattern Text(s) = (Str as s where s.length is 1) | (Str as s where s.length is 2) +fun read(value) = if value is Text(s) then s else "no" +[read("a"), read("ab"), read("abc")] +//β”‚ = ["a", "ab", "no"] + +// A transform's inner binding is distinct from the outer extraction binding +// with the same spelling. The inner s is a string; the extracted s is its length. +:expect 3 +pattern Length(s) = (Str as s => s.length) as s +fun read(value) = if value is Length(s) then s else 0 +read("abc") +//β”‚ = 3 + +// A nested class test refines a record field before its guard and transformation. +:expect "AB" +pattern Name = ((name: Str as s where s.length > 0)) => s.toUpperCase() +fun read(value) = if value is Name(s) then s else "no" +read((name: "ab")) +//β”‚ = "AB" + +// An outer alias receives the transformed result, not the tested input shape. +:ge +pattern Bad = ((Str as s => s.length) as n) => n.toUpperCase() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.96: pattern Bad = ((Str as s => s.length) as n) => n.toUpperCase() +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Int' does not contain member 'toUpperCase' +//β”‚ β•‘ in ../decls/Prelude.mls +//β”‚ β•‘ l.205: val length: Int +//β”‚ ╙── ^^^ + +// Conjunction produces a pair, not the string tested by its left operand. +:ge +pattern Bad = ((Str & _) as pair) => pair.toUpperCase() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.107: pattern Bad = ((Str & _) as pair) => pair.toUpperCase() +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'toUpperCase' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ The output shape of this pattern is not yet known. +//β”‚ β•‘ l.107: pattern Bad = ((Str & _) as pair) => pair.toUpperCase() +//β”‚ ╙── ^^^^^^^ + +// Rebuilding a constructor must not retain its input's generic field shape. +:ge +class Box[A](val value: A) +pattern Bad = (Box(Str as s => s.length) as box) => box.value.toUpperCase() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.119: pattern Bad = (Box(Str as s => s.length) as box) => box.value.toUpperCase() +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ The output shape of this pattern is not yet known. +//β”‚ β•‘ l.119: pattern Bad = (Box(Str as s => s.length) as box) => box.value.toUpperCase() +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'toUpperCase' has no resolved target +//β”‚ β•‘ l.119: pattern Bad = (Box(Str as s => s.length) as box) => box.value.toUpperCase() +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^ + +// FIXME: Concatenation output interfaces are not yet inferred. +:fixme +:expect 2 +pattern Joined = ("a" ~ "b") as s => s.length +fun read(value) = if value is Joined(n) then n else 0 +read("ab") +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.134: pattern Joined = ("a" ~ "b") as s => s.length +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'length' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ The output shape of this pattern is not yet known. +//β”‚ β•‘ l.134: pattern Joined = ("a" ~ "b") as s => s.length +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/newres/PatternRefinement.mls b/hkmc2/shared/src/test/mlscript/newres/PatternRefinement.mls new file mode 100644 index 0000000000..d28ea96d2b --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/PatternRefinement.mls @@ -0,0 +1,49 @@ +:. + +import "../../mlscript-compile/Option.mls" +open Option + +// Some extraction retains the option's declared element interface. +:expect 42 +class Box(val value: Int) +fun read(x: Option[Box]) = if x is Some(b) then b.value else 0 +read(Some(Box(42))) +//β”‚ = 42 + +// A class test refines a structural interface without a redundant cast. +:expect 42 +class Box(val value: Int) +fun read(x: {value: Int}) = if x is Box as b then b.value else 0 +read(Box(42)) +//β”‚ = 42 + +// Null is rejected by the Array test; it must not contribute an unknown +// element type alongside Array[Str]. Exercise both runtime branches. +:expect [1, 0] +fun read(x: Array[Str] | null) = if x is Array as xs then xs.0.length else 0 +[read(["x"]), read(null)] +//β”‚ = [1, 0] + +// Refinement must not grant an absent member to the matched interface. +:e +class Box(val value: Int) +fun read(x: {value: Int}) = if x is Box as b then b.missing else 0 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.30: fun read(x: {value: Int}) = if x is Box as b then b.missing else 0 +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Box' does not contain member 'missing' +//β”‚ β•‘ l.30: fun read(x: {value: Int}) = if x is Box as b then b.missing else 0 +//β”‚ ╙── ^^^ + +// A class pattern inside that same class retains the lexical captures needed +// by subsequent field reads and assignments through a structural interface. +:expect 3 +class Buffer(val items: Array[Int]) with + fun read(value: {length: Int}) = + let values = value + if values is Buffer as buffer do + set values = buffer.items + if values is Array as values then values.length else 0 + fun length = items.length +Buffer([1, 2, 3]).read(Buffer([4, 5, 6])) +//β”‚ = 3 diff --git a/hkmc2/shared/src/test/mlscript/newres/PredefInterfaces.mls b/hkmc2/shared/src/test/mlscript/newres/PredefInterfaces.mls new file mode 100644 index 0000000000..bc024df9e0 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/PredefInterfaces.mls @@ -0,0 +1,40 @@ +:. + +// The migrated public helpers preserve result interfaces across callbacks. +:expect [4, 5, 6, 7] +class Box(val value: Int) +fun box(x: Int): Box = Box(x) +[(4 |> box).value, (box <| 5).value, (6 .> box).value, (box <. 7).value] +//β”‚ = [4, 5, 6, 7] + +:expect [8, 9] +class Box(val value: Int) +fun box(x: Int): Box = Box(x) +fun value(box: Box): Int = box.value +[(box >> value)(8), (value << box)(9)] +//β”‚ = [8, 9] + +:expect [true, false, true, false, false, true] +class Box(val value: Int) +[[[1], [2]] == [[1], [2]], [[1]] == [[2]], Box(3) == Box(3), Box(3) == Box(4), null == 0, null == null] +//β”‚ = [true, false, true, false, false, true] + +:expect ["a", "b", "c"] +fun stringify(n: Int): Str = ["a", "b", "c"].[n] +[0, 1, 2].map(pass1(stringify)) +//β”‚ = ["a", "b", "c"] + + +// A module member alias preserves access to imported polymorphic functions. +module Imported with + val p = Predef + +:expect 42 +Imported.p.id({answer: 42}).answer +//β”‚ = 42 + +:expect true +Imported.p.id(Predef).maybe +//β”‚ = true + + diff --git a/hkmc2/shared/src/test/mlscript/newres/PrimitiveMembers.mls b/hkmc2/shared/src/test/mlscript/newres/PrimitiveMembers.mls new file mode 100644 index 0000000000..d6e8b02525 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/PrimitiveMembers.mls @@ -0,0 +1,65 @@ +:. + +:expect [3, "abab", "A", 98, "1.00", "23"] +["abc".length, "ab".repeat(2), "abc".charAt(0).toUpperCase(), + "abc".charCodeAt(1), (1).toFixed(2), (23).toString()] +//β”‚ = [3, "abab", "A", 98, "1.00", "23"] + +:expect ["AB", "1.5", "a-b"] +fun upper(s: Str) = s.toUpperCase() +fun decimal(n: Num) = n.toFixed(1) +[upper("ab"), decimal(1.5), "a b".split(" ").join("-")] +//β”‚ = ["AB", "1.5", "a-b"] + +:e +"abc".nonexistent +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.15: "abc".nonexistent +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ String literal does not contain member 'nonexistent' +//β”‚ β•‘ l.15: "abc".nonexistent +//β”‚ ╙── ^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:expect [true, 2, false, 1] +let values = new mut Set +values.add("a") +values.add("b") +let present = values.has("a") +let size = values.size +values.delete("a") +[present, size, values.has("a"), values.size] +//β”‚ = [true, 2, false, 1] +//β”‚ present = true +//β”‚ size = 2 +//β”‚ values = Set(1) {"b"} + +:expect [true, "ab", null, false] +let re = new RegExp("a+") +let groups = new RegExp("ab").exec("abc") +[re.test("aa"), (if groups is Array as array then array.at(0) else "missing"), + re.exec("c"), (new Map).has("missing")] +//β”‚ = [true, "ab", null, false] +//β”‚ groups = ["ab"] +//β”‚ re = /a+/ + +:expect [true, true, true, "oops"] +let error = Error("oops") +[error is Error, TypeError("bad") is TypeError, + RangeError("bounds") is RangeError, error.message] +//β”‚ = [true, true, true, "oops"] +//β”‚ error = Error: oops + +:expect [true, 3, "42", true] +let xs = Array(3) +[xs is Array, xs.length, String(42), (new Error("new")) is Error] +//β”‚ = [true, 3, "42", true] +//β”‚ xs = [<3 empty items>] + +// Int inherits toString from Num. The parent reference is captured into Int's +// scope, so the inherited member must leave that scope before the receiver's +// path, here the exit from `one`, is applied. +:expect "1" +fun one() = 1 +one().toString() +//β”‚ = "1" diff --git a/hkmc2/shared/src/test/mlscript/newres/Projections.mls b/hkmc2/shared/src/test/mlscript/newres/Projections.mls new file mode 100644 index 0000000000..1f69609d3f --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/Projections.mls @@ -0,0 +1,273 @@ +:. + +// Both projection forms wait for forward class definitions. +:expect 6 +fun read(a) = a.Later#get() +val project = Later::get +class Later(val x) { fun get() = x } +let a = Later(3) +read(a) + project(a) +//β”‚ = 6 +//β”‚ a = Later(3) +//β”‚ project = fun + +// The class interpretation is independent of its term overload. +:expect 7 +class Overloaded { fun get() = 7 } +fun Overloaded() = 99 +(new Overloaded).Overloaded#get() +//β”‚ = 7 + +// Class aliases retain constructor shapes. +:expect 8 +class Original(val x) { fun get() = x } +val Alias = Original +let a = Original(8) +a.Alias#get() +//β”‚ = 8 +//β”‚ Alias = fun Original { class: class Original } +//β”‚ a = Original(8) + +// Inherited lookup retains the defining member. +:expect 9 +class Parent { fun get() = 9 } +class Child extends Parent {} +(new Child).Child#get() +//β”‚ = 9 + +// Fields and assignments share the same resolution path. +:expect 12 +class Cell { mut val value = 10 } +let cell = new Cell +set cell.Cell#value = 12 +cell.Cell#value +//β”‚ = 12 +//β”‚ cell = Cell + +// Missing members and non-class interpretations are resolution errors. +:e +fun missing(a) = a.Later#missing() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.49: fun missing(a) = a.Later#missing() +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Later' does not contain member 'missing' +//β”‚ β•‘ l.7: class Later(val x) { fun get() = x } +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:e +val NotClass = 1 +fun invalid(a) = a.NotClass#get() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.59: fun invalid(a) = a.NotClass#get() +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be used as a projection class. +//β”‚ β•‘ l.58: val NotClass = 1 +//β”‚ ╙── ^ +//β”‚ NotClass = 1 + +// Different classes can expose the very same inherited member. +:e +module Left { class Item extends Parent {} } +module Right { class Item extends Parent {} } +open Left +open Right +fun ambiguous(a) = a.Item#get() +//β”‚ ╔══[COMPILATION ERROR] The projection class is ambiguous +//β”‚ β•‘ l.74: fun ambiguous(a) = a.Item#get() +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ class: 'Item' +//β”‚ β•‘ l.71: module Right { class Item extends Parent {} } +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ class: 'Item' +//β”‚ β•‘ l.70: module Left { class Item extends Parent {} } +//β”‚ ╙── ^^^^ + +// Forwarded arguments carry shapes through the projected method's result. +// FIXME: The projected wrapper loses the activation of its exposed argument. +:fixme +:expect 17 +class Identity { fun apply(x) = x } +class Answer { fun get() = 17 } +let identity = new Identity +val apply = Identity::apply +apply(identity, new Answer).get() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.93: apply(identity, new Answer).get() +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'get' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ The shape of this value is unknown. +//β”‚ β•‘ l.92: val apply = Identity::apply +//β”‚ ╙── ^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '17', got: 'undefined' +//β”‚ apply = fun +//β”‚ identity = Identity + +// The receiver of a projection is evaluated once, including writes. +:expect 1 +module Counter { mut val count = 0 } +fun receiver() = { + set Counter.count = Counter.count + 1 + cell +} +set receiver().Cell#value = 13 +Counter.count +//β”‚ = 1 + +// Captured class overloads keep the class interpretation. +:expect 21 +fun captured() = { + class Local { fun get() = 21 } + fun Local() = 0 + fun read(value) = value.Local#get() + read(new Local) +} +captured() +//β”‚ = 21 + +// Different wildcard receivers exposing the same class symbol agree on the qualifier. +:expect [1, 1] +class Library { class Entry { fun get() = 1 } } +let first = new Library +let second = new Library +open first +open second +fun sameClass(value) = value.Entry#get() +[sameClass(new first.Entry), sameClass(new second.Entry)] +//β”‚ = [1, 1] +//β”‚ first = Library { Entry: class Entry } +//β”‚ second = Library { Entry: class Entry } + +:e +fun missingClass(value) = value.Unknown#get() +//β”‚ ╔══[COMPILATION ERROR] Cannot resolve the projection class +//β”‚ β•‘ l.142: fun missingClass(value) = value.Unknown#get() +//β”‚ ╙── ^^^^^^^^ + +:e +val instance = new Parent +fun instanceAsClass(value) = value.instance#get() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.149: fun instanceAsClass(value) = value.instance#get() +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Parent' cannot be used as a projection class. +//β”‚ β•‘ l.148: val instance = new Parent +//β”‚ ╙── ^^^^^^^^^^ +//β”‚ instance = Parent + +// A forward wildcard source resolves through its eventual module shape. +:expect 31 +fun forward(value) = { + open Delayed + value.Target#get() +} +module Delayed { class Target { fun get() = 31 } } +forward(new Delayed.Target) +//β”‚ = 31 + +// Repeating the same wildcard source does not introduce ambiguity. +:expect 31 +fun repeated(value) = { + open Delayed + open Delayed + value.Target#get() +} +repeated(new Delayed.Target) +//β”‚ = 31 + +// Member projections also accept qualified class names. +:expect 31 +val qualified = Delayed.Target::get +qualified(new Delayed.Target) +//β”‚ = 31 +//β”‚ qualified = fun + +// Deferred aliases must retain the rejected value's origin, not just its use site. +:e +fun invalidAlias(value) = value.AliasToNumber#get() +val SourceNumber = 123 +val AliasToNumber = SourceNumber +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.187: fun invalidAlias(value) = value.AliasToNumber#get() +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be used as a projection class. +//β”‚ β•‘ l.188: val SourceNumber = 123 +//β”‚ ╙── ^^^ +//β”‚ AliasToNumber = 123 +//β”‚ SourceNumber = 123 + +// Stored class values with different captures still identify one class declaration. +:expect [1, 2] +class CapturedLibrary(val number) { + class Entry { fun get() = number } +} +let leftLibrary = CapturedLibrary(1) +let rightLibrary = CapturedLibrary(2) +fun chooseEntry(flag) = if flag then leftLibrary.Entry.class else rightLibrary.Entry.class +val classFields = {entry: chooseEntry(true)} +open classFields +fun storedProjection(value) = value.entry#get() +[storedProjection(new leftLibrary.Entry), storedProjection(new rightLibrary.Entry)] +//β”‚ = [1, 2] +//β”‚ classFields = {entry: class Entry} +//β”‚ leftLibrary = CapturedLibrary(1) +//β”‚ rightLibrary = CapturedLibrary(2) + +// The qualifier's outer object must not supply the selected method's result shape. +:expect 42 +let recordLibrary = CapturedLibrary({answer: 42}) +let scalarLibrary = CapturedLibrary(0) +val EntryAlias = scalarLibrary.Entry.class +private fun recordProjection(value) = value.EntryAlias#get().answer +recordProjection(new recordLibrary.Entry) +//β”‚ = 42 +//β”‚ EntryAlias = class Entry +//β”‚ recordLibrary = CapturedLibrary({answer: 42}) +//β”‚ scalarLibrary = CapturedLibrary(0) + +// Distinct class symbols remain ambiguous even when both inherit the same method. +:e +class OtherEntry extends Parent {} +private fun chooseClass(flag) = if flag then Parent.class else OtherEntry.class +let classAlias = chooseClass(true) +fun ambiguousAlias(value) = value.classAlias#get() +//β”‚ ╔══[COMPILATION ERROR] The projection class is ambiguous +//β”‚ β•‘ l.233: fun ambiguousAlias(value) = value.classAlias#get() +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ class: 'OtherEntry' +//β”‚ β•‘ l.230: class OtherEntry extends Parent {} +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ class: 'Parent' +//β”‚ β•‘ l.33: class Parent { fun get() = 9 } +//β”‚ ╙── ^^^^^^ +//β”‚ classAlias = class Parent + +// An unknown receiver still exposes the inherited field's declared type. +:expect 6 +class FieldBase(val item: Int) +class FieldChild extends FieldBase(5) {} +fun readInherited(value) = value.FieldChild#item + 1 +readInherited(new FieldChild) +//β”‚ = 6 + +// Constructor functions and raw class objects agree on the disambiguating symbol. +:expect 5 +class Identified(val item) +let qualifier = Identified +set qualifier = Identified.class +fun readMixed(value) = value.qualifier#item +readMixed(Identified(5)) +//β”‚ = 5 +//β”‚ qualifier = class Identified + +// A projection qualifier requires a class interpretation, not a field named class. +:e +let recordQualifier = {"class": Parent} +fun invalidRecordQualifier(value) = value.recordQualifier#get() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.266: fun invalidRecordQualifier(value) = value.recordQualifier#get() +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Record literal cannot be used as a projection class. +//β”‚ β•‘ l.265: let recordQualifier = {"class": Parent} +//β”‚ ╙── ^^^^^^^^^^^^^^^ +//β”‚ recordQualifier = {class: class Parent} diff --git a/hkmc2/shared/src/test/mlscript/newres/PromiseSignatures.mls b/hkmc2/shared/src/test/mlscript/newres/PromiseSignatures.mls new file mode 100644 index 0000000000..4ec03776f7 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/PromiseSignatures.mls @@ -0,0 +1,149 @@ +:. + +// Explicit callback result arguments distinguish fulfillment from rejection. +:await +:expect 1 +Promise.resolve("start").then[Int, Str](value => 1, reason => "recovered") +//β”‚ = 1 + +:await +:expect "recovered" +Promise.reject("failed").then[Int, Str](value => 1, reason => "recovered") +//β”‚ = "recovered" + +// Rejection handlers may return a promised value, just like fulfillment handlers. +:await +:expect 2 +Promise.reject("failed").catch(reason => Promise.resolve({answer: 2})).then(value => value.answer) +//β”‚ = 2 + +// Catch preserves a successful value and includes the rejection handler's result. +:await +:expect 3 +class Answer(val answer: Int) +Promise.resolve(Answer(3)).catch(reason => Promise.resolve(Answer(0))).then(value => value.answer) +//β”‚ = 3 + +// Finally runs after settlement and preserves the original fulfillment interface. +:await +:expect 4 +Promise.resolve({answer: 4}).finally(() => console.log("finally")).then(value => value.answer) +//β”‚ > finally +//β”‚ = 4 + +:await +:expect "recovered: failure" +Promise.reject("failure").finally(() => console.log("rejected finally")).catch(reason => "recovered: " + reason) +//β”‚ > rejected finally +//β”‚ = "recovered: failure" + +// An explicitly typed constructor accepts a promise for its payload. +:await +:expect 5 +(new Promise[{answer: Int}]((resolve, reject) => resolve(Promise.resolve({answer: 5})))).then(value => value.answer) +//β”‚ = 5 + +// Race and any adopt the winning promise before exposing its payload. +:await +:expect 6 +Promise.race([Promise.resolve({answer: 6})]).then(value => value.answer) +//β”‚ = 6 + +:await +:expect 7 +Promise.any([Promise.reject("failed"), Promise.resolve({answer: 7})]).then(value => value.answer) +//β”‚ = 7 + +// An absent rejection handler contributes no additional fulfillment candidate. +:await +:expect 8 +Promise.resolve({answer: 8}).then(value => value, undefined).then(value => value.answer) +//β”‚ = 8 + +:await +:expect 9 +Promise.resolve({answer: 9}).then(value => value, null).then(value => value.answer) +//β”‚ = 9 + +// A promise used only as a completion signal has an undefined payload. +:await +:expect true +(new Promise[undefined]((resolve, reject) => resolve(undefined))).then(value => value === undefined) +//β”‚ = true + +// Explicitly absent catch/finally callbacks preserve the original settlement. +:await +:expect 14 +Promise.resolve({answer: 14}).catch(null).finally(undefined).then(value => value.answer) +//β”‚ = 14 + +:await +:re +Promise.reject("still rejected").catch(undefined).finally(null) +//β”‚ ═══[RUNTIME ERROR] still rejected +//β”‚ = Promise {} + +// Resolve and reject callbacks return undefined, not an unconstrained value. +:!js +:e +new Promise((resolve, reject) => resolve(10).missing) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.89: new Promise((resolve, reject) => resolve(10).missing) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Undefined literal does not contain member 'missing' +//β”‚ β•‘ in ../decls/Prelude.mls +//β”‚ β•‘ l.184: declare class Promise[out A](executor: ((A | Promise[A]) -> undefined, Any -> undefined) -> undefined) { +//β”‚ ╙── ^^^^^^^^^ + +:!js +:e +new Promise((resolve, reject) => reject("failed").missing) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.100: new Promise((resolve, reject) => reject("failed").missing) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Undefined literal does not contain member 'missing' +//β”‚ β•‘ in ../decls/Prelude.mls +//β”‚ β•‘ l.184: declare class Promise[out A](executor: ((A | Promise[A]) -> undefined, Any -> undefined) -> undefined) { +//β”‚ ╙── ^^^^^^^^^ + +// Both then branches contribute to the resulting promise type. +:!js +:e +Promise.resolve({answer: 11}).then(value => value, reason => 0).then(value => value.answer) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.112: Promise.resolve({answer: 11}).then(value => value, reason => 0).then(value => value.answer) +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal does not contain member 'answer' +//β”‚ β•‘ l.112: Promise.resolve({answer: 11}).then(value => value, reason => 0).then(value => value.answer) +//β”‚ ╙── ^ + +// A rejection handler's output does not replace the fulfilled input type. +:!js +:e +Promise.resolve(0).catch(reason => Promise.resolve({answer: 12})).then(value => value.answer) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.123: Promise.resolve(0).catch(reason => Promise.resolve({answer: 12})).then(value => value.answer) +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal does not contain member 'answer' +//β”‚ β•‘ l.123: Promise.resolve(0).catch(reason => Promise.resolve({answer: 12})).then(value => value.answer) +//β”‚ ╙── ^ + +// A finalizer's return value does not become the promise's fulfillment value. +:!js +:e +Promise.resolve(0).finally(() => console.log("finally")).then(value => value.answer) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.134: Promise.resolve(0).finally(() => console.log("finally")).then(value => value.answer) +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal does not contain member 'answer' +//β”‚ β•‘ l.134: Promise.resolve(0).finally(() => console.log("finally")).then(value => value.answer) +//β”‚ ╙── ^ + +// The bottom type stays empty through aliases used by promise interfaces. +:js +:await +:expect 13 +type NeverFulfilled = Nothing +fun neverFulfilled(): Promise[NeverFulfilled] = Promise.reject("failed") +neverFulfilled().catch(reason => Promise.resolve({answer: 13})).then(value => value.answer) +//β”‚ = 13 diff --git a/hkmc2/shared/src/test/mlscript/newres/Promises.mls b/hkmc2/shared/src/test/mlscript/newres/Promises.mls new file mode 100644 index 0000000000..b5e1ad91bf --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/Promises.mls @@ -0,0 +1,499 @@ +:. + +// The prelude's declared Promise interface supports factory calls and chaining. +// Callback parameters and await results inherit the promise's payload type. +:await +:expect 43 +Promise.resolve(42).then(value => value + 1) +//β”‚ = 43 + +:await +:expect "recovered: failed" +Promise.reject("failed").then(value => value, reason => "recovered: " + reason) +//β”‚ = "recovered: failed" + +:await +:expect 42 +Promise.all([Promise.resolve(20), Promise.resolve(22)]).then(values => values.at(0) + values.at(1)) +//β”‚ = 42 + +// Async expressions expose the same Promise interface as declared factories. +:await +:expect {answer: 42} +@annotations.async {answer: 42} +//β”‚ = {answer: 42} + +:await +:expect 42 +let asyncResult = @annotations.async {answer: 42} +asyncResult.then(value => value.answer) +//β”‚ = 42 +//β”‚ asyncResult = Promise {} + +// The Promise interface and its async origin survive a function call. +:e +fun makePromise() = @annotations.async {answer: 42} +makePromise().answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.36: makePromise().answer +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Promise' does not contain member 'answer' +//β”‚ β•‘ l.35: fun makePromise() = @annotations.async {answer: 42} +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ ╙── This async annotation wraps the body's result in a promise. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Async functions expose Promise, so selecting a field of their body is invalid. +:e +@annotations.async +fun asyncFunction() = {answer: 42} +asyncFunction().answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.50: asyncFunction().answer +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Promise' does not contain member 'answer' +//β”‚ β•‘ l.48: @annotations.async +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ ╙── This async annotation wraps the body's result in a promise. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Await propagates the fulfilled value's interface. +:await +:expect 42 +@annotations.async +fun awaitAnswer() = + let value = handlers.await(Promise.resolve({answer: 42})) + value.answer +awaitAnswer() +//β”‚ = 42 + +// Both inferred and annotated bodies expose Promise at the call boundary. +:await +:expect 42 +@annotations.async +fun inferred() = {answer: 42} +inferred().then(value => value.answer) +//β”‚ = 42 + +:await +:expect 43 +@annotations.async +fun typed(value: Int): {answer: Int} = {answer: value} +typed(43).then(value => value.answer) +//β”‚ = 43 + +:e +typed(43).answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.86: typed(43).answer +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Promise' does not contain member 'answer' +//β”‚ β•‘ l.80: @annotations.async +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ ╙── This async annotation wraps the body's result in a promise. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Returning explicitly, or returning a closure, must not expose the payload. +:await +:expect 44 +@annotations.async +fun explicitReturn() = return {answer: 44} +explicitReturn().then(value => value.answer) +//β”‚ = 44 + +:e +@annotations.async +fun returnedClosure() = () => 42 +returnedClosure()() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.107: returnedClosure()() +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Promise' cannot be called like a function. +//β”‚ β•‘ l.105: @annotations.async +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ ╙── This async annotation wraps the body's result in a promise. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Async getters and methods obey the same rule as ordinary functions. +:await +:expect 45 +@annotations.async +fun asyncGetter: {answer: Int} = {answer: 45} +asyncGetter.then(value => value.answer) +//β”‚ = 45 + +:await +:expect 46 +class AsyncMethods with + @annotations.async + fun fetch(value: Int): {answer: Int} = {answer: value} +(new AsyncMethods).fetch(46).then(value => value.answer) +//β”‚ = 46 + +// Partial application is still a function; only the last call returns Promise. +:await +:expect 47 +@annotations.async +fun curried(x: Int)(y: Int): Int = x + y +let remaining = curried(20) +remaining(27).then(value => value) +//β”‚ = 47 +//β”‚ remaining = fun + +:e +curried(20).then(value => value) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.144: curried(20).then(value => value) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'curried' does not contain member 'then' +//β”‚ β•‘ l.136: @annotations.async +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.137: fun curried(x: Int)(y: Int): Int = x + y +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Generic result inference through a callback must infer Promise, not the body. +:await +:expect 42 +fun invoke[T](callback: () -> T): T = callback() +invoke(inferred).then(value => value.answer) +//β”‚ = 42 + +:await +:expect 48 +invoke(() => typed(48)).then(value => value.answer) +//β”‚ = 48 + +// Throwing bodies still have the async calling interface, even without returns. +@annotations.async +fun throws() = throw new Error("failed") +fun inspectThrows() = throws().then((value: Any) => 0) + +// The built-in Promise must be used even when a local declaration shadows it. +:await +:expect 49 +fun shadowed() = + class Promise + @annotations.async + fun result() = 49 + result().then(value => value) +shadowed() +//β”‚ = 49 + +// Separate signatures describe the body's result, including curried arrows. +:await +:expect 50 +fun separatelyTyped: Int -> Int -> Int +@annotations.async +fun separatelyTyped(x)(y) = x + y +let typedRemaining = separatelyTyped(20) +typedRemaining(30).then(value => value) +//β”‚ = 50 +//β”‚ typedRemaining = fun + +:e +separatelyTyped(20).then(value => value) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.195: separatelyTyped(20).then(value => value) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function with a declared signature does not contain member 'then' +//β”‚ β•‘ l.186: fun separatelyTyped: Int -> Int -> Int +//β”‚ ╙── ^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A generic async signature must retain Promise after explicit specialization. +:await +:expect 51 +@annotations.async +fun generic[T](value: T): T = value +generic[Int](51).then(value => value) +//β”‚ = 51 + +// Typed higher-order inference must also see the wrapped result. +:await +:expect 52 +fun invokeWith[T](callback: Int -> T): T = callback(52) +invokeWith(typed).then(value => value.answer) +//β”‚ = 52 + +// Imported interfaces retain async annotations through cloning and completion. +import "../../mlscript-compile/AsyncLibrary.mls" + +:await +:expect 53 +AsyncLibrary.inferred().then(value => value.answer) +//β”‚ = 53 + +:await +:expect 54 +AsyncLibrary.typed(54).then(value => value.answer) +//β”‚ = 54 + +:await +:expect 55 +AsyncLibrary.curried(20)(35).then(value => value) +//β”‚ = 55 + +:await +:expect 56 +AsyncLibrary.getter.then(value => value) +//β”‚ = 56 + +:await +:expect 57 +fun nominalMethod(receiver: AsyncLibrary.Methods) = + receiver.result(57).then(value => value) +nominalMethod(new AsyncLibrary.Methods) +//β”‚ = 57 + +:e +AsyncLibrary.inferred().answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.250: AsyncLibrary.inferred().answer +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Promise' does not contain member 'answer' +//β”‚ β•‘ in ../../mlscript-compile/AsyncLibrary.mls +//β”‚ β•‘ l.3: @annotations.async +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ ╙── This async annotation wraps the body's result in a promise. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// The pending wrapper survives higher-order inference after partial application. +:await +:expect 58 +fun finish[T](callback: Int -> T): T = callback(38) +finish(separatelyTyped(20)).then(value => value) +//β”‚ = 58 + +// An arrow in the payload type is not an extra parameter list of the definition. +:await +:expect 59 +fun signedClosure: () -> () -> Int +@annotations.async +fun signedClosure() = () => 59 +signedClosure().then(callback => callback()) +//β”‚ = 59 + +// An ineffective annotation on a value must not change its interface. +:w +:expect 60 +@annotations.async +val plainValue = {answer: 60} +plainValue.answer +//β”‚ ╔══[WARNING] This annotation has no effect. +//β”‚ β•‘ l.280: @annotations.async +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^ +//β”‚ = 60 +//β”‚ plainValue = {answer: 60} + +// Awaiting an inferred async result retains its record interface. +:await +:expect 61 +@annotations.async +fun awaitInferred() = handlers.await(inferred()).answer + 19 +awaitInferred() +//β”‚ = 61 + +// Both async bodies and Promise.resolve adopt an already promised result. +:await +:expect 62 +@annotations.async +fun nestedAsync() = Promise.resolve(Promise.resolve({answer: 62})) +nestedAsync().then(value => value.answer) +//β”‚ = 62 + +// Chaining flattens promised callback results before the next callback or await. +:await +:expect 63 +@annotations.async +fun awaitChain() = + let value = handlers.await(Promise.resolve({answer: 63}).then(value => @annotations.async value)) + value.answer +awaitChain() +//β”‚ = 63 + +// Promise.all accepts plain values and promises and exposes fulfilled elements. +:await +:expect 64 +class PromiseAnswer(val answer: Int) +Promise.all([PromiseAnswer(32), Promise.resolve(PromiseAnswer(32))]).then(values => + values.[0].answer + values.[1].answer) +//β”‚ = 64 + +// The constructor infers its payload from calls to its resolve callback. +:await +:expect 65 +(new Promise((resolve, reject) => resolve({answer: 65}))).then(value => value.answer) +//β”‚ = 65 + +// Generic async calls retain independent payloads, including nested fields. +:await +:expect "independent" +@annotations.async +fun asyncIdentity[T](value: T): T = value +@annotations.async +fun independentPayloads() = + let first = handlers.await(asyncIdentity({first: {answer: 66}})) + let second = handlers.await(asyncIdentity({second: "independent"})) + first.first.answer + second.second +independentPayloads() +//β”‚ = "independent" + +// Inferred bodies must also keep call-site payloads apart. +:await +:expect "inferred" +@annotations.async +fun inferredIdentity(value) = value +@annotations.async +fun independentInferredPayloads() = + let first = handlers.await(inferredIdentity({first: {answer: 67}})) + let second = handlers.await(inferredIdentity({second: "inferred"})) + first.first.answer + second.second +independentInferredPayloads() +//β”‚ = "inferred" + +// Promise-returning generic async calls flatten their payload per instantiation. +:await +:expect 70 +asyncIdentity(Promise.resolve({answer: 70})).then(value => value.answer) +//β”‚ = 70 + +// Promise constructor resolution also adopts promised values. +:await +:expect 71 +(new Promise((resolve, reject) => resolve(Promise.resolve({answer: 71})))).then(value => value.answer) +//β”‚ = 71 + +:await +:expect 72 +@annotations.async +fun awaitConstructed() = + let value = handlers.await(new Promise((resolve, reject) => resolve(Promise.resolve({answer: 72})))) + value.answer +awaitConstructed() +//β”‚ = 72 + +// Recursive async calls share a deferred fulfillment interface. +:await +:expect 73 +@annotations.async +fun recursiveAsync(n: Int) = if n == 0 then {answer: 73} else recursiveAsync(n - 1) +recursiveAsync(3).then(value => value.answer) +//β”‚ = 73 + +// Inferred generic bodies preserve their substitution without a result annotation. +:await +:expect "generic body" +@annotations.async +fun inferredGeneric[T](value: T) = value +@annotations.async +fun awaitGenericBody() = + let first = handlers.await(inferredGeneric({first: 74})) + let second = handlers.await(inferredGeneric({second: "generic body"})) + first.first + second.second +awaitGenericBody() +//β”‚ = "generic body" + +// A resolve call discovered after a consumer still supplies that consumer's type. +:await +:expect 75 +let settleLater +let pending = new Promise((resolve, reject) => set settleLater = resolve) +let settled = pending.then(value => value.answer) +settleLater({answer: 75}) +settled +//β”‚ = 75 +//β”‚ pending = Promise {} +//β”‚ settleLater = fun +//β”‚ settled = Promise {} + +// Type arguments are retained through an explicitly declared promise boundary. +:await +:expect 76 +fun declaredPromise(): Promise[{answer: Int}] = Promise.resolve({answer: 76}) +declaredPromise().then(value => value.answer) +//β”‚ = 76 + +// Promise payload annotations hide implementation-only fields. +:!js +:e +@annotations.async +fun hidesField(): {answer: Int} = {answer: 68, hidden: 0} +hidesField().then(value => value.hidden) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.425: hidesField().then(value => value.hidden) +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Record type does not contain member 'hidden' +//β”‚ β•‘ l.424: fun hidesField(): {answer: Int} = {answer: 68, hidden: 0} +//β”‚ ╙── ^^^^^^^^^^^ + +// Awaiting a Promise does not make an absent payload member dynamic. +:!js +:e +@annotations.async +fun missingPayloadField() = + let value = handlers.await(Promise.resolve({answer: 69})) + value.missing +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.439: value.missing +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Record literal does not contain member 'missing' +//β”‚ β•‘ l.438: let value = handlers.await(Promise.resolve({answer: 69})) +//β”‚ ╙── ^^^^^^^^^^ + + +// Promise payloads that escape must expose their callbacks' parameter obligations. +:!js +:e +val escapedCallback = @annotations.async {value => value.answer} +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.451: val escapedCallback = @annotations.async {value => value.answer} +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'answer' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'value' admits values of unknown shape. +//β”‚ β•‘ l.451: val escapedCallback = @annotations.async {value => value.answer} +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ This value is fulfilled by this promise. +//β”‚ β•‘ in ../decls/Prelude.mls +//β”‚ β•‘ l.184: declare class Promise[out A](executor: ((A | Promise[A]) -> undefined, Any -> undefined) -> undefined) { +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.185: fun id"then"[B, C](onFulfilled: Awaited[A] -> (B | Promise[B]), ...onRejected: Array[(Any -> (C | Promise[C])) | undefined | null]): Promise[Awaited[B | C]] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.186: fun id"catch"[B](onRejected: (Any -> (B | Promise[B])) | undefined | null): Promise[Awaited[A | B]] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.187: fun id"finally"(onFinally: (() -> undefined) | undefined | null): Promise[A] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.188: ... (more lines omitted) ... +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'escapedCallback' is exposed by this compilation unit. +//β”‚ β•‘ in Promises.mls:451 +//β”‚ β•‘ l.451: val escapedCallback = @annotations.async {value => value.answer} +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// A promise with no finite fulfillment has no payload member interface. +:!js +:e +@annotations.async +fun noFulfillment() = noFulfillment() +noFulfillment().then(value => value.answer) +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'answer' has no resolved target +//β”‚ β•‘ l.481: noFulfillment().then(value => value.answer) +//β”‚ ╙── ^^^^^^^^^^^^ + +// TODO: Infer structural thenables' fulfillment callbacks. Their original fields +// must not be exposed as the payload while JavaScript adopts the resolved value. +:!js +:fixme +class Thenable with + fun id"then"(resolve: {answer: Int} -> undefined, reject: Any -> undefined) = resolve({answer: 77}) +Promise.resolve(new Thenable).then(value => value.answer) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.492: Promise.resolve(new Thenable).then(value => value.answer) +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'answer' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ This value may be a thenable; its fulfillment interface is not known. +//β”‚ β•‘ l.492: Promise.resolve(new Thenable).then(value => value.answer) +//β”‚ ╙── ^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/newres/QuantifiedBounds.mls b/hkmc2/shared/src/test/mlscript/newres/QuantifiedBounds.mls new file mode 100644 index 0000000000..d83ab1348b --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/QuantifiedBounds.mls @@ -0,0 +1,132 @@ +:. + +// Control: an explicit type argument supplies the declared member interface. +class Item(val value: Int) +declare fun make: [A extends Item] -> () -> A +private fun read() = make[Item]().value + +// FIXME: Every result satisfies the upper bound, even without explicit arguments. +// The upper constraint is installed, but observing an unconstrained instance +// still has no interface. Its guarantee must be kept apart from lower candidates. +:fixme +class Item(val value: Int) +declare fun make: [A extends Item] -> () -> A +private fun read() = make().value +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'value' has no resolved target +//β”‚ β•‘ l.14: private fun read() = make().value +//β”‚ ╙── ^^^^^^^^^^^^ + +// FIXME: A generic implementation should also be checked using its upper bound. +:fixme +class Item(val value: Int) +fun read: [A extends Item] -> A -> Int +fun read(x) = x.value +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'value' has no resolved target +//β”‚ β•‘ l.23: fun read(x) = x.value +//β”‚ ╙── ^^^^^^^ + +// A lower bound must supply an inference constraint before the result is used. +class Item(val value: Int) +declare fun make: [A restricts Item] -> () -> A +private fun read() = make().value + +// Dependent lower bounds share the entire instance group, including binders +// whose arguments arrive after the bound relation is installed. +class Item(val value: Int) +declare fun make: [A, B restricts A] -> A -> B +private fun read() = make(Item(1)).value + +class Item(val value: Int) +declare fun make: [A restricts B, B restricts Item] -> () -> A +private fun read() = make().value + +// An upper-bound relation must infer the nested argument from a later A input. +class Item(val value: Int) +class Box[T](val item: T) +declare fun make: [A extends Box[B], B] -> A -> B +private fun read() = make(new Box[Item](Item(2))).value + +// An explicit A supplies the same upper-bound obligation without a value input. +class Item(val value: Int) +class Box[T](val item: T) +declare fun make: [A extends Box[B], B] -> () -> B +private fun read() = make[Box[Item], Item]().value + +// Lower bounds constrain a supplied type, but do not enlarge its interface. +:e +class Base +class Child(val extra: Int) extends Base +declare fun make: [A restricts Child] -> () -> A +private fun read() = make[Base]().extra +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.60: private fun read() = make[Base]().extra +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.60: private fun read() = make[Base]().extra +//β”‚ ╙── ^^^^ + +// A by-name invocation consumes the quantified scheme at the selection itself. +// Its recursive body supplies no concrete candidate. +class Item(val value: Int) +fun make: [A restricts Item] -> A +fun make = make +private fun read() = make.value + +// A recursive lower bound connects a finite graph instead of unfolding a tree. +declare fun make: [A restricts {next: A}] -> () -> A +private fun walk(n: Int) = + let x = make() + if n > 0 then x.next.next.next else x + +// Upper bounds are obligations, not additional inferred lower candidates. +// Seeding Base into A's candidate host would incorrectly hide Child.extra. +class Base +class Child(val extra: Int) extends Base +declare fun identity: [A extends Base] -> A -> A +private fun read() = identity(Child(4)).extra + +// A generic body cannot assume its lower bound's members: A may be a supertype. +:e +class Item(val value: Int) +fun read: [A restricts Item] -> A -> Int +fun read(x) = x.value +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'value' has no resolved target +//β”‚ β•‘ l.92: fun read(x) = x.value +//β”‚ ╙── ^^^^^^^ + +// Capturing an enclosing binder specializes the bounds as well as the result. +:expect [5, 6] +class First(val first: Int) +class Second(val second: Int) +private fun make[T](item: T) = + fun nested: [A restricts T] -> () -> A + fun nested() = item + nested +[make(First(5))().first, make(Second(6))().second] +//β”‚ = [5, 6] + +// FIXME: The recursive body leaks a checking-only T candidate through its +// inferred constraints, even though the declared receiver fixes T to Item. +:fixme +class Item(val value: Int) +class Provider[T] with + fun make: [A restricts T] -> A + fun make = make +private fun read(provider: Provider[Item]) = provider.make.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.115: private fun read(provider: Provider[Item]) = provider.make.value +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•‘ l.113: fun make: [A restricts T] -> A +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'T' does not specify a member interface. +//β”‚ β•‘ l.112: class Provider[T] with +//β”‚ ╙── ^ + +// Without that recursive checking edge, the declared receiver supplies its +// lexical class bindings to the by-name bound. The body supplies no candidates. +class Item(val value: Int) +class Provider[T] with + fun make: [A restricts T] -> A + fun make = throw 0 +private fun read(provider: Provider[Item]) = provider.make.value diff --git a/hkmc2/shared/src/test/mlscript/newres/ReceiverResolution.mls b/hkmc2/shared/src/test/mlscript/newres/ReceiverResolution.mls new file mode 100644 index 0000000000..ae658de08c --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ReceiverResolution.mls @@ -0,0 +1,38 @@ +:. + + +// Lexical member references resolve through their receiver, including members +// declared after the method and overloaded class/term names. +module Receiver { + fun values() = [value, Item(), (new Item).answer()] + val value = 31 + class Item { fun answer() = value } + fun Item() = 32 +} + +:expect [31, 32, 31] +Receiver.values() +//β”‚ = [31, 32, 31] + + +// A later lookup must also obtain the already completed receiver's shape. +fun later() = { + open Receiver {values} + values() +} + +:expect [31, 32, 31] +later() +//β”‚ = [31, 32, 31] + + +// Inherited selections and lexical selections share the same receiver path. +class Parent { fun inherited() = 33 } +class Child extends Parent { + fun values() = [local(), this.inherited()] + fun local() = 34 +} + +:expect [34, 33] +(new Child).values() +//β”‚ = [34, 33] diff --git a/hkmc2/shared/src/test/mlscript/newres/RecordInterfaces.mls b/hkmc2/shared/src/test/mlscript/newres/RecordInterfaces.mls new file mode 100644 index 0000000000..0e948eaa6e --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/RecordInterfaces.mls @@ -0,0 +1,69 @@ +:. + +:expect ["HELLO", 3] +fun describe(fields: {name: Str, count: Int}) = [fields.name.toUpperCase(), fields.count] +describe(name: "hello", count: 3) +//β”‚ = ["HELLO", 3] + +:expect 5 +fun apply(fields: {operation: Int -> Int, value: Int}) = fields.operation(fields.value) +apply(operation: x => x + 1, value: 4) +//β”‚ = 5 + +:e +fun hidden(fields: {name: Str}) = fields.extra +hidden(name: "visible", extra: 1) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.14: fun hidden(fields: {name: Str}) = fields.extra +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Record type does not contain member 'extra' +//β”‚ β•‘ l.14: fun hidden(fields: {name: Str}) = fields.extra +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:e +private fun opaque[A](fields: {value: A}) = fields.value.answer +opaque(value: {answer: 42}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.25: private fun opaque[A](fields: {value: A}) = fields.value.answer +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'answer' of a value with unknown shape. +//β”‚ β•‘ l.25: private fun opaque[A](fields: {value: A}) = fields.value.answer +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' does not specify a member interface. +//β”‚ β•‘ l.25: private fun opaque[A](fields: {value: A}) = fields.value.answer +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:expect ["ABC", 3] +fun get[A](fields: {value: A}): A = fields.value +[get(value: "abc").toUpperCase(), get[Array[Int]](value: [1, 2, 3]).length] +//β”‚ = ["ABC", 3] + +:expect "quoted" +fun quoted(fields: {"name": Str}) = fields.name +quoted(name: "quoted") +//β”‚ = "quoted" + +:expect "OK" +fun callback(fields: {f: Str -> Str}): Str = fields.f("ok") +callback(f: x => x.toUpperCase()) +//β”‚ = "OK" + +// A typed return must also check callbacks stored in its record implementation. +:e +fun expose(): {f: Object -> Int} = {f: x => x.answer} +expose().f({answer: 1}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.55: fun expose(): {f: Object -> Int} = {f: x => x.answer} +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Object' does not contain member 'answer' +//β”‚ β•‘ l.55: fun expose(): {f: Object -> Int} = {f: x => x.answer} +//β”‚ ╙── ^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:expect "ALIAS" +type Wrapped[A] = {value: A} +fun unwrap[A](fields: Wrapped[A]): A = fields.value +unwrap(value: "alias").toUpperCase() +//β”‚ = "ALIAS" diff --git a/hkmc2/shared/src/test/mlscript/newres/RecordIntersections.mls b/hkmc2/shared/src/test/mlscript/newres/RecordIntersections.mls new file mode 100644 index 0000000000..e451f8aa7c --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/RecordIntersections.mls @@ -0,0 +1,100 @@ +:. + +// Aliases contribute complementary fields, and common fields retain both types. +:expect ["LEFT", 3, 4, "RIGHT"] +type Left[A] = {left: A, both: {x: Int}} +type Right = {right: Int, both: {y: Str}} +fun read(value: Left[Str] & Right) = + [value.left.toUpperCase(), value.right, value.both.x, value.both.y.toUpperCase()] +read(left: "left", right: 3, both: {x: 4, y: "right"}) +//β”‚ = ["LEFT", 3, 4, "RIGHT"] + +// Grouping, repetition, and operand order do not change the interface. +:expect [1, 2, 3] +type A = {a: Int} +type B = {b: Int} +type C = {c: Int} +fun regroup(value: (A & B) & (C & (B & A))) = [value.a, value.b, value.c] +regroup(a: 1, b: 2, c: 3) +//β”‚ = [1, 2, 3] + +// Distribution preserves union alternatives instead of combining their fields. +:expect ["L", 5] +type Choice = ({left: Str} | {right: Int}) & {tag: Str, common: Int} +fun common(value: Choice) = [value.tag.toUpperCase(), value.common] +common(left: "left", common: 5, tag: "l") +//β”‚ = ["L", 5] + +:e +fun missing(value: Choice) = value.left +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.29: fun missing(value: Choice) = value.left +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Record type does not contain member 'left' +//β”‚ β•‘ l.23: type Choice = ({left: Str} | {right: Int}) & {tag: Str, common: Int} +//β”‚ ╙── ^^^^^^^^^^ + +// Common fields may themselves contain unions of record intersections. +:expect "YES" +type Nested = ({item: ({x: Int} | {z: Bool})} & {item: {y: Str}}) +fun nested(value: Nested) = value.item.y.toUpperCase() +nested(item: {x: 1, y: "yes"}) +//β”‚ = "YES" + +// Productive mutual recursion is observed one field at a time. +:expect [1, "END"] +type X = {next: X, x: Int} +type Y = {next: Y, y: Str} +fun descend(value: X & Y) = [value.next.next.x, value.next.next.y.toUpperCase()] +class Link(val x: Int, val y: Str) with + val next = this +descend(Link(1, "end")) +//β”‚ = [1, "END"] + +// Recursive Boolean substitutions must stabilize rather than nest merged shapes. +type Loop[A, B] = {value: A, next: Loop[A & B, B & A]} +fun cycle(value: Loop[{x: Int}, {y: Str}]) = + [value.next.next.next.value.x, value.next.next.next.value.y.toUpperCase()] + +// The annotation remains an abstraction boundary. +:e +fun hidden(value: A & B) = value.extra +hidden(a: 1, b: 2, extra: 3) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.61: fun hidden(value: A & B) = value.extra +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Record type does not contain member 'extra' +//β”‚ β•‘ l.14: type A = {a: Int} +//β”‚ ╙── ^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A wildcard open reads a single runtime field even when two records declare it. +:expect 7 +fun opened(value: {shared: Int} & {shared: Int, extra: Bool}) = + open value + shared +opened(shared: 7, extra: true) +//β”‚ = 7 + +// Intersection constraints infer generic fields and check callback parameters. +:expect "TYPED" +fun extract[T](value: {item: T} & {tag: Int}): T = value.item +extract(item: "typed", tag: 0).toUpperCase() +//β”‚ = "TYPED" + +:expect "CALLBACK" +fun callback(value: {run: Str -> Str} & {tag: Int}) = value.run("callback") +callback(run: x => x.toUpperCase(), tag: 0) +//β”‚ = "CALLBACK" + +:e +fun invalidCallback(): {run: Object -> Int} & {tag: Int} = + {run: x => x.secret, tag: 0} +invalidCallback() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.92: {run: x => x.secret, tag: 0} +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Object' does not contain member 'secret' +//β”‚ β•‘ l.91: fun invalidCallback(): {run: Object -> Int} & {tag: Int} = +//β”‚ ╙── ^^^^^^ +//β”‚ = {run: fun, tag: 0} diff --git a/hkmc2/shared/src/test/mlscript/newres/Records.mls b/hkmc2/shared/src/test/mlscript/newres/Records.mls new file mode 100644 index 0000000000..faebc9035f --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/Records.mls @@ -0,0 +1,384 @@ +:. + + +:sr +:expect 1 +{a: 1}.a +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ { let a⁰, a⁰ = 1, "a": a⁰ }.aΒΉ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 1 + +:expect 2 +{"a": 2}.a +//β”‚ = 2 + +:expect 3 +{a: 1, b: a + 2}.b +//β”‚ = 3 + +:expect 4 +{inner: {answer: 4}}.inner.answer +//β”‚ = 4 + +// Duplicate keys use the last property, including its value shape. +:expect 5 +{a: {wrong: 0}, a: {answer: 5}}.a.answer +//β”‚ = 5 + +// Fields are values; stored lambdas receive ordinary arguments. +:expect 6 +{f: x => x + 1}.f(5) +//β”‚ = 6 + +// The record's context must filter values from other calls to the same producer. +fun wrap(x) = {value: x} + +:expect 7 +wrap({left: 7}).value.left +//β”‚ = 7 + +:expect 8 +wrap({right: 8}).value.right +//β”‚ = 8 + +// Reading the same field through a helper retains the receiver context too. +:expect 9 +private fun read(r) = r.value +read(wrap({answer: 9})).answer +//β”‚ = 9 + +:expect 10 +read(wrap({different: 10})).different +//β”‚ = 10 + +// Both selective and wildcard opens can expose record properties. +:expect 11 +let record = {answer: 11} +open record {answer} +answer +//β”‚ = 11 +//β”‚ record = {answer: 11} + +:expect 12 +let record2 = {answer2: 12} +open record2 +answer2 +//β”‚ = 12 +//β”‚ record2 = {answer2: 12} + +:e +{a: 1}.missing +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.71: {a: 1}.missing +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Record literal does not contain member 'missing' +//β”‚ β•‘ l.71: {a: 1}.missing +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:e +{a: 1}.a() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.81: {a: 1}.a() +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be called like a function. +//β”‚ β•‘ l.81: {a: 1}.a() +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:todo // TODO: Resolve computed keys. Until then, do not look through an unknown overwrite. +let key = "a" +{a: {answer: 0}, (key): {other: 1}}.a.answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.92: {a: {answer: 0}, (key): {other: 1}}.a.answer +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' across a computed key or unknown record spread. +//β”‚ β•Ÿβ”€β”€ This computed key can overwrite the selected member. +//β”‚ β•‘ l.92: {a: {answer: 0}, (key): {other: 1}}.a.answer +//β”‚ ╙── ^^^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'answer' has no resolved target +//β”‚ β•‘ l.92: {a: {answer: 0}, (key): {other: 1}}.a.answer +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ key = "a" + +// An explicit field after an unknown key still wins. +:expect 13 +{(key): 0, a: 13}.a +//β”‚ = 13 + + +// Known spreads preserve both field identity and captured values. +:expect 14 +{...{answer: 14}}.answer +//β”‚ = 14 + +:expect 15 +{answer: 0, ...{answer: 15}}.answer +//β”‚ = 15 + +:expect 16 +{...{answer: 0}, answer: 16}.answer +//β”‚ = 16 + +:expect 17 +{answer: 17, ...{other: 0}}.answer +//β”‚ = 17 + +fun spreadWrap(x) = {...{value: x}} + +:expect 18 +spreadWrap({first: 18}).value.first +//β”‚ = 18 + +:expect 19 +spreadWrap({second: 19}).value.second +//β”‚ = 19 + +// Mutable fields retain their identity after writes. +:expect 20 +let mutableRecord = mut {answer: 0} +set mutableRecord.answer = 20 +mutableRecord.answer +//β”‚ = 20 +//β”‚ mutableRecord = {answer: 20} + +// TODO: Track writes to mutable fields. Their initializer must not determine +// static targets after an assignment (even if another receiver has that member). +:fixme +let changing = mut {value: {before: 0}} +set changing.value = {after: 21} +changing.value.after +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.152: changing.value.after +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'after' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ The shape of this value is unknown. +//β”‚ β•‘ l.150: let changing = mut {value: {before: 0}} +//β”‚ ╙── ^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ changing = {value: {after: 21}} + + +// TODO: Nested helper calls currently lose the outer call-site mark when +// entering the helper through a capture, merging these two value shapes. +:fixme +fun nestedWrap(x) = {...wrap(x)} +nestedWrap({first: 22}).value.first +nestedWrap({second: 23}).value.second +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.168: nestedWrap({first: 22}).value.first +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Record literal does not contain member 'first' +//β”‚ β•‘ l.169: nestedWrap({second: 23}).value.second +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'first' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.167: fun nestedWrap(x) = {...wrap(x)} +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'nestedWrap' is exposed by this compilation unit. +//β”‚ β•‘ l.167: fun nestedWrap(x) = {...wrap(x)} +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.169: nestedWrap({second: 23}).value.second +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Record literal does not contain member 'second' +//β”‚ β•‘ l.168: nestedWrap({first: 22}).value.first +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'second' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.167: fun nestedWrap(x) = {...wrap(x)} +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'nestedWrap' is exposed by this compilation unit. +//β”‚ β•‘ l.167: fun nestedWrap(x) = {...wrap(x)} +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + + +// A missing member in a spread must fall through to earlier fields; an unknown +// member must not. This also holds when wildcard opens offer another candidate. +:fixme +let unknownKeys = {(key): 0} +fun unknownOpen() = + open unknownKeys + let knownKeys = {a: 24} + open knownKeys + a +unknownOpen() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in wildcard-open reference +//β”‚ β•‘ l.207: a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' across a computed key or unknown record spread. +//β”‚ β•Ÿβ”€β”€ This computed key can overwrite the selected member. +//β”‚ β•‘ l.202: let unknownKeys = {(key): 0} +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ unknownKeys = {a: 0} + +// A later explicit field makes an earlier unknown spread irrelevant to lookup. +:expect 25 +{...unknownKeys, a: 25}.a +//β”‚ = 25 + +// Resolving forwardRecord.answer must wait for the body of makeRecord below. +:expect 26 +let forwardRecord = {...makeRecord()} +fun makeRecord() = {answer: 26} +forwardRecord.answer +//β”‚ = 26 +//β”‚ forwardRecord = {answer: 26} + +// Strict resolution still diagnoses distinct field identities from alternatives. +:e +fun choose(flag) = if flag then {answer: 1} else {answer: 2} +choose(true).answer +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'answer' is ambiguous, as it has multiple resolved targets +//β”‚ β•‘ l.235: choose(true).answer +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ target: record field 'answer' +//β”‚ β•‘ l.234: fun choose(flag) = if flag then {answer: 1} else {answer: 2} +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ target: record field 'answer' +//β”‚ β•‘ l.234: fun choose(flag) = if flag then {answer: 1} else {answer: 2} +//β”‚ ╙── ^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Recursive spreads must converge even as their candidate graph grows. +// TODO: Resolve fields through widened recursive spreads. +:fixme +fun recursiveRecord(n) = + if n == 0 then {answer: 27} + else {...recursiveRecord(n - 1)} +recursiveRecord(2).answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.253: recursiveRecord(2).answer +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'answer' across a computed key or unknown record spread. +//β”‚ β•Ÿβ”€β”€ This spread has no known element shape. +//β”‚ β•‘ l.252: else {...recursiveRecord(n - 1)} +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + + +// Unknown mutable values in an open must not disappear in favor of another source. +:fixme +fun unknownValueOpen() = + let unknownValue = mut {value: {answer3: 0}} + let fallbackValue = {answer3: 1} + let openedValue = unknownValue.value + open openedValue + open fallbackValue + answer3 +unknownValueOpen() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in wildcard-open reference +//β”‚ β•‘ l.272: answer3 +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'answer3' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ The shape of this value is unknown. +//β”‚ β•‘ l.267: let unknownValue = mut {value: {answer3: 0}} +//β”‚ ╙── ^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + + +// A known-absent name in one open does not mask a later source that supplies it. +:expect 28 +fun knownMissingOpen() = + let unrelated = {...{other: 0}} + let fallback = {wanted: 28} + open unrelated + open fallback + wanted +knownMissingOpen() +//β”‚ = 28 + +// With no supplying source, a known-absent name still produces an error. +:e +fun allMissingOpen() = + let unrelated = {other: 0} + open unrelated + nonexistent +allMissingOpen() +//β”‚ ╔══[COMPILATION ERROR] Name not found in wildcard opens: nonexistent +//β”‚ β•‘ l.300: nonexistent +//β”‚ ╙── ^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + + +// Both a.value and b.value must resolve to the field supplied by {value: 29}. +// The two {...xs} expressions have equal syntax but separate AST nodes and listeners. +// Treating the second expression as already processed would skip its subscription +// to xs, leaving b.value unresolved. +:expect [29, 29] +private fun repeatedRecords(xs) = + let a = {...xs} + let b = {...xs} + [a.value, b.value] +repeatedRecords({value: 29}) +//β”‚ = [29, 29] + +// Both reads of pendingRecord.value are elaborated before laterRecord's body. +// Once its body is available, the resolver must supply the field to both reads. +:expect [30, 30] +let pendingRecord = {...laterRecord()} +fun readPendingRecord() = [pendingRecord.value, pendingRecord.value] +fun laterRecord() = {value: 30} +readPendingRecord() +//β”‚ = [30, 30] +//β”‚ pendingRecord = {value: 30} + +// Updating mutableCopy.value must leave stableRecord.value equal to 31. +:expect [31, 32] +let stableRecord = {value: 31} +let mutableCopy = mut {...stableRecord} +set mutableCopy.value = 32 +[stableRecord.value, mutableCopy.value] +//β”‚ = [31, 32] +//β”‚ mutableCopy = {value: 32} +//β”‚ stableRecord = {value: 31} + +// TODO: Infer the new value of mutableNested.value after the assignment. Until then, +// report its unknown value shape rather than assuming it still contains `before`. +// The unchanged stableNested.value.before must remain resolvable. +:fixme +let stableNested = {value: {before: 33}} +let mutableNested = mut {...stableNested} +set mutableNested.value = {after: 34} +[stableNested.value.before, mutableNested.value.after] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.347: [stableNested.value.before, mutableNested.value.after] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'after' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ The shape of this value is unknown. +//β”‚ β•‘ l.344: let stableNested = {value: {before: 33}} +//β”‚ ╙── ^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ mutableNested = {value: {after: 34}} +//β”‚ stableNested = {value: {before: 33}} + +// Both opened records define `shared`, so the reference is ambiguous. The error +// must point to each field definition and the corresponding open statement. +:e +fun ambiguousRecordOpen() = + let firstRecord = {shared: 1} + let secondRecord = {shared: 2} + open firstRecord + open secondRecord + shared +ambiguousRecordOpen() +//β”‚ ╔══[COMPILATION ERROR] Wildcard-open reference 'shared' is ambiguous +//β”‚ β•‘ l.367: shared +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ candidate: record field 'shared' defined here +//β”‚ β•‘ l.363: let firstRecord = {shared: 1} +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Opened here +//β”‚ β•‘ l.365: open firstRecord +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ candidate: record field 'shared' defined here +//β”‚ β•‘ l.364: let secondRecord = {shared: 2} +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Opened here +//β”‚ β•‘ l.366: open secondRecord +//β”‚ ╙── ^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/newres/RecursiveBooleanInterfaces.mls b/hkmc2/shared/src/test/mlscript/newres/RecursiveBooleanInterfaces.mls new file mode 100644 index 0000000000..7dd508ecf0 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/RecursiveBooleanInterfaces.mls @@ -0,0 +1,359 @@ +:. + +// An ordinary forwarding alias preserves the declared interface. +:expect 1 +class Item(val value: Int) +type Choice[A] = A +private fun read(x: Choice[Item]) = x.value +read(Item(1)) +//β”‚ = 1 + +// Reject the definition itself, even when no value observes its interface. +:e +type Loop = Loop +//β”‚ ╔══[COMPILATION ERROR] Resolution error in member reference +//β”‚ β•‘ l.13: type Loop = Loop +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Loop'. +//β”‚ β•‘ l.13: type Loop = Loop +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.13: type Loop = Loop +//β”‚ ╙── ^^^^ + +:e +type Choice[A] = A | Choice[A] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in alternation +//β”‚ β•‘ l.25: type Choice[A] = A | Choice[A] +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Choice'. +//β”‚ β•‘ l.25: type Choice[A] = A | Choice[A] +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.25: type Choice[A] = A | Choice[A] +//β”‚ ╙── ^^^^^^ + +:e +type Choice[A] = A & Choice[A] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in composition +//β”‚ β•‘ l.37: type Choice[A] = A & Choice[A] +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Choice'. +//β”‚ β•‘ l.37: type Choice[A] = A & Choice[A] +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.37: type Choice[A] = A & Choice[A] +//β”‚ ╙── ^^^^^^ + +// Mutual recursion may cross several transparent aliases before cycling. +:e +type First = Second +type Second = Third +type Third = First +//β”‚ ╔══[COMPILATION ERROR] Resolution error in member reference +//β”‚ β•‘ l.50: type First = Second +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'First'. +//β”‚ β•‘ l.52: type Third = First +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.50: type First = Second +//β”‚ ╙── ^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in member reference +//β”‚ β•‘ l.51: type Second = Third +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Second'. +//β”‚ β•‘ l.50: type First = Second +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.51: type Second = Third +//β”‚ ╙── ^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in member reference +//β”‚ β•‘ l.52: type Third = First +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Third'. +//β”‚ β•‘ l.51: type Second = Third +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.52: type Third = First +//β”‚ ╙── ^^^^^ + +// A guarded branch does not guard a separate recursive alternative. +:e +type Mixed = ({next: Mixed} | Mixed) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in alternation +//β”‚ β•‘ l.83: type Mixed = ({next: Mixed} | Mixed) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Mixed'. +//β”‚ β•‘ l.83: type Mixed = ({next: Mixed} | Mixed) +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.83: type Mixed = ({next: Mixed} | Mixed) +//β”‚ ╙── ^^^^^ + +// Boolean absorption must not conceal a written unguarded cycle. +:e +type Hidden[A] = A | (A & Hidden[A]) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in alternation +//β”‚ β•‘ l.96: type Hidden[A] = A | (A & Hidden[A]) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Hidden'. +//β”‚ β•‘ l.96: type Hidden[A] = A | (A & Hidden[A]) +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.96: type Hidden[A] = A | (A & Hidden[A]) +//β”‚ ╙── ^^^^^^ + +// An enclosing constructor does not make an invalid alias definition valid. +:e +type Wrapper = {value: Loop} +type Loop = Loop +//β”‚ ╔══[COMPILATION ERROR] Resolution error in member reference +//β”‚ β•‘ l.110: type Loop = Loop +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Loop'. +//β”‚ β•‘ l.110: type Loop = Loop +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.110: type Loop = Loop +//β”‚ ╙── ^^^^ + +// Forwarding formals and captures remain transparent to the guardedness check. +:e +type Identity[A] = A +type Loop = Identity[Loop] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.124: type Loop = Identity[Loop] +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Loop'. +//β”‚ β•‘ l.124: type Loop = Identity[Loop] +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.124: type Loop = Identity[Loop] +//β”‚ ╙── ^^^^ + +:e +private fun local() = + type Identity[A] = A + type Loop = Identity[Loop] + () +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.138: type Loop = Identity[Loop] +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Loop'. +//β”‚ β•‘ l.138: type Loop = Identity[Loop] +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.138: type Loop = Identity[Loop] +//β”‚ ╙── ^^^^ + +// A forward reference must resolve before formal-use summaries are finalized. +:e +type Loop = Forward[Loop] +type Forward[A] = Identity[A] +type Identity[A] = A +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.152: type Loop = Forward[Loop] +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Loop'. +//β”‚ β•‘ l.152: type Loop = Forward[Loop] +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.152: type Loop = Forward[Loop] +//β”‚ ╙── ^^^^ + +// Growing arguments do not guard a recursive alias application. +:e +type Loop[A] = Loop[Array[A]] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.167: type Loop[A] = Loop[Array[A]] +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Loop'. +//β”‚ β•‘ l.167: type Loop[A] = Loop[Array[A]] +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.167: type Loop[A] = Loop[Array[A]] +//β”‚ ╙── ^^^^ + +// Guards may appear in a wrapper alias, including one declared later. +type Loop = Wrap[Loop] +type Wrap[A] = {next: A} +private fun walk(x: Loop) = x.next.next + +type Wrap[A] = [A] +type Loop = Wrap[Loop] +private fun walk(x: Loop) = x.0.0 + +type Wrap[A] = () -> A +type Loop = Wrap[Loop] +private fun walk(x: Loop) = x()() + +class Box[A](val value: A) +type Loop = Box[Loop] +private fun walk(x: Loop) = x.value.value + +// A mutual cycle with a structural guard is valid. +type Left = Right +type Right = {next: Left} +private fun walk(x: Left) = x.next.next + +// Reusing an identity alias in a finite expression is not recursive. +:expect 2 +class Item(val value: Int) +type Identity[A] = A +type Twice[A] = Identity[Identity[A]] +private fun read(x: Twice[Item]) = x.value +read(Item(2)) +//β”‚ = 2 + +// An unused actual argument cannot introduce a cycle into the expanded alias. +:expect 3 +class Item(val value: Int) +type Ignore[A] = Item +type Loop = Ignore[Loop] +private fun read(x: Loop) = x.value +read(Item(3)) +//β”‚ = 3 + +// Forwarded parameter uses must be composed across multiple alias applications. +type Pair[A, B] = [B, A] +type Wrap[A] = Pair[Int, A] +type Loop = Wrap[Loop] +private fun walk(x: Loop) = x.0.0 + +// Unrelated guarded aliases in the same source graph remain usable. +:e +type Bad = Bad | Good +type Good = {next: Good} +private fun walk(x: Good) = x.next.next +//β”‚ ╔══[COMPILATION ERROR] Resolution error in alternation +//β”‚ β•‘ l.226: type Bad = Bad | Good +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Bad'. +//β”‚ β•‘ l.226: type Bad = Bad | Good +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.226: type Bad = Bad | Good +//β”‚ ╙── ^^^ + +// Quantification alone is not a structural guard. +:e +type Loop = [A] -> Loop +//β”‚ ╔══[COMPILATION ERROR] Resolution error in universal quantification +//β”‚ β•‘ l.241: type Loop = [A] -> Loop +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Loop'. +//β”‚ β•‘ l.241: type Loop = [A] -> Loop +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.241: type Loop = [A] -> Loop +//β”‚ ╙── ^^^^ + +// Function domains, as well as results, guard recursive references. +type Loop = Loop -> Int +private fun apply(f: Loop, x: Loop): Int = f(x) + +// Guarded and unguarded uses of the same formal must both be considered. +:e +type MixedUse[A] = A | [A] +type Loop = MixedUse[Loop] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.259: type Loop = MixedUse[Loop] +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Loop'. +//β”‚ β•‘ l.259: type Loop = MixedUse[Loop] +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.259: type Loop = MixedUse[Loop] +//β”‚ ╙── ^^^^ + +// A forwarding alias may omit an unused parameter without introducing a guard. +:e +type First[A, B] = A +type Loop = First[Loop] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.273: type Loop = First[Loop] +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Loop'. +//β”‚ β•‘ l.273: type Loop = First[Loop] +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.273: type Loop = First[Loop] +//β”‚ ╙── ^^^^ + +// Negation is a Boolean operation, not a structural guard, even though the +// current resolver otherwise treats negated interfaces as opaque. +:e +type Loop = ~Loop +//β”‚ ╔══[COMPILATION ERROR] Resolution error in negation type +//β”‚ β•‘ l.287: type Loop = ~Loop +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Loop'. +//β”‚ β•‘ l.287: type Loop = ~Loop +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.287: type Loop = ~Loop +//β”‚ ╙── ^^^^ + +:e +type Negated[A] = ~A +type Loop = Negated[Loop] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.300: type Loop = Negated[Loop] +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Loop'. +//β”‚ β•‘ l.300: type Loop = Negated[Loop] +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.300: type Loop = Negated[Loop] +//β”‚ ╙── ^^^^ + +// Keeping negation's source dependencies does not reject nonrecursive types. +type NotInt = ~Int +private fun unused(x: NotInt) = () + +// A constructor underneath negation still guards its recursive reference. +type Loop = ~[Loop] +private fun unused(x: Loop) = () + +// A bound on a quantified parameter is not a structural guard. +:e +type Loop = [A extends Loop] -> A +//β”‚ ╔══[COMPILATION ERROR] Resolution error in universal quantification +//β”‚ β•‘ l.321: type Loop = [A extends Loop] -> A +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Loop'. +//β”‚ β•‘ l.321: type Loop = [A extends Loop] -> A +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.321: type Loop = [A extends Loop] -> A +//β”‚ ╙── ^^^^ + +// Wildcard bounds are transparent, including through a forwarding alias. +:e +type Identity[A] = A +type Loop = Identity[out Loop] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.335: type Loop = Identity[out Loop] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Loop'. +//β”‚ β•‘ l.335: type Loop = Identity[out Loop] +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.335: type Loop = Identity[out Loop] +//β”‚ ╙── ^^^^ + +// Applying an abstract type parameter supplies no known structural guard. +:e +type Apply[F, A] = F[A] +type Identity[A] = A +type Loop = Apply[Identity, Loop] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.350: type Loop = Apply[Identity, Loop] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Loop'. +//β”‚ β•‘ l.350: type Loop = Apply[Identity, Loop] +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.350: type Loop = Apply[Identity, Loop] +//β”‚ ╙── ^^^^ diff --git a/hkmc2/shared/src/test/mlscript/newres/RecursiveEnvironment.mls b/hkmc2/shared/src/test/mlscript/newres/RecursiveEnvironment.mls new file mode 100644 index 0000000000..67ad9ac462 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/RecursiveEnvironment.mls @@ -0,0 +1,122 @@ +:. + +// Capturing the outer constructor crosses both the method and instance scopes. +// The old instance exit cancels the class capture, leaving the fresh instance exit. +data class Env(bindings) with + fun lookup(name) = bindings + fun extend(name) = Env(0) + +:expect 0 +private fun infer(env, x) = if x then env.lookup(x) else infer(env.extend(x), x) +infer(Env(0), true) +//β”‚ = 0 + +// Exercise the recursive branch at runtime as well as during resolution. +:expect 0 +private fun advance(env, n) = if n == 0 then env.lookup(n) else advance(env.extend(n), n - 1) +advance(Env(0), 3) +//β”‚ = 0 + +// Explicit construction must carry the same instance boundary and captures. +data class NewEnv(bindings) with + fun lookup(name) = bindings + fun extend(name) = new NewEnv(0) + +:expect 0 +private fun advanceNew(env, n) = if n == 0 then env.lookup(n) else advanceNew(env.extend(n), n - 1) +advanceNew(new NewEnv(0), 3) +//β”‚ = 0 + +// The instance capture matters even without a recursive source-level call. +// FIXME: Rebuilding the same class loses the original constructor activation. +:fixme +:expect 42 +class Box(val value) with + fun copy() = Box(value) +Box({answer: 42}).copy().value.answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.36: Box({answer: 42}).copy().value.answer +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'answer' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'value' admits values of unknown shape. +//β”‚ β•‘ l.34: class Box(val value) with +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'Box' is exposed by this compilation unit. +//β”‚ β•‘ l.34: class Box(val value) with +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.35: fun copy() = Box(value) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '42', got: 'undefined' + +// Rebuilding a nested class must also retain its enclosing function's capture. +:expect 43 +private fun nested(enclosing) = + class Inner(val value) with + fun copy() = new Inner(value) + fun read() = enclosing.answer + (new Inner(0)).copy().read() +nested({answer: 43}) +//β”‚ = 43 + +// Captures of inherited wildcard opens cross instance scopes too. +// FIXME: Capturing a constructor alias loses its calling activation. +:fixme +:expect 44 +class OpenBox(val value) +let makers = {create: OpenBox} +private fun opened(value) = + open makers + class Wrapper with + fun make() = create(value) + (new Wrapper).make().value.answer +opened({answer: 44}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.72: (new Wrapper).make().value.answer +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'answer' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'value' admits values of unknown shape. +//β”‚ β•‘ l.66: class OpenBox(val value) +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This value is stored in this record field. +//β”‚ β•‘ l.67: let makers = {create: OpenBox} +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ This value is exposed by this compilation unit. +//β”‚ β•‘ l.67: let makers = {create: OpenBox} +//β”‚ ╙── ^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '44', got: 'undefined' +//β”‚ makers = {create: fun OpenBox { class: class OpenBox }} + +// Remaining argument lists retain the context of an explicitly constructed class. +// FIXME: An exposed partial constructor shares its remaining argument flow with callers. +:fixme +:expect [45, 46] +fun partial(a) = + class Pair(val first)(val second) + new Pair(a) +let finish = partial({answer: 45}) +let pair = finish({answer: 46}) +[pair.first.answer, pair.second.answer] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.100: [pair.first.answer, pair.second.answer] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'answer' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'second' admits values of unknown shape. +//β”‚ β•‘ l.96: class Pair(val first)(val second) +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This value is exposed by this compilation unit. +//β”‚ β•‘ l.98: let finish = partial({answer: 45}) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '[45, 46]', got: 'undefined' +//β”‚ finish = fun +//β”‚ pair = Pair({answer: 45}) + +// Recursive data stays a finite graph of shapes even when it retains old instances. +:expect 1 +class Chain(val value, val previous) with + fun prepend(value) = Chain(value, this) +private fun build(chain, n) = if n == 0 then chain else build(chain.prepend(n), n - 1) +build(Chain(0, ()), 3).value +//β”‚ = 1 diff --git a/hkmc2/shared/src/test/mlscript/newres/RecursiveEquality.mls b/hkmc2/shared/src/test/mlscript/newres/RecursiveEquality.mls new file mode 100644 index 0000000000..e902800d78 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/RecursiveEquality.mls @@ -0,0 +1,27 @@ +:. + +// Keep the unannotated deep-equality implementation separate from Predef's +// public Any Γ— Any -> Bool interface. Recursing on array elements must not +// cause unbounded inference or a stack overflow. +:expect [true, false, true, false] +fun deepEqual(a, b) = if + a === b then true + a is Array as a and b is Array as b and a.length === b.length then + a.every((item, index, ...) => deepEqual(item, b.at(index))) + else a !== undefined and a !== null and b !== undefined and b !== null and + let ac = a!constructor + ac !== undefined and ac === b!constructor and + let md = ac!(Predef.Symbols.definitionMetadata) + md !== undefined and + md.2.every((field, ...) => field !== null and deepEqual(a!(field), b!(field))) +class Box(val value: Int) +[deepEqual([[1], [2]], [[1], [2]]), deepEqual([[1]], [[2]]), deepEqual(Box(3), Box(3)), deepEqual(Box(3), Box(4))] +//β”‚ = [true, false, true, false] + +// Refinement must also reach a fixed point through mutually recursive helpers. +// Exposure supplies unknown inputs independently of these concrete local calls. +:expect true +fun visit(value) = if value is Array as items then visitArray(items) else true +fun visitArray(items: Array) = items.every((item, ...) => visit(item)) +visit([1, [2, [3]], []]) +//β”‚ = true diff --git a/hkmc2/shared/src/test/mlscript/newres/RecursiveGetters.mls b/hkmc2/shared/src/test/mlscript/newres/RecursiveGetters.mls new file mode 100644 index 0000000000..548b1688ec --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/RecursiveGetters.mls @@ -0,0 +1,132 @@ +:. + +// Resolving a recursive getter must terminate even when it has no normal result. +fun self = self + +fun left = right +fun right = left + +:e +left.oops +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'oops' has no resolved target +//β”‚ β•‘ l.10: left.oops +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +module Loop with + fun value = value + +:e +Loop.value.oops +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'oops' has no resolved target +//β”‚ β•‘ l.20: Loop.value.oops +//β”‚ ╙── ^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// The recursive edge also runs: getter reads must retain their runtime effects. +:expect 45 +private fun countdown() = + let remaining = 3 + fun result = if remaining == 0 then {answer: 45} else + set remaining = remaining - 1 + result + result.answer +countdown() +//β”‚ = 45 + +// A base result discovered after a forward reference must reach both getters. +:expect [42, 42] +fun first = second +fun second = if true then {answer: 42} else first +[first.answer, second.answer] +//β”‚ = [42, 42] + +// Re-observing completed definitions must replay their inferred result. +:expect 42 +first.answer +//β”‚ = 42 + +// Recursive getters retain the enclosing call's captured value. +:expect [1, 2] +private fun captured(value) = + fun result = if true then value else result + result +[captured({left: 1}).left, captured({right: 2}).right] +//β”‚ = [1, 2] + +// Selected member getters use the same finite result graph as direct references. +:expect 43 +module Getters with + fun first = second + fun second = if true then {answer: 43} else first +Getters.first.answer +//β”‚ = 43 + +// Sharing results must retain each invocation's generic substitution. +:expect [3, 4] +class First(val left: Int) +class Second(val right: Int) +private fun generic[T](value: T) = + fun result = if true then value else result + result +[generic(First(3)).left, generic(Second(4)).right] +//β”‚ = [3, 4] + +// A by-name generic consumes its own type arguments before returning a closure. +:expect [5, 6] +private fun identity[T] = if true then (value: T) => value else identity[T] +[identity[First](First(5)).left, identity[Second](Second(6)).right] +//β”‚ = [5, 6] + +// Imported bodies retain copied listeners and independent caller activations. +import "../../mlscript-compile/GenericLibrary.mls" + +:expect [8, 9, 7] +[GenericLibrary.recursiveIdentity(First(8)).left, + GenericLibrary.recursiveIdentity(Second(9)).right, + GenericLibrary.recursiveGetter.known] +//β”‚ = [8, 9, 7] + +// A result cycle does not grant dynamic member access. +:ge +fun invalid = if true then {answer: 44} else invalid +invalid.missing +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.93: invalid.missing +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Record literal does not contain member 'missing' +//β”‚ β•‘ l.92: fun invalid = if true then {answer: 44} else invalid +//β”‚ ╙── ^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A valid alternative must not hide a later alternative without the member. +:ge +fun mixed = if true then {answer: 46} else if true then {other: 47} else mixed +mixed.answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.105: mixed.answer +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Record literal does not contain member 'answer' +//β”‚ β•‘ l.104: fun mixed = if true then {answer: 46} else if true then {other: 47} else mixed +//β”‚ ╙── ^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Exposure of a getter's enclosing function must still propagate unknown inputs. +:ge +fun exposed(value) = + fun result = if true then value else result + result.answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.118: result.answer +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'answer' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'value' admits values of unknown shape. +//β”‚ β•‘ l.116: fun exposed(value) = +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ 'exposed' is exposed by this compilation unit. +//β”‚ β•‘ l.116: fun exposed(value) = +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.117: fun result = if true then value else result +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.118: result.answer +//β”‚ ╙── ^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/newres/ScopePaths.mls b/hkmc2/shared/src/test/mlscript/newres/ScopePaths.mls new file mode 100644 index 0000000000..92b0a94bc8 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ScopePaths.mls @@ -0,0 +1,66 @@ +:. + +// The same generic method site sees both a locally created receiver and one +// captured from outside the function. Its parameter hosts have one endpoint. +:expect [2, 1, 3] +class Item(val value: Int) +let outside = [Item(1)] +private fun run(x, local) = + let inside = [x] + let values = if local then inside else outside + values.map((item, ...) => item) +[run(Item(2), true).0.value, run(Item(3), false).0.value, run(Item(3), true).0.value] +//β”‚ = [2, 1, 3] +//β”‚ outside = [Item(1)] + +// A local module's type path includes both the method and the enclosing class. +:expect [4, 5] +class First(val first: Int) +class Second(val second: Int) +class Factory[A](val item: A) with + module Types with + class Box(val value: A) + type Alias = Box + fun read() = + fun make(): Types.Alias = Types.Box(item) + fun use(box: Types.Alias) = box.value + use(make()) +[Factory(First(4)).read().first, Factory(Second(5)).read().second] +//β”‚ = [4, 5] + +// A receiver can escape the generic function that declared its local class. +:expect [6, 7] +class First(val first: Int) +class Second(val second: Int) +private fun make[A](item: A) = + module Types with + class Box(val value: A) + type Alias = Box + fun get(): Types.Alias = Types.Box(item) + get +let first = make(First(6)) +let second = make(Second(7)) +[first().value.first, second().value.second] +//β”‚ = [6, 7] +//β”‚ first = fun get +//β”‚ second = fun get + +// Different outer activations must not supply one another's member shapes. +:e +class First(val first: Int) +class Second(val second: Int) +private fun make[A](item: A) = + module Types with + class Box(val value: A) + type Alias = Box + fun get(): Types.Alias = Types.Box(item) + get() +make(Second(8)).value.second +make(First(9)).value.second +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.59: make(First(9)).value.second +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'First' does not contain member 'second' +//β”‚ β•‘ l.59: make(First(9)).value.second +//β”‚ ╙── ^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/newres/ScrutineeReferences.mls b/hkmc2/shared/src/test/mlscript/newres/ScrutineeReferences.mls new file mode 100644 index 0000000000..cfd37164fe --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ScrutineeReferences.mls @@ -0,0 +1,140 @@ +:. +:js + +// Local references stay direct before optimization, so field selections and +// pattern matches remain visibly attached to the same value for deforestation. +:sir +class Item(val value: Int) +fun read(item) = if item is Item(_) then item.Item#value else 0 +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ define read⁰ as fun readΒΉ(item) { +//β”‚ let arg$Item$0$; +//β”‚ match item +//β”‚ Item⁰ => +//β”‚ set arg$Item$0$ = item.value⁰; +//β”‚ return item.value⁰ +//β”‚ else +//β”‚ return 0 +//β”‚ end +//β”‚ }; +//β”‚ define ItemΒΉ as class Item⁰ { +//β”‚ val valueΒΉ; +//β”‚ constructor ItemΒ²(value) { define valueΒΉ as val value⁰ = value; end } +//β”‚ }; +//β”‚ end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +:expect 7 +read(Item(7)) +//β”‚ = 7 + + +// Captured references need their lexical scope information for extraction, +// even though their runtime match does not need a scrutinee temporary. +:sir +class Box[out A](val value: A) +private fun reader[A](value: Box[A] | null) = + fun read() = + if value is ~null as box then box.value else throw "empty" + read +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ @private +//β”‚ define reader⁰ as fun readerΒΉ(value) { +//β”‚ let read; +//β”‚ define read as fun readΒ²() { +//β”‚ let box, output; +//β”‚ match value +//β”‚ null => +//β”‚ throw "empty" +//β”‚ else +//β”‚ set output = value; +//β”‚ set box = output; +//β”‚ return box.valueΒ² +//β”‚ end +//β”‚ }; +//β”‚ return readΒ² +//β”‚ }; +//β”‚ define Box⁰ as class BoxΒ² { +//β”‚ val valueΒ³; +//β”‚ constructor BoxΒΉ(value) { define valueΒ³ as val valueΒ² = value; end } +//β”‚ }; +//β”‚ end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +:expect [3, 4] +[reader[Int](Box(3))(), reader[Str](Box("four"))().length] +//β”‚ = [3, 4] + + +// Reading a captured loop condition again must observe assignments in the body. +:sir +fun drain(flag) = + fun run() = + while flag do + set flag = false + run() + flag +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ define drain⁰ as fun drainΒΉ(flag) { +//β”‚ let run; +//β”‚ define run as fun run⁰() { +//β”‚ loop lbl: +//β”‚ match flag +//β”‚ true => +//β”‚ set flag = false; +//β”‚ continue lbl +//β”‚ else +//β”‚ end +//β”‚ end +//β”‚ return runtime⁰.Unit⁰ +//β”‚ }; +//β”‚ do run⁰(); +//β”‚ return flag +//β”‚ }; +//β”‚ end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +:expect [false, false] +[drain(true), drain(false)] +//β”‚ = [false, false] + + +// A by-name reference invokes a getter. Matching several alternatives must +// evaluate it once and retain the result, rather than reusing the reference. +:sir +:expect [7, 1] +let reads = 0 +fun source = + set reads += 1 + reads +let result = if source is + 0 then 0 + 1 then 7 + else 9 +[result, reads] +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let scrut, tmp; +//β”‚ define source⁰ as fun sourceΒΉ() { +//β”‚ let tmp1; +//β”‚ set tmp1 = +⁰(reads⁰, 1); +//β”‚ set reads⁰ = tmp1; +//β”‚ return reads⁰ +//β”‚ }; +//β”‚ set reads⁰ = 0; +//β”‚ set scrut = sourceΒΉ(); +//β”‚ match scrut +//β”‚ 0 => +//β”‚ set tmp = 0; +//β”‚ end +//β”‚ 1 => +//β”‚ set tmp = 7; +//β”‚ end +//β”‚ else +//β”‚ set tmp = 9; +//β”‚ end +//β”‚ set result⁰ = tmp; +//β”‚ return [result⁰, reads⁰] +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = [7, 1] +//β”‚ reads = 1 +//β”‚ result = 7 diff --git a/hkmc2/shared/src/test/mlscript/newres/SealedClasses.mls b/hkmc2/shared/src/test/mlscript/newres/SealedClasses.mls new file mode 100644 index 0000000000..497b843fe7 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/SealedClasses.mls @@ -0,0 +1,142 @@ +:. + +import "../../mlscript-compile/SealedClasses.mls" + +// Worksheet blocks and nested scopes still belong to the same compilation unit. +class Local { fun value() = 3 } + +:expect [3, 3, 3] +class LocalChild extends Local +module Nested with + class LocalChild extends Local +[(new LocalChild).value(), (new Nested.LocalChild).value(), + (new Local with { fun extra() = 4 }).value()] +//β”‚ = [3, 3, 3] + +// A sealed class can still be constructed outside its defining unit. +:expect 1 +(new SealedClasses.Closed).value() +//β”‚ = 1 + +:expect [2, 4, 1] +class OpenChild extends SealedClasses.Open +class AbstractChild extends SealedClasses.Abstract { fun value() = 4 } +class IndirectChild extends SealedClasses.OpenChild +[(new OpenChild).value(), (new AbstractChild).value(), (new IndirectChild).value()] +//β”‚ = [2, 4, 1] + +:e +class Invalid extends SealedClasses.Closed +//β”‚ ╔══[COMPILATION ERROR] Cannot extend sealed class 'Closed' outside its compilation unit +//β”‚ β•‘ l.29: class Invalid extends SealedClasses.Closed +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Closed' is defined here; declare it 'open' or 'abstract' to allow external subclasses +//β”‚ β•‘ in ../../mlscript-compile/SealedClasses.mls +//β”‚ β•‘ l.3: class Closed { fun value() = 1 } +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// Neither modifier on the subclass grants access to a sealed parent. +:e +open class InvalidOpen extends SealedClasses.Closed +//β”‚ ╔══[COMPILATION ERROR] Cannot extend sealed class 'Closed' outside its compilation unit +//β”‚ β•‘ l.40: open class InvalidOpen extends SealedClasses.Closed +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Closed' is defined here; declare it 'open' or 'abstract' to allow external subclasses +//β”‚ β•‘ in ../../mlscript-compile/SealedClasses.mls +//β”‚ β•‘ l.3: class Closed { fun value() = 1 } +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:e +abstract class InvalidAbstract extends SealedClasses.Closed +//β”‚ ╔══[COMPILATION ERROR] Cannot extend sealed class 'Closed' outside its compilation unit +//β”‚ β•‘ l.50: abstract class InvalidAbstract extends SealedClasses.Closed +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Closed' is defined here; declare it 'open' or 'abstract' to allow external subclasses +//β”‚ β•‘ in ../../mlscript-compile/SealedClasses.mls +//β”‚ β•‘ l.3: class Closed { fun value() = 1 } +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// Openness is not inherited by subclasses. +:e +class InvalidGrandchild extends SealedClasses.ClosedChild +//β”‚ ╔══[COMPILATION ERROR] Cannot extend sealed class 'ClosedChild' outside its compilation unit +//β”‚ β•‘ l.61: class InvalidGrandchild extends SealedClasses.ClosedChild +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'ClosedChild' is defined here; declare it 'open' or 'abstract' to allow external subclasses +//β”‚ β•‘ in ../../mlscript-compile/SealedClasses.mls +//β”‚ β•‘ l.8: class ClosedChild extends Open +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:e +object InvalidObject extends SealedClasses.Closed +//β”‚ ╔══[COMPILATION ERROR] Cannot extend sealed class 'Closed' outside its compilation unit +//β”‚ β•‘ l.71: object InvalidObject extends SealedClasses.Closed +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Closed' is defined here; declare it 'open' or 'abstract' to allow external subclasses +//β”‚ β•‘ in ../../mlscript-compile/SealedClasses.mls +//β”‚ β•‘ l.3: class Closed { fun value() = 1 } +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:e +new SealedClasses.Closed with { fun extra() = 4 } +//β”‚ ╔══[COMPILATION ERROR] Cannot extend sealed class 'Closed' outside its compilation unit +//β”‚ β•‘ l.81: new SealedClasses.Closed with { fun extra() = 4 } +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Closed' is defined here; declare it 'open' or 'abstract' to allow external subclasses +//β”‚ β•‘ in ../../mlscript-compile/SealedClasses.mls +//β”‚ β•‘ l.3: class Closed { fun value() = 1 } +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ = $anon + +:e +let storedParent = SealedClasses.Closed +class InvalidStored extends storedParent +//β”‚ ╔══[COMPILATION ERROR] Cannot extend sealed class 'Closed' outside its compilation unit +//β”‚ β•‘ l.93: class InvalidStored extends storedParent +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Closed' is defined here; declare it 'open' or 'abstract' to allow external subclasses +//β”‚ β•‘ in ../../mlscript-compile/SealedClasses.mls +//β”‚ β•‘ l.3: class Closed { fun value() = 1 } +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ storedParent = class Closed + +:e +class InvalidAlias extends SealedClasses.alias +//β”‚ ╔══[COMPILATION ERROR] Cannot extend sealed class 'Closed' outside its compilation unit +//β”‚ β•‘ l.104: class InvalidAlias extends SealedClasses.alias +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Closed' is defined here; declare it 'open' or 'abstract' to allow external subclasses +//β”‚ β•‘ in ../../mlscript-compile/SealedClasses.mls +//β”‚ β•‘ l.3: class Closed { fun value() = 1 } +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:e +class InvalidNested extends SealedClasses.Nested.Closed +//β”‚ ╔══[COMPILATION ERROR] Cannot extend sealed class 'Closed' outside its compilation unit +//β”‚ β•‘ l.114: class InvalidNested extends SealedClasses.Nested.Closed +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Closed' is defined here; declare it 'open' or 'abstract' to allow external subclasses +//β”‚ β•‘ in ../../mlscript-compile/SealedClasses.mls +//β”‚ β•‘ l.11: class Closed +//β”‚ ╙── ^^^^^^^^^^^^ + +// The prelude's universal superclass is explicitly open. +:expect 5 +class ExplicitObject extends Object { fun value() = 5 } +(new ExplicitObject).value() +//β”‚ = 5 + +:e +class InvalidParameterized extends SealedClasses.Parameterized[Int](1) +//β”‚ ╔══[COMPILATION ERROR] Cannot extend sealed class 'Parameterized' outside its compilation unit +//β”‚ β•‘ l.130: class InvalidParameterized extends SealedClasses.Parameterized[Int](1) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class 'Parameterized' is defined here; declare it 'open' or 'abstract' to allow external subclasses +//β”‚ β•‘ in ../../mlscript-compile/SealedClasses.mls +//β”‚ β•‘ l.12: class Parameterized[A](val item: A) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:expect 7 +class OpenParameterized extends SealedClasses.OpenParameterized[Int](7) +(new OpenParameterized).item +//β”‚ = 7 diff --git a/hkmc2/shared/src/test/mlscript/newres/SelfPatternRefinement.mls b/hkmc2/shared/src/test/mlscript/newres/SelfPatternRefinement.mls new file mode 100644 index 0000000000..abe3d81d3f --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/SelfPatternRefinement.mls @@ -0,0 +1,88 @@ +:. + +// A base-class method can run on a subclass. Matching this must expose the +// subclass's declared fields, including recursive fields of the base type. +:expect 3 +data class Expr with + constructor + Lit(value: Int) + Add(lhs: Expr, rhs: Expr) + fun eval: Int = if this is + Lit(value) then value + Add(lhs, rhs) then lhs.eval + rhs.eval +Add(Lit(1), Lit(2)).eval +//β”‚ = 3 + +// The receiver can be captured by a nested function; aliases and nested +// constructor patterns must retain the subclass interface and lexical path. +:expect [3, 0, 4] +class Reader with + fun read(): Int = + let self = this + fun nested() = if self is Text as text then text.value.length else 0 + nested() + fun first(): Int = if this is Pair(Text as text, _) then text.value.length else 0 +class Text(val value: Str) extends Reader +class Pair(val left: Reader, val right: Reader) extends Reader +[Text("abc").read(), (new Reader).read(), Pair(Text("four"), Text("five")).first()] +//β”‚ = [3, 0, 4] + +// Each enclosing generic activation retains its own field type when a local +// subclass is matched by an inherited method. +:expect [5, 6] +class First(val first: Int) +class Second(val second: Int) +private fun make[A](item: A) = + class Parent with + fun read() = if this is Child(items) then items.0 else item + class Child(val items: Array[A]) extends Parent + Child([item]) +[make(First(5)).read().first, make(Second(6)).read().second] +//β”‚ = [5, 6] + +// A concrete allocation is not an abstract self reference. An impossible +// subclass branch must not add an extra candidate to the result. +:expect 7 +class ConcreteBase +class ConcreteChild extends ConcreteBase +let result = if new ConcreteBase is ConcreteChild as child then child else [7] +result.0 +//β”‚ = 7 +//β”‚ result = [7] + +// Refinement supplies the declared field interface, not arbitrary members. +:e +data class Bad with + constructor + Entry(text: Str) + fun read = if this is Entry(text) then text.absent +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.58: fun read = if this is Entry(text) then text.absent +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Str' does not contain member 'absent' +//β”‚ β•‘ l.57: Entry(text: Str) +//β”‚ ╙── ^^^ + +// A generic subclass's omitted argument stays abstract even when a local +// allocation happens to supply a string. +:e +class Parent with + fun read = if this is Child(item) then item.length else 0 +class Child[A](val item: A) extends Parent +Child("abc").read +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'length' has no resolved target +//β”‚ β•‘ l.70: fun read = if this is Child(item) then item.length else 0 +//β”‚ ╙── ^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Exercise the imported recursive methods whose pattern bindings previously +// needed casts, including nested aliases in the pretty-printer. +:expect [true, true, "x y", "(Ξ»x. x) y", true, false] +import "../../mlscript-compile/ups/EvaluationContext.mls" +open EvaluationContext +let term = App(Var("x"), Var("y")) +[term.freeVars.has("x"), term.freeVars.has("y"), term.show, + App(Abs(Var("x"), Var("x")), Var("y")).show, + term.equals(App(Var("x"), Var("y"))), term.equals(App(Var("x"), Var("z")))] +//β”‚ = [true, true, "x y", "(Ξ»x. x) y", true, false] +//β”‚ term = App(Var("x"), Var("y")) diff --git a/hkmc2/shared/src/test/mlscript/newres/ShapeFixture.mls b/hkmc2/shared/src/test/mlscript/newres/ShapeFixture.mls new file mode 100644 index 0000000000..d1165b8780 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ShapeFixture.mls @@ -0,0 +1,59 @@ +:. +:js + +import "../../mlscript-compile/Block.mls" +import "../../mlscript-compile/Shape.mls" +import "../../mlscript-compile/Option.mls" + +class Pair(val left: Int, val right: Str) +val pair = Block.ConcreteClassSymbol("Pair", Pair, Option.Some([ + Block.Symbol("left"), Block.Symbol("right")]), []) +//β”‚ pair = ConcreteClassSymbol( +//β”‚ _, +//β”‚ fun Pair { class: class Pair }, +//β”‚ Some([Symbol("left"), Symbol("right")]), +//β”‚ [] +//β”‚ ) + +:expect "Class(Pair, [Dyn, Dyn])" +Shape.show(Shape.silh(Block.Cls(pair, Block.ValueLit(null)))) +//β”‚ = "Class(Pair, [Dyn, Dyn])" + +:expect "Arr(Dyn, Dyn, Dyn)" +Shape.show(Shape.silh(Block.Tup(3))) +//β”‚ = "Arr(Dyn, Dyn, Dyn)" + +:expect ["Lit(42)"] +val pairShape = Shape.Class(pair, [Shape.Lit(42), Shape.Lit("hello")]) +Shape.sel(pairShape, Shape.Lit("left")).map((s, ...) => Shape.show(s)) +//β”‚ = ["Lit(42)"] +//β”‚ pairShape = Class( +//β”‚ ConcreteClassSymbol( +//β”‚ _, +//β”‚ fun Pair { class: class Pair }, +//β”‚ Some([Symbol("left"), Symbol("right")]), +//β”‚ [] +//β”‚ ), +//β”‚ [Lit(42), Lit("hello")] +//β”‚ ) + +:expect ["Lit(\"hello\")"] +Shape.sel(pairShape, Shape.Lit("right")).map((s, ...) => Shape.show(s)) +//β”‚ = ["Lit(\"hello\")"] + +:expect [] +Shape.sel(pairShape, Shape.Lit("missing")) +//β”‚ = [] + +:expect [true, false, false] +[Shape.static(Shape.Lit(42)), Shape.static(Shape.Lit("Int")), + Shape.static(Shape.silh(Block.Tup(1)))] +//β”‚ = [true, false, false] + +:expect "_name" +Block.showSymbol(Block.Symbol("$name")) +//β”‚ = "_name" + +:expect "42 then 1" +Block.showArm(Block.Arm(Block.Lit(42), Block.Return(Block.ValueLit(1)))) +//β”‚ = "42 then 1" diff --git a/hkmc2/shared/src/test/mlscript/newres/ShapeViews.mls b/hkmc2/shared/src/test/mlscript/newres/ShapeViews.mls new file mode 100644 index 0000000000..a7cf19e80d --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ShapeViews.mls @@ -0,0 +1,156 @@ +:. +:flowBasedOpt off +// ^ FIXME: DPE causes problems + +// Returning a generic curried function captures outer binders without consuming +// its own scheme. Marks on its partial applications still distinguish callers. +:expect [1, 3, 5, 2, 4, 6] +class First(val first: Int) +class Second(val second: Int) +private fun enclosing[A](x: A) = + private fun pair[B](y: B)(z) = [x, y, z] + pair +private val left = enclosing(First(1)) +private val right = enclosing(Second(2)) +private val leftTail = left[Second](Second(3)) +private val rightTail = right[First](First(4)) +let a = leftTail(First(5)) +let b = rightTail(Second(6)) +[a.0.first, a.1.second, a.2.first, b.0.second, b.1.first, b.2.second] +//β”‚ = [1, 3, 5, 2, 4, 6] +//β”‚ a = [First(1), Second(3), First(5)] +//β”‚ b = [Second(2), First(4), Second(6)] +//β”‚ left = fun pair +//β”‚ leftTail = fun +//β”‚ right = fun pair +//β”‚ rightTail = fun + +// A specialization and a consumed term list survive multiple nested captures. +:expect [7, 8, 9] +class First(val first: Int) +class Second(val second: Int) +private fun enclosing[A](x: A) = + private fun pair[B](y: B)(z) = [x, y, z] + private fun middle() = + private fun inner() = pair[Second](Second(8)) + inner() + middle() +private val tail = enclosing(First(7)) +let result = tail(First(9)) +[result.0.first, result.1.second, result.2.first] +//β”‚ = [7, 8, 9] +//β”‚ result = [First(7), Second(8), First(9)] +//β”‚ tail = fun + +// Callback checking must retain the binder view of a partially applied function. +:expect 10 +class Item(val value: Int) +private fun first[A](x: A)(get) = get(x) +private fun use[B](f: (Item -> Int) -> B): B = f(x => x.value) +use(first[Item](Item(10))) +//β”‚ = 10 + +// A partial new expression keeps its constructor parameters and captured marks. +:expect [11, 12, 13, 14] +class First(val first: Int) +class Second(val second: Int) +class Pair[A](val left: A)(val right) +private fun start[A](x: A) = new Pair[A](x) +private val left = start(First(11)) +private val right = start(Second(13)) +let a = left(Second(12)) +let b = right(First(14)) +[a.left.first, a.right.second, b.left.second, b.right.first] +//β”‚ = [11, 12, 13, 14] +//β”‚ a = Pair(First(11)) +//β”‚ b = Pair(Second(13)) +//β”‚ left = fun +//β”‚ right = fun + +// Constructor patterns recognize an explicitly specialized constructor value. +:expect 15 +class Box[A](val value: A) +private val IntBox = Box[Int] +private fun read(x) = if x is IntBox(value) then value else 0 +read(Box(15)) +//β”‚ = 15 +//β”‚ IntBox = fun Box { class: class Box } + +// A captured constructor value is still available as a constructor pattern. +:expect 16 +class Box[A](val value: A) +private fun makeConstructor[A](unused: A) = Box +private val Alias = makeConstructor(0) +private fun read(x) = if x is Alias(value) then value else 0 +read(Box(16)) +//β”‚ = 16 +//β”‚ Alias = fun Box { class: class Box } + +// An application head identifies the original constructor, but an already +// constructed object is not itself a constructor pattern. +:e +class Box(val value: Int) +private val instance = Box(17) +private fun invalid(x) = if x is instance(value) then value else 0 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in constructor +//β”‚ β•‘ l.94: private fun invalid(x) = if x is instance(value) then value else 0 +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Box' cannot be used as a constructor pattern. +//β”‚ β•‘ l.93: private val instance = Box(17) +//β”‚ ╙── ^^^^^^^ +//β”‚ instance = Box(17) + +// Nor does a partially applied constructor denote the original constructor. +:e +class Pair(val left: Int)(val right: Int) +private val partial = Pair(18) +private fun invalid(x) = if x is partial(left) then left else 0 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in constructor +//β”‚ β•‘ l.107: private fun invalid(x) = if x is partial(left) then left else 0 +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Instance of class 'Pair' cannot be used as a constructor pattern. +//β”‚ β•‘ l.106: private val partial = Pair(18) +//β”‚ ╙── ^^^^^^^^ +//β”‚ partial = fun + +// Explicit parent projections preserve an annotated receiver's binder view. +:expect [19, 20] +class First(val first: Int) +class Second(val second: Int) +class Parent[A](val item: A) +class Child[A](item: A) extends Parent[A](item) +private fun make[A](value: A): Child[A] = new Child[A](value) +private fun read[A](value: Child[A]) = value.Parent#item +[read(make[First](First(19))).first, read(make[Second](Second(20))).second] +//β”‚ = [19, 20] + +// A nominal parent may itself refer to a captured enclosing type parameter. +:expect [21, 22] +class First(val first: Int) +class Second(val second: Int) +private fun enclosing[A](item: A) = + class Parent(val value: A) + class Child(value: A) extends Parent(value) + private fun nested() = + let receiver = new Child(item) as Child + receiver.Parent#value + nested() +[enclosing(First(21)).first, enclosing(Second(22)).second] +//β”‚ = [21, 22] + +// A selected annotated method can cross nested captures before its remaining +// empty parameter list is consumed. Each receiver keeps its own type argument. +:expect [23, 24] +class First(val first: Int) +class Second(val second: Int) +class Holder[A](val item: A) with + fun keep[B](other: B)(): A = item +private fun capture[A](holder: Holder[A]) = + private fun nested() = holder.keep[Int](0) + nested() +private val first = capture(Holder[First](First(23))) +private val second = capture(Holder[Second](Second(24))) +[first().first, second().second] +//β”‚ = [23, 24] +//β”‚ first = fun +//β”‚ second = fun diff --git a/hkmc2/shared/src/test/mlscript/newres/SourceLocations.mls b/hkmc2/shared/src/test/mlscript/newres/SourceLocations.mls new file mode 100644 index 0000000000..635a0174d4 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/SourceLocations.mls @@ -0,0 +1,43 @@ +:. + +// Source metadata is expanded at its lexical use, including inside functions. +:expect true +source.line > 0 +//β”‚ = true + +:expect "" +fun location() = source.name +location() +//β”‚ = "" + +:expect "SourceMarker" +module SourceMarker with + fun nested() = source.name +SourceMarker.nested() +//β”‚ = "SourceMarker" + +:expect true +source.file.endsWith("SourceLocations.mls") +//β”‚ = true + +// A captured reference still denotes the builtin module. +:expect true +fun line() = source.line +line() > 0 +//β”‚ = true + +// A user binding with the same name keeps ordinary member access. +:expect [42, "user", "custom"] +fun shadow() = + let source = (line: 42, name: "user", file: "custom") + [source.line, source.name, source.file] +shadow() +//β”‚ = [42, "user", "custom"] + +:expect 1 +fun consecutiveLines() = + val first = source.line + val second = source.line + second - first +consecutiveLines() +//β”‚ = 1 diff --git a/hkmc2/shared/src/test/mlscript/newres/SpecializationCaptures.mls b/hkmc2/shared/src/test/mlscript/newres/SpecializationCaptures.mls new file mode 100644 index 0000000000..5659c5b521 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/SpecializationCaptures.mls @@ -0,0 +1,64 @@ +:. + +// Explicit arguments are supplied after the reference enters the caller's scope. +:expect 1 +class Item(val value: Int) +private fun identity[A](value: A) = value +private fun use() = identity[Item -> Int](x => x.value)(Item(1)) +use() +//β”‚ = 1 + +// More than one enclosing capture must retain the order of scope crossings. +:expect 2 +class Item(val value: Int) +private fun identity[A](value: A) = value +private fun wrap() = + fun inner() = identity[Item -> Int](x => x.value)(Item(2)) + inner() +wrap() +//β”‚ = 2 + +// A by-name definition consumes the supplied arguments before producing a value. +:expect 3 +class Item(val value: Int) +private fun identity[A] = (value: A) => value +private fun use() = identity[Item -> Int](x => x.value)(Item(3)) +use() +//β”‚ = 3 + +// The supplied type itself can refer to the enclosing function's type parameter. +:expect [4, 5] +class First(val first: Int) +class Second(val second: Int) +private fun identity[A](value: A) = value +private fun wrap[B](value: B) = identity[B](value) +[wrap(First(4)).first, wrap(Second(5)).second] +//β”‚ = [4, 5] + +// By-name specialization also interprets enclosing parameters at the use site. +:expect [6, 7] +class First(val first: Int) +class Second(val second: Int) +private fun identity[A] = (value: A) => value +private fun wrap[B](value: B) = identity[B](value) +[wrap(First(6)).first, wrap(Second(7)).second] +//β”‚ = [6, 7] + +// By-name rebasing preserves multiple nested captures too. +:expect [8, 9] +class First(val first: Int) +class Second(val second: Int) +private fun identity[A] = (value: A) => value +private fun wrap[B](value: B) = + fun inner() = identity[B](value) + inner() +[wrap(First(8)).first, wrap(Second(9)).second] +//β”‚ = [8, 9] + +// Captured constructor specialization must terminate before any result use. +:expect Box(Item(10)) +class Item(val value: Int) +class Box[T](val item: T) with + fun copy() = Box[T](item) +(new Box[Item](Item(10))).copy() +//β”‚ = Box(Item(10)) diff --git a/hkmc2/shared/src/test/mlscript/newres/SpreadCalls.mls b/hkmc2/shared/src/test/mlscript/newres/SpreadCalls.mls new file mode 100644 index 0000000000..7faa485313 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/SpreadCalls.mls @@ -0,0 +1,435 @@ +:. + +// Multiple spreads, nested spreads, empty spreads, and fixed arguments after them. +:expect [1, 2, 3, 4] +fun four(a, b, c, d) = [a, b, c, d] +four(...[], ...[1, ...[2]], 3, ...[4], ...[]) +//β”‚ = [1, 2, 3, 4] + +// Both spread producers become available after the caller is elaborated. +:expect [5, 6, 7, 8] +fun delayed() = four(...left(), ...right()) +fun left() = [5, 6] +fun right() = [7, 8] +delayed() +//β”‚ = [5, 6, 7, 8] + +// Returned tuples preserve captured values through nested spreads and rest forwarding. +:expect [11, 12, 21, 22] +class Box(val value) +fun pair(x, y) = [x, ...[y]] +fun second(a, b) = b +private fun forward(head, ...tail) = second(...tail) +private fun captured(x, y) = () => forward(0, ...pair(x, y)) +let first = captured(Box(10), Box(11)) +let next = captured(Box(20), Box(21)) +[first().value, captured(Box(0), Box(12))().value, next().value, captured(Box(0), Box(22))().value] +//β”‚ = [11, 12, 21, 22] +//β”‚ first = fun +//β”‚ next = fun + +// Residual fields from distinct spreads and direct arguments keep separate marks. +:expect 32 +private fun mixed(x, y) = forward(...[0, x], y) +mixed(Box(31), Box(32)).value +//β”‚ = 32 + +// An empty rest tuple can itself be spread into a zero-argument function. +:expect 40 +fun zero() = 40 +fun empty(head, ...tail) = zero(...tail) +empty(0) +//β”‚ = 40 + +// Each list of a curried function or constructor is checked independently. +// FIXME: Forwarding to a captured constructor loses the caller activation. +:fixme +:expect [51, 52, 53, 54] +class Pair(val left)(val right) +fun curried(x)(y) = Pair(x)(y) +let built = curried(...[Box(51)])(...[Box(52)]) +let direct = new Pair(...[Box(53)])(...[Box(54)]) +[built.left.value, built.right.value, direct.left.value, direct.right.value] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.52: [built.left.value, built.right.value, direct.left.value, direct.right.value] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.49: fun curried(x)(y) = Pair(x)(y) +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'curried' is exposed by this compilation unit. +//β”‚ β•‘ l.49: fun curried(x)(y) = Pair(x)(y) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.52: [built.left.value, built.right.value, direct.left.value, direct.right.value] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'y' admits values of unknown shape. +//β”‚ β•‘ l.49: fun curried(x)(y) = Pair(x)(y) +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'curried' is exposed by this compilation unit. +//β”‚ β•‘ l.49: fun curried(x)(y) = Pair(x)(y) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '[51, 52, 53, 54]', got: 'undefined' +//β”‚ built = Pair(Box(51)) +//β”‚ direct = Pair(Box(53)) + +// Plain tuple fields need no shapes for the tuple's arity to be known. +:expect 60 +fun typed(x: Int) = second(...[x, 60]) +typed(0) +//β”‚ = 60 + +// A delayed concrete spread establishes a real mismatch, including nested fields. +:e +fun tooShort() = four(...short()) +fun short() = [1, ...[2]] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.86: fun tooShort() = four(...short()) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'four' expected 4 arguments, but got 2 +//β”‚ β•‘ l.5: fun four(a, b, c, d) = [a, b, c, d] +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:e +four(...[1, 2], ...[3, 4], 5) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.96: four(...[1, 2], ...[3, 4], 5) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'four' expected 4 arguments, but got 5 +//β”‚ β•‘ l.5: fun four(a, b, c, d) = [a, b, c, d] +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] Error: Function 'four' expected 4 arguments but got 5 + +// Opaque values are left to the runtime spread operation to validate. +:re +four(...Box(1)) +//β”‚ ═══[RUNTIME ERROR] TypeError: Spread syntax requires ...iterable[Symbol.iterator] to be a function + +// Multiple alternatives for each spread are retained as their shapes arrive. +:expect [[1, 2, 3, 4], [5, 6, 7, 8]] +fun combine(xs, ys) = four(...xs, ...ys) +[combine([1, 2], [3, 4]), combine([5, 6], [7, 8])] +//β”‚ = [[1, 2, 3, 4], [5, 6, 7, 8]] + +// Direct tuple returns preserve the field shapes of distinct call sites. +:expect [71, 72] +class Left(val l) +class Right(val r) +[second(...pair(0, Left(71))).l, second(...pair(0, Right(72))).r] +//β”‚ = [71, 72] + +// Returning a rest tuple preserves its caller's capture marks. +:expect [81, 82] +fun packed(...xs) = xs +[second(...packed(0, Left(81))).l, second(...packed(0, Right(82))).r] +//β”‚ = [81, 82] + +// Nested applications of the same tuple producer remain distinct. +:expect [0, 0, 1] +fun nested(xs) = [0, ...xs] +nested(nested([1])) +//β”‚ = [0, 0, 1] + +// Recursive rest forwarding should reach a fixed point. +fun cycle(...xs) = cycle(...xs) +fun seed() = cycle(1) + +// Slicing a rest tuple retains both known fields and approximated spread segments. +:expect [0, 0, 1] +fun nestedRest(ignored, ...xs) = [0, ...xs] +nestedRest(9, ...nestedRest(9, 1)) +//β”‚ = [0, 0, 1] + +// A later alternative must be checked even after a valid combination was seen. +:e +fun varying(xs, ys) = four(...xs, ...ys) +fun knownGood() = varying([1], [2, 3, 4]) +fun laterBad() = varying([5], [6, 7]) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.147: fun varying(xs, ys) = four(...xs, ...ys) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'four' expected 4 arguments, but got 3 +//β”‚ β•‘ l.5: fun four(a, b, c, d) = [a, b, c, d] +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// Recursive tuple growth has unknown length, but a rest-only function can forward it. +:expect [3, 2, 1] +fun growing = case + 0 then [] + n then [n, ...growing(n - 1)] +packed(...growing(3)) +//β”‚ = [3, 2, 1] + +// Known fields surrounding an unknown-length spread survive rest forwarding. +:expect [9, 3, 2, 1, 8] +fun keep(head, ...tail) = [head, ...tail] +keep(9, ...growing(3), 8) +//β”‚ = [9, 3, 2, 1, 8] + + +// Hide both length and element types while retaining concrete runtime test values. +fun opaque(values): Array[Any] = values +let arr = opaque([1,2,3,4]) +//β”‚ arr = [1, 2, 3, 4] + +:sr +[...arr] +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ [ ...arr⁰ ] +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = [1, 2, 3, 4] + +four(...arr) +//β”‚ = [1, 2, 3, 4] + + +let arr = if maybe then [1, 2] else [1, 2, 3, 4] +//β”‚ arr = [1, 2] + +:e +four(...arr) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.192: four(...arr) +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'four' expected 4 arguments, but got 2 +//β”‚ β•‘ l.5: fun four(a, b, c, d) = [a, b, c, d] +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] Error: Function 'four' expected 4 arguments but got 2 + + +// Exact call arity anchors known suffixes and prefixes around opaque spreads. +:expect [91, 92, 93] +fun lastOfTwo(a, b) = b +fun firstOfTwo(a, b) = a +fun onlyOne(a) = a +[lastOfTwo(...opaque(["ignored"]), Box(91)).value, + firstOfTwo(Box(92), ...opaque(["ignored"])).value, + onlyOne(...opaque([]), Box(93)).value] +//β”‚ = [91, 92, 93] + +// Every unknown segment can supply the remaining argument slots. +:expect [1, 2, 3, 4] +four(...opaque([1, 2]), ...opaque([3, 4])) +//β”‚ = [1, 2, 3, 4] + +// Rest consumption across an unknown segment stays unknown on subsequent calls. +:expect [1, 2, 3, 4] +fun dropTwo(a, b, ...tail) = tail +four(...dropTwo(...opaque([0, 0, 1, 2, 3]), 4)) +//β”‚ = [1, 2, 3, 4] + +// Known fields that must remain in the rest tail retain their value shapes. +:expect 94 +fun afterOne(a, ...tail) = tail +fun tailLast(a, b, c) = c +tailLast(...afterOne(...opaque(["ignored"]), 1, 2, Box(94))).value +//β”‚ = 94 + +// An opaque spread does not authorize a call through its unknown element. +:e +fun invokeUnknown(f) = f() +invokeUnknown(...opaque([() => 95])) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.232: fun invokeUnknown(f) = f() +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Value with no known interface cannot be called like a function. +//β”‚ β•‘ l.173: fun opaque(values): Array[Any] = values +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Type 'Any' is declared without a member interface. +//β”‚ β•‘ in ../decls/Prelude.mls +//β”‚ β•‘ l.2: declare type Any +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Unknown elements must not silently resolve against another known candidate. +:e +fun inspectUnknown(x) = x.value +inspectUnknown(Box(96)) +inspectUnknown(...opaque([Box(96)])) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.248: fun inspectUnknown(x) = x.value +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•‘ l.173: fun opaque(values): Array[Any] = values +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Type 'Any' is declared without a member interface. +//β”‚ β•‘ in ../decls/Prelude.mls +//β”‚ β•‘ l.2: declare type Any +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ in SpreadCalls.mls:248 +//β”‚ β•‘ l.248: fun inspectUnknown(x) = x.value +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'inspectUnknown' is exposed by this compilation unit. +//β”‚ β•‘ l.248: fun inspectUnknown(x) = x.value +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Too many known fields remain a proven mismatch even with an unknown spread. +:e +four(...opaque([]), 1, 2, 3, 4, 5) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.273: four(...opaque([]), 1, 2, 3, 4, 5) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'four' expected 4 arguments, but got at least 5 +//β”‚ β•‘ l.5: fun four(a, b, c, d) = [a, b, c, d] +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] Error: Function 'four' expected 4 arguments but got 5 + +// Rest arity may be valid while the callable argument remains unknown. +:e +fun invokeRest(f, x, ...tail) = f() +invokeRest(...opaque([() => 97]), Box(0)) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.284: fun invokeRest(f, x, ...tail) = f() +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Value with no known interface cannot be called like a function. +//β”‚ β•‘ l.173: fun opaque(values): Array[Any] = values +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Type 'Any' is declared without a member interface. +//β”‚ β•‘ in ../decls/Prelude.mls +//β”‚ β•‘ l.2: declare type Any +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// When repeatedTuples receives [Box(98)], both a and b must acquire its element +// shape so that both `.value` selections resolve. Although the tuple expressions +// have equal syntax, resolving a does not notify the listeners attached to b. +:expect [98, 98] +private fun repeatedTuples(xs) = + let a = [0, ...xs] + let b = [0, ...xs] + [second(...a).value, second(...b).value] +repeatedTuples([Box(98)]) +//β”‚ = [98, 98] + +// A public callback signature supplies the callable interface even for an opaque spread. +:expect 97 +fun invokeAnnotatedRest(f: () -> Int, x, ...tail) = f() +invokeAnnotatedRest(...opaque([() => 97]), Box(0)) +//β”‚ = 97 + +// Array element annotations survive spreads; their length remains unknown. +:expect 97 +let callbacks = new mut Array[() -> Int](0) +callbacks.push(() => 97) +private fun invokeTypedRest(f, x, ...tail) = f() +invokeTypedRest(...callbacks, Box(0)) +//β”‚ = 97 +//β”‚ callbacks = [fun] + +// A public unannotated callback is checked for unknown callers despite a typed local call. +:e +fun invokePublicRest(f, x, ...tail) = f() +invokePublicRest(...callbacks, Box(0)) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.326: fun invokePublicRest(f, x, ...tail) = f() +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Value of unknown shape cannot be called like a function. +//β”‚ β•‘ l.326: fun invokePublicRest(f, x, ...tail) = f() +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Parameter 'f' admits values of unknown shape. +//β”‚ β•‘ l.326: fun invokePublicRest(f, x, ...tail) = f() +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'invokePublicRest' is exposed by this compilation unit. +//β”‚ β•‘ l.326: fun invokePublicRest(f, x, ...tail) = f() +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A function parameter's Array annotation carries its element interface through a spread. +:expect 97 +fun invokeArray(xs: Array[() -> Int]) = + private fun invoke(f, x, ...tail) = f() + invoke(...xs, Box(0)) +invokeArray(callbacks) +//β”‚ = 97 + +// A private tuple-based call can infer the callback directly. +:expect 98 +private fun invokeTuple(f, x, ...tail) = f() +invokeTuple(...[() => 98], Box(0)) +//β”‚ = 98 + +// Aliases and nested tuple elements retain their declared shapes through rest forwarding. +:expect 42 +class Item(val value: Int) +type Pairs[T] = Array[[T, Int]] +private fun first(head, ...tail) = head +private fun skip(head, ...tail) = first(...tail) +fun readPairs(xs: Pairs[Item]) = skip(0, ...xs).0.value +readPairs([[Item(42), 1]]) +//β”‚ = 42 + +// Annotating an array with a base element type must hide subclass-only members. +:ge +class Base +class Child extends Base with + val extra = 42 +fun readBase(xs: Array[Base]) = first(...xs).extra +readBase([new Child]) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.371: fun readBase(xs: Array[Base]) = first(...xs).extra +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'extra' +//β”‚ β•‘ l.371: fun readBase(xs: Array[Base]) = first(...xs).extra +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Different type arguments keep distinct element interfaces at their call sites. +:expect [41, 42] +class Left(val left: Int) +class Right(val right: Int) +fun head[A](xs: Array[A]): A = first(...xs) +[head[Left]([Left(41)]).left, head[Right]([Right(42)]).right] +//β”‚ = [41, 42] + +// Constructor type arguments are scoped to each array allocation. +:expect [43, 44] +let lefts = new mut Array[Left](0) +let rights = new mut Array[Right](0) +lefts.push(Left(43)) +rights.push(Right(44)) +[first(...lefts).left, first(...rights).right] +//β”‚ = [43, 44] +//β”‚ lefts = [Left(43)] +//β”‚ rights = [Right(44)] + +// An unannotated allocation infers its own element type from pushed values. +:expect 99 +let unknownCallbacks = new mut Array(0) +unknownCallbacks.push(() => 99) +private fun invokeUnannotatedArray(f, ...tail) = f() +invokeUnannotatedArray(...unknownCallbacks) +//β”‚ = 99 +//β”‚ unknownCallbacks = [fun] + +// TODO: calling a captured helper merges the enclosing activations, also for ordinary tuples. +:fixme +:expect [43, 44] +private fun firstInArray(xs) = first(...xs) +[firstInArray(lefts).left, firstInArray(rights).right] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.413: [firstInArray(lefts).left, firstInArray(rights).right] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Right' does not contain member 'left' +//β”‚ β•‘ l.392: let rights = new mut Array[Right](0) +//β”‚ ╙── ^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.413: [firstInArray(lefts).left, firstInArray(rights).right] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Left' does not contain member 'right' +//β”‚ β•‘ l.391: let lefts = new mut Array[Left](0) +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '[43, 44]', got: 'undefined' + +// Passing an allocation through a private parameter preserves its declared element type. +:expect [43, 44] +private fun localFirstInArray(xs) = + private fun head(x, ...tail) = x + head(...xs) +[localFirstInArray(lefts).left, localFirstInArray(rights).right] +//β”‚ = [43, 44] diff --git a/hkmc2/shared/src/test/mlscript/newres/StoredSpecializations.mls b/hkmc2/shared/src/test/mlscript/newres/StoredSpecializations.mls new file mode 100644 index 0000000000..510db34d3b --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/StoredSpecializations.mls @@ -0,0 +1,88 @@ +:. + +// Explicit specializations through one shared reference remain independent. +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +private fun identity[A](x: A) = x +private val shared = identity +private val first = shared[First] +private val second = shared[Second] +[first(First(1)).first, second(Second(2)).second] +//β”‚ = [1, 2] +//β”‚ first = fun identity +//β”‚ second = fun identity +//β”‚ shared = fun identity + +// Deferred inferred results retain the supplied type reference. +:expect [3, 4] +class First(val first: Int) +class Second(val second: Int) +private fun delayed[A](x: A) = {get: () => x} +private val shared = delayed +private val first = shared[First] +private val second = shared[Second] +[first(First(3)).get().first, second(Second(4)).get().second] +//β”‚ = [3, 4] +//β”‚ first = fun delayed +//β”‚ second = fun delayed +//β”‚ shared = fun delayed + +// Curried tails retain their specialization and captured argument flows. +:expect [5, 6, 7, 8] +class First(val first: Int) +class Second(val second: Int) +private fun pair[A](x: A)(y) = [x, y] +private val shared = pair +private val left = shared[First](First(5)) +private val right = shared[Second](Second(7)) +[left(Second(6)).0.first, left(Second(6)).1.second, + right(First(8)).0.second, right(First(8)).1.first] +//β”‚ = [5, 6, 7, 8] +//β”‚ left = fun +//β”‚ right = fun +//β”‚ shared = fun pair + +// Callback checking must observe the specialization before an implementation call. +:expect 9 +class Item(val value: Int) +private fun identity[A](value: A) = value +private fun use[B](f: Item -> B): B = f(Item(9)) +use(identity[Item]).value +//β”‚ = 9 + +// Supplying the scheme's arguments consumes its type parameter list immediately. +:ge +private fun inferred[A](value: A) = value +private fun declared[A](value: A): A = value +private val first = inferred[Int] +private val second = declared[Int] +first[Str] +second[Str] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.60: first[Str] +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'inferred' expected 0 type arguments, but got 1 +//β”‚ β•‘ l.56: private fun inferred[A](value: A) = value +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.61: second[Str] +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'declared' expected 0 type arguments, but got 1 +//β”‚ β•‘ l.57: private fun declared[A](value: A): A = value +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ = fun declared +//β”‚ first = fun inferred +//β”‚ second = fun declared + +// Arity is checked at specialization even if the value is never called. +:ge +private fun identity[A](value: A) = value +private val tooMany = identity[Int, Str] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.81: private val tooMany = identity[Int, Str] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'identity' expected 1 type argument, but got 2 +//β”‚ β•‘ l.80: private fun identity[A](value: A) = value +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ tooMany = fun identity diff --git a/hkmc2/shared/src/test/mlscript/newres/TuplePatternBindings.mls b/hkmc2/shared/src/test/mlscript/newres/TuplePatternBindings.mls new file mode 100644 index 0000000000..63fb3ef1bd --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/TuplePatternBindings.mls @@ -0,0 +1,61 @@ +:. + +:expect "ABC" +fun second(pair: [Int, Str]) = if pair is [_, text] then text.toUpperCase() +second([1, "abc"]) +//β”‚ = "ABC" + +:expect "a-b" +fun joined(...parts: Array[Str]): Str = parts.join("-") +joined("a", "b") +//β”‚ = "a-b" + +:expect ["a", "b", "c"] +private fun split(values) = if values is [first, ...middle, last] then [first.name, middle.0.name, last.name] +split([{name: "a"}, {name: "b"}, {name: "c"}]) +//β”‚ = ["a", "b", "c"] + +:expect "ABC" +fun head(values: Array[Str]) = if values is [first, ...] then first.toUpperCase() +head(["abc", "def"]) +//β”‚ = "ABC" + +:expect "FIRST" +private fun select(value) = if value is [[first, _], _] then first.toUpperCase() +select([["first", 2], 3]) +//β”‚ = "FIRST" + +:e +fun opaque[A](pair: [A, Int]) = if pair is [item, _] then item.name +opaque([{name: "hidden"}, 1]) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.29: fun opaque[A](pair: [A, Int]) = if pair is [item, _] then item.name +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'name' of a value with unknown shape. +//β”‚ β•‘ l.29: fun opaque[A](pair: [A, Int]) = if pair is [item, _] then item.name +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' does not specify a member interface. +//β”‚ β•‘ l.29: fun opaque[A](pair: [A, Int]) = if pair is [item, _] then item.name +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Removing fields from either end in different orders reaches the same residual +// layouts. Each remainder must be one candidate, not one per removal order, +// or this 24-field tuple produces 2^24 candidates. +:expect 24 +private fun peel(values) = if values is + [] then 0 + [first, ...rest] and first > 100 then peel(rest) + [...rest, last] then 1 + peel(rest) +peel([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]) +//β”‚ = 24 + +// A failed singleton-tuple guard must continue to the literal case or the fallback. +:expect [1, 3, 3, 2, 3, 3, 3] +fun classify(value) = + if value is + [item] and item > 0 then 1 + 0 then 2 + else 3 +[classify([1]), classify([0]), classify([-1]), classify(0), classify(1), classify([]), classify([1, 2])] +//β”‚ = [1, 3, 3, 2, 3, 3, 3] diff --git a/hkmc2/shared/src/test/mlscript/newres/TypeApplications.mls b/hkmc2/shared/src/test/mlscript/newres/TypeApplications.mls new file mode 100644 index 0000000000..64f2cfefae --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/TypeApplications.mls @@ -0,0 +1,15 @@ +:. + +// Type application preserves the runtime constructor, including in inheritance. +:expect 42 +class Parent[T] +class Child[T](val value) extends Parent[T] +(new Child[Int](42)).value +//β”‚ = 42 + +// Applying type arguments to a function preserves the shape of its result. +:expect 43 +fun identity[T](x) = x +identity[Int](new Child[Int](43)).value +//β”‚ = 43 + diff --git a/hkmc2/shared/src/test/mlscript/newres/TypeArgumentVariance.mls b/hkmc2/shared/src/test/mlscript/newres/TypeArgumentVariance.mls new file mode 100644 index 0000000000..d9a3cd36a4 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/TypeArgumentVariance.mls @@ -0,0 +1,121 @@ +:. + +// An output wildcard retains the declared element interface. +:expect 1 +class Item(val value: Int) +let xs = (mut [Item(1)] as Array[out Item]) +xs.0.value +//β”‚ = 1 +//β”‚ xs = [Item(1)] + +// An input wildcard accepts elements but supplies no output member interface. +:ge +class Item(val value: Int) +let xs = (mut [] as Array[in Item]) +xs.push(Item(2)) +xs.0.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.16: xs.0.value +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ This type argument specifies only an input bound. +//β”‚ β•‘ l.14: let xs = (mut [] as Array[in Item]) +//β”‚ β•‘ ^^^^^^^ +//β”‚ ╙── Values read through it have no output bound more specific than 'Any'. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ xs = [Item(2)] + +// An output-only argument must not send an unrelated lower bound back to the array. +:ge +class Item(val value: Int) +private fun connect[A](xs: Array[out A], value: A) = () +let xs = mut [] +connect(xs, Item(3)) +xs.0.value +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'value' has no resolved target +//β”‚ β•‘ l.34: xs.0.value +//β”‚ ╙── ^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ xs = [] + +// Declaration-site contravariance does not infer A from the sink's input type. +:expect 4 +class Base +class Child(val extra: Int) extends Base +class Sink[in T] with + fun put(value: T) = () +private fun infer[A](sink: Sink[A], value: A): A = value +infer(new Sink[Base], Child(4)).extra +//β”‚ = 4 + +// A written invariant argument uses the declaration's output variance. +:expect 5 +class Item(val value: Int) +class Box[out T](val item: T) +private fun get[A](box: Box[A]): A = box.item +get(new Box(Item(5))).value +//β”‚ = 5 + +// A use-site input wildcard overrides an output declaration. +:ge +class Item(val value: Int) +class Box[out T](val item: T) +let box = (new Box(Item(6)) as Box[in Item]) +box.item.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.64: box.item.value +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ This type argument specifies only an input bound. +//β”‚ β•‘ l.63: let box = (new Box(Item(6)) as Box[in Item]) +//β”‚ β•‘ ^^^^^^^ +//β”‚ ╙── Values read through it have no output bound more specific than 'Any'. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ box = Box(Item(6)) + +// Callback inputs reverse function polarity inside a member's own input. +:expect 7 +class Item(val value: Int) +class Box[T](val item: T) with + fun apply(f: T -> Int): Int = f(item) +let box = (new Box(Item(7)) as Box[out Item]) +box.apply(x => x.value) +//β”‚ = 7 +//β”‚ box = Box(Item(7)) + +// The same callback sees Any when the argument supplies only an input part. +:ge +class Item(val value: Int) +class Box[T](val item: T) with + fun apply(f: T -> Int): Int = f(item) +let box = (new Box(Item(8)) as Box[in Item]) +box.apply(x => x.value) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.92: box.apply(x => x.value) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ This type argument specifies only an input bound. +//β”‚ β•‘ l.91: let box = (new Box(Item(8)) as Box[in Item]) +//β”‚ β•‘ ^^^^^^^ +//β”‚ ╙── Values read through it have no output bound more specific than 'Any'. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ box = Box(Item(8)) + +// Explicit input and output parts can both be written on the same argument. +:expect 9 +class Item(val value: Int) +let xs = (mut [] as Array[in Item out Item]) +xs.push(Item(9)) +xs.0.value +//β”‚ = 9 +//β”‚ xs = [Item(9)] + +// Recursive covariant aliases retain the source argument's lexical environment. +:expect 10 +type Tree[out A] = Node[A] | Empty +object Empty +data class Node[out A](item: A, tail: Tree[A]) +class Item(val value: Int) +private fun item[A](node: Node[A]): A = node.item +item(Node(Item(10), Node(Item(11), Empty))).value +//β”‚ = 10 diff --git a/hkmc2/shared/src/test/mlscript/newres/TypeGraphPartialAliases.mls b/hkmc2/shared/src/test/mlscript/newres/TypeGraphPartialAliases.mls new file mode 100644 index 0000000000..57e94d8075 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/TypeGraphPartialAliases.mls @@ -0,0 +1,49 @@ +:. + +// Supplying the unused formal allows eager alias normalization to reach a fixed point. +type First[A, B] = A +type Chain[A] = {value: A, next: Chain[First[A, Int]]} +private fun walk[A](chain: Chain[A], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () + +// Omitting that same unused formal must preserve the finite graph. +type First[A, B] = A +type Chain[A] = {value: A, next: Chain[First[A]]} +private fun walk[A](chain: Chain[A], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () + +// A fully supplied forwarding alias may itself contain a partial application. +type First[A, B] = A +type Forward[A] = First[A] +type Chain[A] = {value: A, next: Chain[Forward[A]]} +private fun walk[A](chain: Chain[A], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () + +// Partial projections must also permit a finite argument permutation. +type First[A, B, C] = A +type Chain[A, B] = {value: A, next: Chain[First[B, A], First[A, B]]} +private fun walk[A, B](chain: Chain[A, B], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () + +// A fixed omitted argument is a reset with a stable source hole. +type Identity[A] = A +type Chain[A] = {value: A, next: Chain[Identity]} +private fun walk[A](chain: Chain[A], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () + +// Omitting an unused formal on the recursive alias itself already terminates. +type Chain[A, B] = {value: A, next: Chain[A]} +private fun walk[A](chain: Chain[A], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () + +// Nominal recursive fields need not unfold their changing arguments eagerly. +class Chain[A](val value: A, val next: Chain[Array[A]]) +private fun walk[A](chain: Chain[A], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () + +// A relevant omitted formal resets to its source hole, even though another +// argument changes on the recursive edge. Normalization must reuse that hole. +type Second[A, B] = B +type Chain[A] = {value: A, next: Chain[Second[A]]} +private fun walk[A](chain: Chain[A], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () diff --git a/hkmc2/shared/src/test/mlscript/newres/TypeGraphProjectionSites.mls b/hkmc2/shared/src/test/mlscript/newres/TypeGraphProjectionSites.mls new file mode 100644 index 0000000000..29c3e76021 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/TypeGraphProjectionSites.mls @@ -0,0 +1,51 @@ +:. + +// The structural constraint closes a cycle through a by-name getter. +type Link = {next: Link} +class Node with + fun next = this +private fun take(x: Link) = () +take(new Node) + +// An unused type binder must not turn this finite constraint into +// infinitely many instantiations. The source field is a stable projection site. +type Link = {next: Link} +class Node with + fun next[A] = this +private fun take(x: Link) = () +take(new Node) + +// Declared results obey the same finite-site rule. +type Link = {next: Link} +class Node with + fun next[A]: Node = this +private fun take(x: Link) = () +take(new Node) + +// Separate quantified signatures obey the same finite-site rule. +type Link = {next: Link} +class Node with + fun next: [A] -> Node + fun next = this +private fun take(x: Link) = () +take(new Node) + +// Sharing the static projection site must preserve the receivers' ordinary +// activation marks and their distinct class arguments through the recursive edge. +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +type Link[A] = {value: A, next: Link[A]} +class Node[T](val value: T) with + fun next[Unused] = this +private fun read[A](link: Link[A]) = link.next.value +[read(new Node[First](First(1))).first, read(new Node[Second](Second(2))).second] +//β”‚ = [1, 2] + +// A quantified lower bound can itself close the projection cycle. +type Link = {next: Link} +class Node with + fun next: [A restricts Node] -> A + fun next = this +private fun take(x: Link) = () +take(new Node) diff --git a/hkmc2/shared/src/test/mlscript/newres/TypeGraphTermination.mls b/hkmc2/shared/src/test/mlscript/newres/TypeGraphTermination.mls new file mode 100644 index 0000000000..9ee2019aa9 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/TypeGraphTermination.mls @@ -0,0 +1,410 @@ +:. + +// A finite observation of a recursive alias has a finite declared interface. +// It must also preserve the no-repeated-boundary invariant when mark checks are enabled. +type Chain[A] = {value: A, next: Chain[A]} +fun read(chain: Chain[Int]): Int = chain.next.next.value + +// Relating a recursive structural alias must reach a graph fixed point. +type Chain[A] = {value: A, next: Chain[A]} +private fun walk[A](chain: Chain[A], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () + +// This non-regular structural alias receives a regularity diagnostic: +// its value types expand to Array[Array[...]]. It must not overflow during checking. +:e +type Chain[A] = {value: A, next: Chain[Array[A]]} +private fun walk[A](chain: Chain[A], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.16: type Chain[A] = {value: A, next: Chain[Array[A]]} +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive type 'Chain' is not supported by the current regularity check. +//β”‚ β•‘ l.16: type Chain[A] = {value: A, next: Chain[Array[A]]} +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' occurs under a type constructor on a recursive dependency cycle. +//β”‚ β•‘ l.16: type Chain[A] = {value: A, next: Chain[Array[A]]} +//β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'next' has no resolved target +//β”‚ β•‘ l.18: if n > 0 then walk(chain.next, n - 1) else () +//β”‚ ╙── ^^^^^^^^^^ + +// A regular parameter permutation has two interfaces, not an expanding environment. +class First(val first: Int) +class Second(val second: Int) +type Alternating[A, B] = {value: A, next: Alternating[B, A]} +fun read(chain: Alternating[First, Second]): Int = chain.next.value.second + +// Mutually recursive aliases preserve a finite set of declared interfaces. +type Left[A] = {value: A, next: Right[A]} +type Right[A] = {value: A, next: Left[A]} +fun read(chain: Left[Int]): Int = chain.next.next.value + +// An alias captures its enclosing function's parameter, without an alias scope. +:expect [1, 2] +class First(val first: Int) +class Second(val second: Int) +private fun choose[A](value: A) = + type Pair = {left: A, right: A} + let pair = ({left: value, right: value} as Pair) + pair.right +[choose[First](First(1)).first, choose[Second](Second(2)).second] +//β”‚ = [1, 2] + +// A structural callback's inputs and outputs retain the supplied argument. +:expect 3 +class Item(val value: Int) +type Callback[A] = {run: A -> A} +private fun make[A](value: A): Callback[A] = {run: (x: A) => x} +make[Item](Item(0)).run(Item(3)).value +//β”‚ = 3 + +// A member alias must retain the receiver context through its method result. +:expect 4 +class Item(val value: Int) +class Box[A](val value: A) with + type Slot = {item: A} + fun get(): Slot = {item: value} +(new Box[Item](Item(4))).get().item.value +//β”‚ = 4 + +// Resetting an argument to a closed type has a finite structural unfolding. +// The environment retained on Int must not nest the previous alias binding. +type Reset[A] = {value: A, next: Reset[Int]} +fun read(chain: Reset[Str]): Int = chain.next.next.value + +// A fixed compound argument is also independent of the previous alias binding. +type Reset[A] = {value: A, next: Reset[Array[Int]]} +fun read(chain: Reset[Str]): Array[Int] = chain.next.next.value + +// An unused argument cannot make a structurally constant recursive type grow. +type Loop[A] = {value: Int, next: Loop[Array[A]]} +fun read(chain: Loop[Str]): Int = chain.next.next.value + +// A wrapping edge followed by a reset has a finite mutual unfolding. +type Left[A] = {value: A, next: Right[Array[A]]} +type Right[B] = {value: B, next: Left[Str]} +fun read(chain: Left[Int]): Str = chain.next.next.next.value.0 + +// Transparent aliases do not change the recursive argument's type. +type Identity[X] = X +type Chain[A] = {value: A, next: Chain[Identity[A]]} +fun read(chain: Chain[Int]): Int = chain.next.next.value + +// Forward aliases must retain an enclosing binder even before the target is known. +:expect [5, 6] +class First(val first: Int) +class Second(val second: Int) +private fun delayed[A](value: A) = + type Early = Later + let wrapped = ({item: value} as Early) + type Later = {item: A} + wrapped.item +[delayed[First](First(5)).first, delayed[Second](Second(6)).second] +//β”‚ = [5, 6] + +// A nominal annotation must retain outer binders used only by its members. +:expect [7, 8] +class First(val first: Int) +class Second(val second: Int) +private fun local[A](value: A) = + class Local(val item: A) + let box = (new Local(value) as Local) + box.item +[local[First](First(7)).first, local[Second](Second(8)).second] +//β”‚ = [7, 8] + +// A separate signature reached through a member alias keeps its result context. +:expect [9, 10] +class First(val first: Int) +class Second(val second: Int) +class Box[A](val value: A) with + type Slot = {item: A} + type Getter = () -> Slot + fun get: Getter + fun get() = {item: value} +[(new Box[First](First(9))).get().item.first, + (new Box[Second](Second(10))).get().item.second] +//β”‚ = [9, 10] + +// An inherited field retains the supplied argument through both class scopes. +:expect [11, 12] +class First(val first: Int) +class Second(val second: Int) +class Parent[A](val item: A) +class Child[A](item: A) extends Parent[A](item) +private fun read[A](child: Child[A]) = child.item +[read[First](new Child[First](First(11))).first, + read[Second](new Child[Second](Second(12))).second] +//β”‚ = [11, 12] + +// A nominal module annotation introduces no class invocation boundary. +:expect 13 +class Item(val value: Int) +module Items with + val item: Item = Item(13) +private fun read(module items: Items) = items.item +read(Items).value +//β”‚ = 13 + +// Nested forwarding aliases normalize before the recursive environment is stored. +type Identity[X] = X +type Forward[Y] = Identity[Identity[Y]] +type Chain[A] = {value: A, next: Chain[Forward[A]]} +fun read(chain: Chain[Int]): Int = chain.next.next.next.value + +// A forwarding alias can select and permute arguments without nesting its environment. +type FirstOf[X, Y] = X +type SecondOf[X, Y] = Y +class First(val first: Int) +class Second(val second: Int) +type Alternating[A, B] = {value: A, next: Alternating[SecondOf[A, B], FirstOf[A, B]]} +fun read(chain: Alternating[First, Second]): Int = chain.next.value.second + +// An unguarded alias is rejected before unfolding its growing arguments. +:e +type Loop[A] = Loop[Array[A]] +private fun unused(value: Loop[Int]) = () +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.166: type Loop[A] = Loop[Array[A]] +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Loop'. +//β”‚ β•‘ l.166: type Loop[A] = Loop[Array[A]] +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.166: type Loop[A] = Loop[Array[A]] +//β”‚ ╙── ^^^^ + +// An unresolved alias body must keep its argument binding until the target is known. +type Forward[A] = Identity[A] +type Chain[A] = {value: A, next: Chain[Forward[A]]} +fun read(chain: Chain[Int]): Int = chain.next.next.value +type Identity[X] = X + +// The same forwarding normalization is needed for aliases captured by a function. +type Identity[X] = X +private fun local[A](value: A) = + type Chain[B] = {value: B, next: Chain[Identity[B]]} + fun read(chain: Chain[A]): A = chain.next.next.value + value +local[Int](1) +//β”‚ = 1 + +// Repeated unions reach a finite set of components. +type Chain[A] = {value: A, next: Chain[A | Int]} +private fun walk[A](chain: Chain[A], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () + +// Repeated intersections also reach a fixed point. +type Chain[A] = {value: A, next: Chain[A & Int]} +private fun walk[A](chain: Chain[A], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () + +// Alternating Boolean operations need distribution and absorption to saturate. +type Chain[A] = {value: A, next: Chain[(A | Int) & Str]} +private fun walk[A](chain: Chain[A], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () + +// An absorbed constructor does not cause recursive argument growth. +type Chain[A] = {value: A, next: Chain[A | (A & Array[A])]} +private fun walk[A](chain: Chain[A], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () + +// A union does not hide a constructor that survives normalization. +:e +type Chain[A] = {value: A, next: Chain[A | Array[A]]} +private fun unused[A](chain: Chain[A]) = () +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.215: type Chain[A] = {value: A, next: Chain[A | Array[A]]} +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive type 'Chain' is not supported by the current regularity check. +//β”‚ β•‘ l.215: type Chain[A] = {value: A, next: Chain[A | Array[A]]} +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' occurs under a type constructor on a recursive dependency cycle. +//β”‚ β•‘ l.215: type Chain[A] = {value: A, next: Chain[A | Array[A]]} +//β”‚ ╙── ^ + +// Growth through a forwarding alias and a mutual cycle is still non-regular. +:e +type Wrap[A] = Array[A] +type Left[A] = {value: A, next: Right[Wrap[A]]} +type Right[B] = {value: B, next: Left[B]} +private fun unused[A](chain: Left[A]) = () +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.230: type Left[A] = {value: A, next: Right[Wrap[A]]} +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive type 'Right' is not supported by the current regularity check. +//β”‚ β•‘ l.230: type Left[A] = {value: A, next: Right[Wrap[A]]} +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'B' occurs under a type constructor on a recursive dependency cycle. +//β”‚ β•‘ l.231: type Right[B] = {value: B, next: Left[B]} +//β”‚ ╙── ^ + +// Tuple and function constructors also count as growth. +:e +type Chain[A] = {value: A, next: Chain[[A, A]]} +private fun unused[A](chain: Chain[A]) = () +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.245: type Chain[A] = {value: A, next: Chain[[A, A]]} +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive type 'Chain' is not supported by the current regularity check. +//β”‚ β•‘ l.245: type Chain[A] = {value: A, next: Chain[[A, A]]} +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' occurs under a type constructor on a recursive dependency cycle. +//β”‚ β•‘ l.245: type Chain[A] = {value: A, next: Chain[[A, A]]} +//β”‚ ╙── ^ + +:e +type Chain[A] = {value: A, next: Chain[Int -> A]} +private fun unused[A](chain: Chain[A]) = () +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.258: type Chain[A] = {value: A, next: Chain[Int -> A]} +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive type 'Chain' is not supported by the current regularity check. +//β”‚ β•‘ l.258: type Chain[A] = {value: A, next: Chain[Int -> A]} +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' occurs under a type constructor on a recursive dependency cycle. +//β”‚ β•‘ l.258: type Chain[A] = {value: A, next: Chain[Int -> A]} +//β”‚ ╙── ^ + +// A forward alias must be resolved before deciding whether an argument grows. +:e +type Chain[A] = {value: A, next: Chain[Later[A]]} +private fun unused[A](chain: Chain[A]) = () +type Later[A] = Array[A] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.272: type Chain[A] = {value: A, next: Chain[Later[A]]} +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive type 'Chain' is not supported by the current regularity check. +//β”‚ β•‘ l.272: type Chain[A] = {value: A, next: Chain[Later[A]]} +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' occurs under a type constructor on a recursive dependency cycle. +//β”‚ β•‘ l.272: type Chain[A] = {value: A, next: Chain[Later[A]]} +//β”‚ ╙── ^ + +// Boolean combinations can include an omitted argument, which remains a source hole. +type Chain[A] = {value: A, next: Chain[A | Array]} +private fun walk[A](chain: Chain[A], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () + +// A record constructor is growth even when its field is itself a union. +:e +type Chain[A] = {value: A, next: Chain[{item: A | Int}]} +private fun unused[A](chain: Chain[A]) = () +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.292: type Chain[A] = {value: A, next: Chain[{item: A | Int}]} +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive type 'Chain' is not supported by the current regularity check. +//β”‚ β•‘ l.292: type Chain[A] = {value: A, next: Chain[{item: A | Int}]} +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' occurs under a type constructor on a recursive dependency cycle. +//β”‚ β•‘ l.292: type Chain[A] = {value: A, next: Chain[{item: A | Int}]} +//β”‚ ╙── ^ + +// Captured forwarding aliases inside a Boolean argument must also saturate. +type Identity[X] = X +private fun local[A](value: A) = + type Chain[B] = {value: B, next: Chain[B | Identity[B]]} + private fun walk[B](chain: Chain[B], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () + value +local[Int](1) +//β”‚ = 1 + +// Reusing one alias body twice must not hide the second constructor layer. +:e +type Wrap[A] = Array[A] +type Chain[A] = {value: A, next: Chain[Wrap[Wrap[A]]]} +private fun unused[A](chain: Chain[A]) = () +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.317: type Chain[A] = {value: A, next: Chain[Wrap[Wrap[A]]]} +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive type 'Chain' is not supported by the current regularity check. +//β”‚ β•‘ l.317: type Chain[A] = {value: A, next: Chain[Wrap[Wrap[A]]]} +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' occurs under a type constructor on a recursive dependency cycle. +//β”‚ β•‘ l.317: type Chain[A] = {value: A, next: Chain[Wrap[Wrap[A]]]} +//β”‚ ╙── ^ + +// Boolean-only recursion has no structural guard and is rejected. +:e +type Choice[A] = A | Choice[A | Int] +fun choose(value: Choice[Str]): Choice[Str] = value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in alternation +//β”‚ β•‘ l.331: type Choice[A] = A | Choice[A | Int] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Choice'. +//β”‚ β•‘ l.331: type Choice[A] = A | Choice[A | Int] +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.331: type Choice[A] = A | Choice[A | Int] +//β”‚ ╙── ^^^^^^ + +// Growing arguments do not guard Boolean-only alias recursion either. +:e +type Choice[A] = A | Choice[Array[A]] +private fun unused(value: Choice[Int]) = () +//β”‚ ╔══[COMPILATION ERROR] Resolution error in alternation +//β”‚ β•‘ l.345: type Choice[A] = A | Choice[Array[A]] +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Unguarded recursive type alias 'Choice'. +//β”‚ β•‘ l.345: type Choice[A] = A | Choice[Array[A]] +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive references must pass through a record, tuple, function, or nominal type. +//β”‚ β•‘ l.345: type Choice[A] = A | Choice[Array[A]] +//β”‚ ╙── ^^^^^^ + +// Selecting an absent output resets the observable argument to Any. +// The current conservative check rejects this regular case; retain the example +// for the deferred input/output dependency refinement. +:e +type Chain[A] = {value: A, next: Chain[in Array[A]]} +private fun read[A](chain: Chain[A]) = chain.next.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.361: type Chain[A] = {value: A, next: Chain[in Array[A]]} +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive type 'Chain' is not supported by the current regularity check. +//β”‚ β•‘ l.361: type Chain[A] = {value: A, next: Chain[in Array[A]]} +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' occurs under a type constructor on a recursive dependency cycle. +//β”‚ β•‘ l.361: type Chain[A] = {value: A, next: Chain[in Array[A]]} +//β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'value' has no resolved target +//β”‚ β•‘ l.362: private fun read[A](chain: Chain[A]) = chain.next.value +//β”‚ ╙── ^^^^^^^^^^^^^^^^ + +// Absorption across recursive steps can depend on the relationship between arguments. +// After two steps both arguments stabilize, despite a syntactic constructor cycle. +// The current conservative check rejects this regular case too. +:e +type Stable[A, B] = {value: A, next: Stable[A | (B & Array[A]), A]} +private fun walk[A, B](chain: Stable[A, B], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.380: type Stable[A, B] = {value: A, next: Stable[A | (B & Array[A]), A]} +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Recursive type 'Stable' is not supported by the current regularity check. +//β”‚ β•‘ l.380: type Stable[A, B] = {value: A, next: Stable[A | (B & Array[A]), A]} +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' occurs under a type constructor on a recursive dependency cycle. +//β”‚ β•‘ l.380: type Stable[A, B] = {value: A, next: Stable[A | (B & Array[A]), A]} +//β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'next' has no resolved target +//β”‚ β•‘ l.382: if n > 0 then walk(chain.next, n - 1) else () +//β”‚ ╙── ^^^^^^^^^^ + +// A constructor-free output wildcard preserves A and reaches a finite graph. +// Normalizing both argument parts must not retain nested wildcard environments. +type Chain[A] = {value: A, next: Chain[out A]} +private fun walk[A](chain: Chain[A], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () + +// Input-only and two-part wildcards are finite too. Neither part may retain +// the preceding wildcard as an unevaluated binding layer. +type Chain[A] = {value: A, next: Chain[in A]} +private fun walk[A](chain: Chain[A], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () + +type Chain[A, B] = {value: A, next: Chain[in B out A, in A out B]} +private fun walk[A, B](chain: Chain[A, B], n: Int) = + if n > 0 then walk(chain.next, n - 1) else () diff --git a/hkmc2/shared/src/test/mlscript/newres/TypeInterfaceDiagnostics.mls b/hkmc2/shared/src/test/mlscript/newres/TypeInterfaceDiagnostics.mls new file mode 100644 index 0000000000..a04ffc96bc --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/TypeInterfaceDiagnostics.mls @@ -0,0 +1,294 @@ +:. + +// Following an alias must retain the input wildcard's source witness. +:ge +class Item(val value: Int) +type Items[A] = Array[A] +let xs = (mut [] as Items[in Item]) +xs.0.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.8: xs.0.value +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ This type argument specifies only an input bound. +//β”‚ β•‘ l.7: let xs = (mut [] as Items[in Item]) +//β”‚ β•‘ ^^^^^^^ +//β”‚ ╙── Values read through it have no output bound more specific than 'Any'. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ xs = [] + +// Boolean normalization must retain why the output became unrestricted. +:ge +class Item(val value: Int) +type Items[A] = Array[A | Int] +let xs = (mut [] as Items[in Item]) +xs.0.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.25: xs.0.value +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ This type argument specifies only an input bound. +//β”‚ β•‘ l.24: let xs = (mut [] as Items[in Item]) +//β”‚ β•‘ ^^^^^^^ +//β”‚ ╙── Values read through it have no output bound more specific than 'Any'. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ xs = [] + +// A precise intersection must not inherit the discarded top operand's diagnostic. +:expect 3 +class Item(val value: Int) +type Items[A] = Array[A & Item] +let xs = (mut [Item(3)] as Items[in Item]) +xs.0.value +//β”‚ = 3 +//β”‚ xs = [Item(3)] + +// Declaration-site input variance identifies the responsible parameter. +:ge +class Item(val value: Int) +class Box[in T](val item: T) +let box = (new Box(Item(4)) as Box[Item]) +box.item.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.51: box.item.value +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ This type argument supplies only an input bound for 'T'. +//β”‚ β•‘ l.50: let box = (new Box(Item(4)) as Box[Item]) +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'T' is declared contravariant here. +//β”‚ β•‘ l.49: class Box[in T](val item: T) +//β”‚ β•‘ ^ +//β”‚ ╙── Values read through it have no output bound more specific than 'Any'. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ box = Box(Item(4)) + +// Explicit Any does not become an unnamed abstract type in the diagnostic. +:ge +class Item(val value: Int) +let xs = (mut [Item(5)] as Array[Any]) +xs.0.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.70: xs.0.value +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•‘ l.69: let xs = (mut [Item(5)] as Array[Any]) +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Type 'Any' is declared without a member interface. +//β”‚ β•‘ in ../decls/Prelude.mls +//β”‚ β•‘ l.2: declare type Any +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ xs = [Item(5)] + +// An abstract declaration is identified by its name and declaration location. +:ge +declare type Hidden +fun inspect(value: Hidden) = value.member +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.87: fun inspect(value: Hidden) = value.member +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'member' of a value with unknown shape. +//β”‚ β•‘ l.87: fun inspect(value: Hidden) = value.member +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Type 'Hidden' is declared without a member interface. +//β”‚ β•‘ l.86: declare type Hidden +//β”‚ ╙── ^^^^^^ + +// Unsupported singleton interfaces must not be described as abstract declarations. +:ge +fun inspect(value: 1) = value.member +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.100: fun inspect(value: 1) = value.member +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal does not contain member 'member' +//β”‚ β•‘ l.100: fun inspect(value: 1) = value.member +//β”‚ ╙── ^ + +// Negation has a different explanation from an abstract declaration. +:ge +fun inspect(value: ~Int) = value.member +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.110: fun inspect(value: ~Int) = value.member +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'member' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ This negated type does not specify a member interface. +//β”‚ β•‘ l.110: fun inspect(value: ~Int) = value.member +//β”‚ ╙── ^^^^ + +// Calling a value retains the same explanation as selecting its members. +:ge +class Item(val value: Int) +fun invoke(xs: Array[in Item]) = xs.0() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.122: fun invoke(xs: Array[in Item]) = xs.0() +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value with no known interface cannot be called like a function. +//β”‚ β•‘ l.122: fun invoke(xs: Array[in Item]) = xs.0() +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This type argument specifies only an input bound. +//β”‚ β•‘ l.122: fun invoke(xs: Array[in Item]) = xs.0() +//β”‚ β•‘ ^^^^^^^ +//β”‚ ╙── Values read through it have no output bound more specific than 'Any'. + +// Shared argument types must not attribute a different declaration's variance. +:ge +class Item(val value: Int) +class Left[in L](val item: L) +class Right[in R](val item: R) +type Pair[A] = [Left[A], Right[A]] +let pair = ([new Left(Item(1)), new Right(Item(2))] as Pair[Item]) +pair.1.item.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.141: pair.1.item.value +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ This type argument supplies only an input bound for 'R'. +//β”‚ β•‘ l.139: type Pair[A] = [Left[A], Right[A]] +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'R' is declared contravariant here. +//β”‚ β•‘ l.138: class Right[in R](val item: R) +//β”‚ β•‘ ^ +//β”‚ ╙── Values read through it have no output bound more specific than 'Any'. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ pair = [Left(Item(1)), Right(Item(2))] + +// Capturing and returning a typed value preserves the selected parameter's witness. +:ge +class Item(val value: Int) +class Left[in L](val item: L) +class Right[in R](val item: R) +type Pair[A] = [Left[A], Right[A]] +private fun delay[A](value: A) = () => value +let pair = ([new Left(Item(1)), new Right(Item(2))] as Pair[Item]) +delay(pair)().1.item.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.164: delay(pair)().1.item.value +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ This type argument supplies only an input bound for 'R'. +//β”‚ β•‘ l.161: type Pair[A] = [Left[A], Right[A]] +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'R' is declared contravariant here. +//β”‚ β•‘ l.160: class Right[in R](val item: R) +//β”‚ β•‘ ^ +//β”‚ ╙── Values read through it have no output bound more specific than 'Any'. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ pair = [Left(Item(1)), Right(Item(2))] + +// Known nominal interfaces retain the annotation use through aliases and captures. +:ge +class Base +type Alias = Base +fun inspect(value: Alias) = (() => value)().missing +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.183: fun inspect(value: Alias) = (() => value)().missing +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'missing' +//β”‚ β•‘ l.183: fun inspect(value: Alias) = (() => value)().missing +//β”‚ ╙── ^^^^^ + +// A function result identifies the annotation that hides implementation members. +:ge +class Base +fun make(): Base = new Base +make().missing +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.195: make().missing +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'missing' +//β”‚ β•‘ l.194: fun make(): Base = new Base +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Structural interfaces explain which written fields restrict a selection. +:ge +fun inspect(value: {present: Int}) = value.missing +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.206: fun inspect(value: {present: Int}) = value.missing +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Record type does not contain member 'missing' +//β”‚ β•‘ l.206: fun inspect(value: {present: Int}) = value.missing +//β”‚ ╙── ^^^^^^^^^^^^ + +// Calling a nominal value points to the annotation that made it non-callable. +:ge +class Base +fun invoke(value: Base) = value() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.217: fun invoke(value: Base) = value() +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' cannot be called like a function. +//β”‚ β•‘ l.217: fun invoke(value: Base) = value() +//β”‚ ╙── ^^^^ + +// Arity errors on a declared callback identify its function type. +:ge +fun invoke(callback: Int -> Int) = callback() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.227: fun invoke(callback: Int -> Int) = callback() +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function with a declared signature expected 1 argument, but got 0 +//β”‚ β•‘ l.227: fun invoke(callback: Int -> Int) = callback() +//β”‚ ╙── ^^^^^^^^^^ + +// Type argument arity errors retain the same callback signature witness. +:ge +fun invoke(callback: Int -> Int) = callback[Int](0) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in type application +//β”‚ β•‘ l.237: fun invoke(callback: Int -> Int) = callback[Int](0) +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function with a declared signature expected 0 type arguments, but got 1 +//β”‚ β•‘ l.237: fun invoke(callback: Int -> Int) = callback[Int](0) +//β”‚ ╙── ^^^^^^^^^^ + +// A function interface also explains missing members on the callback itself. +:ge +fun inspect(callback: Int -> Int) = callback.missing +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.247: fun inspect(callback: Int -> Int) = callback.missing +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function with a declared signature does not contain member 'missing' +//β”‚ β•‘ l.247: fun inspect(callback: Int -> Int) = callback.missing +//β”‚ ╙── ^^^^^^^^^^ + +// Checked indexing reports the annotation that does not expose an array. +:ge +class Base +fun inspect(value: Base) = value.[0] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in array index +//β”‚ β•‘ l.258: fun inspect(value: Base) = value.[0] +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not support checked array indexing. +//β”‚ β•‘ l.258: fun inspect(value: Base) = value.[0] +//β”‚ β•‘ ^^^^ +//β”‚ ╙── Use '![...]' for a dynamic access. + +// An annotated instance cannot be used as a constructor. +:ge +class Base +val instance: Base = new Base +new instance +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.271: new instance +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value with a declared type cannot be instantiated with keyword 'new'. +//β”‚ β•‘ l.270: val instance: Base = new Base +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ instance = Base + +// An alias keeps the annotation use as well as the abstract declaration's location. +:ge +declare type Hidden +type Alias = Hidden +fun inspect(value: Alias) = value.member +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.285: fun inspect(value: Alias) = value.member +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'member' of a value with unknown shape. +//β”‚ β•‘ l.285: fun inspect(value: Alias) = value.member +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Type 'Hidden' is declared without a member interface. +//β”‚ β•‘ l.283: declare type Hidden +//β”‚ ╙── ^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/newres/TypeResolution.mls b/hkmc2/shared/src/test/mlscript/newres/TypeResolution.mls new file mode 100644 index 0000000000..7fc94d6ad2 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/TypeResolution.mls @@ -0,0 +1,60 @@ +:. + +// Wildcard lookup waits for the module's type members. +open Types +fun double(n: Scalar): Scalar = n + n +module Types with + type Scalar = Int +double(21) +//β”‚ = 42 + +// Explicit bindings take precedence over wildcard type names. +open OtherTypes +type Scalar = Int +module OtherTypes with + type Scalar = Str +fun explicit(n: Scalar): Scalar = n +explicit(42) +//β”‚ = 42 + +// An unused signature still diagnoses ambiguous wildcard type names. +:ge +open LeftTypes +open RightTypes +module LeftTypes with + type Ambiguous = Int +module RightTypes with + type Ambiguous = Str +fun ambiguous(n: Ambiguous) = n +//β”‚ ╔══[COMPILATION ERROR] Resolution error in wildcard-open reference +//β”‚ β•‘ l.28: fun ambiguous(n: Ambiguous) = n +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This type is ambiguous, as it has multiple resolved targets +//β”‚ β•‘ l.28: fun ambiguous(n: Ambiguous) = n +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ candidate: type alias +//β”‚ β•‘ l.27: type Ambiguous = Str +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ candidate: type alias +//β”‚ β•‘ l.25: type Ambiguous = Int +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^ + +// A type annotation selects the class even when its name also has a value. +class Box +fun Box() = 7 +fun identity(box: Box): Box = box +identity(new Box) is Box +//β”‚ = true + +// Forward aliases are checked even when the annotated function is unused. +:ge +fun unused(value: Invalid) = value +type Invalid = notAType +val notAType = 1 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in member reference +//β”‚ β•‘ l.52: type Invalid = notAType +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value symbol 'notAType' cannot be used as a type +//β”‚ β•‘ l.53: val notAType = 1 +//β”‚ ╙── ^^^^^^^^^^^^^^^^ +//β”‚ notAType = 1 diff --git a/hkmc2/shared/src/test/mlscript/newres/ValCtxSens.mls b/hkmc2/shared/src/test/mlscript/newres/ValCtxSens.mls new file mode 100644 index 0000000000..9e9ddb9fc7 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ValCtxSens.mls @@ -0,0 +1,123 @@ +:. + + +:sr +module A { + val u = 1 +} +module B { + val u = 2 +} +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ module A⁰::AΒΉ { val u⁰::uΒΉ β€Ήflow:u⁰› = 1 }, module B⁰::BΒΉ { val uΒ²::uΒ³ β€Ήflow:uΒΉβ€Ί = 2 } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + + +val foo = x => x +//β”‚ foo = fun + +:sr +foo(A).u +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ foo⁰(A⁰)β€Ήapp⁰›.u⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 1 + +foo(A).u +//β”‚ = 1 + +// * We now preserve context sensitivity +:sr +:expect 2 +foo(B).u +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ foo⁰(B⁰)β€ΉappΒΉβ€Ί.uΒ² +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 2 + +:expect 1 +foo(A).u +//β”‚ = 1 + +:expect 2 +foo(B).u +//β”‚ = 2 + + +// Capturing the function in a value alias still loses context sensitivity, as in CtxSens.mls. +val alias = foo +//β”‚ alias = fun + +// FIXME: Alias capture loses the activation of the exposed lambda. +:fixme +:expect 2 +alias(B).u +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.54: alias(B).u +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'u' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.16: val foo = x => x +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'alias' is exposed by this compilation unit. +//β”‚ β•‘ l.48: val alias = foo +//β”‚ ╙── ^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' + +:todo // The captured reference to foo is shared by both calls of alias. +:sr +alias(A).u +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.70: alias(A).u +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'u' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.16: val foo = x => x +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'alias' is exposed by this compilation unit. +//β”‚ β•‘ l.48: val alias = foo +//β”‚ ╙── ^^^^^^^^^^^^^^^ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ alias⁰(A⁰)β€ΉappΒ²β€Ί.uβ€Ήu⁰, uΒ²β€Ί +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + + +// Returning a captured value +val captured = x => A +//β”‚ captured = fun + +:expect 1 +captured(B).u +//β”‚ = 1 + +:expect 1 +captured(A).u +//β”‚ = 1 + + +// :de +val foo = case x then x +//β”‚ foo = fun + +// :de +:sr +foo(A).u +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fooΒΉ(A⁰)β€ΉappΒ³β€Ί.u⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 1 + +foo(A).u +//β”‚ = 1 + +// * We now preserve context sensitivity +:sr +foo(B).u +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ fooΒΉ(B⁰)β€Ήapp⁴›.uΒ² +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 2 + + diff --git a/hkmc2/shared/src/test/mlscript/newres/ValueForms.mls b/hkmc2/shared/src/test/mlscript/newres/ValueForms.mls new file mode 100644 index 0000000000..b628d99ea8 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/ValueForms.mls @@ -0,0 +1,63 @@ +:qq +:. + +// Quotation constructs syntax; it does not return the quoted integer. +:expect Lit(42) +let syntaxValue = `42 +syntaxValue +//β”‚ = Lit(42) +//β”‚ syntaxValue = Lit(42) + +// Splices supply syntax to the enclosing quotation. +:expect Lam([Symbol("x")], Ref(Symbol("x"))) +x `=> x +//β”‚ = Lam([Symbol("x")], Ref(Symbol("x"))) + +// Unknown syntax interfaces must not silently authorize static selections. +:e +let syntaxValue = `42 +syntaxValue.answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.19: syntaxValue.answer +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'answer' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ The shape of this value is unknown. +//β”‚ β•‘ l.18: let syntaxValue = `42 +//β”‚ ╙── ^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ syntaxValue = Lit(42) + +// Type syntax is not a runtime value. +:e +let typeValue = dyn +typeValue +//β”‚ ╔══[COMPILATION ERROR] Resolution error in dynamic type +//β”‚ β•‘ l.32: let typeValue = dyn +//β”‚ β•‘ ^^^ +//β”‚ ╙── A type cannot be used as a runtime value. +//β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (dynamic type) +//β”‚ β•‘ l.32: let typeValue = dyn +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ typeValue = undefined + +// Async expression annotations return a promise, not the body's record. +:e +let promise = @annotations.async {answer: 42} +promise.answer +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.47: promise.answer +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Promise' does not contain member 'answer' +//β”‚ β•‘ l.46: let promise = @annotations.async {answer: 42} +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ +//β”‚ ╙── This async annotation wraps the body's result in a promise. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ promise = Promise {} + +// A splice executes while constructing syntax and can return from its function. +:expect 42 +fun returnedFromSplice() = + `let quotedValue = (return {answer: 42}) `in quotedValue +returnedFromSplice().answer +//β”‚ = 42 diff --git a/hkmc2/shared/src/test/mlscript/newres/VarianceAliases.mls b/hkmc2/shared/src/test/mlscript/newres/VarianceAliases.mls new file mode 100644 index 0000000000..b0b536ce33 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/VarianceAliases.mls @@ -0,0 +1,105 @@ +:. + +// Identity aliases preserve a negative occurrence, including nested aliases. +// The callback must handle Base, just as it would for a direct T -> Int domain. +:ge +class Base +class Child(val value: Int) extends Base +type Identity[A] = A +type Callback[A] = Identity[Identity[A]] -> Int +class Receiver[T](val value: T) with + fun use(f: Callback[T]): Int = f(value) +let receiver = (new Receiver[Base](new Base) as Receiver[in Child out Base]) +receiver.use(x => x.value) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.13: receiver.use(x => x.value) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'value' +//β”‚ β•‘ l.12: let receiver = (new Receiver[Base](new Base) as Receiver[in Child out Base]) +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ receiver = Receiver(Base) + +// A negative nominal argument has the same bounds through a forward alias. +:ge +class Base +class Child(val value: Int) extends Base +class Receiver[T](val value: T) with + fun accept(xs: Items[T]): () = + xs.push(value) + () +type Items[A] = Array[Identity[A]] +type Identity[A] = A +let receiver = (new Receiver[Base](new Base) as Receiver[in Child out Base]) +let xs = mut [Child(1)] +receiver.accept(xs) +xs.1.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.36: xs.1.value +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'value' +//β”‚ β•‘ l.33: let receiver = (new Receiver[Base](new Base) as Receiver[in Child out Base]) +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ receiver = Receiver(Base) +//β”‚ xs = [Child(1), Base] + +// Captured and local alias binders must see the same composed polarity. +:ge +class Base +class Child(val value: Int) extends Base +class Receiver[T](val value: T) with + type Callbacks[A] = {direct: T -> Int, indirect: A -> Int} + fun use(callbacks: Callbacks[T]): Int = + callbacks.direct(value) + callbacks.indirect(value) +let receiver = (new Receiver[Base](new Base) as Receiver[in Child out Base]) +receiver.use({direct: x => x.value, indirect: x => x.value}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.56: receiver.use({direct: x => x.value, indirect: x => x.value}) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'value' +//β”‚ β•‘ l.55: let receiver = (new Receiver[Base](new Base) as Receiver[in Child out Base]) +//β”‚ ╙── ^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.56: receiver.use({direct: x => x.value, indirect: x => x.value}) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'value' +//β”‚ β•‘ l.55: let receiver = (new Receiver[Base](new Base) as Receiver[in Child out Base]) +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ receiver = Receiver(Base) + +// Valid callbacks retain the same behavior after alias expansion. +:expect 7 +class Base +class Child(val value: Int) extends Base +type Identity[A] = A +type Callback[A] = Identity[Identity[A]] -> Int +class Receiver[T](val value: T) with + fun use(f: Callback[T]): Int = f(value) +let receiver = (new Receiver[Base](new Base) as Receiver[in Child out Base]) +receiver.use(x => 7) +//β”‚ = 7 +//β”‚ receiver = Receiver(Base) + +// A covariant declaration keeps the output projection of the nested argument. +:expect 8 +class Base +class Child(val value: Int) extends Base +class Box[out T](val item: T) +class Holder[T](val box: Box[T]) +let holder = (new Holder[Child](new Box[Child](Child(8))) as Holder[out Child]) +holder.box.item.value +//β”‚ = 8 +//β”‚ holder = Holder(Box(Child(8))) + +// A written output wildcard overrides declaration-site contravariance. +:expect 9 +class Base +class Child(val value: Int) extends Base +class Box[in T](val item: T) +class Holder[T](val box: Box[out T]) +let holder = (new Holder[Child](new Box[out Child](Child(9))) as Holder[out Child]) +holder.box.item.value +//β”‚ = 9 +//β”‚ holder = Holder(Box(Child(9))) diff --git a/hkmc2/shared/src/test/mlscript/newres/VarianceSoundness.mls b/hkmc2/shared/src/test/mlscript/newres/VarianceSoundness.mls new file mode 100644 index 0000000000..1dc8e13c27 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/VarianceSoundness.mls @@ -0,0 +1,95 @@ +:. + +// The receiver can insert a Base. Its nested invariant argument must propagate +// that possibility back to the array instead of preserving an all-Child view. +:ge +class Base +class Child(val value: Int) extends Base +class Receiver[T](val value: T) with + fun accept(xs: Array[T]): () = + xs.push(value) + () +let receiver = (new Receiver[Base](new Base) as Receiver[in Child out Base]) +let xs = mut [Child(1)] +receiver.accept(xs) +xs.1.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.15: xs.1.value +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'value' +//β”‚ β•‘ l.12: let receiver = (new Receiver[Base](new Base) as Receiver[in Child out Base]) +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ receiver = Receiver(Base) +//β”‚ xs = [Child(1), Base] + +// A positive invariant argument must retain the holder's input bound too. +// The actual cell calls its argument on a Base, so a Child-only callback is unsafe. +:ge +class Base +class Child(val value: Int) extends Base +class Cell[T](val consume: T -> Int) with + fun put(value: T): Int = consume(value) +class Holder[T](val cell: Cell[T]) +let holder = (new Holder[Base -> Int](new Cell[Base -> Int](f => f(new Base))) + as Holder[in (Base -> Int) out (Child -> Int)]) +holder.cell.put(x => x.value) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.36: holder.cell.put(x => x.value) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'value' +//β”‚ β•‘ l.35: as Holder[in (Base -> Int) out (Child -> Int)]) +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ holder = Holder(Cell(fun)) + +// A declaration-site input annotation composes polarity just like written in. +:ge +class Base +class Child(val value: Int) extends Base +class Sink[in T](val consume: T -> Int) with + fun put(value: T): Int = consume(value) +class Holder[T](val sink: Sink[T]) +let holder = (new Holder[Base -> Int](new Sink[Base -> Int](f => f(new Base))) + as Holder[in (Base -> Int) out (Child -> Int)]) +holder.sink.put(x => x.value) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.55: holder.sink.put(x => x.value) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'value' +//β”‚ β•‘ l.54: as Holder[in (Base -> Int) out (Child -> Int)]) +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ holder = Holder(Sink(fun)) + +// A callback that handles every Base remains valid through both nested views. +:expect [3, 4] +class Base +class Child(val value: Int) extends Base +class Cell[T](val consume: T -> Int) with + fun put(value: T): Int = consume(value) +class Sink[in T](val consume: T -> Int) with + fun put(value: T): Int = consume(value) +class Holder[T](val cell: Cell[T], val sink: Sink[T]) +let holder = (new Holder[Base -> Int]( + new Cell[Base -> Int](f => f(new Base)), new Sink[Base -> Int](f => f(new Base))) + as Holder[in (Base -> Int) out (Child -> Int)]) +[holder.cell.put(x => 3), holder.sink.put(x => 4)] +//β”‚ = [3, 4] +//β”‚ holder = Holder(Cell(fun), Sink(fun)) + +// Resolution honors the supplied Child interface. Rejecting the incompatible +// array argument is the type checker's job, covered in InvalMLVarianceSubstitution. +// With type checking disabled, execution still inserts a Base into this array. +class Base +class Child(val value: Int) extends Base +class Receiver[T](val value: T) with + fun accept(xs: Array[T]): () = + xs.push(value) + () +let receiver = (new Receiver[Base](new Base) as Receiver[in Child out Base]) +let xs = (mut [Child(5)] as Array[Child]) +receiver.accept(xs) +xs.1.value +//β”‚ receiver = Receiver(Base) +//β”‚ xs = [Child(5), Base] diff --git a/hkmc2/shared/src/test/mlscript/newres/VarianceSubstitution.mls b/hkmc2/shared/src/test/mlscript/newres/VarianceSubstitution.mls new file mode 100644 index 0000000000..a61be23b48 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/VarianceSubstitution.mls @@ -0,0 +1,163 @@ +:. + +// The callback receives Sink[in (Base -> Int) out (Child -> Int)]. Its put +// input uses the lower bound, so the supplied function must handle every Base. +:ge +class Base +class Child(val value: Int) extends Base +class Sink[T] with + fun put(value: T): () = () +class Producer[T] with + fun use(f: Sink[T] -> Int): Int = f(new Sink[T]) +let producer = (new Producer as Producer[in (Base -> Int) out (Child -> Int)]) +producer.use(sink => + sink.put(x => x.value) + 1 +) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.14: sink.put(x => x.value) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'value' +//β”‚ β•‘ l.12: let producer = (new Producer as Producer[in (Base -> Int) out (Child -> Int)]) +//β”‚ ╙── ^^^^ +//β”‚ = 1 +//β”‚ producer = Producer + +// The projected input is Box[in Base out Child]. The input part contributes +// Base to the inferred box argument, so its item cannot retain a Child-only view. +:ge +class Base +class Child(val value: Int) extends Base +class Box[T](val item: T) +class Receiver[T] with + fun accept(box: Box[T]): () = () +let receiver = (new Receiver as Receiver[in Child out Base]) +let box = Box(Child(2)) +receiver.accept(box) +box.item.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.37: box.item.value +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'value' +//β”‚ β•‘ l.34: let receiver = (new Receiver as Receiver[in Child out Base]) +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ box = Box(Child(2)) +//β”‚ receiver = Receiver + +// Control: an explicitly fixed argument gives the callback Child. +:expect 3 +class Base +class Child(val value: Int) extends Base +class Sink[T] with + fun put(value: T): () = () +class Producer with + fun use(f: Sink[Child -> Int] -> Int): Int = f(new Sink[Child -> Int]) +let producer = new Producer +producer.use(sink => + sink.put(x => x.value) + 3 +) +//β”‚ = 3 +//β”‚ producer = Producer + +// Control: an explicitly fixed argument preserves Box[Child]. +:expect 4 +class Base +class Child(val value: Int) extends Base +class Box[T](val item: T) +class Receiver with + fun accept(box: Box[Child]): () = () +let receiver = new Receiver +let box = Box(Child(4)) +receiver.accept(box) +box.item.value +//β”‚ = 4 +//β”‚ box = Box(Child(4)) +//β”‚ receiver = Receiver + +// Tuple fields preserve the domain polarity, including both nominal argument parts. +:ge +class Base +class Child(val value: Int) extends Base +class Box[T](val item: T) +class Receiver[T] with + fun accept(boxes: [Box[T]]): () = () +let receiver = (new Receiver as Receiver[in Child out Base]) +let box = Box(Child(5)) +receiver.accept([box]) +box.item.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.89: box.item.value +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'value' +//β”‚ β•‘ l.86: let receiver = (new Receiver as Receiver[in Child out Base]) +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ box = Box(Child(5)) +//β”‚ receiver = Receiver + +// Structural fields preserve the domain polarity and the nested input bound. +:ge +class Base +class Child(val value: Int) extends Base +class Box[T](val item: T) +class Receiver[T] with + fun accept(record: {item: Box[T]}): () = () +let receiver = (new Receiver as Receiver[in Child out Base]) +let box = Box(Child(6)) +receiver.accept({item: box}) +box.item.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.110: box.item.value +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'value' +//β”‚ β•‘ l.107: let receiver = (new Receiver as Receiver[in Child out Base]) +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ box = Box(Child(6)) +//β”‚ receiver = Receiver + +// Declaration-site contravariance reverses polarity and selects Base -> Int. +:ge +class Base +class Child(val value: Int) extends Base +class Sink[in T] with + fun put(value: T): () = () +class Producer[T] with + fun use(f: Sink[T] -> Int): Int = f(new Sink[T]) +let producer = (new Producer as Producer[in (Base -> Int) out (Child -> Int)]) +producer.use(sink => + sink.put(x => x.value) + 7 +) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.131: sink.put(x => x.value) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'value' +//β”‚ β•‘ l.129: let producer = (new Producer as Producer[in (Base -> Int) out (Child -> Int)]) +//β”‚ ╙── ^^^^ +//β”‚ = 7 +//β”‚ producer = Producer + +// A written wildcard input reverses the member input polarity: T selects Base. +// Its input constraint contributes Base to the argument, invalidating .value. +:ge +class Base +class Child(val value: Int) extends Base +class Box[T](val item: T) +class Receiver[T] with + fun accept(box: Box[in T]): () = () +let receiver = (new Receiver as Receiver[in Child out Base]) +let box = Box(Child(8)) +receiver.accept(box) +box.item.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.154: box.item.value +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Base' does not contain member 'value' +//β”‚ β•‘ l.151: let receiver = (new Receiver as Receiver[in Child out Base]) +//β”‚ ╙── ^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ box = Box(Child(8)) +//β”‚ receiver = Receiver diff --git a/hkmc2/shared/src/test/mlscript/newres/loose/.mls b/hkmc2/shared/src/test/mlscript/newres/loose/.mls new file mode 100644 index 0000000000..7e0f4b5167 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/loose/.mls @@ -0,0 +1,3 @@ +:.. +#lang(strictResolution: false) +#lang(eagerResolution: false) diff --git a/hkmc2/shared/src/test/mlscript/newres/loose/Basics.mls b/hkmc2/shared/src/test/mlscript/newres/loose/Basics.mls new file mode 100644 index 0000000000..98601f1337 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/loose/Basics.mls @@ -0,0 +1,39 @@ +:. + + +:sr +module A { + val u = 1 +} +module B { + val u = 2 +} +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ module A⁰::AΒΉ { val u⁰::uΒΉ β€Ήflow:u⁰› = 1 }, module B⁰::BΒΉ { val uΒ²::uΒ³ β€Ήflow:uΒΉβ€Ί = 2 } +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +:sr +let m = A +m.u +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let m⁰, m⁰ = A⁰, m⁰.u⁰ +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 1 +//β”‚ m = class A { u: 1 } + +:sr +let m = if hide(true) then A else B +m.u +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Resolved |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let mΒΉ, +//β”‚ mΒΉ = if { +//β”‚ let scrut⁰ = Predef⁰.hide⁰(true)β€Ήapp⁰›; +//β”‚ scrut⁰ is true then A⁰; +//β”‚ else B⁰ +//β”‚ }, +//β”‚ mΒΉ.uβ€ΉuΒ², u⁰› +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = 1 +//β”‚ m = class A { u: 1 } + + diff --git a/hkmc2/shared/src/test/mlscript/newres/loose/BuiltinFunction.mls b/hkmc2/shared/src/test/mlscript/newres/loose/BuiltinFunction.mls new file mode 100644 index 0000000000..233956ef22 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/loose/BuiltinFunction.mls @@ -0,0 +1,24 @@ +:. + +// Explicit Function annotations check erased callbacks even without strict exports. +:ge +private fun discard(f: Function) = () +discard(x => x.value) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.6: discard(x => x.value) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.6: discard(x => x.value) +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•‘ l.5: private fun discard(f: Function) = () +//β”‚ β•‘ ^^^^^^^^ +//β”‚ ╙── The 'Function' type does not specify parameter types. + +// A callback's own annotation still supplies the interface used by its body. +:expect 42 +class Item(val value: Int) +private fun invoke(f: Function) = f(Item(42)) +invoke((x: Item) => x.value) +//β”‚ = 42 + diff --git a/hkmc2/shared/src/test/mlscript/newres/loose/InterfaceExposure.mls b/hkmc2/shared/src/test/mlscript/newres/loose/InterfaceExposure.mls new file mode 100644 index 0000000000..440b0bfe75 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/loose/InterfaceExposure.mls @@ -0,0 +1,75 @@ +:. + +// Non-strict worksheet blocks retain local inference for public definitions. +:expect 1 +fun select(x) = x.a +select({a: 1}) +//β”‚ = 1 + +:expect 2 +fun returned() = x => x.a +returned()({a: 2}) +//β”‚ = 2 + +// Type parameters remain opaque independently of visibility and exposure checks. +:e +fun generic[A](x: A) = x.a +generic({a: 1}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.16: fun generic[A](x: A) = x.a +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' of a value with unknown shape. +//β”‚ β•‘ l.16: fun generic[A](x: A) = x.a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' does not specify a member interface. +//β”‚ β•‘ l.16: fun generic[A](x: A) = x.a +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:e +private fun generic[A](x: A) = x.a +generic({a: 1}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.30: private fun generic[A](x: A) = x.a +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' of a value with unknown shape. +//β”‚ β•‘ l.30: private fun generic[A](x: A) = x.a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' does not specify a member interface. +//β”‚ β•‘ l.30: private fun generic[A](x: A) = x.a +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:e +class Item(val a: Int) +private fun generic[A](x: A) = x.a +generic[Item](Item(1)) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.45: private fun generic[A](x: A) = x.a +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' of a value with unknown shape. +//β”‚ β•‘ l.45: private fun generic[A](x: A) = x.a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' does not specify a member interface. +//β”‚ β•‘ l.45: private fun generic[A](x: A) = x.a +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Abstract checking must not contaminate results at concrete calling activations. +:expect [3, 4] +fun identity[A](x: A): A = x +[identity(Item(3)).a, identity[Item](Item(4)).a] +//β”‚ = [3, 4] + +// A private generic body is checked even without local instantiations. +:e +private fun unused[A](x: A) = x.a +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.66: private fun unused[A](x: A) = x.a +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'a' of a value with unknown shape. +//β”‚ β•‘ l.66: private fun unused[A](x: A) = x.a +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Type parameter 'A' does not specify a member interface. +//β”‚ β•‘ l.66: private fun unused[A](x: A) = x.a +//β”‚ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/newres/loose/Leniency.mls b/hkmc2/shared/src/test/mlscript/newres/loose/Leniency.mls new file mode 100644 index 0000000000..051ee54bea --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/loose/Leniency.mls @@ -0,0 +1,210 @@ +:. + + +module M1 { val u = 1 } +module M2 { val v = 2 } + + +fun foo(x: M1 | M2) = x.u + +:expect [1, undefined] +fun bar(x, flag) = if flag then x.u else 0 +tuple of + bar(M1, true) + bar(M2, true) +//β”‚ = [1, undefined] + + +// A miss can arrive before the candidate that resolves the selection. +:expect [0, 1] +fun later(x, flag) = if flag then x.u else 0 +[later(M2, false), later(M1, true)] +//β”‚ = [0, 1] + +// Forward declarations can supply the target after the selection is visited. +:expect 3 +fun forward(x: Before | After) = x.u +module Before { val v = 0 } +module After { val u = 3 } +forward(After) +//β”‚ = 3 + +// When no candidate supplies the member, retain both annotation witnesses. +:e +fun missing(x: M1 | M2) = x.absent +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.34: fun missing(x: M1 | M2) = x.absent +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'M1' does not contain member 'absent' +//β”‚ β•‘ l.34: fun missing(x: M1 | M2) = x.absent +//β”‚ β•‘ ^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'M2' does not contain member 'absent' +//β”‚ β•‘ l.34: fun missing(x: M1 | M2) = x.absent +//β”‚ ╙── ^^ + +// Indexed and dynamic selections have targets without nominal member symbols. +:expect 4 +let tuples = if hide(true) then [4] else [] +tuples.0 +//β”‚ = 4 +//β”‚ tuples = [4] + +:expect 5 +let dynamic = if hide(true) then ({value: 5} as dyn) else {other: 0} +dynamic.value +//β”‚ = 5 +//β”‚ dynamic = {value: 5} + +// Deferred misses must still be reported for a wholly unresolved record access. +:e +let records = if hide(true) then {left: 1} else {right: 2} +records.absent +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.61: records.absent +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Record literal does not contain member 'absent' +//β”‚ β•‘ l.60: let records = if hide(true) then {left: 1} else {right: 2} +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Record literal does not contain member 'absent' +//β”‚ β•‘ l.60: let records = if hide(true) then {left: 1} else {right: 2} +//β”‚ ╙── ^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ records = {left: 1} + +// A successful selection in the next worksheet block must not replay old misses. +:expect 1 +foo(M1) +//β”‚ = 1 + +// Resetting the language preset enables leniency without an explicit override. +:expect 1 +#lang(0.3.x) +fun defaultLeniency(x: M2 | M1) = x.u +defaultLeniency(M1) +//β”‚ = 1 + +// Eager checking rejects a missing alternative even with a resolved target. +:e +#lang(eagerResolution: true) +fun eager(x: M1 | M2) = x.u +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.89: fun eager(x: M1 | M2) = x.u +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'M2' does not contain member 'u' +//β”‚ β•‘ l.89: fun eager(x: M1 | M2) = x.u +//β”‚ ╙── ^^ + +// Requiring a unique target is independent of requiring every shape to match. +:expect 1 +#lang(eagerResolution: false, strictResolution: true) +fun unique(x: M1 | M2) = x.u +unique(M1) +//β”‚ = 1 + +:e +module M3 { val u = 3 } +fun ambiguous(x: M1 | M2 | M3) = x.u +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'u' is ambiguous, as it has multiple resolved targets +//β”‚ β•‘ l.106: fun ambiguous(x: M1 | M2 | M3) = x.u +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ target: value 'u' in module 'M3' +//β”‚ β•‘ l.105: module M3 { val u = 3 } +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ target: value 'u' in module 'M1' +//β”‚ β•‘ l.4: module M1 { val u = 1 } +//β”‚ ╙── ^ + + +// Unknown inputs are tolerated when another input resolves the selection. +:expect [0, 1] +#lang(0.3.x) +fun withUnknown(x, flag) = if flag then x.u else 0 +[withUnknown(M2 as Any, false), withUnknown(M1, true)] +//β”‚ = [0, 1] + +// Later calls use the completed selection's target without replaying its misses. +:expect 0 +withUnknown(M2 as Any, false) +//β”‚ = 0 + +// Unresolved unknown inputs keep the path back to the annotation hiding their shape. +:e +private fun discard(f: Function) = () +discard(x => x.value) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.133: discard(x => x.value) +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.133: discard(x => x.value) +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•‘ l.132: private fun discard(f: Function) = () +//β”‚ β•‘ ^^^^^^^^ +//β”‚ ╙── The 'Function' type does not specify parameter types. + +// Preserve both known misses and unknown provenance when neither supplies a target. +:e +fun noCandidate(x) = x.absent +[noCandidate(M2 as Any), noCandidate(M1)] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.147: fun noCandidate(x) = x.absent +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'absent' of a value with unknown shape. +//β”‚ β•‘ l.148: [noCandidate(M2 as Any), noCandidate(M1)] +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Type 'Any' is declared without a member interface. +//β”‚ β•‘ in ../../decls/Prelude.mls +//β”‚ β•‘ l.2: declare type Any +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Module 'M1' does not contain member 'absent' +//β”‚ β•‘ in Leniency.mls:4 +//β”‚ β•‘ l.4: module M1 { val u = 1 } +//β”‚ ╙── ^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A computed key is another source of uncertainty about the selected member. +:expect 6 +let key = hide("value") +let computed = if hide(true) then {value: 6} else {(key): 0} +computed.value +//β”‚ = 6 +//β”‚ computed = {value: 6} +//β”‚ key = "value" + +:e +{(key): 0}.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.175: {(key): 0}.value +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' across a computed key or unknown record spread. +//β”‚ β•Ÿβ”€β”€ This computed key can overwrite the selected member. +//β”‚ β•‘ l.175: {(key): 0}.value +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:e +#lang(eagerResolution: true) +let eagerUnknown = if hide(true) then M1 else (M2 as Any) +eagerUnknown.u +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.188: eagerUnknown.u +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'u' of a value with unknown shape. +//β”‚ β•‘ l.187: let eagerUnknown = if hide(true) then M1 else (M2 as Any) +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Type 'Any' is declared without a member interface. +//β”‚ β•‘ in ../../decls/Prelude.mls +//β”‚ β•‘ l.2: declare type Any +//β”‚ ╙── ^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ eagerUnknown = class M1 { u: 1 } + + +// Compiled selections retain their targets when imports supply new receiver shapes. +:expect [7, 8, 0] +#lang(0.3.x) +import "../../../mlscript-compile/LenientSelections.mls" +[LenientSelections.observed, + LenientSelections.read(LenientSelections.WithValue(8), true), + LenientSelections.read((new LenientSelections.WithoutValue) as Any, false)] +//β”‚ = [7, 8, 0] diff --git a/hkmc2/shared/src/test/mlscript/newres/loose/Overrides.mls b/hkmc2/shared/src/test/mlscript/newres/loose/Overrides.mls new file mode 100644 index 0000000000..876fddeb2a --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/loose/Overrides.mls @@ -0,0 +1,49 @@ +:. + +class Base { open fun foo() = 1 } +class Left extends Base { override fun foo() = 2 } +class Right extends Base { override fun foo() = 3 } + +// Loose mode also retains the common member symbol in the lowered selection. +:sir +:expect [2, 3] +private fun read(x) = x.foo() +[read(new Left), read(new Right)] +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let tmp, tmp1, tmp2, tmp3; +//β”‚ @private +//β”‚ define read⁰ as fun readΒΉ(x) { +//β”‚ return x.foo⁰() +//β”‚ }; +//β”‚ set tmp = new Left⁰(); +//β”‚ set tmp1 = readΒΉ(tmp); +//β”‚ set tmp2 = new Right⁰(); +//β”‚ set tmp3 = readΒΉ(tmp2); +//β”‚ return [tmp1, tmp3] +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = [2, 3] + +// Unrelated targets still use loose mode's dynamic selection. +:sir +:expect [1, 4] +class Other { fun foo() = 4 } +private fun readOther(x) = x.foo() +[readOther(new Base), readOther(new Other)] +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ let tmp, tmp1, tmp2, tmp3; +//β”‚ @private +//β”‚ define readOther⁰ as fun readOtherΒΉ(x) { +//β”‚ return x.fooοΉ–() +//β”‚ }; +//β”‚ define Other⁰ as class OtherΒΉ { +//β”‚ method fooΒΉ = fun fooΒ²() { +//β”‚ return 4 +//β”‚ } +//β”‚ }; +//β”‚ set tmp = new Base⁰(); +//β”‚ set tmp1 = readOtherΒΉ(tmp); +//β”‚ set tmp2 = new OtherΒΉ(); +//β”‚ set tmp3 = readOtherΒΉ(tmp2); +//β”‚ return [tmp1, tmp3] +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ = [1, 4] diff --git a/hkmc2/shared/src/test/mlscript/newres/loose/PublicInterfaces.mls b/hkmc2/shared/src/test/mlscript/newres/loose/PublicInterfaces.mls new file mode 100644 index 0000000000..4a00678180 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/loose/PublicInterfaces.mls @@ -0,0 +1,265 @@ +:. +#lang(strictResolution: true, eagerResolution: false) + +// Local targets cannot discharge the inputs admitted by a public interface. +:e +fun read(x) = x.value +read({value: 1}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.6: fun read(x) = x.value +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.6: fun read(x) = x.value +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'read' is exposed by this compilation unit. +//β”‚ β•‘ l.6: fun read(x) = x.value +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Rest parameters retain the same obligation through tuple element lookup. +:e +fun rest(...xs) = xs.0.value +rest({value: 2}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.22: fun rest(...xs) = xs.0.value +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Rest parameter 'xs' admits elements of unknown shape. +//β”‚ β•‘ l.22: fun rest(...xs) = xs.0.value +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ 'rest' is exposed by this compilation unit. +//β”‚ β•‘ l.22: fun rest(...xs) = xs.0.value +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Returned closures are reachable from the public interface too. +:e +fun escaped() = x => x.value +escaped()({value: 3}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.38: fun escaped() = x => x.value +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.38: fun escaped() = x => x.value +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ This value is returned here. +//β”‚ β•‘ l.38: fun escaped() = x => x.value +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'escaped' is exposed by this compilation unit. +//β”‚ β•‘ l.38: fun escaped() = x => x.value +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A completed private helper must still be checked when it later escapes. +:expect 4 +private fun hidden(x) = x.value +hidden({value: 4}) +//β”‚ = 4 + +:e +val exported = {callback: hidden} +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.57: private fun hidden(x) = x.value +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.57: private fun hidden(x) = x.value +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ This value is stored in this record field. +//β”‚ β•‘ l.62: val exported = {callback: hidden} +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'exported' is exposed by this compilation unit. +//β”‚ β•‘ l.62: val exported = {callback: hidden} +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ exported = {callback: fun hidden} + +// Record spreads preserve the public origin across nested copies. +:e +fun recordSpread(x) = ({...{...x}}).value +recordSpread({value: 5}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.80: fun recordSpread(x) = ({...{...x}}).value +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' across a computed key or unknown record spread. +//β”‚ β•Ÿβ”€β”€ This spread has no known element shape. +//β”‚ β•‘ l.80: fun recordSpread(x) = ({...{...x}}).value +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.80: fun recordSpread(x) = ({...{...x}}).value +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'recordSpread' is exposed by this compilation unit. +//β”‚ β•‘ l.80: fun recordSpread(x) = ({...{...x}}).value +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Tuple spreads preserve it on their unknown elements. +:e +fun tupleSpread(xs) = [...xs].0.value +tupleSpread([{value: 6}]) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.99: fun tupleSpread(xs) = [...xs].0.value +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ This spread has no known element shape. +//β”‚ β•‘ l.99: fun tupleSpread(xs) = [...xs].0.value +//β”‚ β•‘ ^^ +//β”‚ β•Ÿβ”€β”€ Parameter 'xs' admits values of unknown shape. +//β”‚ β•‘ l.99: fun tupleSpread(xs) = [...xs].0.value +//β”‚ β•‘ ^^ +//β”‚ β•Ÿβ”€β”€ 'tupleSpread' is exposed by this compilation unit. +//β”‚ β•‘ l.99: fun tupleSpread(xs) = [...xs].0.value +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Checked indexing carries the origin and its diagnostic path to the result. +:e +fun indexed(xs) = xs.[0].value +indexed([{value: 7}]) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.118: fun indexed(xs) = xs.[0].value +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ This array index has no known element shape. +//β”‚ β•‘ l.118: fun indexed(xs) = xs.[0].value +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Parameter 'xs' admits values of unknown shape. +//β”‚ β•‘ l.118: fun indexed(xs) = xs.[0].value +//β”‚ β•‘ ^^ +//β”‚ β•Ÿβ”€β”€ 'indexed' is exposed by this compilation unit. +//β”‚ β•‘ l.118: fun indexed(xs) = xs.[0].value +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Type substitution must keep the public input distinct from ordinary unknowns. +:e +private fun identity[A](x: A): A = x +fun throughType(x) = identity(x).value +throughType({value: 8}) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.138: fun throughType(x) = identity(x).value +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.138: fun throughType(x) = identity(x).value +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'throughType' is exposed by this compilation unit. +//β”‚ β•‘ l.138: fun throughType(x) = identity(x).value +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•‘ l.137: private fun identity[A](x: A): A = x +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Omitted type arguments admit external values despite a known local element. +:e +fun hole(xs: Array) = xs.0.value +hole([{value: 9}]) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.156: fun hole(xs: Array) = xs.0.value +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'xs' admits values of unknown shape. +//β”‚ β•‘ l.156: fun hole(xs: Array) = xs.0.value +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ 'hole' is exposed by this compilation unit. +//β”‚ β•‘ l.156: fun hole(xs: Array) = xs.0.value +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Constructor refinement must preserve the input's origin on unknown fields. +:e +private class Box(val item) +fun refined(x) = if x is Box(item) then item.value else 0 +refined(Box({value: 10})) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.173: fun refined(x) = if x is Box(item) then item.value else 0 +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ This constructor field may contain a value of unknown shape. +//β”‚ β•‘ l.172: private class Box(val item) +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.173: fun refined(x) = if x is Box(item) then item.value else 0 +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'refined' is exposed by this compilation unit. +//β”‚ β•‘ l.173: fun refined(x) = if x is Box(item) then item.value else 0 +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Private helpers that do not escape retain local inference. +:expect 11 +private fun local(x) = x.value +local({value: 11}) +//β”‚ = 11 + +// Explicitly described alternatives still use lenient missing-member checking. +:expect 12 +module Known { val value = 12 } +module Other { val other = 0 } +fun declared(x: Known | Other) = x.value +declared(Known) +//β”‚ = 12 + +// A dynamic target does not exempt the function's other possible public inputs. +:e +fun dynamic(x) = x.value +dynamic({value: 13} as dyn) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.206: fun dynamic(x) = x.value +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.206: fun dynamic(x) = x.value +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'dynamic' is exposed by this compilation unit. +//β”‚ β•‘ l.206: fun dynamic(x) = x.value +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A tuple target likewise cannot close an unannotated public parameter. +:e +fun first(xs) = xs.0 +first([14]) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.222: fun first(xs) = xs.0 +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member '0' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'xs' admits values of unknown shape. +//β”‚ β•‘ l.222: fun first(xs) = xs.0 +//β”‚ β•‘ ^^ +//β”‚ β•Ÿβ”€β”€ 'first' is exposed by this compilation unit. +//β”‚ β•‘ l.222: fun first(xs) = xs.0 +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// An annotated callback interface must be checked in a non-strict worksheet too. +:e +#lang(strictResolution: false) +private fun erase(f: Function) = () +private fun callback(x) = x.value +callback({value: 15}) +erase(callback) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.240: private fun callback(x) = x.value +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.240: private fun callback(x) = x.value +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•‘ l.239: private fun erase(f: Function) = () +//β”‚ β•‘ ^^^^^^^^ +//β”‚ ╙── The 'Function' type does not specify parameter types. +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Recursive spread widening must not hide public inputs behind a local target. +// FIXME: Preserve public-input provenance in the bounded tuple widening representation. +:breakme +:e +#lang(strictResolution: true) +private fun padded(x, n: Int) = if n == 0 then [x] else [x, ...padded(x, n - 1)] +fun deep(x) = + let value = if hide(true) then padded(x, 4).4 else {value: 0} + value.value +deep({value: 16}) +//β”‚ = 16 diff --git a/hkmc2/shared/src/test/mlscript/newres/loose/SelectionDiagnostics.mls b/hkmc2/shared/src/test/mlscript/newres/loose/SelectionDiagnostics.mls new file mode 100644 index 0000000000..48db40a7ae --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/loose/SelectionDiagnostics.mls @@ -0,0 +1,170 @@ +:. + +module First { val value = 1 } +module Second { val other = 2 } +module Third { val another = 3 } + +// Each nested union component has its own witness in one selection error. +:e +fun nested(x: First | (Second | Third)) = x.absent +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.9: fun nested(x: First | (Second | Third)) = x.absent +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'First' does not contain member 'absent' +//β”‚ β•‘ l.9: fun nested(x: First | (Second | Third)) = x.absent +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Second' does not contain member 'absent' +//β”‚ β•‘ l.9: fun nested(x: First | (Second | Third)) = x.absent +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Third' does not contain member 'absent' +//β”‚ β•‘ l.9: fun nested(x: First | (Second | Third)) = x.absent +//β”‚ ╙── ^^^^^ + +// Following aliases preserves the component's written use, not its definition. +:e +type Left = First +type Right = Second +fun aliases(x: Left | Right) = x.absent +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.27: fun aliases(x: Left | Right) = x.absent +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'First' does not contain member 'absent' +//β”‚ β•‘ l.27: fun aliases(x: Left | Right) = x.absent +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Second' does not contain member 'absent' +//β”‚ β•‘ l.27: fun aliases(x: Left | Right) = x.absent +//β”‚ ╙── ^^^^^ + +// Applied components retain the whole application as their witness. +:e +class Box[A] +fun applied(x: Box[Int] | Box[Str]) = x.absent +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.41: fun applied(x: Box[Int] | Box[Str]) = x.absent +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Box' does not contain member 'absent' +//β”‚ β•‘ l.41: fun applied(x: Box[Int] | Box[Str]) = x.absent +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Box' does not contain member 'absent' +//β”‚ β•‘ l.41: fun applied(x: Box[Int] | Box[Str]) = x.absent +//β”‚ ╙── ^^^^^^^^ + +// A union inside an alias supplies witnesses at its component declarations. +:e +type Alternatives = First | Second +fun aliasedUnion(x: Alternatives) = x.absent +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.55: fun aliasedUnion(x: Alternatives) = x.absent +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'First' does not contain member 'absent' +//β”‚ β•‘ l.54: type Alternatives = First | Second +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Second' does not contain member 'absent' +//β”‚ β•‘ l.54: type Alternatives = First | Second +//β”‚ ╙── ^^^^^^ + +// Uncertain components retain both the use and the declaration explaining them. +:e +declare type Hidden +fun unknowns(x: Any | Hidden) = x.absent +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.69: fun unknowns(x: Any | Hidden) = x.absent +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'absent' of a value with unknown shape. +//β”‚ β•‘ l.69: fun unknowns(x: Any | Hidden) = x.absent +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Type 'Any' is declared without a member interface. +//β”‚ β•‘ in ../../decls/Prelude.mls +//β”‚ β•‘ l.2: declare type Any +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'absent' of a value with unknown shape. +//β”‚ β•‘ in SelectionDiagnostics.mls:68 +//β”‚ β•‘ l.69: fun unknowns(x: Any | Hidden) = x.absent +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Type 'Hidden' is declared without a member interface. +//β”‚ β•‘ l.68: declare type Hidden +//β”‚ ╙── ^^^^^^ + +// Repeated calls must not repeat the same witness within the error. +:e +private fun repeated(x) = x.absent +[repeated(First), repeated(First)] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.90: private fun repeated(x) = x.absent +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Module 'First' does not contain member 'absent' +//β”‚ β•‘ l.3: module First { val value = 1 } +//β”‚ ╙── ^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Two selections retain separate errors, each with both witnesses. +:e +fun separate(x: First | Second) = [x.left, x.right] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.102: fun separate(x: First | Second) = [x.left, x.right] +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'First' does not contain member 'left' +//β”‚ β•‘ l.102: fun separate(x: First | Second) = [x.left, x.right] +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Second' does not contain member 'left' +//β”‚ β•‘ l.102: fun separate(x: First | Second) = [x.left, x.right] +//β”‚ ╙── ^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.102: fun separate(x: First | Second) = [x.left, x.right] +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'First' does not contain member 'right' +//β”‚ β•‘ l.102: fun separate(x: First | Second) = [x.left, x.right] +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Second' does not contain member 'right' +//β”‚ β•‘ l.102: fun separate(x: First | Second) = [x.left, x.right] +//β”‚ ╙── ^^^^^^ + +// Eager checking still combines the misses when another alternative has a target. +:e +#lang(eagerResolution: true) +fun eager(x: First | Second | Third) = x.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.125: fun eager(x: First | Second | Third) = x.value +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Second' does not contain member 'value' +//β”‚ β•‘ l.125: fun eager(x: First | Second | Third) = x.value +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Value of type 'Third' does not contain member 'value' +//β”‚ β•‘ l.125: fun eager(x: First | Second | Third) = x.value +//β”‚ ╙── ^^^^^ + +// Combine deferred misses with the eager public-input obligation when unresolved. +:e +#lang(eagerResolution: false, strictResolution: true) +fun exposed(x) = x.absent +exposed(First) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.139: fun exposed(x) = x.absent +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Module 'First' does not contain member 'absent' +//β”‚ β•‘ l.3: module First { val value = 1 } +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'absent' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.139: fun exposed(x) = x.absent +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'exposed' is exposed by this compilation unit. +//β”‚ β•‘ l.139: fun exposed(x) = x.absent +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A resolved public selection reports the unknown input, not other known misses. +:e +fun resolved(x) = x.value +[resolved(First), resolved(Second)] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.158: fun resolved(x) = x.value +//β”‚ β•‘ ^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Cannot resolve member 'value' of a value with unknown shape. +//β”‚ β•Ÿβ”€β”€ Parameter 'x' admits values of unknown shape. +//β”‚ β•‘ l.158: fun resolved(x) = x.value +//β”‚ β•‘ ^ +//β”‚ β•Ÿβ”€β”€ 'resolved' is exposed by this compilation unit. +//β”‚ β•‘ l.158: fun resolved(x) = x.value +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/newres/loose/Targets.mls b/hkmc2/shared/src/test/mlscript/newres/loose/Targets.mls new file mode 100644 index 0000000000..1fd51a8b33 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/loose/Targets.mls @@ -0,0 +1,81 @@ +:. + +// Both receivers expose the same runtime property. Non-strict resolution keeps +// the property read and call even though their definitions are different. +:expect 1 +module Left { fun get() = 1 } +module Right { fun get() = 2 } +let receiver = if hide(true) then Left else Right +receiver.get() +//β”‚ = 1 +//β”‚ receiver = class Left + +:e +receiver.missing +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.14: receiver.missing +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Module 'Left' does not contain member 'missing' +//β”‚ β•‘ l.6: module Left { fun get() = 1 } +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Module 'Right' does not contain member 'missing' +//β”‚ β•‘ l.7: module Right { fun get() = 2 } +//β”‚ ╙── ^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:e +1() +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.27: 1() +//β”‚ β•‘ ^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be called like a function. +//β”‚ β•‘ l.27: 1() +//β”‚ ╙── ^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Missing names are still errors when used as callees. +:e +missingCallee() +//β”‚ ╔══[COMPILATION ERROR] Name not found: missingCallee +//β”‚ β•‘ l.38: missingCallee() +//β”‚ ╙── ^^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:expect 2 +let writableLeft = mut {value: 0} +let writableRight = mut {value: 1} +let writable = if hide(true) then writableLeft else writableRight +set writable.value = 2 +writable.value +//β”‚ = 2 +//β”‚ writable = {value: 2} +//β”‚ writableLeft = {value: 2} +//β”‚ writableRight = {value: 1} + +// A direct conditional callee retains both possible functions. +:expect 3 +fun first() = 3 +fun second() = 4 +let call = if hide(true) then first else second +call() +//β”‚ = 3 +//β”‚ call = fun first + +:e +fun unknownReceiver(x) = x.missing +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'missing' has no resolved target +//β”‚ β•‘ l.65: fun unknownReceiver(x) = x.missing +//β”‚ ╙── ^^^^^^^^^ + +// Allowing multiple targets does not make class definitions assignable. +:e +module ClassLeft { class Item } +module ClassRight { class Item } +let classes = if hide(true) then ClassLeft else ClassRight +set classes.Item = 0 +//β”‚ ╔══[COMPILATION ERROR] Assignment requires a term member +//β”‚ β•‘ l.75: set classes.Item = 0 +//β”‚ ╙── ^^^^^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ classes = class ClassLeft { Item: class Item } + diff --git a/hkmc2/shared/src/test/mlscript/newres/wasm/.mls b/hkmc2/shared/src/test/mlscript/newres/wasm/.mls new file mode 100644 index 0000000000..9e21925992 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/wasm/.mls @@ -0,0 +1,3 @@ +:.. +:!js +:wasm diff --git a/hkmc2/shared/src/test/mlscript/newres/wasm/BackendFlags.mls b/hkmc2/shared/src/test/mlscript/newres/wasm/BackendFlags.mls new file mode 100644 index 0000000000..a6715ad715 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/wasm/BackendFlags.mls @@ -0,0 +1,17 @@ +:. + +// Empty blocks do not warn; each compiled block warns when both are enabled. +:global +:js + +:w +1 +//β”‚ ═══[WARNING] Enabling both :js and :wasm is not currently supported; disable one before compiling this block. +//β”‚ = 1 +//β”‚ Wasm result: +//β”‚ = 1 + +:!js +2 +//β”‚ Wasm result: +//β”‚ = 2 diff --git a/hkmc2/shared/src/test/mlscript/newres/wasm/DeclaredTypes.mls b/hkmc2/shared/src/test/mlscript/newres/wasm/DeclaredTypes.mls new file mode 100644 index 0000000000..1c8b54042c --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/wasm/DeclaredTypes.mls @@ -0,0 +1,50 @@ +:. + +// An annotated receiver cannot import its constructor parameter's capture context. +class Box(val value) +fun read(box: Box) = box.value +read(Box(42)) +//β”‚ Wasm result: +//β”‚ = 42 + +// A declared field type supplies the next receiver independently of observed calls. +class Item(val value) +class Box(val item: Item) +fun read(box: Box) = box.item.value +read(Box(Item(42))) +//β”‚ Wasm result: +//β”‚ = 42 + +// Function constraints resolve this example; first-class anonymous functions remain unsupported in WASM. +:fixme +class Foo[X](val f: X -> X) +fun test[A](x: Foo[[A, Int]]) = x.f +test(new Foo(arg => [arg.0 + 1, arg.1 + 2]))([1, 2]) +//β”‚ ═══[COMPILATION ERROR] WatBuilder::returningTerm for FunDefn(...) where `!sym.nameIsMeaningful` not implemented yet +//β”‚ ╔══[COMPILATION ERROR] Expected WAT of `fun` expression in Call(...) to have a `(ref )` type +//β”‚ β•‘ l.22: test(new Foo(arg => [arg.0 + 1, arg.1 + 2]))([1, 2]) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// Static tuple projections lower to WASM array accesses. +:expect 42 +let xs = [1, 42] +xs.1 +//β”‚ Wasm result: +//β”‚ = 42 + +// Tuple writes use the same zero-based indexed representation. +:expect 42 +let xs = mut [1, 2] +set xs.0 = 42 +xs.0 +//β”‚ Wasm result: +//β”‚ = 42 + +// Calling a function loaded from a tuple still needs WASM callable representation support. +:fixme +:expect 42 +fun increment(x: Int): Int = x + 1 +[increment].0(41) +//β”‚ ╔══[COMPILATION ERROR] Expected WAT of `fun` expression in Call(...) to have a `(ref )` type +//β”‚ β•‘ l.47: [increment].0(41) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/newres/wasm/Erasure.mls b/hkmc2/shared/src/test/mlscript/newres/wasm/Erasure.mls new file mode 100644 index 0000000000..7fdea1dddf --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/wasm/Erasure.mls @@ -0,0 +1,24 @@ +:. + + + + +// Erasure must prepare an unboxed call target before lowering the caller's body. +:expect 42 +fun caller(): Number = later(wasm.i32.const(41)) +fun later(value: Number): Number = wasm.i32.add(value, wasm.i32.const(1)) +type Number = LaterNumber +type LaterNumber = Int32 +caller() +//β”‚ Wasm result: +//β”‚ = 42 + + +// Forward aliases determine both the constructor slot and the generated field's representation. +:expect 42 +class Box(val value: Number) with + fun get(): Number = value +type Number = Int32 +Box(wasm.i32.const(42)).get() +//β”‚ Wasm result: +//β”‚ = 42 diff --git a/hkmc2/shared/src/test/mlscript/newres/wasm/OverloadedClasses.mls b/hkmc2/shared/src/test/mlscript/newres/wasm/OverloadedClasses.mls new file mode 100644 index 0000000000..9e94ed78ac --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/wasm/OverloadedClasses.mls @@ -0,0 +1,78 @@ +:. + + +// A class constructor and an explicitly overloaded function have distinct identities. +:noInline +:expect 42 +class Factory { + val answer: Int = 21 +} +fun Factory(x: Int): Int = x + 1 +fun originalFactory() = new Factory +type OriginalFactory = Factory +Factory(20) + (new Factory).answer +//β”‚ Wasm result: +//β”‚ = 42 + + +// Imported constructors and term functions remain distinct in later blocks. +:noInline +:expect 43 +Factory(21) + (new Factory).answer +//β”‚ Wasm result: +//β”‚ = 43 + + +// Declaration order and nominal class tests select the same constructor. +:noInline +:expect 42 +fun Reverse(): Int = 40 +class Reverse { val answer: Int = 2 } +Reverse() + (new Reverse).answer +//β”‚ Wasm result: +//β”‚ = 42 + + +:noInline +:expect 1 +if new Reverse is Reverse then 1 else 0 +//β”‚ Wasm result: +//β”‚ = 1 + + +// Duplicated local classes and their function companions keep distinct identities. +:lift +:inlineThreshold 10000 +:expect 45 +fun make(n: Int): Int = { + class Local { val answer: Int = 20 } + fun Local(x: Int): Int = x + 1 + Local(n) + (new Local).answer +} +make(1) + make(2) +//β”‚ Wasm result: +//β”‚ = 45 + + +// A class-only import must not require its old companion function's signature. +:noInline +:expect 99 +fun Factory(): Int = 99 +Factory() +//β”‚ Wasm result: +//β”‚ = 99 + +:inlineThreshold 10000 +:expect 21 +originalFactory().answer +//β”‚ Wasm result: +//β”‚ = 21 + + +// A cast depends on the original class too, without referring to its constructor. +:noInline +:expect 21 +fun checkOriginal(x): OriginalFactory = x +checkOriginal(originalFactory()).answer +//β”‚ Wasm result: +//β”‚ = 21 diff --git a/hkmc2/shared/src/test/mlscript/newres/wasm/Super.mls b/hkmc2/shared/src/test/mlscript/newres/wasm/Super.mls new file mode 100644 index 0000000000..5005cb6608 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/wasm/Super.mls @@ -0,0 +1,99 @@ +:. + +:expect 10 +class Base() { open fun f(x) = x + 1 } +class Child() extends Base() { override fun f(x) = super.f(x + 1) * 2 } +Child().f(3) +//β”‚ Wasm result: +//β”‚ = 10 + +:expect 21 +class Root() { open fun f() = 1 } +class Middle() extends Root() +class Leaf() extends Middle() { override fun f() = 21 } +fun read(x: Root) = x.f() +read(Leaf()) +//β”‚ Wasm result: +//β”‚ = 21 + +:expect 7 +abstract class Abstract() { fun f() } +class Concrete() extends Abstract() { fun f() = 7 } +fun readAbstract(x: Abstract) = x.f() +readAbstract(Concrete()) +//β”‚ Wasm result: +//β”‚ = 7 + +:expect 10 +class GetterBase() { open fun value = 5 } +class GetterChild() extends GetterBase() { override fun value = super.value * 2 } +GetterChild().value +//β”‚ Wasm result: +//β”‚ = 10 + +:expect 9 +class FieldBase() { open val x: Int = 1 } +class FieldChild() extends FieldBase() { override val x: Int = 9 } +fun field(x: FieldBase) = x.x +field(FieldChild()) +//β”‚ Wasm result: +//β”‚ = 9 + +:expect 7 +abstract class AbstractField() { val x: Int } +class ConcreteField() extends AbstractField() { val x: Int = 7 } +fun abstractField(x: AbstractField) = x.x +abstractField(ConcreteField()) +//β”‚ Wasm result: +//β”‚ = 7 + +:lift +:inlineThreshold 10000 +:expect 10 +fun local() = { + class Base() { open fun f(x) = x + 1 } + class Child() extends Base() { override fun f(x) = super.f(x) * 2 } + Child().f(4) +} +local() +//β”‚ Wasm result: +//β”‚ = 10 + +:expect 12 +abstract class WideField() { val value: Any } +class NarrowField() extends WideField() { val value: Int = 12 } +fun readNarrow(x: NarrowField): Int = x.value +readNarrow(NarrowField()) +//β”‚ Wasm result: +//β”‚ = 12 + +:wat +:expect 9 +FieldChild().x +//β”‚ Wat: +//β”‚ (module +//β”‚ (type $TypeInfoBase (sub (struct (field $$tag i32) (field $$parent (ref null $TypeInfoBase))))) +//β”‚ (type $Object (sub (struct (field $$typeinfo (mut (ref $TypeInfoBase)))))) +//β”‚ (type $FieldBase (sub $Object (struct (field $$typeinfo (mut (ref $TypeInfoBase))) (field $x (mut (ref null i31)))))) +//β”‚ (type $FieldChild (sub $FieldBase (struct (field $$typeinfo (mut (ref $TypeInfoBase))) (field $x (mut (ref null i31)))))) +//β”‚ (type $FieldBase_typeinfo (sub $TypeInfoBase (struct (field $$tag i32) (field $$parent (ref null $TypeInfoBase))))) +//β”‚ (type $FieldBase_FieldBase (func (result (ref $FieldBase)))) +//β”‚ (type $FieldChild_typeinfo (sub $FieldBase_typeinfo (struct (field $$tag i32) (field $$parent (ref null $TypeInfoBase))))) +//β”‚ (type $FieldChild_FieldChild (func (result (ref $FieldChild)))) +//β”‚ (type $entry (func (result (ref null i31)))) +//β”‚ (import "repl" "FieldBase" (func $FieldBase_FieldBase (type $FieldBase_FieldBase))) +//β”‚ (import "repl" "FieldChild" (func $FieldChild_FieldChild (type $FieldChild_FieldChild))) +//β”‚ (import "repl" "FieldBase_typeinfo" (global $FieldBase_typeinfo (ref $FieldBase_typeinfo))) +//β”‚ (import "repl" "FieldChild_typeinfo" (global $FieldChild_typeinfo (ref $FieldChild_typeinfo))) +//β”‚ (func $entry (export "entry") (type $entry) (result (ref null i31)) +//β”‚ (local $tmp (ref null any)) +//β”‚ (block (result (ref null i31)) +//β”‚ (local.set $tmp +//β”‚ (call $FieldChild_FieldChild)) +//β”‚ (return +//β”‚ (struct.get $FieldChild $x +//β”‚ (ref.cast (ref $FieldChild) +//β”‚ (local.get $tmp)))))) +//β”‚ (elem $entry declare func $entry)) +//β”‚ Wasm result: +//β”‚ = 9 diff --git a/hkmc2/shared/src/test/mlscript/newres/wasm/WasmBasics.mls b/hkmc2/shared/src/test/mlscript/newres/wasm/WasmBasics.mls new file mode 100644 index 0000000000..8d6333fd71 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/wasm/WasmBasics.mls @@ -0,0 +1,11 @@ +:. + + + +// WASM lowering emits target-specific intrinsics; do not execute its IR as JS. +:expect 3 +1 + 2 +//β”‚ Wasm result: +//β”‚ = 3 + + diff --git a/hkmc2/shared/src/test/mlscript/newres/wasm/WasmMethodResolution.mls b/hkmc2/shared/src/test/mlscript/newres/wasm/WasmMethodResolution.mls new file mode 100644 index 0000000000..8b089e1106 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/wasm/WasmMethodResolution.mls @@ -0,0 +1,39 @@ +:. + + + + +:expect 3 +class A(val x) { fun get() = x } +let a = A(1) +a.get() + A(2).get() +//β”‚ Wasm result: +//β”‚ = 3 + + +:expect 1 +class A(val x) with + fun get() = x +let a = A(1) +a.A#get() +//β”‚ Wasm result: +//β”‚ = 1 + + +:expect 4 +class B(val x) { fun get() = x } +let b = B(1) +b.B#get() + B(2).B#get() + b.get() +//β”‚ Wasm result: +//β”‚ = 4 + +// Resolution succeeds; WASM does not yet support the generated anonymous function. +:fixme +:expect 6 +class Add(val x) { fun add(y) = x + y } +val add = Add::add +add(Add(2), 4) +//β”‚ ═══[COMPILATION ERROR] WatBuilder::returningTerm for FunDefn(...) where `!sym.nameIsMeaningful` not implemented yet +//β”‚ ╔══[COMPILATION ERROR] Expected static function reference in Call(...) expression +//β”‚ β•‘ l.35: add(Add(2), 4) +//β”‚ ╙── ^^^ diff --git a/hkmc2/shared/src/test/mlscript/nofib/.mls b/hkmc2/shared/src/test/mlscript/nofib/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/nofib/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/nofib/ansi.mls b/hkmc2/shared/src/test/mlscript/nofib/ansi.mls index 04aee5c4e8..599a33ef1c 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/ansi.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/ansi.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/ansi.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/atom.mls b/hkmc2/shared/src/test/mlscript/nofib/atom.mls index 40f30ace6e..fc5597befe 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/atom.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/atom.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/atom.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/awards.mls b/hkmc2/shared/src/test/mlscript/nofib/awards.mls index e3211acff9..998d675c70 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/awards.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/awards.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/awards.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/banner.mls b/hkmc2/shared/src/test/mlscript/nofib/banner.mls index 98a42f1d4c..5ceb25f203 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/banner.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/banner.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/banner.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/boyer.mls b/hkmc2/shared/src/test/mlscript/nofib/boyer.mls index b660f42654..33a9aa5abe 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/boyer.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/boyer.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/boyer.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/boyer2.mls b/hkmc2/shared/src/test/mlscript/nofib/boyer2.mls index 078957f0ec..8b361e61a5 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/boyer2.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/boyer2.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/boyer2.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/calendar.mls b/hkmc2/shared/src/test/mlscript/nofib/calendar.mls index 886b255109..5e74a9a5eb 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/calendar.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/calendar.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/calendar.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/cichelli.mls b/hkmc2/shared/src/test/mlscript/nofib/cichelli.mls index 8fd8eeb4d5..71a72cfe52 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/cichelli.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/cichelli.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/cichelli.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/circsim.mls b/hkmc2/shared/src/test/mlscript/nofib/circsim.mls index 76365dadc2..64f2263db8 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/circsim.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/circsim.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/circsim.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/clausify.mls b/hkmc2/shared/src/test/mlscript/nofib/clausify.mls index 3a82414420..55237e49f7 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/clausify.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/clausify.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/clausify.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/constraints.mls b/hkmc2/shared/src/test/mlscript/nofib/constraints.mls index 64f5a4d2de..d160e239ee 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/constraints.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/constraints.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/constraints.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/cryptarithm1.mls b/hkmc2/shared/src/test/mlscript/nofib/cryptarithm1.mls index 009cd5ae60..74cda05d56 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/cryptarithm1.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/cryptarithm1.mls @@ -1,3 +1,4 @@ +:. :js :re diff --git a/hkmc2/shared/src/test/mlscript/nofib/cryptarithm2.mls b/hkmc2/shared/src/test/mlscript/nofib/cryptarithm2.mls index a1d12ba3eb..300b73e91e 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/cryptarithm2.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/cryptarithm2.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/cryptarithm2.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/cse.mls b/hkmc2/shared/src/test/mlscript/nofib/cse.mls index 43d678f089..f7cfd4fc67 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/cse.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/cse.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/cse.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/eliza.mls b/hkmc2/shared/src/test/mlscript/nofib/eliza.mls index 15b4b391aa..4499060cde 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/eliza.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/eliza.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/eliza.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/fish.mls b/hkmc2/shared/src/test/mlscript/nofib/fish.mls index d4a51d64ba..45e9490f98 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/fish.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/fish.mls @@ -1,3 +1,4 @@ +:. :js // This is caused by the implementation of `+:` in `NofibPrelude.mls`. Replacing diff --git a/hkmc2/shared/src/test/mlscript/nofib/gcd.mls b/hkmc2/shared/src/test/mlscript/nofib/gcd.mls index bc82435615..14a0850494 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/gcd.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/gcd.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/nofib/integer.mls b/hkmc2/shared/src/test/mlscript/nofib/integer.mls index d72e8091fe..e506c76508 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/integer.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/integer.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/integer.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/knights.mls b/hkmc2/shared/src/test/mlscript/nofib/knights.mls index d76a7cc8ca..64654de69e 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/knights.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/knights.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/knights.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/lambda.mls b/hkmc2/shared/src/test/mlscript/nofib/lambda.mls index 81a8b57e06..7cf077e86d 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/lambda.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/lambda.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/lambda.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/last-piece.mls b/hkmc2/shared/src/test/mlscript/nofib/last-piece.mls index 2098253fca..710ce2cae6 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/last-piece.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/last-piece.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/lastpiece.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/lcss.mls b/hkmc2/shared/src/test/mlscript/nofib/lcss.mls index a8bbbc5604..d3a1c3eb1e 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/lcss.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/lcss.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/nofib/life.mls b/hkmc2/shared/src/test/mlscript/nofib/life.mls index d8e640e840..79099b2875 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/life.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/life.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/life.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/mandel.mls b/hkmc2/shared/src/test/mlscript/nofib/mandel.mls index 8fd4ce158f..1353b48699 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/mandel.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/mandel.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/nofib/mandel2.mls b/hkmc2/shared/src/test/mlscript/nofib/mandel2.mls index 66811b5c2c..49f981d6b3 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/mandel2.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/mandel2.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/mandel2.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/mate.mls b/hkmc2/shared/src/test/mlscript/nofib/mate.mls index 0c6a7c5cdb..90ea407deb 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/mate.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/mate.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/nofib/minimax.mls b/hkmc2/shared/src/test/mlscript/nofib/minimax.mls index d1cf1b9595..40afeaa1a1 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/minimax.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/minimax.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/nofib/para.mls b/hkmc2/shared/src/test/mlscript/nofib/para.mls index 2bef06b48d..5e6a336ee0 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/para.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/para.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/nofib/power.mls b/hkmc2/shared/src/test/mlscript/nofib/power.mls index 3c47a30052..1864eb922a 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/power.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/power.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/power.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/pretty.mls b/hkmc2/shared/src/test/mlscript/nofib/pretty.mls index 0447b4a378..4142663e6c 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/pretty.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/pretty.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/pretty.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/primetest.mls b/hkmc2/shared/src/test/mlscript/nofib/primetest.mls index f4c545232d..c3a997034b 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/primetest.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/primetest.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/nofib/puzzle.mls b/hkmc2/shared/src/test/mlscript/nofib/puzzle.mls index 9afd0ba071..dbda3a7a73 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/puzzle.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/puzzle.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/nofib/rsa.mls b/hkmc2/shared/src/test/mlscript/nofib/rsa.mls index 08c20bbecf..54f9a5c35a 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/rsa.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/rsa.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/rsa.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/scc.mls b/hkmc2/shared/src/test/mlscript/nofib/scc.mls index d7fc427493..85c85630b7 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/scc.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/scc.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/nofib/secretary.mls b/hkmc2/shared/src/test/mlscript/nofib/secretary.mls index 796de90420..e5d7ef6d69 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/secretary.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/secretary.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/secretary.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/sorting.mls b/hkmc2/shared/src/test/mlscript/nofib/sorting.mls index c0c4816737..094746cd24 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/sorting.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/sorting.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/sorting.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/sphere.mls b/hkmc2/shared/src/test/mlscript/nofib/sphere.mls index be049cf287..4cd705486d 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/sphere.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/sphere.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/nofib/sphere.mls" diff --git a/hkmc2/shared/src/test/mlscript/nofib/treejoin.mls b/hkmc2/shared/src/test/mlscript/nofib/treejoin.mls index 3fa18c0477..0ce568b9ef 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/treejoin.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/treejoin.mls @@ -1,3 +1,4 @@ +:. :js :re // nodejs stack size 8192 can handle this diff --git a/hkmc2/shared/src/test/mlscript/objbuf/.mls b/hkmc2/shared/src/test/mlscript/objbuf/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/objbuf/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/objbuf/BasicsObjBuf.mls b/hkmc2/shared/src/test/mlscript/objbuf/BasicsObjBuf.mls index 4dc81a4357..a73c0e1c0f 100644 --- a/hkmc2/shared/src/test/mlscript/objbuf/BasicsObjBuf.mls +++ b/hkmc2/shared/src/test/mlscript/objbuf/BasicsObjBuf.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/ObjectBuffer.mls" @@ -48,9 +49,9 @@ class A2(x) with @buffered class B0(x) //β”‚ ╔══[COMPILATION ERROR] Buffered classes must not have a main parameter list; use `constructor(...)` syntax instead. -//β”‚ β•‘ l.48: @buffered +//β”‚ β•‘ l.49: @buffered //β”‚ β•‘ ^^^^^^^^^ -//β”‚ β•‘ l.49: class B0(x) +//β”‚ β•‘ l.50: class B0(x) //β”‚ ╙── ^^^^^^^^^^^ :ge @@ -58,16 +59,16 @@ class B0(x) @buffered class B1(x) //β”‚ ╔══[COMPILATION ERROR] Only one of bufferable annotation is allowed. -//β”‚ β•‘ l.57: @buffered -//β”‚ β•‘ ^^^^^^^^^ //β”‚ β•‘ l.58: @buffered +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•‘ l.59: @buffered //β”‚ ╙── ^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Buffered classes must not have a main parameter list; use `constructor(...)` syntax instead. -//β”‚ β•‘ l.57: @buffered -//β”‚ β•‘ ^^^^^^^^^ //β”‚ β•‘ l.58: @buffered //β”‚ β•‘ ^^^^^^^^^ -//β”‚ β•‘ l.59: class B1(x) +//β”‚ β•‘ l.59: @buffered +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•‘ l.60: class B1(x) //β”‚ ╙── ^^^^^^^^^^^ :ge @@ -75,30 +76,30 @@ class B1(x) @bufferable class B2(x) //β”‚ ╔══[COMPILATION ERROR] Only one of bufferable annotation is allowed. -//β”‚ β•‘ l.74: @buffered +//β”‚ β•‘ l.75: @buffered //β”‚ β•‘ ^^^^^^^^^ -//β”‚ β•‘ l.75: @bufferable +//β”‚ β•‘ l.76: @bufferable //β”‚ ╙── ^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Buffered classes must not have a main parameter list; use `constructor(...)` syntax instead. -//β”‚ β•‘ l.74: @buffered +//β”‚ β•‘ l.75: @buffered //β”‚ β•‘ ^^^^^^^^^ -//β”‚ β•‘ l.75: @bufferable +//β”‚ β•‘ l.76: @bufferable //β”‚ β•‘ ^^^^^^^^^^^ -//β”‚ β•‘ l.76: class B2(x) +//β”‚ β•‘ l.77: class B2(x) //β”‚ ╙── ^^^^^^^^^^^ :w @bufferable module B3 //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.91: @bufferable +//β”‚ β•‘ l.92: @bufferable //β”‚ ╙── ^^^^^^^^^^^ :w @bufferable object B3 //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.98: @bufferable +//β”‚ β•‘ l.99: @bufferable //β”‚ ╙── ^^^^^^^^^^^ @@ -177,11 +178,11 @@ class A with @untyped public data class LocatedBuffer(x) //β”‚ ╔══[COMPILATION ERROR] Buffered classes must not have a main parameter list; use `constructor(...)` syntax instead. -//β”‚ β•‘ l.176: @buffered +//β”‚ β•‘ l.177: @buffered //β”‚ β•‘ ^^^^^^^^^ -//β”‚ β•‘ l.177: @untyped +//β”‚ β•‘ l.178: @untyped //β”‚ β•‘ ^^^^^^^^ -//β”‚ β•‘ l.178: public data class LocatedBuffer(x) +//β”‚ β•‘ l.179: public data class LocatedBuffer(x) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // The following declaration starts a fresh span. @@ -189,7 +190,75 @@ public data class LocatedBuffer(x) @buffered class NextLocatedBuffer(x) //β”‚ ╔══[COMPILATION ERROR] Buffered classes must not have a main parameter list; use `constructor(...)` syntax instead. -//β”‚ β•‘ l.189: @buffered +//β”‚ β•‘ l.190: @buffered //β”‚ β•‘ ^^^^^^^^^ -//β”‚ β•‘ l.190: class NextLocatedBuffer(x) +//β”‚ β•‘ l.191: class NextLocatedBuffer(x) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +// A buffered method keeps all its original argument lists and result interface. +@annotations.buffered +class Curried with + constructor(value: Str) + fun append(suffix: Str)(last: Str): Str = value + suffix + last + fun valueLength: Int = value.length + fun echo[T](value: T): T = value + +:expect "abc" +let curried = buf.mkNew(Curried)("a") +Curried.append(buf, curried)("b")("c").toLowerCase() +//β”‚ = "abc" +//β”‚ curried = 6 + +:expect "1.0" +Curried.valueLength(buf, curried).toFixed(1) +//β”‚ = "1.0" + +:expect ["ABC", "12.0"] +[Curried.echo(buf, curried)("abc").toUpperCase(), Curried.echo(buf, curried)(12).toFixed(1)] +//β”‚ = ["ABC", "12.0"] + +// Even a zero-sized class must run its constructor. +:expect 1 +let initialized = 0 +@buffered +class Empty with + set initialized += 1 +let empty = buf.mkNew(Empty) +initialized +//β”‚ = 1 +//β”‚ empty = 0 +//β”‚ initialized = 1 + +// Generated static members belong only to annotated classes and exclude fields. +:e +Curried.missing +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.235: Curried.missing +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class value does not contain member 'missing' +//β”‚ β•‘ l.201: class Curried with +//β”‚ ╙── ^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:e +Curried.value +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.245: Curried.value +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class value does not contain member 'value' +//β”‚ β•‘ l.201: class Curried with +//β”‚ ╙── ^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +:e +class Ordinary with + fun append(value: Str) = value +Ordinary.append +//β”‚ ╔══[COMPILATION ERROR] Resolution error in selection +//β”‚ β•‘ l.257: Ordinary.append +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class value does not contain member 'append' +//β”‚ β•‘ l.255: class Ordinary with +//β”‚ ╙── ^^^^^^^^ +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/objbuf/ObjectBufferAllocator.mls b/hkmc2/shared/src/test/mlscript/objbuf/ObjectBufferAllocator.mls index bd7a6add7c..496c63f0ab 100644 --- a/hkmc2/shared/src/test/mlscript/objbuf/ObjectBufferAllocator.mls +++ b/hkmc2/shared/src/test/mlscript/objbuf/ObjectBufferAllocator.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/ObjectBuffer.mls" diff --git a/hkmc2/shared/src/test/mlscript/opt/.mls b/hkmc2/shared/src/test/mlscript/opt/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/opt/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/opt/AbortivePrefix.mls b/hkmc2/shared/src/test/mlscript/opt/AbortivePrefix.mls index 5561d8fd89..f327ea4ecd 100644 --- a/hkmc2/shared/src/test/mlscript/opt/AbortivePrefix.mls +++ b/hkmc2/shared/src/test/mlscript/opt/AbortivePrefix.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/AlmostTailCall.mls b/hkmc2/shared/src/test/mlscript/opt/AlmostTailCall.mls index 5627beb8f2..85b83203b9 100644 --- a/hkmc2/shared/src/test/mlscript/opt/AlmostTailCall.mls +++ b/hkmc2/shared/src/test/mlscript/opt/AlmostTailCall.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/BasicVarPropag.mls b/hkmc2/shared/src/test/mlscript/opt/BasicVarPropag.mls index 1d6bf7f7ea..70bc7d3a62 100644 --- a/hkmc2/shared/src/test/mlscript/opt/BasicVarPropag.mls +++ b/hkmc2/shared/src/test/mlscript/opt/BasicVarPropag.mls @@ -1,3 +1,4 @@ +:. :js open scope { locally } diff --git a/hkmc2/shared/src/test/mlscript/opt/CapturedVariableMutation.mls b/hkmc2/shared/src/test/mlscript/opt/CapturedVariableMutation.mls index fd3a137254..94e1594a58 100644 --- a/hkmc2/shared/src/test/mlscript/opt/CapturedVariableMutation.mls +++ b/hkmc2/shared/src/test/mlscript/opt/CapturedVariableMutation.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/CaseOfCase.mls b/hkmc2/shared/src/test/mlscript/opt/CaseOfCase.mls index 2d35074fa2..d91a709c75 100644 --- a/hkmc2/shared/src/test/mlscript/opt/CaseOfCase.mls +++ b/hkmc2/shared/src/test/mlscript/opt/CaseOfCase.mls @@ -1,3 +1,4 @@ +:. // * [test:case-of-case] :js @@ -217,7 +218,7 @@ prefixDependency(false) @inline -fun partlyKnownProducer(x) = if x is +fun partlyKnownProducer(x: Str) = if x is "" then 0 "1" then x.length "2" then 1 @@ -233,12 +234,13 @@ x => if partlyKnownProducer(x) is //β”‚ let lambda; //β”‚ @private //β”‚ define lambda as fun lambdaΒΉ(x) { -//β”‚ let inlinedVal; -//β”‚ match x +//β”‚ let x1, inlinedVal; +//β”‚ set x1 = (x as! Str⁰); +//β”‚ match x1 //β”‚ "" => //β”‚ return Predef⁰.print⁰("Z") //β”‚ "1" => -//β”‚ set inlinedVal = x.lengthοΉ–; +//β”‚ set inlinedVal = x1.length⁰; //β”‚ match inlinedVal //β”‚ 0 => //β”‚ return Predef⁰.print⁰("Z") @@ -306,13 +308,14 @@ x => if partlyKnownProducer(x) is //β”‚ let lambda; //β”‚ @private //β”‚ define lambda as fun lambdaΒ³(x) { -//β”‚ let inlinedVal; -//β”‚ match x +//β”‚ let x1, inlinedVal; +//β”‚ set x1 = (x as! Str⁰); +//β”‚ match x1 //β”‚ "" => //β”‚ set inlinedVal = 0; //β”‚ end //β”‚ "1" => -//β”‚ set inlinedVal = x.lengthοΉ–; +//β”‚ set inlinedVal = x1.length⁰; //β”‚ end //β”‚ "2" => //β”‚ set inlinedVal = 1; @@ -342,12 +345,13 @@ x => if partlyKnownProducer(x) is //β”‚ let lambda; //β”‚ @private //β”‚ define lambda as fun lambda⁴(x) { -//β”‚ let inlinedVal; -//β”‚ match x +//β”‚ let x1, inlinedVal; +//β”‚ set x1 = (x as! Str⁰); +//β”‚ match x1 //β”‚ "" => //β”‚ return Predef⁰.print⁰("Z") //β”‚ "1" => -//β”‚ set inlinedVal = x.lengthοΉ–; +//β”‚ set inlinedVal = x1.length⁰; //β”‚ match inlinedVal //β”‚ 0 => //β”‚ return Predef⁰.print⁰("Z") diff --git a/hkmc2/shared/src/test/mlscript/opt/ConstantFolding.mls b/hkmc2/shared/src/test/mlscript/opt/ConstantFolding.mls index 497ac0b432..3d0ce0fad6 100644 --- a/hkmc2/shared/src/test/mlscript/opt/ConstantFolding.mls +++ b/hkmc2/shared/src/test/mlscript/opt/ConstantFolding.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/DeadAssignmentRemoval.mls b/hkmc2/shared/src/test/mlscript/opt/DeadAssignmentRemoval.mls index b86a3e8af6..276b14aa89 100644 --- a/hkmc2/shared/src/test/mlscript/opt/DeadAssignmentRemoval.mls +++ b/hkmc2/shared/src/test/mlscript/opt/DeadAssignmentRemoval.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/DeadBranchRemoval.mls b/hkmc2/shared/src/test/mlscript/opt/DeadBranchRemoval.mls index c1f5fb83ce..62c05d1e9a 100644 --- a/hkmc2/shared/src/test/mlscript/opt/DeadBranchRemoval.mls +++ b/hkmc2/shared/src/test/mlscript/opt/DeadBranchRemoval.mls @@ -1,3 +1,4 @@ +:. :js #config(deadBranchRemoval: true) diff --git a/hkmc2/shared/src/test/mlscript/opt/DeadConstructorElim.mls b/hkmc2/shared/src/test/mlscript/opt/DeadConstructorElim.mls index 5839a3d518..5cf9eab637 100644 --- a/hkmc2/shared/src/test/mlscript/opt/DeadConstructorElim.mls +++ b/hkmc2/shared/src/test/mlscript/opt/DeadConstructorElim.mls @@ -1,3 +1,4 @@ +:. :js :flowBasedOpt debugDce @@ -314,7 +315,7 @@ s(new O) //β”‚ dead-constructor-elim > eliminable: Call(member:AΒ²β€Ήterm:A⁰›, [[Select(y⁰, Ident(x))]]) //β”‚ dead-constructor-elim > <<< dead-constructor-elim results <<< //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Optimized IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ let tmp; define s⁰ as fun sΒΉ(y) { do y.xοΉ–; return 2 }; set tmp = new O⁰(); return sΒΉ(tmp) +//β”‚ let tmp; define s⁰ as fun sΒΉ(y) { do y.xΒΉ; return 2 }; set tmp = new O⁰(); return sΒΉ(tmp) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ > get x //β”‚ = 2 @@ -332,7 +333,7 @@ t(new O) //β”‚ dead-constructor-elim > <<< dead-constructor-elim results <<< //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Optimized IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let tmp; -//β”‚ define t⁰ as fun tΒΉ(y) { do y.zοΉ–; do y.xοΉ–; do y!"z"; return 2 }; +//β”‚ define t⁰ as fun tΒΉ(y) { do y.z⁰; do y.xΒΉ; do y!"z"; return 2 }; //β”‚ set tmp = new O⁰(); //β”‚ return tΒΉ(tmp) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” @@ -356,8 +357,8 @@ u(new O) //β”‚ let tmp; //β”‚ define u⁰ as fun uΒΉ(y) { //β”‚ let b; -//β”‚ do y.zοΉ–; -//β”‚ do y.xοΉ–; +//β”‚ do y.z⁰; +//β”‚ do y.xΒΉ; //β”‚ set b = A⁰(undefined); //β”‚ match b //β”‚ AΒΉ => @@ -382,7 +383,7 @@ v(new O) //β”‚ dead-constructor-elim > >>> dead-constructor-elim results >>> //β”‚ dead-constructor-elim > <<< dead-constructor-elim results <<< //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Optimized IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ let tmp; define v⁰ as fun vΒΉ(y) { return [y.zοΉ–, y.xοΉ–] }; set tmp = new O⁰(); return vΒΉ(tmp) +//β”‚ let tmp; define v⁰ as fun vΒΉ(y) { return [y.z⁰, y.xΒΉ] }; set tmp = new O⁰(); return vΒΉ(tmp) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ > get z //β”‚ > get x @@ -402,7 +403,7 @@ w(new O) //β”‚ let tmp; //β”‚ define w⁰ as fun wΒΉ(y) { //β”‚ let b; -//β”‚ set b = [y.zοΉ–, 1]; +//β”‚ set b = [y.z⁰, 1]; //β”‚ match b //β”‚ Array(2) => //β”‚ do runtime⁰.Tuple⁰.get⁰(b, 0); @@ -443,17 +444,20 @@ consume() //β”‚ }; //β”‚ @data //β”‚ define Observed⁰ as class ObservedΒ² { -//β”‚ val xΒΉ; +//β”‚ val xΒ²; //β”‚ constructor ObservedΒΉ(x) { -//β”‚ define xΒΉ as val xΒ² = x; +//β”‚ define xΒ² as val xΒ³ = x; //β”‚ end //β”‚ } //β”‚ }; //β”‚ define ConsumerΒΉ as class ConsumerΒ² { -//β”‚ private val xΒ³; +//β”‚ private val x⁴; //β”‚ constructor Consumer⁰(x) { -//β”‚ set ConsumerΒ².this.xΒ³ = x; -//β”‚ match ConsumerΒ².this.xΒ³ +//β”‚ let scrut; +//β”‚ @private +//β”‚ define x⁡ as val x⁴ = x; +//β”‚ set scrut = ConsumerΒ².this.x⁴; +//β”‚ match scrut //β”‚ ObservedΒ² => //β”‚ end //β”‚ else @@ -500,8 +504,9 @@ capture() //β”‚ define capture⁰ as fun captureΒΉ() { //β”‚ let instance; //β”‚ set instance = new CapturingΒΉ(0, undefined); -//β”‚ return instance.getοΉ–() +//β”‚ return instance.getΒ²() //β”‚ }; //β”‚ return captureΒΉ() //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ ═══[RUNTIME ERROR] Expected: '[1, 2]', got: '()' +//β”‚ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. +//β”‚ ═══[RUNTIME ERROR] Expected: '[1, 2]', got: 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/opt/DeadObjRemoval.mls b/hkmc2/shared/src/test/mlscript/opt/DeadObjRemoval.mls index a9130444fb..333d391c7d 100644 --- a/hkmc2/shared/src/test/mlscript/opt/DeadObjRemoval.mls +++ b/hkmc2/shared/src/test/mlscript/opt/DeadObjRemoval.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/DeadSelRemoval.mls b/hkmc2/shared/src/test/mlscript/opt/DeadSelRemoval.mls index 813c14dbd2..e2d82fd7fe 100644 --- a/hkmc2/shared/src/test/mlscript/opt/DeadSelRemoval.mls +++ b/hkmc2/shared/src/test/mlscript/opt/DeadSelRemoval.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/DeadStatRemoval.mls b/hkmc2/shared/src/test/mlscript/opt/DeadStatRemoval.mls index fead8cfc85..f91a5073a5 100644 --- a/hkmc2/shared/src/test/mlscript/opt/DeadStatRemoval.mls +++ b/hkmc2/shared/src/test/mlscript/opt/DeadStatRemoval.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/DeadVarRemoval.mls b/hkmc2/shared/src/test/mlscript/opt/DeadVarRemoval.mls index e21bf6bc6e..e42bf4a249 100644 --- a/hkmc2/shared/src/test/mlscript/opt/DeadVarRemoval.mls +++ b/hkmc2/shared/src/test/mlscript/opt/DeadVarRemoval.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/EtaExpansion.mls b/hkmc2/shared/src/test/mlscript/opt/EtaExpansion.mls index 6f6bb3c566..54e0f46e9e 100644 --- a/hkmc2/shared/src/test/mlscript/opt/EtaExpansion.mls +++ b/hkmc2/shared/src/test/mlscript/opt/EtaExpansion.mls @@ -1,3 +1,4 @@ +:. :js :flowBasedOpt debugEta @@ -125,10 +126,10 @@ g(f, 1)(2) + g(h, 3)(4) :w fun h(x) = @affine(1) y => x + y //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.126: fun h(x) = @affine(1) y => x + y +//β”‚ β•‘ l.127: fun h(x) = @affine(1) y => x + y //β”‚ β•‘ ^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ This annotation is not supported on function literal terms. -//β”‚ β•‘ l.126: fun h(x) = @affine(1) y => x + y +//β”‚ β•‘ l.127: fun h(x) = @affine(1) y => x + y //β”‚ ╙── ^^^^^^^^^^ //β”‚ eta-expansion > >>> eta-expansion targets shapes >>> //β”‚ eta-expansion > <<< eta-expansion targets shapes <<< @@ -136,7 +137,7 @@ fun h(x) = @affine(1) y => x + y :w @affine(0) val v = 1 //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.137: @affine(0) val v = 1 +//β”‚ β•‘ l.138: @affine(0) val v = 1 //β”‚ ╙── ^^^^^^^^^^ //β”‚ eta-expansion > >>> eta-expansion targets shapes >>> //β”‚ eta-expansion > <<< eta-expansion targets shapes <<< @@ -145,10 +146,10 @@ fun h(x) = @affine(1) y => x + y :w fun h(x) = @affine(0) x //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.146: fun h(x) = @affine(0) x +//β”‚ β•‘ l.147: fun h(x) = @affine(0) x //β”‚ β•‘ ^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ This annotation is not supported on reference terms. -//β”‚ β•‘ l.146: fun h(x) = @affine(0) x +//β”‚ β•‘ l.147: fun h(x) = @affine(0) x //β”‚ ╙── ^ //β”‚ eta-expansion > >>> eta-expansion targets shapes >>> //β”‚ eta-expansion > <<< eta-expansion targets shapes <<< diff --git a/hkmc2/shared/src/test/mlscript/opt/IllFormedAccesses.mls b/hkmc2/shared/src/test/mlscript/opt/IllFormedAccesses.mls index 767549b7be..5a99ab0024 100644 --- a/hkmc2/shared/src/test/mlscript/opt/IllFormedAccesses.mls +++ b/hkmc2/shared/src/test/mlscript/opt/IllFormedAccesses.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/InfiniteInlining.mls b/hkmc2/shared/src/test/mlscript/opt/InfiniteInlining.mls index d8c281f8e5..7bcb47ae8a 100644 --- a/hkmc2/shared/src/test/mlscript/opt/InfiniteInlining.mls +++ b/hkmc2/shared/src/test/mlscript/opt/InfiniteInlining.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/InlineAcrossFiles.mls b/hkmc2/shared/src/test/mlscript/opt/InlineAcrossFiles.mls index b22d3bed34..5dd555df6e 100644 --- a/hkmc2/shared/src/test/mlscript/opt/InlineAcrossFiles.mls +++ b/hkmc2/shared/src/test/mlscript/opt/InlineAcrossFiles.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/InlineModuleMethods.mls b/hkmc2/shared/src/test/mlscript/opt/InlineModuleMethods.mls index 5e7ff89b51..0e2e2bce70 100644 --- a/hkmc2/shared/src/test/mlscript/opt/InlineModuleMethods.mls +++ b/hkmc2/shared/src/test/mlscript/opt/InlineModuleMethods.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/InlineNestedFunctionsAcrossFiles.mls b/hkmc2/shared/src/test/mlscript/opt/InlineNestedFunctionsAcrossFiles.mls index 0dc8f581f6..868eea7b21 100644 --- a/hkmc2/shared/src/test/mlscript/opt/InlineNestedFunctionsAcrossFiles.mls +++ b/hkmc2/shared/src/test/mlscript/opt/InlineNestedFunctionsAcrossFiles.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/LoopOpt.mls b/hkmc2/shared/src/test/mlscript/opt/LoopOpt.mls index 943467ae25..1cd397a416 100644 --- a/hkmc2/shared/src/test/mlscript/opt/LoopOpt.mls +++ b/hkmc2/shared/src/test/mlscript/opt/LoopOpt.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/MatchSimplification.mls b/hkmc2/shared/src/test/mlscript/opt/MatchSimplification.mls index 41778b5ca5..3a29fb3f70 100644 --- a/hkmc2/shared/src/test/mlscript/opt/MatchSimplification.mls +++ b/hkmc2/shared/src/test/mlscript/opt/MatchSimplification.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/NestedLoopOpt.mls b/hkmc2/shared/src/test/mlscript/opt/NestedLoopOpt.mls index bdeec7674d..7b3e751d6b 100644 --- a/hkmc2/shared/src/test/mlscript/opt/NestedLoopOpt.mls +++ b/hkmc2/shared/src/test/mlscript/opt/NestedLoopOpt.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/NoCaseOfCase.mls b/hkmc2/shared/src/test/mlscript/opt/NoCaseOfCase.mls index a3a3656da6..dd7253edb9 100644 --- a/hkmc2/shared/src/test/mlscript/opt/NoCaseOfCase.mls +++ b/hkmc2/shared/src/test/mlscript/opt/NoCaseOfCase.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/PrivateFields.mls b/hkmc2/shared/src/test/mlscript/opt/PrivateFields.mls index 3d3594d83e..ab247845d1 100644 --- a/hkmc2/shared/src/test/mlscript/opt/PrivateFields.mls +++ b/hkmc2/shared/src/test/mlscript/opt/PrivateFields.mls @@ -1,3 +1,4 @@ +:. :js @@ -138,3 +139,65 @@ UnusedObjectPrivate.get + UnusedModulePrivate.get //β”‚ = "()()" +// Unused constructor parameters and explicit private values need no instance fields. +:soir +class UnusedParameter(value) +class UnusedField with + private val value = 1 +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Optimized IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ define UnusedParameter⁰ as class UnusedParameterΒ² { +//β”‚ constructor UnusedParameterΒΉ +//β”‚ }; +//β”‚ define UnusedField⁰ as class UnusedFieldΒΉ; +//β”‚ end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + + +// TODO: improve +// A read in the constructor alone currently keeps the initialized member storage live. +:soir +class ConstructorLocal(value) with + let stored = value + print(stored) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Optimized IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ define ConstructorLocal⁰ as class ConstructorLocalΒ² { +//β”‚ private val value⁰; +//β”‚ private val stored⁰; +//β”‚ constructor ConstructorLocalΒΉ(value) { +//β”‚ @private +//β”‚ define valueΒΉ as val value⁰ = value; +//β”‚ set ConstructorLocalΒ².this.stored⁰ = ConstructorLocalΒ².this.value⁰; +//β”‚ do Predef⁰.print⁰(ConstructorLocalΒ².this.stored⁰); +//β”‚ end +//β”‚ } +//β”‚ }; +//β”‚ end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +ConstructorLocal(123) +//β”‚ > 123 +//β”‚ = ConstructorLocal(_) + + +:soir +class ConstructorRead with + private val value = 1 + print(value) +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Optimized IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” +//β”‚ define ConstructorRead⁰ as class ConstructorReadΒΉ { +//β”‚ private val valueΒ²; +//β”‚ constructor() { +//β”‚ @private +//β”‚ define valueΒ³ as val valueΒ² = 1; +//β”‚ do Predef⁰.print⁰(ConstructorReadΒΉ.this.valueΒ²); +//β”‚ end +//β”‚ } +//β”‚ }; +//β”‚ end +//β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” + +new ConstructorRead +//β”‚ > 1 +//β”‚ = ConstructorRead + + diff --git a/hkmc2/shared/src/test/mlscript/opt/PureCallPropagation.mls b/hkmc2/shared/src/test/mlscript/opt/PureCallPropagation.mls index f7ef55b4b4..2876ff84b0 100644 --- a/hkmc2/shared/src/test/mlscript/opt/PureCallPropagation.mls +++ b/hkmc2/shared/src/test/mlscript/opt/PureCallPropagation.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/TailRecOpt.mls b/hkmc2/shared/src/test/mlscript/opt/TailRecOpt.mls index 278f3e5240..749ac70b36 100644 --- a/hkmc2/shared/src/test/mlscript/opt/TailRecOpt.mls +++ b/hkmc2/shared/src/test/mlscript/opt/TailRecOpt.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/UnspecifiedBehavior.mls b/hkmc2/shared/src/test/mlscript/opt/UnspecifiedBehavior.mls index 1d8fcd7692..0c95e40334 100644 --- a/hkmc2/shared/src/test/mlscript/opt/UnspecifiedBehavior.mls +++ b/hkmc2/shared/src/test/mlscript/opt/UnspecifiedBehavior.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/ValPropag.mls b/hkmc2/shared/src/test/mlscript/opt/ValPropag.mls index e5f404bf65..6b979dd7fb 100644 --- a/hkmc2/shared/src/test/mlscript/opt/ValPropag.mls +++ b/hkmc2/shared/src/test/mlscript/opt/ValPropag.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/VarPropagStress.mls b/hkmc2/shared/src/test/mlscript/opt/VarPropagStress.mls index b067592f04..7b78a384bb 100644 --- a/hkmc2/shared/src/test/mlscript/opt/VarPropagStress.mls +++ b/hkmc2/shared/src/test/mlscript/opt/VarPropagStress.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/opt/WorkerWrapper.mls b/hkmc2/shared/src/test/mlscript/opt/WorkerWrapper.mls index cdc91a0a57..8fd2e329aa 100644 --- a/hkmc2/shared/src/test/mlscript/opt/WorkerWrapper.mls +++ b/hkmc2/shared/src/test/mlscript/opt/WorkerWrapper.mls @@ -1,3 +1,4 @@ +:. :js open annotations { noInline } diff --git a/hkmc2/shared/src/test/mlscript/std/.mls b/hkmc2/shared/src/test/mlscript/std/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/std/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/std/FingerTreeListTest.mls b/hkmc2/shared/src/test/mlscript/std/FingerTreeListTest.mls index f4292dcb8b..8815cff96a 100644 --- a/hkmc2/shared/src/test/mlscript/std/FingerTreeListTest.mls +++ b/hkmc2/shared/src/test/mlscript/std/FingerTreeListTest.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/FingerTreeList.mls" @@ -101,14 +102,14 @@ if xs is //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ let element0$; //β”‚ if (runtime.Tuple.isArrayLike(xs2) && xs2.length >= 1) { -//β”‚ let xs3, inlinedVal27, tmp16; +//β”‚ let xs3, inlinedVal38, tmp16; //β”‚ element0$ = runtime.Tuple.get(xs2, 0); //β”‚ xs3 = xs2; //β”‚ tmp16 = xs3.length - 0; -//β”‚ inlinedVal27 = runtime.safeCall(xs3.slice(1, tmp16)); +//β”‚ inlinedVal38 = runtime.safeCall(xs3.slice(1, tmp16)); //β”‚ return globalThis.Object.freeze([ //β”‚ element0$, -//β”‚ inlinedVal27 +//β”‚ inlinedVal38 //β”‚ ]) //β”‚ } //β”‚ throw globalThis.Object.freeze(new globalThis.Error("match error")); @@ -132,7 +133,7 @@ fun popUntilEmpty(lft, acc) = [] then acc :sjs -fun popByIndex(start, end, acc, lft) = +fun popByIndex(start, end, acc, lft: Indexable) = if start >= end then acc else popByIndex(start + 1, end, [...acc, lft.at(start)], lft) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” diff --git a/hkmc2/shared/src/test/mlscript/std/IterTest.mls b/hkmc2/shared/src/test/mlscript/std/IterTest.mls index 73f1be4d76..ec6a6773a0 100644 --- a/hkmc2/shared/src/test/mlscript/std/IterTest.mls +++ b/hkmc2/shared/src/test/mlscript/std/IterTest.mls @@ -1,3 +1,4 @@ +:. :js @@ -28,7 +29,7 @@ arr //β”‚ = ["0!", "1!", "3!"] ["abc", "01234"] - |> Iter.withMap of _.toUpperCase() + |> Iter.withMap of (s: Str) => s.toUpperCase() |> Iter.flattening |> Iter.withFilter of _ != "0" |> Iter.toArray @@ -52,7 +53,7 @@ mkStr of fun xs = [1, 2, 3] Iter.mapping(_ + "!" !> print) xs -//β”‚ = Iterable(_) +//β”‚ = IterableImpl(_) xs.toArray() //β”‚ > 1! @@ -67,7 +68,7 @@ xs.toArray() //β”‚ = ["1!", "2!", "3!"] let ys = xs -//β”‚ ys = Iterable(_) +//β”‚ ys = IterableImpl(_) ys.toArray() //β”‚ > 1! diff --git a/hkmc2/shared/src/test/mlscript/std/LazyArrayTest.mls b/hkmc2/shared/src/test/mlscript/std/LazyArrayTest.mls index a53454d991..24c33bfc48 100644 --- a/hkmc2/shared/src/test/mlscript/std/LazyArrayTest.mls +++ b/hkmc2/shared/src/test/mlscript/std/LazyArrayTest.mls @@ -1,8 +1,10 @@ +:. :js import "../../mlscript-compile/LazyArray.mls" import "../../mlscript-compile/MutMap.mls" +open LazyArray { equals } open LazyArray // Splices diff --git a/hkmc2/shared/src/test/mlscript/std/MutMapTest.mls b/hkmc2/shared/src/test/mlscript/std/MutMapTest.mls new file mode 100644 index 0000000000..5462dcba4a --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/std/MutMapTest.mls @@ -0,0 +1,36 @@ +:. +:js + +import "../../mlscript-compile/MutMap.mls" +import "../../mlscript-compile/Option.mls" + +open Option { Some, None } + +// updateWith receives None for a missing key and inserts a returned Some. +:expect Some(20) +val m = MutMap.empty +m |> MutMap.updateWith("key") of case + None then Some(20) + Some(value) then Some(value + 1) +m |> MutMap.get("key") +//β”‚ = Some(20) +//β”‚ m = MutMap(Map(1) {"key" => 20}) + +// An existing value is passed to the callback and replaced by its result. +:expect Some(42) +m |> MutMap.updateWith("key") of case + None then Some(0) + Some(value) then Some(value + 22) +m |> MutMap.get("key") +//β”‚ = Some(42) + +// Returning None deletes an existing key and leaves an absent key absent. +:expect None +m |> MutMap.updateWith("key") of ((_) => None) +m |> MutMap.get("key") +//β”‚ = None + +:expect None +m |> MutMap.updateWith("missing") of ((_) => None) +m |> MutMap.get("missing") +//β”‚ = None diff --git a/hkmc2/shared/src/test/mlscript/std/PredefTest.mls b/hkmc2/shared/src/test/mlscript/std/PredefTest.mls index c82205e440..fee9a43103 100644 --- a/hkmc2/shared/src/test/mlscript/std/PredefTest.mls +++ b/hkmc2/shared/src/test/mlscript/std/PredefTest.mls @@ -1,3 +1,4 @@ +:. :js @@ -9,9 +10,13 @@ id(123) :e :re id() -//β”‚ ╔══[COMPILATION ERROR] Expected 1 arguments, got 0 -//β”‚ β•‘ l.11: id() -//β”‚ ╙── ^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.12: id() +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'id' expected 1 argument, but got 0 +//β”‚ β•‘ in ../../mlscript-compile/Predef.mls +//β”‚ β•‘ l.19: fun id(x) = x +//β”‚ ╙── ^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. @@ -183,14 +188,26 @@ foldr(tuple)(0, 1, 2, 3) :todo foldr(+)() -//β”‚ ╔══[COMPILATION ERROR] Expected at least 1 arguments, got 0 -//β”‚ β•‘ l.185: foldr(+)() -//β”‚ ╙── ^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.190: foldr(+)() +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'foldr' expected 1 argument, but got 0 +//β”‚ β•‘ in ../../mlscript-compile/Predef.mls +//β”‚ β•‘ l.118: fun foldr[A](f: (A, A) -> A)(first: A, ...rest: Array[A]): A = +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.119: let len = rest.length +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.120: if len === 0 then first else... +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.121: let +//β”‚ β•‘ ^^^^^ +//β”‚ β•‘ l.122: ... (more lines omitted) ... +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. // An alternative implementation of foldr -fun foldr(f)(first, ...rest) = +fun foldr(f)(first, ...rest: Array) = let len = rest.length if len == 0 then first else... let @@ -218,12 +235,28 @@ fold(+) of import "../../mlscript-compile/Iter.mls" +:e Iter.joined(_, "") of [ "0" "1" "2" "3" ] +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.239: Iter.joined(_, "") of [ +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.240: "0" +//β”‚ β•‘ ^^^^^ +//β”‚ β•‘ l.241: "1" +//β”‚ β•‘ ^^^^^ +//β”‚ β•‘ l.242: "2" +//β”‚ β•‘ ^^^^^ +//β”‚ β•‘ l.243: ... (more lines omitted) ... +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ String literal cannot be called like a function. +//β”‚ β•‘ in ../../mlscript-compile/Iter.mls +//β”‚ β•‘ l.226: if next.done then "" else +//β”‚ ╙── ^^ //β”‚ = fun diff --git a/hkmc2/shared/src/test/mlscript/std/PrettyPrintTest.mls b/hkmc2/shared/src/test/mlscript/std/PrettyPrintTest.mls index 679abe04e4..1d9c1103eb 100644 --- a/hkmc2/shared/src/test/mlscript/std/PrettyPrintTest.mls +++ b/hkmc2/shared/src/test/mlscript/std/PrettyPrintTest.mls @@ -1,7 +1,9 @@ +:. :js import "../../mlscript-compile/Rendering.mls" +open Rendering { render } open Rendering // We set `breakLength` to 74. With the `print` function and diff test comments @@ -195,14 +197,14 @@ let testMap = new Map([[testKey, testKey]]) show of testMap //β”‚ > Map(1) {{x: 0, y: 0, z: 0} => {x: 0, y: 0, z: 0}} β”Š -set testKey.w = 0, show of testMap +set testKey!w = 0, show of testMap //β”‚ > Map(1) {{x: 0, y: 0, z: 0, w: 0} => {x: 0, y: 0, z: 0, w: 0}} β”Š -set testKey.a = 42, show of testMap +set testKey!a = 42, show of testMap //β”‚ > Map(1) {{x: 0, y: 0, z: 0, w: 0, a: 42} => {x: 0, y: 0, z: 0, w: 0, a: 42}} // TODO -set testKey.b = 404, show of testMap +set testKey!b = 404, show of testMap //β”‚ > Map(1) { β”Š //β”‚ > {x: 0, y: 0, z: 0, w: 0, a: 42, b: 404} => {x: 0, y: 0, z: 0, w: 0, a: 42, b: 404} //β”‚ > } β”Š @@ -214,15 +216,15 @@ let testMap = new Map([[testKey, shortText]]) show of testMap //β”‚ > Map(1) {{a: 1, b: 10, c: 100, d: 1000, e: 10000} => "Hello, world!"} β”Š -set testKey.x = 42, show of testMap +set testKey!x = 42, show of testMap //β”‚ > Map(1) {{a: 1, b: 10, c: 100, d: 1000, e: 10000, x: 42} => "Hello, world!"} -set testKey.y = 100, show of testMap +set testKey!y = 100, show of testMap //β”‚ > Map(1) { β”Š //β”‚ > {a: 1, b: 10, c: 100, d: 1000, e: 10000, x: 42, y: 100} => "Hello, world!" //β”‚ > } β”Š -set testKey.z = shortText, show of testMap +set testKey!z = shortText, show of testMap //β”‚ > Map(1) { β”Š //β”‚ > {a: 1, b: 10, c: 100, d: 1000, e: 10000, x: 42, y: 100, z: "Hello, world!"} => "Hello, world!" //β”‚ > } β”Š diff --git a/hkmc2/shared/src/test/mlscript/std/RecordTest.mls b/hkmc2/shared/src/test/mlscript/std/RecordTest.mls index 7a70e9e7ed..9b4603bc1f 100644 --- a/hkmc2/shared/src/test/mlscript/std/RecordTest.mls +++ b/hkmc2/shared/src/test/mlscript/std/RecordTest.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/Record.mls" @@ -25,8 +26,8 @@ let dst = import "../../mlscript-compile/Iter.mls" -fun steal(...fields_from) = - let from = fields_from.at(-1).from +fun steal(...fields_from: Array) = + let from = (fields_from.at(-1) as {from: Object}).from let fields = fields_from.slice(0, -1) let rcd = new mut Object fields Iter.each of f => set rcd.(f) = from.(f) diff --git a/hkmc2/shared/src/test/mlscript/std/StackTests.mls b/hkmc2/shared/src/test/mlscript/std/StackTests.mls index b5746514ba..f702ff1ea4 100644 --- a/hkmc2/shared/src/test/mlscript/std/StackTests.mls +++ b/hkmc2/shared/src/test/mlscript/std/StackTests.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/Stack.mls" diff --git a/hkmc2/shared/src/test/mlscript/syntax/.mls b/hkmc2/shared/src/test/mlscript/syntax/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/syntax/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/syntax/AbusiveSuspensions.mls b/hkmc2/shared/src/test/mlscript/syntax/AbusiveSuspensions.mls index 68c9b93c25..3cadc514af 100644 --- a/hkmc2/shared/src/test/mlscript/syntax/AbusiveSuspensions.mls +++ b/hkmc2/shared/src/test/mlscript/syntax/AbusiveSuspensions.mls @@ -1,3 +1,4 @@ +:. :js @@ -30,22 +31,22 @@ fun foo(expr1) = Ok(a) then... a //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position; found new line instead -//β”‚ β•‘ l.28: if expr1 is +//β”‚ β•‘ l.29: if expr1 is //β”‚ β•‘ ^ -//β”‚ β•‘ l.29: Error(b) then b +//β”‚ β•‘ l.30: Error(b) then b //β”‚ ╙── ^^ //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern split (β€Ήerroneous syntaxβ€Ί). -//β”‚ β•‘ l.28: if expr1 is +//β”‚ β•‘ l.29: if expr1 is //β”‚ β•‘ ^ -//β”‚ β•‘ l.29: Error(b) then b +//β”‚ β•‘ l.30: Error(b) then b //β”‚ ╙── ^^ //β”‚ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here -//β”‚ β•‘ l.29: Error(b) then b +//β”‚ β•‘ l.30: Error(b) then b //β”‚ ╙── ^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here -//β”‚ β•‘ l.30: Ok(a) then... +//β”‚ β•‘ l.31: Ok(a) then... //β”‚ β•‘ ^^^^^^^^^^^^^ -//β”‚ β•‘ l.31: a +//β”‚ β•‘ l.32: a //β”‚ ╙── ^^^ fun foo(expr1) = @@ -61,13 +62,13 @@ fun foo(expr1) = is Ok(a) then... a //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position; found 'is' keyword instead -//β”‚ β•‘ l.60: is Error(b) then b +//β”‚ β•‘ l.61: is Error(b) then b //β”‚ ╙── ^^ //β”‚ ╔══[PARSE ERROR] Unexpected identifier here -//β”‚ β•‘ l.60: is Error(b) then b +//β”‚ β•‘ l.61: is Error(b) then b //β”‚ ╙── ^^^^^ //β”‚ ╔══[COMPILATION ERROR] Unrecognized term split (identifier) -//β”‚ β•‘ l.59: if expr1 +//β”‚ β•‘ l.60: if expr1 //β”‚ ╙── ^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/syntax/BackslashApps.mls b/hkmc2/shared/src/test/mlscript/syntax/BackslashApps.mls index 9cffe89835..7fe2af2a92 100644 --- a/hkmc2/shared/src/test/mlscript/syntax/BackslashApps.mls +++ b/hkmc2/shared/src/test/mlscript/syntax/BackslashApps.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/syntax/BasicIfs.mls b/hkmc2/shared/src/test/mlscript/syntax/BasicIfs.mls index e9fc5c0d96..c4933b38f1 100644 --- a/hkmc2/shared/src/test/mlscript/syntax/BasicIfs.mls +++ b/hkmc2/shared/src/test/mlscript/syntax/BasicIfs.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/syntax/KeywordStutters.mls b/hkmc2/shared/src/test/mlscript/syntax/KeywordStutters.mls index 944f1761a5..327953e695 100644 --- a/hkmc2/shared/src/test/mlscript/syntax/KeywordStutters.mls +++ b/hkmc2/shared/src/test/mlscript/syntax/KeywordStutters.mls @@ -1,3 +1,4 @@ +:. :js @@ -30,7 +31,7 @@ abstract class Foo(a) Bar(b) //β”‚ ╔══[PARSE ERROR] Unexpected identifier here -//β”‚ β•‘ l.31: Bar(b) +//β”‚ β•‘ l.32: Bar(b) //β”‚ ╙── ^^^ @@ -46,10 +47,10 @@ object \$ extends Foo A B //β”‚ ╔══[LEXICAL ERROR] unexpected character '$' -//β”‚ β•‘ l.45: object \$ extends Foo +//β”‚ β•‘ l.46: object \$ extends Foo //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Expected end of input; found error instead -//β”‚ β•‘ l.45: object \$ extends Foo +//β”‚ β•‘ l.46: object \$ extends Foo //β”‚ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/syntax/TypesInTerms.mls b/hkmc2/shared/src/test/mlscript/syntax/TypesInTerms.mls index 2c0d0b680c..15969985a2 100644 --- a/hkmc2/shared/src/test/mlscript/syntax/TypesInTerms.mls +++ b/hkmc2/shared/src/test/mlscript/syntax/TypesInTerms.mls @@ -1,25 +1,26 @@ +:. :js :ge 1 | 2 //β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (alternation) -//β”‚ β•‘ l.5: 1 | 2 +//β”‚ β•‘ l.6: 1 | 2 //β”‚ ╙── ^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :ge print("A") & print("B") //β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (composition) -//β”‚ β•‘ l.12: print("A") & print("B") +//β”‚ β•‘ l.13: print("A") & print("B") //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :ge ~false //β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (negation type) -//β”‚ β•‘ l.19: ~false -//β”‚ ╙── ^^^^^ +//β”‚ β•‘ l.20: ~false +//β”‚ ╙── ^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/syntax/WeirdBrackets.mls b/hkmc2/shared/src/test/mlscript/syntax/WeirdBrackets.mls index adb124e0be..0d03a10d32 100644 --- a/hkmc2/shared/src/test/mlscript/syntax/WeirdBrackets.mls +++ b/hkmc2/shared/src/test/mlscript/syntax/WeirdBrackets.mls @@ -1,3 +1,4 @@ +:. :js @@ -11,15 +12,19 @@ :e \{ Int } //β”‚ ╔══[COMPILATION ERROR] Unsupported refinement in this position. -//β”‚ β•‘ l.12: \{ Int } +//β”‚ β•‘ l.13: \{ Int } //β”‚ ╙── ^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e \(0) -//β”‚ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 -//β”‚ β•‘ l.19: \(0) -//β”‚ ╙── ^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.20: \(0) +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'passTo' expected 2 arguments, but got 1 +//β”‚ β•‘ in ../../mlscript-compile/Predef.mls +//β”‚ β•‘ l.46: fun (\) passTo(receiver, f: dyn)(...args) = f(receiver, ...args) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ = fun fun (\) huh(x) = x @@ -33,7 +38,7 @@ fun (\) huh(x) = x // syntax. In the new `Desugarer`, we chose not to support this syntax. fun f(x) = if x is {( Int then 1, Bool then 2 )} //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern split (parenthesis section). -//β”‚ β•‘ l.34: fun f(x) = if x is {( Int then 1, Bool then 2 )} +//β”‚ β•‘ l.39: fun f(x) = if x is {( Int then 1, Bool then 2 )} //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -42,23 +47,29 @@ fun f(x) = if x is {( Int then 1, Bool then 2 )} :e fun f(x) = if x is ( Int then 1, Bool then 2 ) //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern split (parenthesis section). -//β”‚ β•‘ l.43: fun f(x) = if x is ( Int then 1, Bool then 2 ) +//β”‚ β•‘ l.48: fun f(x) = if x is ( Int then 1, Bool then 2 ) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^ :e fun f(x) = if x is \{ Int then 1, Bool then 2 } //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern split (refinement). -//β”‚ β•‘ l.49: fun f(x) = if x is \{ Int then 1, Bool then 2 } +//β”‚ β•‘ l.54: fun f(x) = if x is \{ Int then 1, Bool then 2 } //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :e fun f(x) = if x is \( Int then 1, Bool then 2 ) -//β”‚ ╔══[COMPILATION ERROR] Cannot use this reference as a pattern. -//β”‚ β•‘ l.55: fun f(x) = if x is \( Int then 1, Bool then 2 ) -//β”‚ ╙── ^ -//β”‚ ╔══[COMPILATION ERROR] Cannot use this reference as a pattern. -//β”‚ β•‘ l.55: fun f(x) = if x is \( Int then 1, Bool then 2 ) -//β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in constructor +//β”‚ β•‘ l.60: fun f(x) = if x is \( Int then 1, Bool then 2 ) +//β”‚ β•‘ ^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'huh' cannot be used as a constructor pattern. +//β”‚ β•‘ l.30: fun (\) huh(x) = x +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in constructor +//β”‚ β•‘ l.60: fun f(x) = if x is \( Int then 1, Bool then 2 ) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'huh' cannot be used as a constructor pattern. +//β”‚ β•‘ l.30: fun (\) huh(x) = x +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^ class Box[T](val content: T) @@ -67,12 +78,12 @@ fun interesting(x) = if x is Box( Int then 1, Bool then 2 ) //β”‚ Split with nested patterns: //β”‚ > if -//β”‚ > x is Box(Int) then 1 -//β”‚ > x is Box(Bool) then 2 +//β”‚ > x is member:Box⁰^term:interesting⁰(member:Int⁰^term:interesting⁰) then 1 +//β”‚ > x is member:Box⁰^term:interesting⁰(member:Bool⁰^term:interesting⁰) then 2 //β”‚ Expanded split with flattened patterns: //β”‚ > β€Ήif|whileβ€Ί -//β”‚ > x is Box(arg$Box$0$) and tmp:arg$Box$0$ is Int then 1 -//β”‚ > x is Box(arg$Box$0$) and tmp:arg$Box$0$ is Bool then 2 +//β”‚ > x is member:Box⁰^term:interesting⁰(arg$Box$0$) and tmp:arg$Box$0$ is member:Int⁰^term:interesting⁰ then 1 +//β”‚ > x is member:Box⁰^term:interesting⁰(arg$Box$0$) and tmp:arg$Box$0$ is member:Bool⁰^term:interesting⁰ then 2 interesting of Box(42) //β”‚ = 1 diff --git a/hkmc2/shared/src/test/mlscript/syntax/annotations/.mls b/hkmc2/shared/src/test/mlscript/syntax/annotations/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/syntax/annotations/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/syntax/annotations/AnnotationPrecedence.mls b/hkmc2/shared/src/test/mlscript/syntax/annotations/AnnotationPrecedence.mls index 7a4bdd5aee..9e00b1b27c 100644 --- a/hkmc2/shared/src/test/mlscript/syntax/annotations/AnnotationPrecedence.mls +++ b/hkmc2/shared/src/test/mlscript/syntax/annotations/AnnotationPrecedence.mls @@ -1,3 +1,4 @@ +:. :js @@ -20,70 +21,83 @@ fun foo(x) = if x is (@annotations.compile A(0)) as y then y :e fun foo(x) = if x is @annotations.compile (A(0) as y) then y //β”‚ ╔══[PARSE ERROR] Unexpected keyword 'then' in this position -//β”‚ β•‘ l.21: fun foo(x) = if x is @annotations.compile (A(0) as y) then y +//β”‚ β•‘ l.22: fun foo(x) = if x is @annotations.compile (A(0) as y) then y //β”‚ ╙── ^^^^ //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern (β€Ήerroneous syntaxβ€Ί). -//β”‚ β•‘ l.21: fun foo(x) = if x is @annotations.compile (A(0) as y) then y +//β”‚ β•‘ l.22: fun foo(x) = if x is @annotations.compile (A(0) as y) then y //β”‚ ╙── ^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.22: fun foo(x) = if x is @annotations.compile (A(0) as y) then y +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Pattern 'A' cannot be called like a function. +//β”‚ β•‘ l.8: pattern A(pattern B) = B +//β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Name not found: y -//β”‚ β•‘ l.21: fun foo(x) = if x is @annotations.compile (A(0) as y) then y +//β”‚ β•‘ l.22: fun foo(x) = if x is @annotations.compile (A(0) as y) then y //β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.22: fun foo(x) = if x is @annotations.compile (A(0) as y) then y +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Object 'compile' cannot be called like a function. +//β”‚ β•‘ in ../../decls/Prelude.mls +//β”‚ β•‘ l.564: object compile +//β”‚ ╙── ^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Name not found: y -//β”‚ β•‘ l.21: fun foo(x) = if x is @annotations.compile (A(0) as y) then y +//β”‚ β•‘ l.22: fun foo(x) = if x is @annotations.compile (A(0) as y) then y //β”‚ ╙── ^ //β”‚ ╔══[WARNING] This annotation is not supported here. -//β”‚ β•‘ l.21: fun foo(x) = if x is @annotations.compile (A(0) as y) then y +//β”‚ β•‘ l.22: fun foo(x) = if x is @annotations.compile (A(0) as y) then y //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ╙── Note: Patterns only support the `@compile` annotation. :e fun foo(x) = if x is @annotations.compile {A(0) as y} then y //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'y' -//β”‚ β•‘ l.40: fun foo(x) = if x is @annotations.compile {A(0) as y} then y +//β”‚ β•‘ l.54: fun foo(x) = if x is @annotations.compile {A(0) as y} then y //β”‚ ╙── ^ :pe // parses as `@annotations.compile(A(0) as y) β€Ήmissing annot bodyβ€Ί` :e fun foo(x) = if x is (@annotations.compile (A(0) as y)) then y //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position -//β”‚ β•‘ l.47: fun foo(x) = if x is (@annotations.compile (A(0) as y)) then y +//β”‚ β•‘ l.61: fun foo(x) = if x is (@annotations.compile (A(0) as y)) then y //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ found a lone annotation instead -//β”‚ β•‘ l.47: fun foo(x) = if x is (@annotations.compile (A(0) as y)) then y +//β”‚ β•‘ l.61: fun foo(x) = if x is (@annotations.compile (A(0) as y)) then y //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern (β€Ήerroneous syntaxβ€Ί). -//β”‚ β•‘ l.47: fun foo(x) = if x is (@annotations.compile (A(0) as y)) then y +//β”‚ β•‘ l.61: fun foo(x) = if x is (@annotations.compile (A(0) as y)) then y //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Name not found: y -//β”‚ β•‘ l.47: fun foo(x) = if x is (@annotations.compile (A(0) as y)) then y +//β”‚ β•‘ l.61: fun foo(x) = if x is (@annotations.compile (A(0) as y)) then y //β”‚ ╙── ^ :pe // parses as `@annotations.compile(A(0) as y) β€Ήmissing annot bodyβ€Ί` :e fun foo(x) = if x is (@annotations.compile(A(0) as y)) then y //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position -//β”‚ β•‘ l.63: fun foo(x) = if x is (@annotations.compile(A(0) as y)) then y +//β”‚ β•‘ l.77: fun foo(x) = if x is (@annotations.compile(A(0) as y)) then y //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ found a lone annotation instead -//β”‚ β•‘ l.63: fun foo(x) = if x is (@annotations.compile(A(0) as y)) then y +//β”‚ β•‘ l.77: fun foo(x) = if x is (@annotations.compile(A(0) as y)) then y //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern (β€Ήerroneous syntaxβ€Ί). -//β”‚ β•‘ l.63: fun foo(x) = if x is (@annotations.compile(A(0) as y)) then y +//β”‚ β•‘ l.77: fun foo(x) = if x is (@annotations.compile(A(0) as y)) then y //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Name not found: y -//β”‚ β•‘ l.63: fun foo(x) = if x is (@annotations.compile(A(0) as y)) then y +//β”‚ β•‘ l.77: fun foo(x) = if x is (@annotations.compile(A(0) as y)) then y //β”‚ ╙── ^ :e fun foo(x) = if x is (@annotations.compile {A(0) as y}) then y //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'y' -//β”‚ β•‘ l.78: fun foo(x) = if x is (@annotations.compile {A(0) as y}) then y +//β”‚ β•‘ l.92: fun foo(x) = if x is (@annotations.compile {A(0) as y}) then y //β”‚ ╙── ^ :fixme fun foo(x) = if x is (@annotations.compile A(0) as y) then y //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'y' -//β”‚ β•‘ l.84: fun foo(x) = if x is (@annotations.compile A(0) as y) then y +//β”‚ β•‘ l.98: fun foo(x) = if x is (@annotations.compile A(0) as y) then y //β”‚ ╙── ^ @@ -91,43 +105,63 @@ fun foo(x) = if x is (@annotations.compile A(0) as y) then y type Test +:e :w @Test 1 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in member reference +//β”‚ β•‘ l.110: @Test 1 +//β”‚ β•‘ ^^^^^ +//β”‚ ╙── Expected a term; got type alias 'Test' //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.95: @Test 1 -//β”‚ β•‘ ^^^^^ +//β”‚ β•‘ l.110: @Test 1 +//β”‚ β•‘ ^^^^^ //β”‚ β•Ÿβ”€β”€ This annotation is not supported on integer literal terms. -//β”‚ β•‘ l.95: @Test 1 -//β”‚ ╙── ^ +//β”‚ β•‘ l.110: @Test 1 +//β”‚ ╙── ^ //β”‚ = 1 +:e :w @Test 2 + 1 +//β”‚ ╔══[COMPILATION ERROR] Resolution error in member reference +//β”‚ β•‘ l.125: @Test 2 + 1 +//β”‚ β•‘ ^^^^^ +//β”‚ ╙── Expected a term; got type alias 'Test' //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.105: @Test 2 + 1 +//β”‚ β•‘ l.125: @Test 2 + 1 //β”‚ β•‘ ^^^^^ //β”‚ β•Ÿβ”€β”€ This annotation is not supported on application terms. -//β”‚ β•‘ l.105: @Test 2 + 1 +//β”‚ β•‘ l.125: @Test 2 + 1 //β”‚ ╙── ^^^^^ //β”‚ = 3 +:e :w @Test 2 as Int +//β”‚ ╔══[COMPILATION ERROR] Resolution error in member reference +//β”‚ β•‘ l.140: @Test 2 as Int +//β”‚ β•‘ ^^^^^ +//β”‚ ╙── Expected a term; got type alias 'Test' //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.115: @Test 2 as Int +//β”‚ β•‘ l.140: @Test 2 as Int //β”‚ β•‘ ^^^^^ //β”‚ β•Ÿβ”€β”€ This annotation is not supported on type ascription terms. -//β”‚ β•‘ l.115: @Test 2 as Int +//β”‚ β•‘ l.140: @Test 2 as Int //β”‚ ╙── ^^^^^^^^ //β”‚ = 2 +:e :w @Test id(2) as Int +//β”‚ ╔══[COMPILATION ERROR] Resolution error in member reference +//β”‚ β•‘ l.155: @Test id(2) as Int +//β”‚ β•‘ ^^^^^ +//β”‚ ╙── Expected a term; got type alias 'Test' //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.125: @Test id(2) as Int +//β”‚ β•‘ l.155: @Test id(2) as Int //β”‚ β•‘ ^^^^^ //β”‚ β•Ÿβ”€β”€ This annotation is not supported on type ascription terms. -//β”‚ β•‘ l.125: @Test id(2) as Int +//β”‚ β•‘ l.155: @Test id(2) as Int //β”‚ ╙── ^^^^^^^^^^^^ //β”‚ = 2 @@ -135,10 +169,10 @@ type Test :re (@Test) //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position -//β”‚ β•‘ l.136: (@Test) +//β”‚ β•‘ l.170: (@Test) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ found a lone annotation instead -//β”‚ β•‘ l.136: (@Test) +//β”‚ β•‘ l.170: (@Test) //β”‚ ╙── ^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -146,10 +180,10 @@ type Test :re print(@Test) //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position -//β”‚ β•‘ l.147: print(@Test) +//β”‚ β•‘ l.181: print(@Test) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ found a lone annotation instead -//β”‚ β•‘ l.147: print(@Test) +//β”‚ β•‘ l.181: print(@Test) //β”‚ ╙── ^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/syntax/annotations/Call.mls b/hkmc2/shared/src/test/mlscript/syntax/annotations/Call.mls index de61cfc842..f890766485 100644 --- a/hkmc2/shared/src/test/mlscript/syntax/annotations/Call.mls +++ b/hkmc2/shared/src/test/mlscript/syntax/annotations/Call.mls @@ -1,3 +1,4 @@ +:. :sir //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” @@ -31,16 +32,30 @@ fun curried(x)(y) = x //β”‚ return @special curriedΒΉ(1)(2) +:e @special identity(1)(2) +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.36: @special identity(1)(2) +//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Integer literal cannot be called like a function. +//β”‚ β•‘ l.36: @special identity(1)(2) +//β”‚ ╙── ^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ let baseCall; set baseCall = identityΒΉ(1); return @special baseCall(2) +//β”‚ throw "This code cannot be run as its compilation yielded an error." fun nullary = 1 //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ define nullary⁰ as fun nullaryΒΉ() { return 1 }; end +:w @special nullary +//β”‚ ╔══[WARNING] This annotation has no effect. +//β”‚ β•‘ l.52: @special nullary +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This annotation is not supported on member reference terms. +//β”‚ β•‘ l.52: @special nullary +//β”‚ ╙── ^^^^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ return @special nullaryΒΉ() @@ -49,7 +64,7 @@ class Box(value: Int) //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Lowered IR |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ define Box⁰ as class BoxΒ² { //β”‚ private val value⁰; -//β”‚ constructor BoxΒΉ(value) { set BoxΒ².this.value⁰ = value; end } +//β”‚ constructor BoxΒΉ(value) { @private define valueΒΉ as val value⁰ = value; end } //β”‚ }; //β”‚ end @@ -64,8 +79,10 @@ class CurriedBox(first: Int)(second: Int) //β”‚ private val first⁰; //β”‚ private val second⁰; //β”‚ constructor CurriedBoxΒΉ(first, second) { -//β”‚ set CurriedBoxΒ².this.first⁰ = first; -//β”‚ set CurriedBoxΒ².this.second⁰ = second; +//β”‚ @private +//β”‚ define firstΒΉ as val first⁰ = first; +//β”‚ @private +//β”‚ define secondΒΉ as val second⁰ = second; //β”‚ end //β”‚ } //β”‚ }; diff --git a/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls b/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls index 35fa9187ad..a6dcbce499 100644 --- a/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls +++ b/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls @@ -1,3 +1,4 @@ +:. :js object tailrec @@ -9,8 +10,8 @@ tailrec @tailrec fun fact_n(n, acc) = if n == 0 then acc else fact_n(n - 1, n * acc) //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.9: @tailrec fun fact_n(n, acc) = -//β”‚ ╙── ^^^^^^^^ +//β”‚ β•‘ l.10: @tailrec fun fact_n(n, acc) = +//β”‚ ╙── ^^^^^^^^ :w fun fact(n) = @@ -18,7 +19,7 @@ fun fact(n) = if n == 0 then acc else go(n - 1, n * acc) go(n, 1) //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.17: @tailrec fun go(n, acc) = +//β”‚ β•‘ l.18: @tailrec fun go(n, acc) = //β”‚ ╙── ^^^^^^^^ data class Freezed(degree: Num) @@ -26,19 +27,19 @@ data class Freezed(degree: Num) :w @Freezed(-273.15) class AbsoluteZero //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.27: @Freezed(-273.15) class AbsoluteZero +//β”‚ β•‘ l.28: @Freezed(-273.15) class AbsoluteZero //β”‚ ╙── ^^^^^^^^^^^^^^^^^ :w @Freezed(-18) class Beverage(name: Str) //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.33: @Freezed(-18) class Beverage(name: Str) +//β”‚ β•‘ l.34: @Freezed(-18) class Beverage(name: Str) //β”‚ ╙── ^^^^^^^^^^^^^ :w @Freezed(-4) let drink = Beverage("Coke") //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.39: @Freezed(-4) let drink = Beverage("Coke") +//β”‚ β•‘ l.40: @Freezed(-4) let drink = Beverage("Coke") //β”‚ ╙── ^^^^^^^^^^^^ //β”‚ drink = Beverage(_) @@ -48,7 +49,7 @@ module Foo with :w @Foo.Bar("baz") class Qux //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.49: @Foo.Bar("baz") class Qux +//β”‚ β•‘ l.50: @Foo.Bar("baz") class Qux //β”‚ ╙── ^^^^^^^^^^^^^^^ // * Not an annotation, but an applicationo f operator `@`... @@ -56,7 +57,7 @@ module Foo with :re @42 class Qux //β”‚ ╔══[PARSE ERROR] Expected end of input; found 'class' keyword instead -//β”‚ β•‘ l.57: @42 class Qux +//β”‚ β•‘ l.58: @42 class Qux //β”‚ ╙── ^^^^^ //β”‚ ═══[RUNTIME ERROR] TypeError: 42 is not a function @@ -64,7 +65,7 @@ module Foo with :re @(1 + 2) class Qux //β”‚ ╔══[PARSE ERROR] Expected end of input; found 'class' keyword instead -//β”‚ β•‘ l.65: @(1 + 2) class Qux +//β”‚ β•‘ l.66: @(1 + 2) class Qux //β”‚ ╙── ^^^^^ //β”‚ ═══[RUNTIME ERROR] TypeError: 3 is not a function @@ -72,7 +73,7 @@ module Foo with :re @1 module Qux //β”‚ ╔══[PARSE ERROR] Expected end of input; found 'module' keyword instead -//β”‚ β•‘ l.73: @1 module Qux +//β”‚ β•‘ l.74: @1 module Qux //β”‚ ╙── ^^^^^^ //β”‚ ═══[RUNTIME ERROR] TypeError: 1 is not a function @@ -85,10 +86,10 @@ fun min(x, y) = if x < y then x else y max(x, y) = if x > y then x else y //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.83: @inline +//β”‚ β•‘ l.84: @inline //β”‚ ╙── ^^^^^^^ //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.83: @inline +//β”‚ β•‘ l.84: @inline //β”‚ ╙── ^^^^^^^ :w @@ -96,10 +97,10 @@ fun abs(x) = if x < 0 then -x else x clamp(x, lo, hi) = min(max(x, lo), hi) //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.95: @inline let +//β”‚ β•‘ l.96: @inline let //β”‚ ╙── ^^^^^^^ //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.95: @inline let +//β”‚ β•‘ l.96: @inline let //β”‚ ╙── ^^^^^^^ //β”‚ abs = fun abs //β”‚ clamp = fun clamp @@ -110,7 +111,7 @@ let @inline success = 0 failure = 1 //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.110: @inline success = 0 +//β”‚ β•‘ l.111: @inline success = 0 //β”‚ ╙── ^^^^^^^ //β”‚ failure = 1 //β”‚ success = 0 @@ -122,10 +123,10 @@ let @inline @tailrec fun fib(n) = if n < 2 then n else fib(n - 1) + fib(n - 2) //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.122: @inline @tailrec fun fib(n) = +//β”‚ β•‘ l.123: @inline @tailrec fun fib(n) = //β”‚ ╙── ^^^^^^^ //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.122: @inline @tailrec fun fib(n) = +//β”‚ β•‘ l.123: @inline @tailrec fun fib(n) = //β”‚ ╙── ^^^^^^^^ object internal @@ -137,16 +138,16 @@ object internal transient volatile //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.134: @internal object +//β”‚ β•‘ l.135: @internal object //β”‚ ╙── ^^^^^^^^^ //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.134: @internal object +//β”‚ β•‘ l.135: @internal object //β”‚ ╙── ^^^^^^^^^ //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.134: @internal object +//β”‚ β•‘ l.135: @internal object //β”‚ ╙── ^^^^^^^^^ //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.134: @internal object +//β”‚ β•‘ l.135: @internal object //β”‚ ╙── ^^^^^^^^^ :w @@ -156,31 +157,31 @@ object internal @volatile @transient empty = "" normal = () //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.153: @volatile let +//β”‚ β•‘ l.154: @volatile let //β”‚ ╙── ^^^^^^^^^ //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.154: @shared counter = 0 +//β”‚ β•‘ l.155: @shared counter = 0 //β”‚ ╙── ^^^^^^^ //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.153: @volatile let +//β”‚ β•‘ l.154: @volatile let //β”‚ ╙── ^^^^^^^^^ //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.155: @transient @thread_local cache = () +//β”‚ β•‘ l.156: @transient @thread_local cache = () //β”‚ ╙── ^^^^^^^^^^ //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.155: @transient @thread_local cache = () +//β”‚ β•‘ l.156: @transient @thread_local cache = () //β”‚ ╙── ^^^^^^^^^^^^^ //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.153: @volatile let +//β”‚ β•‘ l.154: @volatile let //β”‚ ╙── ^^^^^^^^^ //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.156: @volatile @transient empty = "" +//β”‚ β•‘ l.157: @volatile @transient empty = "" //β”‚ ╙── ^^^^^^^^^ //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.156: @volatile @transient empty = "" +//β”‚ β•‘ l.157: @volatile @transient empty = "" //β”‚ ╙── ^^^^^^^^^^ //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.153: @volatile let +//β”‚ β•‘ l.154: @volatile let //β”‚ ╙── ^^^^^^^^^ //β”‚ cache = () //β”‚ counter = 0 diff --git a/hkmc2/shared/src/test/mlscript/syntax/annotations/Pattern.mls b/hkmc2/shared/src/test/mlscript/syntax/annotations/Pattern.mls index 89941a9a80..390aac3733 100644 --- a/hkmc2/shared/src/test/mlscript/syntax/annotations/Pattern.mls +++ b/hkmc2/shared/src/test/mlscript/syntax/annotations/Pattern.mls @@ -1,3 +1,4 @@ +:. :js open annotations { compile } @@ -22,23 +23,23 @@ object do_not_compile :w fun foo(x) = if x is @do_not_compile Box then "yes" else "no" //β”‚ ╔══[WARNING] This annotation is not supported here. -//β”‚ β•‘ l.23: fun foo(x) = if x is @do_not_compile Box then "yes" else "no" +//β”‚ β•‘ l.24: fun foo(x) = if x is @do_not_compile Box then "yes" else "no" //β”‚ β•‘ ^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ Note: Patterns only support the `@compile` annotation. -//β”‚ β•‘ l.23: fun foo(x) = if x is @do_not_compile Box then "yes" else "no" +//β”‚ β•‘ l.24: fun foo(x) = if x is @do_not_compile Box then "yes" else "no" //β”‚ ╙── ^^^ :w :e fun foo(x) = if x is @name_not_found Box then "yes" else "no" //β”‚ ╔══[COMPILATION ERROR] Name not found: name_not_found -//β”‚ β•‘ l.33: fun foo(x) = if x is @name_not_found Box then "yes" else "no" +//β”‚ β•‘ l.34: fun foo(x) = if x is @name_not_found Box then "yes" else "no" //β”‚ ╙── ^^^^^^^^^^^^^^^ //β”‚ ╔══[WARNING] This annotation is not supported here. -//β”‚ β•‘ l.33: fun foo(x) = if x is @name_not_found Box then "yes" else "no" +//β”‚ β•‘ l.34: fun foo(x) = if x is @name_not_found Box then "yes" else "no" //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ Note: Patterns only support the `@compile` annotation. -//β”‚ β•‘ l.33: fun foo(x) = if x is @name_not_found Box then "yes" else "no" +//β”‚ β•‘ l.34: fun foo(x) = if x is @name_not_found Box then "yes" else "no" //β”‚ ╙── ^^^ pattern Box' = Box @@ -48,9 +49,9 @@ fun foo(x) = if x is @compile Box' then "yes" else "no" :w fun foo(x) = if x is @do_not_compile Box' then "yes" else "no" //β”‚ ╔══[WARNING] This annotation is not supported here. -//β”‚ β•‘ l.49: fun foo(x) = if x is @do_not_compile Box' then "yes" else "no" +//β”‚ β•‘ l.50: fun foo(x) = if x is @do_not_compile Box' then "yes" else "no" //β”‚ β•‘ ^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ Note: Patterns only support the `@compile` annotation. -//β”‚ β•‘ l.49: fun foo(x) = if x is @do_not_compile Box' then "yes" else "no" +//β”‚ β•‘ l.50: fun foo(x) = if x is @do_not_compile Box' then "yes" else "no" //β”‚ ╙── ^^^^ diff --git a/hkmc2/shared/src/test/mlscript/syntax/annotations/Unsupported.mls b/hkmc2/shared/src/test/mlscript/syntax/annotations/Unsupported.mls index 354b84e99d..7bbbc23d92 100644 --- a/hkmc2/shared/src/test/mlscript/syntax/annotations/Unsupported.mls +++ b/hkmc2/shared/src/test/mlscript/syntax/annotations/Unsupported.mls @@ -1,3 +1,4 @@ +:. :js // The annotations for the following expressions are not supported for now. @@ -9,59 +10,59 @@ import "../../../mlscript-compile/StrOps.mls" @debug open StrOps //β”‚ ╔══[WARNING] This annotation has no effect -//β”‚ β•‘ l.9: @debug -//β”‚ β•‘ ^^^^^^ +//β”‚ β•‘ l.10: @debug +//β”‚ β•‘ ^^^^^^ //β”‚ β•Ÿβ”€β”€ Annotations are not supported on open terms. -//β”‚ β•‘ l.10: open StrOps +//β”‚ β•‘ l.11: open StrOps //β”‚ ╙── ^^^^^^^^^^^ :w @debug 0 //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.19: @debug 0 +//β”‚ β•‘ l.20: @debug 0 //β”‚ β•‘ ^^^^^^ //β”‚ β•Ÿβ”€β”€ This annotation is not supported on integer literal terms. -//β”‚ β•‘ l.19: @debug 0 +//β”‚ β•‘ l.20: @debug 0 //β”‚ ╙── ^ //β”‚ = 0 :w @debug 1 + 2 //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.29: @debug 1 + 2 +//β”‚ β•‘ l.30: @debug 1 + 2 //β”‚ β•‘ ^^^^^^ //β”‚ β•Ÿβ”€β”€ This annotation is not supported on application terms. -//β”‚ β•‘ l.29: @debug 1 + 2 +//β”‚ β•‘ l.30: @debug 1 + 2 //β”‚ ╙── ^^^^^ //β”‚ = 3 :w (@debug 1 + 2) //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.39: (@debug 1 + 2) +//β”‚ β•‘ l.40: (@debug 1 + 2) //β”‚ β•‘ ^^^^^^ //β”‚ β•Ÿβ”€β”€ This annotation is not supported on application terms. -//β”‚ β•‘ l.39: (@debug 1 + 2) +//β”‚ β•‘ l.40: (@debug 1 + 2) //β”‚ ╙── ^^^^^ //β”‚ = 3 :w (@debug 1) + 2 //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.49: (@debug 1) + 2 +//β”‚ β•‘ l.50: (@debug 1) + 2 //β”‚ β•‘ ^^^^^^ //β”‚ β•Ÿβ”€β”€ This annotation is not supported on integer literal terms. -//β”‚ β•‘ l.49: (@debug 1) + 2 +//β”‚ β•‘ l.50: (@debug 1) + 2 //β”‚ ╙── ^ //β”‚ = 3 :w (1 + @debug 2) //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.59: (1 + @debug 2) +//β”‚ β•‘ l.60: (1 + @debug 2) //β”‚ β•‘ ^^^^^^ //β”‚ β•Ÿβ”€β”€ This annotation is not supported on integer literal terms. -//β”‚ β•‘ l.59: (1 + @debug 2) +//β”‚ β•‘ l.60: (1 + @debug 2) //β”‚ ╙── ^ //β”‚ = 3 @@ -70,10 +71,10 @@ data class Log(msg: Str) :w id(@Log 5) //β”‚ ╔══[WARNING] This annotation has no effect. -//β”‚ β•‘ l.71: id(@Log 5) +//β”‚ β•‘ l.72: id(@Log 5) //β”‚ β•‘ ^^^^ //β”‚ β•Ÿβ”€β”€ This annotation is not supported on integer literal terms. -//β”‚ β•‘ l.71: id(@Log 5) +//β”‚ β•‘ l.72: id(@Log 5) //β”‚ ╙── ^ //β”‚ = 5 @@ -81,6 +82,6 @@ id(@Log 5) :re @1 + 2 class Qux //β”‚ ╔══[PARSE ERROR] Expected end of input; found 'class' keyword instead -//β”‚ β•‘ l.82: @1 + 2 class Qux +//β”‚ β•‘ l.83: @1 + 2 class Qux //β”‚ ╙── ^^^^^ //β”‚ ═══[RUNTIME ERROR] TypeError: 3 is not a function diff --git a/hkmc2/shared/src/test/mlscript/tailrec/.mls b/hkmc2/shared/src/test/mlscript/tailrec/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/tailrec/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/tailrec/Errors.mls b/hkmc2/shared/src/test/mlscript/tailrec/Errors.mls index ba9d364a61..1ba2afdb86 100644 --- a/hkmc2/shared/src/test/mlscript/tailrec/Errors.mls +++ b/hkmc2/shared/src/test/mlscript/tailrec/Errors.mls @@ -1,3 +1,4 @@ +:. :js open annotations @@ -7,14 +8,14 @@ fun f(x) = @tailcall f(x) f(x) //β”‚ ╔══[COMPILATION ERROR] This call is not in tail position. -//β”‚ β•‘ l.7: @tailcall f(x) +//β”‚ β•‘ l.8: @tailcall f(x) //β”‚ ╙── ^^^^ :e fun f(x) = 2 fun g(x) = @tailcall f(x) //β”‚ ╔══[COMPILATION ERROR] This call is not optimized as it does not directly recurse through its parent function. -//β”‚ β•‘ l.15: fun g(x) = @tailcall f(x) +//β”‚ β•‘ l.16: fun g(x) = @tailcall f(x) //β”‚ ╙── ^^^^ :e @@ -22,10 +23,10 @@ fun g(x) = @tailcall f(x) g(x) fun g(x) = f(x); f(x) //β”‚ ╔══[COMPILATION ERROR] This function is marked @tailrec but is not tail recursive. -//β”‚ β•‘ l.21: @tailrec fun f(x) = +//β”‚ β•‘ l.22: @tailrec fun f(x) = //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ It could self-recurse through this call, which is not a tail call. -//β”‚ β•‘ l.23: fun g(x) = f(x); f(x) +//β”‚ β•‘ l.24: fun g(x) = f(x); f(x) //β”‚ ╙── ^^^^ :e @@ -37,10 +38,10 @@ fun g(x) = fun h(x) = g(x) //β”‚ ╔══[COMPILATION ERROR] This function is marked @tailrec but is not tail recursive. -//β”‚ β•‘ l.32: @tailrec fun f(x) = +//β”‚ β•‘ l.33: @tailrec fun f(x) = //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ It could self-recurse through this call, which is not a tail call. -//β”‚ β•‘ l.35: f(x) +//β”‚ β•‘ l.36: f(x) //β”‚ ╙── ^^^^ :e @@ -52,10 +53,10 @@ fun g(x) = fun h(x) = f(x) //β”‚ ╔══[COMPILATION ERROR] This function is marked @tailrec but is not tail recursive. -//β”‚ β•‘ l.47: @tailrec fun f(x) = +//β”‚ β•‘ l.48: @tailrec fun f(x) = //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ It could self-recurse through this call, which is not a tail call. -//β”‚ β•‘ l.50: h(x) +//β”‚ β•‘ l.51: h(x) //β”‚ ╙── ^^^^ :e @@ -65,14 +66,14 @@ module A with @tailcall f(x - 1) @tailcall f(x - 1) //β”‚ ╔══[COMPILATION ERROR] This call is not in tail position. -//β”‚ β•‘ l.65: @tailcall f(x - 1) +//β”‚ β•‘ l.66: @tailcall f(x - 1) //β”‚ ╙── ^^^^^^^^ :w @tailrec fun f = 2 //β”‚ ╔══[WARNING] This function is marked @tailrec but has no direct self-recursion. -//β”‚ β•‘ l.73: fun f = 2 +//β”‚ β•‘ l.74: fun f = 2 //β”‚ ╙── ^ :w @@ -80,7 +81,7 @@ module A with @tailrec fun f() = 2 //β”‚ ╔══[WARNING] This function is marked @tailrec but has no direct self-recursion. -//β”‚ β•‘ l.81: fun f() = 2 +//β”‚ β•‘ l.82: fun f() = 2 //β”‚ ╙── ^ :fixme // TODO: support @@ -89,7 +90,7 @@ fun foo() = Foo.bar() module Foo with fun bar() = foo() //β”‚ ╔══[WARNING] This function is marked @tailrec but has no direct self-recursion. -//β”‚ β•‘ l.88: fun foo() = Foo.bar() +//β”‚ β•‘ l.89: fun foo() = Foo.bar() //β”‚ ╙── ^^^ :e @@ -98,7 +99,7 @@ fun f(x) = fun g() = g() g() //β”‚ ╔══[COMPILATION ERROR] This @tailrec function was not processed by the tail-call optimizer. -//β”‚ β•‘ l.98: fun g() = g() +//β”‚ β•‘ l.99: fun g() = g() //β”‚ ╙── ^ :e @@ -107,7 +108,7 @@ fun scan(idx, acc) = fun go(idx, tok) = scan(idx, tok) go(idx, acc) //β”‚ ╔══[COMPILATION ERROR] This @tailrec function was not processed by the tail-call optimizer. -//β”‚ β•‘ l.107: fun go(idx, tok) = scan(idx, tok) +//β”‚ β•‘ l.108: fun go(idx, tok) = scan(idx, tok) //β”‚ ╙── ^^ :e @@ -118,10 +119,10 @@ fun scan(idx, acc) = fun go(idx, tok) = scan(idx, tok) go(idx, acc) //β”‚ ╔══[WARNING] This function is marked @tailrec but has no direct self-recursion. -//β”‚ β•‘ l.116: fun scan(idx, acc) = +//β”‚ β•‘ l.117: fun scan(idx, acc) = //β”‚ ╙── ^^^^ //β”‚ ╔══[COMPILATION ERROR] This @tailrec function was not processed by the tail-call optimizer. -//β”‚ β•‘ l.118: fun go(idx, tok) = scan(idx, tok) +//β”‚ β•‘ l.119: fun go(idx, tok) = scan(idx, tok) //β”‚ ╙── ^^ :e @@ -129,21 +130,21 @@ fun scan(idx, acc) = @config(tailRecOpt: false) fun foo() = foo() //β”‚ ╔══[COMPILATION ERROR] This @tailrec function was not processed by the tail-call optimizer. -//β”‚ β•‘ l.130: fun foo() = foo() +//β”‚ β•‘ l.131: fun foo() = foo() //β”‚ ╙── ^^^ :e fun f = @tailcall id(2) //β”‚ ╔══[COMPILATION ERROR] Only functions in this compilation unit may be marked @tailcall. -//β”‚ β•‘ l.137: @tailcall id(2) +//β”‚ β•‘ l.138: @tailcall id(2) //β”‚ ╙── ^^^^^ :e fun f(x)(y) = @tailcall f(x) //β”‚ ╔══[COMPILATION ERROR] Only fully applied calls may be marked @tailcall. -//β”‚ β•‘ l.144: @tailcall f(x) +//β”‚ β•‘ l.145: @tailcall f(x) //β”‚ ╙── ^^^^ :e @@ -152,10 +153,10 @@ fun f(x)() = fun g(x)(y) = @tailcall f(x) //β”‚ ╔══[COMPILATION ERROR] Only fully applied calls may be marked @tailcall. -//β”‚ β•‘ l.153: @tailcall f(x) +//β”‚ β•‘ l.154: @tailcall f(x) //β”‚ ╙── ^^^^ //β”‚ ╔══[COMPILATION ERROR] This call is not optimized as it does not directly recurse through its parent function. -//β”‚ β•‘ l.151: @tailcall g(x)(0) +//β”‚ β•‘ l.152: @tailcall g(x)(0) //β”‚ ╙── ^^^^^^^ @@ -164,7 +165,7 @@ module M with @tailrec fun f = print("ok"); f //β”‚ ╔══[WARNING] This function is marked @tailrec but has no direct self-recursion. -//β”‚ β•‘ l.165: fun f = print("ok"); f +//β”‚ β•‘ l.166: fun f = print("ok"); f //β”‚ ╙── ^ :w @@ -172,7 +173,7 @@ module M with @tailrec fun f = case 0 then f(1) //β”‚ ╔══[WARNING] This function is marked @tailrec but has no direct self-recursion. -//β”‚ β•‘ l.173: fun f = case 0 then f(1) +//β”‚ β•‘ l.174: fun f = case 0 then f(1) //β”‚ ╙── ^ @@ -181,10 +182,10 @@ module M with fun foo(x) = foo(1) + 1 //β”‚ ╔══[COMPILATION ERROR] This function is marked @tailrec but is not tail recursive. -//β”‚ β•‘ l.181: fun foo(x) = +//β”‚ β•‘ l.182: fun foo(x) = //β”‚ β•‘ ^^^ //β”‚ β•Ÿβ”€β”€ It could self-recurse through this call, which is not a tail call. -//β”‚ β•‘ l.182: foo(1) + 1 +//β”‚ β•‘ l.183: foo(1) + 1 //β”‚ ╙── ^^^^^^ :w @@ -192,6 +193,6 @@ fun foo(x) = fun foo(x) = 1 |> foo //β”‚ ╔══[WARNING] This function is marked @tailrec but has no direct self-recursion. -//β”‚ β•‘ l.192: fun foo(x) = +//β”‚ β•‘ l.193: fun foo(x) = //β”‚ ╙── ^^^ diff --git a/hkmc2/shared/src/test/mlscript/tailrec/MultiArgLists.mls b/hkmc2/shared/src/test/mlscript/tailrec/MultiArgLists.mls index a6a0ffc618..9f9420adb1 100644 --- a/hkmc2/shared/src/test/mlscript/tailrec/MultiArgLists.mls +++ b/hkmc2/shared/src/test/mlscript/tailrec/MultiArgLists.mls @@ -1,3 +1,4 @@ +:. :js open annotations @@ -180,7 +181,7 @@ fun resume(x)(y) = handleEffects(x) //β”‚ 1 => //β”‚ let tmp; //β”‚ set tmp = resume⁰(undefined); -//β”‚ do sink⁰.pushοΉ–(tmp); +//β”‚ do sink⁰.push⁰(tmp); //β”‚ set id = 0; //β”‚ continue loopLabel //β”‚ else @@ -243,8 +244,8 @@ fun g(n)(m) = //β”‚ set param0_tmp = param0; //β”‚ set param0 = tmp; //β”‚ set argList = [...tmp1, 3, ...tmp2, ...tmp3, 7, param0_tmp]; -//β”‚ set tmp4 = -ΒΉ(argList.lengthοΉ–, 0); -//β”‚ set inlinedVal = argList.sliceοΉ–(4, tmp4); +//β”‚ set tmp4 = -ΒΉ(argList.length⁰, 0); +//β”‚ set inlinedVal = argList.slice⁰(4, tmp4); //β”‚ set param1 = argList.0; //β”‚ do argList.1; //β”‚ do argList.2; @@ -287,8 +288,8 @@ fun f(a)(...z) = if z is //β”‚ Array(1+) => //β”‚ let inlinedVal, tmp1; //β”‚ do runtime⁰.Tuple⁰.get⁰(z, 0); -//β”‚ set tmp1 = -ΒΉ(z.lengthοΉ–, 0); -//β”‚ set inlinedVal = z.sliceοΉ–(1, tmp1); +//β”‚ set tmp1 = -ΒΉ(z.length⁰, 0); +//β”‚ set inlinedVal = z.slice⁰(1, tmp1); //β”‚ set tmp = +⁰(a, 1); //β”‚ set a = tmp; //β”‚ set z = inlinedVal; @@ -494,8 +495,8 @@ fun f(n)(a)(...z) = //β”‚ Array(1+) => //β”‚ let inlinedVal, tmp2; //β”‚ do runtime⁰.Tuple⁰.get⁰(z, 0); -//β”‚ set tmp2 = -ΒΉ(z.lengthοΉ–, 0); -//β”‚ set inlinedVal = z.sliceοΉ–(1, tmp2); +//β”‚ set tmp2 = -ΒΉ(z.length⁰, 0); +//β”‚ set inlinedVal = z.slice⁰(1, tmp2); //β”‚ set tmp1 = +⁰(a, 1); //β”‚ set res = f$tailrecΒ³(n, tmp1, inlinedVal); //β”‚ do Predef⁰.print⁰("hi!"); diff --git a/hkmc2/shared/src/test/mlscript/tailrec/Simple.mls b/hkmc2/shared/src/test/mlscript/tailrec/Simple.mls index 9f44db8584..4ac492599e 100644 --- a/hkmc2/shared/src/test/mlscript/tailrec/Simple.mls +++ b/hkmc2/shared/src/test/mlscript/tailrec/Simple.mls @@ -1,3 +1,4 @@ +:. :js open annotations @@ -14,7 +15,7 @@ module Foo with @tailrec fun bar = bar //β”‚ ╔══[WARNING] This function is marked @tailrec but has no direct self-recursion. -//β”‚ β•‘ l.15: fun bar = bar +//β”‚ β•‘ l.16: fun bar = bar //β”‚ ╙── ^^^ @tailrec @@ -53,6 +54,6 @@ module A with module B with fun g(x) = A.f(x * 2) //β”‚ ╔══[WARNING] This function is marked @tailrec but has no direct self-recursion. -//β”‚ β•‘ l.52: fun f(x) = B.g(x + 1) +//β”‚ β•‘ l.53: fun f(x) = B.g(x + 1) //β”‚ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls b/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls index c6bc716852..a1e363d783 100644 --- a/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls +++ b/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls @@ -1,3 +1,4 @@ +:. :js open annotations @@ -177,8 +178,8 @@ fun g(n) = //β”‚ set tmp1 = [3, 4]; //β”‚ set tmp2 = [5, param0]; //β”‚ set argList = [param0, ...tmp, 3, ...tmp1, ...tmp2, 7, param0]; -//β”‚ set tmp3 = -ΒΉ(argList.lengthοΉ–, 0); -//β”‚ set inlinedVal = argList.sliceοΉ–(5, tmp3); +//β”‚ set tmp3 = -ΒΉ(argList.length⁰, 0); +//β”‚ set inlinedVal = argList.slice⁰(5, tmp3); //β”‚ set param0 = argList.0; //β”‚ do argList.1; //β”‚ do argList.2; @@ -213,7 +214,7 @@ fun f(x) = @tailcall f(x) @tailcall g() //β”‚ ╔══[COMPILATION ERROR] This call is not in tail position. -//β”‚ β•‘ l.212: @tailcall f(x) +//β”‚ β•‘ l.213: @tailcall f(x) //β”‚ ╙── ^^^^ :lift @@ -405,21 +406,27 @@ module A with @tailcall g(x - 1) A.f(10000) //β”‚ ╔══[COMPILATION ERROR] This tail call exits the current scope and is not optimized. -//β”‚ β•‘ l.404: fun g(x) = if x < 0 then 0 else @tailcall f(x) +//β”‚ β•‘ l.405: fun g(x) = if x < 0 then 0 else @tailcall f(x) //β”‚ ╙── ^^^^ //β”‚ ╔══[COMPILATION ERROR] This tail call exits the current scope and is not optimized. -//β”‚ β•‘ l.405: @tailcall g(x - 1) +//β”‚ β•‘ l.406: @tailcall g(x - 1) //β”‚ ╙── ^^^^^^^^ //β”‚ = 0 -// These calls are represented as field selections and don't yet have the explicitTailCall parameter. -:breakme -:e +// Getter selections do not yet carry an explicit tail-call flag. Require the +// unused-annotation warning until the non-tail getter call can be diagnosed. +:w module A with fun f = g fun g = @tailcall f f +//β”‚ ╔══[WARNING] This annotation has no effect. +//β”‚ β•‘ l.422: @tailcall f +//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ This annotation is not supported on selection terms. +//β”‚ β•‘ l.422: @tailcall f +//β”‚ ╙── ^ :expect 0 module A with @@ -435,12 +442,12 @@ class A with fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) (new A).f(10) //β”‚ ╔══[COMPILATION ERROR] Calls from class methods cannot yet be marked @tailcall. -//β”‚ β•‘ l.434: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) +//β”‚ β•‘ l.441: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) //β”‚ ╙── ^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Class methods may not yet be marked @tailrec. -//β”‚ β•‘ l.434: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) +//β”‚ β•‘ l.441: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Calls from class methods cannot yet be marked @tailcall. -//β”‚ β•‘ l.435: fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) +//β”‚ β•‘ l.442: fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) //β”‚ ╙── ^^^^^^^^ //β”‚ = 0 diff --git a/hkmc2/shared/src/test/mlscript/ucs/.mls b/hkmc2/shared/src/test/mlscript/ucs/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ucs/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ucs/AndNewline.mls b/hkmc2/shared/src/test/mlscript/ucs/AndNewline.mls index 731f20a09e..f0121e319f 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/AndNewline.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/AndNewline.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/ucs/examples/.mls b/hkmc2/shared/src/test/mlscript/ucs/examples/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ucs/examples/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ucs/examples/BinarySearchTree.mls b/hkmc2/shared/src/test/mlscript/ucs/examples/BinarySearchTree.mls index f056d748ca..9625d98348 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/examples/BinarySearchTree.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/examples/BinarySearchTree.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Option.mls" diff --git a/hkmc2/shared/src/test/mlscript/ucs/examples/EitherOrBoth.mls b/hkmc2/shared/src/test/mlscript/ucs/examples/EitherOrBoth.mls index 2e448387d2..940d4c4b1d 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/examples/EitherOrBoth.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/examples/EitherOrBoth.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/StrOps.mls" @@ -5,6 +6,7 @@ import "../../../mlscript-compile/Option.mls" open Option open StrOps +open StrOps { concat2, ~ } abstract class EitherOrBoth[out A, out B]: (Left[A, B] | Right[A, B] | Both[A, B]) data class Left[out A, out B](value: A) extends EitherOrBoth[A, B] @@ -49,7 +51,7 @@ fun map[A, B, C, D](eob: EitherOrBoth[A, B], f: A -> C, g: B -> D): EitherOrBoth Right(right) then Right(g(right)) Both(left, right) then Both(f(left), g(right)) -fun fold[A, B, C](eob: EitherOrBoth[A, B], f: A -> C, g: B -> C, h: [A, B] -> C): C = +fun fold[A, B, C](eob: EitherOrBoth[A, B], f: A -> C, g: B -> C, h: (A, B) -> C): C = if eob is Left(left) then f(left) Right(right) then g(right) @@ -78,3 +80,10 @@ fun eobToString[A, B](eob: EitherOrBoth[A, B]): Str = Left(left) then "Left(" ~ StrOps.from(left) ~ ")" Right(right) then "Right(" ~ StrOps.from(right) ~ ")" Both(left, right) then "Both(" ~ StrOps.from(left) ~ ", " ~ StrOps.from(right) ~ ")" + +// The two-value branch uses a binary callback. +:expect [2, 3, 5] +[fold(Left(2), x => x, y => y, (x, y) => x + y), + fold(Right(3), x => x, y => y, (x, y) => x + y), + fold(Both(2, 3), x => x, y => y, (x, y) => x + y)] +//β”‚ = [2, 3, 5] diff --git a/hkmc2/shared/src/test/mlscript/ucs/examples/LeftistTree.mls b/hkmc2/shared/src/test/mlscript/ucs/examples/LeftistTree.mls index 1a6f26f4b9..568b11e80f 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/examples/LeftistTree.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/examples/LeftistTree.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Option.mls" diff --git a/hkmc2/shared/src/test/mlscript/ucs/examples/ListFold.mls b/hkmc2/shared/src/test/mlscript/ucs/examples/ListFold.mls index 0ddc2be534..1d86cec9e9 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/examples/ListFold.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/examples/ListFold.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Stack.mls" @@ -7,6 +8,7 @@ import "../../../mlscript-compile/StrOps.mls" open Stack open Option open StrOps +open StrOps { concat2, ~ } fun (|>) pipe(x, f) = f(x) diff --git a/hkmc2/shared/src/test/mlscript/ucs/examples/Permutations.mls b/hkmc2/shared/src/test/mlscript/ucs/examples/Permutations.mls index 5f2f8e6268..d02ae082c7 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/examples/Permutations.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/examples/Permutations.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Stack.mls" diff --git a/hkmc2/shared/src/test/mlscript/ucs/examples/SimpleTree.mls b/hkmc2/shared/src/test/mlscript/ucs/examples/SimpleTree.mls index 7602d1ed58..6fb0a6de6c 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/examples/SimpleTree.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/examples/SimpleTree.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Option.mls" diff --git a/hkmc2/shared/src/test/mlscript/ucs/examples/ULC.mls b/hkmc2/shared/src/test/mlscript/ucs/examples/ULC.mls index d6799fd716..823dfebe53 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/examples/ULC.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/examples/ULC.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Option.mls" diff --git a/hkmc2/shared/src/test/mlscript/ucs/future/.mls b/hkmc2/shared/src/test/mlscript/ucs/future/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ucs/future/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ucs/future/AppSplits.mls b/hkmc2/shared/src/test/mlscript/ucs/future/AppSplits.mls index 35ce1e6da0..619b4e6738 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/future/AppSplits.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/future/AppSplits.mls @@ -1,3 +1,4 @@ +:. :js // AppSplits.mls @@ -12,11 +13,11 @@ if foo of 0 then "a" 1 then "b" //β”‚ ╔══[COMPILATION ERROR] Unrecognized term split (application) -//β”‚ β•‘ l.11: if foo of +//β”‚ β•‘ l.12: if foo of //β”‚ β•‘ ^^^^^^ -//β”‚ β•‘ l.12: 0 then "a" +//β”‚ β•‘ l.13: 0 then "a" //β”‚ β•‘ ^^^^^^^^^^^^ -//β”‚ β•‘ l.13: 1 then "b" +//β”‚ β•‘ l.14: 1 then "b" //β”‚ ╙── ^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: match error @@ -27,11 +28,11 @@ if foo of 1, 0 then "a" 1 then "b" //β”‚ ╔══[COMPILATION ERROR] Unrecognized term split (application) -//β”‚ β•‘ l.26: if foo of 1, +//β”‚ β•‘ l.27: if foo of 1, //β”‚ β•‘ ^^^^^^^^^ -//β”‚ β•‘ l.27: 0 then "a" +//β”‚ β•‘ l.28: 0 then "a" //β”‚ β•‘ ^^^^^^^^^^^^ -//β”‚ β•‘ l.28: 1 then "b" +//β”‚ β•‘ l.29: 1 then "b" //β”‚ ╙── ^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: match error @@ -42,9 +43,9 @@ if foo (0) then "a" (1) then "b" //β”‚ ╔══[COMPILATION ERROR] Unrecognized term split (juxtaposition) -//β”‚ β•‘ l.41: if foo +//β”‚ β•‘ l.42: if foo //β”‚ β•‘ ^^^ -//β”‚ β•‘ l.42: (0) then "a" +//β”‚ β•‘ l.43: (0) then "a" //β”‚ ╙── ^^ //β”‚ ═══[RUNTIME ERROR] Error: match error diff --git a/hkmc2/shared/src/test/mlscript/ucs/future/Or.mls b/hkmc2/shared/src/test/mlscript/ucs/future/Or.mls index 1b6d500fef..b4f2624395 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/future/Or.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/future/Or.mls @@ -1,3 +1,4 @@ +:. :js data class Some[T](value: T) @@ -11,9 +12,9 @@ fun f(a, b) = if a is or b is Some(v) then v else 0 //β”‚ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here -//β”‚ β•‘ l.10: and b is Some(v') then v + v' +//β”‚ β•‘ l.11: and b is Some(v') then v + v' //β”‚ β•‘ ^^^^^^ -//β”‚ β•‘ l.11: or b is Some(v) then v +//β”‚ β•‘ l.12: or b is Some(v) then v //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ucs/future/SymbolicClass.mls b/hkmc2/shared/src/test/mlscript/ucs/future/SymbolicClass.mls index 7cc3365fd9..1e15c28e91 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/future/SymbolicClass.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/future/SymbolicClass.mls @@ -1,3 +1,4 @@ +:. :js // :de @@ -19,7 +20,7 @@ Cons(1, Nil) :ge 1 :: 2 //β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Int' at an unrelated type 'Object' -//β”‚ β•‘ l.20: 1 :: 2 +//β”‚ β•‘ l.21: 1 :: 2 //β”‚ ╙── ^ //β”‚ = Cons(1, 2) @@ -33,39 +34,37 @@ fun map(f, xs) = if xs is :re :sjs new 1 :: 2 -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found integer literal. -//β”‚ β•‘ l.35: new 1 :: 2 -//β”‚ β•‘ ^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Int' at an unrelated type 'Object' -//β”‚ β•‘ l.35: new 1 :: 2 -//β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation +//β”‚ β•‘ l.36: new 1 :: 2 +//β”‚ β•‘ ^^^^^ +//β”‚ β•Ÿβ”€β”€ Invalid class expression: integer literal +//β”‚ β•‘ l.36: new 1 :: 2 +//β”‚ ╙── ^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ let tmp; tmp = globalThis.Object.freeze(new 1()); return Cons1(tmp, 2) +//β”‚ throw "This code cannot be run as its compilation yielded an error." //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ ═══[RUNTIME ERROR] TypeError: 1 is not a constructor +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // TODO? :e :re :sjs new (1 :: 2) -//β”‚ ╔══[COMPILATION ERROR] Expected a statically known class; found application of type Cons. +//β”‚ ╔══[COMPILATION ERROR] Resolution error in object instantiation //β”‚ β•‘ l.52: new (1 :: 2) -//β”‚ β•‘ ^^^^^^ -//β”‚ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Int' at an unrelated type 'Object' +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Invalid class expression: application //β”‚ β•‘ l.52: new (1 :: 2) -//β”‚ ╙── ^ +//β”‚ ╙── ^^^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| JS (unsanitized) |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ let tmp1; tmp1 = Cons1(1, 2); return globalThis.Object.freeze(new tmp1()) +//β”‚ throw "This code cannot be run as its compilation yielded an error." //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Output |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” -//β”‚ ═══[RUNTIME ERROR] TypeError: tmp1 is not a constructor +//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :ge new ::(1, 2) //β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Int' at an unrelated type 'Object' -//β”‚ β•‘ l.66: new ::(1, 2) +//β”‚ β•‘ l.65: new ::(1, 2) //β”‚ ╙── ^ //β”‚ = Cons(1, 2) diff --git a/hkmc2/shared/src/test/mlscript/ucs/general/.mls b/hkmc2/shared/src/test/mlscript/ucs/general/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ucs/general/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ucs/general/BooleanPatterns.mls b/hkmc2/shared/src/test/mlscript/ucs/general/BooleanPatterns.mls index 2474405b55..d9176900c7 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/general/BooleanPatterns.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/general/BooleanPatterns.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/ucs/general/CardSuits.mls b/hkmc2/shared/src/test/mlscript/ucs/general/CardSuits.mls index 137778fe7a..266ccd8d3a 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/general/CardSuits.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/general/CardSuits.mls @@ -1,3 +1,4 @@ +:. :js data class Card[T](color: T) diff --git a/hkmc2/shared/src/test/mlscript/ucs/general/CrossModules.mls b/hkmc2/shared/src/test/mlscript/ucs/general/CrossModules.mls index 8d022599b9..eceb5e9e85 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/general/CrossModules.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/general/CrossModules.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/ucs/general/DualOptions.mls b/hkmc2/shared/src/test/mlscript/ucs/general/DualOptions.mls index ee2ae665be..9576c6d311 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/general/DualOptions.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/general/DualOptions.mls @@ -1,3 +1,4 @@ +:. :js // DualOptions.mls diff --git a/hkmc2/shared/src/test/mlscript/ucs/general/InterleavedLet.mls b/hkmc2/shared/src/test/mlscript/ucs/general/InterleavedLet.mls index 56bbdcac11..89330baef5 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/general/InterleavedLet.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/general/InterleavedLet.mls @@ -1,3 +1,4 @@ +:. :js abstract class List[out T] diff --git a/hkmc2/shared/src/test/mlscript/ucs/general/JoinPoints.mls b/hkmc2/shared/src/test/mlscript/ucs/general/JoinPoints.mls index dfbe6719ef..332573cebf 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/general/JoinPoints.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/general/JoinPoints.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/ucs/general/List.mls b/hkmc2/shared/src/test/mlscript/ucs/general/List.mls index 3d25e59ace..079a4b3bf1 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/general/List.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/general/List.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Stack.mls" diff --git a/hkmc2/shared/src/test/mlscript/ucs/general/LogicalConnectives.mls b/hkmc2/shared/src/test/mlscript/ucs/general/LogicalConnectives.mls index 9d57763640..6552221360 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/general/LogicalConnectives.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/general/LogicalConnectives.mls @@ -1,17 +1,18 @@ +:. :js :fixme true or true //β”‚ ╔══[COMPILATION ERROR] Logical `or` is not yet supported. -//β”‚ β•‘ l.5: true or true +//β”‚ β•‘ l.6: true or true //β”‚ ╙── ^^^^^^^^^^^^ //β”‚ = true :fixme while false or false do print("ok") //β”‚ ╔══[COMPILATION ERROR] Logical `or` is not yet supported. -//β”‚ β•‘ l.12: while false or false do print("ok") +//β”‚ β•‘ l.13: while false or false do print("ok") //β”‚ ╙── ^^^^^^^^^^^^^^ @@ -38,7 +39,7 @@ true and test(42) :fixme true or test(42) //β”‚ ╔══[COMPILATION ERROR] Logical `or` is not yet supported. -//β”‚ β•‘ l.39: true or test(42) +//β”‚ β•‘ l.40: true or test(42) //β”‚ ╙── ^^^^^^^^^^^^^^^^ //β”‚ > 42 //β”‚ = false diff --git a/hkmc2/shared/src/test/mlscript/ucs/general/Seqs.mls b/hkmc2/shared/src/test/mlscript/ucs/general/Seqs.mls index 016a074f82..8654aa1372 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/general/Seqs.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/general/Seqs.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/ucs/general/Simple.mls b/hkmc2/shared/src/test/mlscript/ucs/general/Simple.mls index 02ca86fcd4..4fac9814b3 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/general/Simple.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/general/Simple.mls @@ -1,3 +1,4 @@ +:. :js // Simple.mls diff --git a/hkmc2/shared/src/test/mlscript/ucs/hygiene/.mls b/hkmc2/shared/src/test/mlscript/ucs/hygiene/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ucs/hygiene/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ucs/hygiene/CrossBranchCapture.mls b/hkmc2/shared/src/test/mlscript/ucs/hygiene/CrossBranchCapture.mls index 1ff5f02e95..7c1e6f4f6a 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/hygiene/CrossBranchCapture.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/hygiene/CrossBranchCapture.mls @@ -1,3 +1,4 @@ +:. :js data class Numb(n: Int) @@ -8,8 +9,8 @@ fun process(e) = Numb(n) and n > 0 then n Numb(m) then n //β”‚ ╔══[COMPILATION ERROR] Name not found: n -//β”‚ β•‘ l.9: Numb(m) then n -//β”‚ ╙── ^ +//β”‚ β•‘ l.10: Numb(m) then n +//β”‚ ╙── ^ :re @@ -42,19 +43,19 @@ fun process(e) = Pair(Vec(n), Numb(n)) then n Pair(Numb(n), Vec(n)) then n //β”‚ ╔══[COMPILATION ERROR] Name not found: n -//β”‚ β•‘ l.41: Pair(Vec(xs), Vec(ys)) then n +//β”‚ β•‘ l.42: Pair(Vec(xs), Vec(ys)) then n //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Duplicated pattern variable. -//β”‚ β•‘ l.42: Pair(Vec(n), Numb(n)) then n +//β”‚ β•‘ l.43: Pair(Vec(n), Numb(n)) then n //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ The previous definition is as follows. -//β”‚ β•‘ l.42: Pair(Vec(n), Numb(n)) then n +//β”‚ β•‘ l.43: Pair(Vec(n), Numb(n)) then n //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Duplicated pattern variable. -//β”‚ β•‘ l.43: Pair(Numb(n), Vec(n)) then n +//β”‚ β•‘ l.44: Pair(Numb(n), Vec(n)) then n //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ The previous definition is as follows. -//β”‚ β•‘ l.43: Pair(Numb(n), Vec(n)) then n +//β”‚ β•‘ l.44: Pair(Numb(n), Vec(n)) then n //β”‚ ╙── ^ @@ -64,10 +65,10 @@ fun process(e) = if e is Pair(Numb(n), Numb(n)) then n //β”‚ ╔══[COMPILATION ERROR] Duplicated pattern variable. -//β”‚ β•‘ l.65: Pair(Numb(n), Numb(n)) then n +//β”‚ β•‘ l.66: Pair(Numb(n), Numb(n)) then n //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ The previous definition is as follows. -//β”‚ β•‘ l.65: Pair(Numb(n), Numb(n)) then n +//β”‚ β•‘ l.66: Pair(Numb(n), Numb(n)) then n //β”‚ ╙── ^ process(Pair(Numb(1), Numb(2))) diff --git a/hkmc2/shared/src/test/mlscript/ucs/hygiene/Hygiene.mls b/hkmc2/shared/src/test/mlscript/ucs/hygiene/Hygiene.mls index d0b74f1987..7a2544b205 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/hygiene/Hygiene.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/hygiene/Hygiene.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/ucs/hygiene/HygienicBindings.mls b/hkmc2/shared/src/test/mlscript/ucs/hygiene/HygienicBindings.mls index 42e03ea869..34cc193987 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/hygiene/HygienicBindings.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/hygiene/HygienicBindings.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Option.mls" @@ -40,16 +41,16 @@ fun h1(a) = a is None then 0 //β”‚ Normalized: //β”‚ > β€Ήif|whileβ€Ί -//β”‚ > a is Option.Some(arg$Some$0$) and +//β”‚ > a is member:Option⁰.Some^term:h1⁰(arg$Some$0$) and //β”‚ > let x = tmp:arg$Some$0$⁰ -//β”‚ > x is Left(arg$Left$0$) and +//β”‚ > x is member:Left⁰^term:h1⁰(arg$Left$0$) and //β”‚ > let y = tmp:arg$Left$0$⁰ //β”‚ > else y⁰ //β”‚ > let y = tmp:arg$Some$0$⁰ -//β”‚ > y is Right(arg$Right$0$) and +//β”‚ > y is member:Right⁰^term:h1⁰(arg$Right$0$) and //β”‚ > let z = tmp:arg$Right$0$⁰ //β”‚ > else z⁰ -//β”‚ > a is Option.None then 0 +//β”‚ > a is member:Option⁰.None^term:h1⁰ then 0 h1(Some(Left(42))) //β”‚ = 42 @@ -72,18 +73,18 @@ fun h2(a) = a is None then 0 //β”‚ Normalized: //β”‚ > β€Ήif|whileβ€Ί -//β”‚ > a is Option.Some(arg$Some$0$) and +//β”‚ > a is member:Option⁰.Some^term:h2⁰(arg$Some$0$) and //β”‚ > let x = tmp:arg$Some$0$ΒΉ //β”‚ > let x' = x⁰ -//β”‚ > x' is Left(arg$Left$0$) and +//β”‚ > x' is member:Left⁰^term:h2⁰(arg$Left$0$) and //β”‚ > let y = tmp:arg$Left$0$ΒΉ //β”‚ > else yΒΉ //β”‚ > let y = tmp:arg$Some$0$ΒΉ //β”‚ > let y' = yΒ² -//β”‚ > y' is Right(arg$Right$0$) and +//β”‚ > y' is member:Right⁰^term:h2⁰(arg$Right$0$) and //β”‚ > let z = tmp:arg$Right$0$ΒΉ //β”‚ > else zΒΉ -//β”‚ > a is Option.None then 0 +//β”‚ > a is member:Option⁰.None^term:h2⁰ then 0 // :w fun h3(x, y, f, p) = diff --git a/hkmc2/shared/src/test/mlscript/ucs/hygiene/PatVars.mls b/hkmc2/shared/src/test/mlscript/ucs/hygiene/PatVars.mls index a5fff9b39a..83ae620fc6 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/hygiene/PatVars.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/hygiene/PatVars.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/ucs/normalization/.mls b/hkmc2/shared/src/test/mlscript/ucs/normalization/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ucs/normalization/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ucs/normalization/Deduplication.mls b/hkmc2/shared/src/test/mlscript/ucs/normalization/Deduplication.mls index a186b3fcbb..b67bca66db 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/normalization/Deduplication.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/normalization/Deduplication.mls @@ -1,3 +1,4 @@ +:. :js :patMatConsequentSharingThreshold 5 @@ -335,7 +336,7 @@ fun foo(x, y, z) = if //β”‚ > y is 2 then "_2_" //β”‚ > else "___" -fun expensive_call(str) = Math.pow(2, str.length) +fun expensive_call(str: Str) = Math.pow(2, str.length) :sir fun foo(x, y, z) = if x is @@ -362,19 +363,19 @@ fun foo(x, y, z) = if x is //β”‚ return "001" //β”‚ else //β”‚ set value = "hello"; -//β”‚ return expensive_call⁰(value) +//β”‚ return expensive_call⁰((value as! Str⁰)) //β”‚ end //β”‚ 1 => //β”‚ return "01" //β”‚ else //β”‚ set value = "hello"; -//β”‚ return expensive_call⁰(value) +//β”‚ return expensive_call⁰((value as! Str⁰)) //β”‚ end //β”‚ 1 => //β”‚ return "1" //β”‚ else //β”‚ set value = "hello"; -//β”‚ return expensive_call⁰(value) +//β”‚ return expensive_call⁰((value as! Str⁰)) //β”‚ end //β”‚ }; //β”‚ end @@ -500,11 +501,11 @@ fun foo1(x, y, z) = if //β”‚ Normalized: //β”‚ > β€Ήif|whileβ€Ί //β”‚ > let-split Οƒ = -//β”‚ > z is A then (member:PredefΒΉ.)printβ€Ήmember:printβ€Ί(2) -//β”‚ > else (member:PredefΒΉ.)printβ€Ήmember:printβ€Ί(3) -//β”‚ > x is A and -//β”‚ > let tmp:scrut = member:pred⁰(xΒΉ) -//β”‚ > tmp:scrut is true then (member:PredefΒΉ.)printβ€Ήmember:printβ€Ί(1) +//β”‚ > z is member:AΒΉ^term:foo1⁰ then member:PredefΒΉ.print^term:foo1⁰(2) +//β”‚ > else member:PredefΒΉ.print^term:foo1⁰(3) +//β”‚ > x is member:AΒΉ^term:foo1⁰ and +//β”‚ > let tmp:scrut = member:pred⁰^term:foo1⁰(xΒΉ) +//β”‚ > tmp:scrut is true then member:PredefΒΉ.print^term:foo1⁰(1) //β”‚ > $Οƒ //β”‚ > $Οƒ @@ -519,15 +520,15 @@ fun foo2(x, y, z) = if //β”‚ > β€Ήif|whileβ€Ί //β”‚ > let-split Οƒ = //β”‚ > let-split Οƒ = -//β”‚ > z is A then (member:PredefΒΉ.)printβ€Ήmember:printβ€Ί(3) -//β”‚ > else (member:PredefΒΉ.)printβ€Ήmember:printβ€Ί(4) -//β”‚ > y is A and -//β”‚ > let tmp:scrut = member:pred⁰(yΒ²) -//β”‚ > tmp:scrut is true then (member:PredefΒΉ.)printβ€Ήmember:printβ€Ί(2) +//β”‚ > z is member:AΒΉ^term:foo2⁰ then member:PredefΒΉ.print^term:foo2⁰(3) +//β”‚ > else member:PredefΒΉ.print^term:foo2⁰(4) +//β”‚ > y is member:AΒΉ^term:foo2⁰ and +//β”‚ > let tmp:scrut = member:pred⁰^term:foo2⁰(yΒ²) +//β”‚ > tmp:scrut is true then member:PredefΒΉ.print^term:foo2⁰(2) //β”‚ > $Οƒ //β”‚ > $Οƒ -//β”‚ > x is A and -//β”‚ > let tmp:scrut = member:pred⁰(xΒ²) -//β”‚ > tmp:scrut is true then (member:PredefΒΉ.)printβ€Ήmember:printβ€Ί(1) +//β”‚ > x is member:AΒΉ^term:foo2⁰ and +//β”‚ > let tmp:scrut = member:pred⁰^term:foo2⁰(xΒ²) +//β”‚ > tmp:scrut is true then member:PredefΒΉ.print^term:foo2⁰(1) //β”‚ > $Οƒ //β”‚ > $Οƒ diff --git a/hkmc2/shared/src/test/mlscript/ucs/normalization/DeduplicationWhile.mls b/hkmc2/shared/src/test/mlscript/ucs/normalization/DeduplicationWhile.mls index 885a14f64a..216670c0d3 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/normalization/DeduplicationWhile.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/normalization/DeduplicationWhile.mls @@ -1,3 +1,4 @@ +:. :js :patMatConsequentSharingThreshold 1 // ^ force dedup even for small consequents diff --git a/hkmc2/shared/src/test/mlscript/ucs/normalization/ExcessiveDeduplication.mls b/hkmc2/shared/src/test/mlscript/ucs/normalization/ExcessiveDeduplication.mls index a51f67d297..c7cd8af007 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/normalization/ExcessiveDeduplication.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/normalization/ExcessiveDeduplication.mls @@ -1,3 +1,4 @@ +:. :js :patMatConsequentSharingThreshold 1 // ^ force dedup even for small consequents diff --git a/hkmc2/shared/src/test/mlscript/ucs/normalization/InheritanceNormalization.mls b/hkmc2/shared/src/test/mlscript/ucs/normalization/InheritanceNormalization.mls index 34ff6df09f..e618853c1c 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/normalization/InheritanceNormalization.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/normalization/InheritanceNormalization.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/ucs/normalization/OverlapOfPrimitives.mls b/hkmc2/shared/src/test/mlscript/ucs/normalization/OverlapOfPrimitives.mls index 7f80a6dac2..a2736e4939 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/normalization/OverlapOfPrimitives.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/normalization/OverlapOfPrimitives.mls @@ -1,3 +1,4 @@ +:. :js :ucs normalized @@ -5,7 +6,7 @@ fun test(x) = if x is Int then "Int" 0 then "Zero" //β”‚ Normalized: -//β”‚ > β€Ήif|whileβ€Ί x is Int then "Int" +//β”‚ > β€Ήif|whileβ€Ί x is member:Int⁰^term:test⁰ then "Int" test(0) //β”‚ = "Int" @@ -16,7 +17,7 @@ fun test(x, p) = if x is else "qax" //β”‚ Normalized: //β”‚ > β€Ήif|whileβ€Ί -//β”‚ > x is refined Int and +//β”‚ > x is refined member:Int⁰^term:testΒΉ and //β”‚ > let tmp:scrut = p⁰(x⁰) //β”‚ > tmp:scrut is true then "foo" //β”‚ > x is 0 then "bar" @@ -29,7 +30,7 @@ fun test(x, p) = if x is else "qax" //β”‚ Normalized: //β”‚ > β€Ήif|whileβ€Ί -//β”‚ > x is refined Str and +//β”‚ > x is refined member:Str⁰^term:testΒ² and //β”‚ > let tmp:scrut = pΒΉ(xΒΉ) //β”‚ > tmp:scrut is true then "foo" //β”‚ > x is "lol" then "bar" @@ -55,7 +56,7 @@ fun test(x, p) = if x is else "other" //β”‚ Normalized: //β”‚ > β€Ήif|whileβ€Ί -//β”‚ > x is refined Num and +//β”‚ > x is refined member:Num⁰^term:testΒ³ and //β”‚ > let tmp:scrut = pΒ²(xΒ²) //β”‚ > tmp:scrut is true then "great" //β”‚ > x is 2.71828 then "E" @@ -68,7 +69,7 @@ fun test(x, p) = if x is true then "false" false then "true" //β”‚ Normalized: -//β”‚ > β€Ήif|whileβ€Ί x is refined Bool and +//β”‚ > β€Ήif|whileβ€Ί x is refined member:Bool⁰^term:test⁴ and //β”‚ > let tmp:scrut = pΒ³(xΒ³) //β”‚ > tmp:scrut is true then "great" //β”‚ > x is true then "false" @@ -81,10 +82,10 @@ fun test(x, p) = if x is false then "true" //β”‚ Normalized: //β”‚ > β€Ήif|whileβ€Ί -//β”‚ > x is Object and +//β”‚ > x is member:Object⁰^term:test⁡ and //β”‚ > let tmp:scrut = p⁴(x⁴) //β”‚ > tmp:scrut is true then "great" -//β”‚ > x is refined Bool and +//β”‚ > x is refined member:Bool⁰^term:test⁡ and //β”‚ > let tmp:scrut = p⁴(x⁴) //β”‚ > tmp:scrut is true then "great, again" //β”‚ > x is true then "false" @@ -101,7 +102,7 @@ fun f(arg) = if //β”‚ > β€Ήif|whileβ€Ί //β”‚ > arg is undefined then "undefined" //β”‚ > arg is null then "null" -//β”‚ > arg is Object then member:String⁰(arg⁰) +//β”‚ > arg is member:Object⁰^term:f⁰ then member:String⁰^term:f⁰(arg⁰) f of undefined //β”‚ = "undefined" @@ -119,7 +120,7 @@ fun f(arg) = if null then "null" //β”‚ Normalized: //β”‚ > β€Ήif|whileβ€Ί -//β”‚ > arg is Object then member:String⁰(argΒΉ) +//β”‚ > arg is member:Object⁰^term:fΒΉ then member:String⁰^term:fΒΉ(argΒΉ) //β”‚ > arg is undefined then "undefined" //β”‚ > arg is null then "null" @@ -136,7 +137,7 @@ fun f(arg) = if arg is Object then String(arg) Foo then "just Foo" //β”‚ Normalized: -//β”‚ > β€Ήif|whileβ€Ί arg is Object then member:String⁰(argΒ²) +//β”‚ > β€Ήif|whileβ€Ί arg is member:Object⁰^term:fΒ² then member:String⁰^term:fΒ²(argΒ²) import "../../../mlscript-compile/Runtime.mls" @@ -144,28 +145,28 @@ fun f(arg) = if arg is Object then String(arg) Runtime.MatchSuccess then "just Foo" //β”‚ Normalized: -//β”‚ > β€Ήif|whileβ€Ί arg is Object then member:String⁰(argΒ³) +//β”‚ > β€Ήif|whileβ€Ί arg is member:Object⁰^term:fΒ³ then member:String⁰^term:fΒ³(argΒ³) fun f(arg) = if arg is Foo then "just Foo" Object then String(arg) //β”‚ Normalized: //β”‚ > β€Ήif|whileβ€Ί -//β”‚ > arg is Foo then "just Foo" -//β”‚ > arg is Object then member:String⁰(arg⁴) +//β”‚ > arg is member:Foo⁰^term:f⁴ then "just Foo" +//β”‚ > arg is member:Object⁰^term:f⁴ then member:String⁰^term:f⁴(arg⁴) fun f(arg) = if arg is Int then "Int" Object then String(arg) //β”‚ Normalized: //β”‚ > β€Ήif|whileβ€Ί -//β”‚ > arg is Int then "Int" -//β”‚ > arg is Object then member:String⁰(arg⁡) +//β”‚ > arg is member:Int⁰^term:f⁡ then "Int" +//β”‚ > arg is member:Object⁰^term:f⁡ then member:String⁰^term:f⁡(arg⁡) fun f(arg) = if arg is Object then String(arg) Int then "Int" //β”‚ Normalized: //β”‚ > β€Ήif|whileβ€Ί -//β”‚ > arg is Object then member:String⁰(arg⁢) -//β”‚ > arg is Int then "Int" +//β”‚ > arg is member:Object⁰^term:f⁢ then member:String⁰^term:f⁢(arg⁢) +//β”‚ > arg is member:Int⁰^term:f⁢ then "Int" diff --git a/hkmc2/shared/src/test/mlscript/ucs/normalization/RecordImpliedByClass.mls b/hkmc2/shared/src/test/mlscript/ucs/normalization/RecordImpliedByClass.mls index b76487e624..e704eedf0c 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/normalization/RecordImpliedByClass.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/normalization/RecordImpliedByClass.mls @@ -1,3 +1,4 @@ +:. :js @@ -9,7 +10,7 @@ fun f(x) = else 0 //β”‚ Normalized: //β”‚ > β€Ήif|whileβ€Ί -//β”‚ > x is Foo and +//β”‚ > x is member:Foo⁰^term:f⁰ and //β”‚ > x is { a: tmp:field_a$, b: tmp:field_b$ } and //β”‚ > let bv = tmp:field_b$⁰ //β”‚ > let av = tmp:field_a$⁰ diff --git a/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls b/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls index d48457a8c0..c1e00b98bc 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/ucs/normalization/SpecializedSplitSharing.mls b/hkmc2/shared/src/test/mlscript/ucs/normalization/SpecializedSplitSharing.mls index 932049bb1e..d8ddb29822 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/normalization/SpecializedSplitSharing.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/normalization/SpecializedSplitSharing.mls @@ -1,3 +1,4 @@ +:. :js @@ -26,10 +27,10 @@ fun f1(x, y) = if else 3 //β”‚ Normalized: //β”‚ > β€Ήif|whileβ€Ί -//β”‚ > x is Cat() and +//β”‚ > x is member:Cat⁰^term:f1⁰() and //β”‚ > y is 0 then 1 //β”‚ > else 3 -//β”‚ > x is Dog() then 2 +//β”‚ > x is member:Dog⁰^term:f1⁰() then 2 //β”‚ > else 3 // * Same shape but with an inner refining test on the `Dog()` branch β€” the @@ -41,10 +42,10 @@ fun f2(x, y, z) = if else 3 //β”‚ Normalized: //β”‚ > β€Ήif|whileβ€Ί -//β”‚ > x is Cat() and +//β”‚ > x is member:Cat⁰^term:f2⁰() and //β”‚ > y is 0 then 1 //β”‚ > else 3 -//β”‚ > x is Dog() and +//β”‚ > x is member:Dog⁰^term:f2⁰() and //β”‚ > z is 0 then 2 //β”‚ > else 3 //β”‚ > else 3 @@ -67,7 +68,7 @@ fun g1(x) = if x is S(0) then "zero" S(_) then "any" //β”‚ Normalized: -//β”‚ > β€Ήif|whileβ€Ί x is S(arg$S$0$) and +//β”‚ > β€Ήif|whileβ€Ί x is member:S⁰^term:g1⁰(arg$S$0$) and //β”‚ > tmp:arg$S$0$ is 0 then "zero" //β”‚ > else "any" @@ -79,7 +80,7 @@ fun g2(x) = if x is S(1) then "one" S(_) then "other" //β”‚ Normalized: -//β”‚ > β€Ήif|whileβ€Ί x is S(arg$S$0$) and +//β”‚ > β€Ήif|whileβ€Ί x is member:S⁰^term:g2⁰(arg$S$0$) and //β”‚ > tmp:arg$S$0$ is 0 then "zero" //β”‚ > tmp:arg$S$0$ is 1 then "one" //β”‚ > else "other" diff --git a/hkmc2/shared/src/test/mlscript/ucs/normalization/UnifySubScrutinees.mls b/hkmc2/shared/src/test/mlscript/ucs/normalization/UnifySubScrutinees.mls index e690d6b6b5..0d831706d2 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/normalization/UnifySubScrutinees.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/normalization/UnifySubScrutinees.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Stack.mls" diff --git a/hkmc2/shared/src/test/mlscript/ucs/normalization/UnifyTupleElements.mls b/hkmc2/shared/src/test/mlscript/ucs/normalization/UnifyTupleElements.mls index 328390e10c..917b3050ad 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/normalization/UnifyTupleElements.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/normalization/UnifyTupleElements.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Option.mls" diff --git a/hkmc2/shared/src/test/mlscript/ucs/papers/.mls b/hkmc2/shared/src/test/mlscript/ucs/papers/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ucs/papers/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ucs/papers/OperatorSplit.mls b/hkmc2/shared/src/test/mlscript/ucs/papers/OperatorSplit.mls index a95feb7531..9fc79d2b22 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/papers/OperatorSplit.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/papers/OperatorSplit.mls @@ -1,3 +1,4 @@ +:. :js fun example(x) = diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/AliasPattern.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/AliasPattern.mls index 85ebdc5920..b56907f663 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/AliasPattern.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/AliasPattern.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Option.mls" diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/BooleansOps.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/BooleansOps.mls index 0e0f3bee93..538f31e2e8 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/BooleansOps.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/BooleansOps.mls @@ -1,3 +1,4 @@ +:. :js @@ -7,7 +8,7 @@ let n = 0 :e n is 0 | 1 //β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (alternation) -//β”‚ β•‘ l.8: n is 0 | 1 +//β”‚ β•‘ l.9: n is 0 | 1 //β”‚ ╙── ^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -18,13 +19,17 @@ n is (0 | 1) :e if 5 is a & b then a //β”‚ ╔══[COMPILATION ERROR] Name not found: b -//β”‚ β•‘ l.19: if 5 is a & b then a +//β”‚ β•‘ l.20: if 5 is a & b then a //β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in composition +//β”‚ β•‘ l.20: if 5 is a & b then a +//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ ╙── A type cannot be used as a runtime value. //β”‚ ╔══[COMPILATION ERROR] Name not found: a -//β”‚ β•‘ l.19: if 5 is a & b then a +//β”‚ β•‘ l.20: if 5 is a & b then a //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (composition) -//β”‚ β•‘ l.19: if 5 is a & b then a +//β”‚ β•‘ l.20: if 5 is a & b then a //β”‚ ╙── ^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -35,9 +40,9 @@ if 5 is (a & b) as c then [a, b, c] :e if 5 is ~a then a //β”‚ ╔══[COMPILATION ERROR] Name not found: a -//β”‚ β•‘ l.36: if 5 is ~a then a +//β”‚ β•‘ l.41: if 5 is ~a then a //β”‚ ╙── ^ -//β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +//β”‚ ═══[RUNTIME ERROR] Error: match error if 5 is ~Bool as c then c //β”‚ = 5 diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/Compilation.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/Compilation.mls index 537eacf7e6..baa0159d19 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/Compilation.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/Compilation.mls @@ -1,3 +1,4 @@ +:. :js open annotations @@ -8,30 +9,30 @@ open annotations fun trimStart(str) = if str is @compile { (" " | "\t") ~ rest } then trimStart(rest) else str //β”‚ ╔══[COMPILATION ERROR] String concatenation is not supported in pattern compilation. -//β”‚ β•‘ l.9: if str is @compile { (" " | "\t") ~ rest } then trimStart(rest) else str -//β”‚ ╙── ^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.10: if str is @compile { (" " | "\t") ~ rest } then trimStart(rest) else str +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'rest' -//β”‚ β•‘ l.9: if str is @compile { (" " | "\t") ~ rest } then trimStart(rest) else str -//β”‚ ╙── ^^^^ +//β”‚ β•‘ l.10: if str is @compile { (" " | "\t") ~ rest } then trimStart(rest) else str +//β”‚ ╙── ^^^^ :fixme fun trimStart(str) = if str is (@compile ((" " | "\t") ~ rest)) then trimStart(rest) else str //β”‚ ╔══[COMPILATION ERROR] String concatenation is not supported in pattern compilation. -//β”‚ β•‘ l.19: if str is (@compile ((" " | "\t") ~ rest)) then trimStart(rest) else str +//β”‚ β•‘ l.20: if str is (@compile ((" " | "\t") ~ rest)) then trimStart(rest) else str //β”‚ ╙── ^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'rest' -//β”‚ β•‘ l.19: if str is (@compile ((" " | "\t") ~ rest)) then trimStart(rest) else str +//β”‚ β•‘ l.20: if str is (@compile ((" " | "\t") ~ rest)) then trimStart(rest) else str //β”‚ ╙── ^^^^ :fixme fun trimStart(str) = if str is @compile ((" " | "\t") ~ rest) then trimStart(rest) else str //β”‚ ╔══[COMPILATION ERROR] String concatenation is not supported in pattern compilation. -//β”‚ β•‘ l.29: if str is @compile ((" " | "\t") ~ rest) then trimStart(rest) else str +//β”‚ β•‘ l.30: if str is @compile ((" " | "\t") ~ rest) then trimStart(rest) else str //β”‚ ╙── ^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'rest' -//β”‚ β•‘ l.29: if str is @compile ((" " | "\t") ~ rest) then trimStart(rest) else str +//β”‚ β•‘ l.30: if str is @compile ((" " | "\t") ~ rest) then trimStart(rest) else str //β”‚ ╙── ^^^^ @@ -41,16 +42,16 @@ fun trimStart(str) = fun trimStart(str) = if str is (@compile((" " | "\t") ~ rest)) then trimStart(rest) else str //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position -//β”‚ β•‘ l.42: if str is (@compile((" " | "\t") ~ rest)) then trimStart(rest) else str +//β”‚ β•‘ l.43: if str is (@compile((" " | "\t") ~ rest)) then trimStart(rest) else str //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ found a lone annotation instead -//β”‚ β•‘ l.42: if str is (@compile((" " | "\t") ~ rest)) then trimStart(rest) else str +//β”‚ β•‘ l.43: if str is (@compile((" " | "\t") ~ rest)) then trimStart(rest) else str //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern (β€Ήerroneous syntaxβ€Ί). -//β”‚ β•‘ l.42: if str is (@compile((" " | "\t") ~ rest)) then trimStart(rest) else str +//β”‚ β•‘ l.43: if str is (@compile((" " | "\t") ~ rest)) then trimStart(rest) else str //β”‚ ╙── ^ -//β”‚ ╔══[COMPILATION ERROR] Name not found: rest -//β”‚ β•‘ l.42: if str is (@compile((" " | "\t") ~ rest)) then trimStart(rest) else str +//β”‚ ╔══[COMPILATION ERROR] Name not found in wildcard opens: rest +//β”‚ β•‘ l.43: if str is (@compile((" " | "\t") ~ rest)) then trimStart(rest) else str //β”‚ ╙── ^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/CompiledClassPatterns.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/CompiledClassPatterns.mls index 91544def01..ab27ca1643 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/CompiledClassPatterns.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/CompiledClassPatterns.mls @@ -1,3 +1,4 @@ +:. :js open annotations @@ -153,10 +154,10 @@ class Add2(val x, val y, z) :e case @compile (Add2(0, _, _) | Add2(_, 1, _)) then 0 //β”‚ ╔══[COMPILATION ERROR] Parameter `z` is not accessible. -//β”‚ β•‘ l.151: class Add2(val x, val y, z) +//β”‚ β•‘ l.152: class Add2(val x, val y, z) //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Parameter `z` is not accessible. -//β”‚ β•‘ l.151: class Add2(val x, val y, z) +//β”‚ β•‘ l.152: class Add2(val x, val y, z) //β”‚ ╙── ^ //β”‚ = fun diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/CompiledQualifiedConstructors.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/CompiledQualifiedConstructors.mls index acbe37642d..52fb50ade8 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/CompiledQualifiedConstructors.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/CompiledQualifiedConstructors.mls @@ -1,3 +1,4 @@ +:. :js open annotations diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/ConjunctionPattern.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/ConjunctionPattern.mls index 3c34ebde1b..7339d2a832 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/ConjunctionPattern.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/ConjunctionPattern.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Stack.mls" @@ -6,7 +7,7 @@ open Stack abstract class Q -abstract class A extends Q +class A extends Q abstract class B extends Q // Note: A and B are sibling classes (both extend Q but neither extends the diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/GuardedPatternBindings.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/GuardedPatternBindings.mls index f488cd7364..bc4da1be6b 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/GuardedPatternBindings.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/GuardedPatternBindings.mls @@ -1,13 +1,14 @@ +:. :js :w pattern PlainPair = [x, y] //β”‚ ╔══[WARNING] Unused pattern binding: x. -//β”‚ β•‘ l.5: pattern PlainPair = [x, y] +//β”‚ β•‘ l.6: pattern PlainPair = [x, y] //β”‚ ╙── ^ //β”‚ ╔══[WARNING] Unused pattern binding: y. -//β”‚ β•‘ l.5: pattern PlainPair = [x, y] +//β”‚ β•‘ l.6: pattern PlainPair = [x, y] //β”‚ ╙── ^ @@ -28,35 +29,35 @@ pattern Symmetry = pattern PairSum = [x, y] where (let x = true, let y = x, y) // We should check the free variables in `where` guards. //β”‚ ╔══[WARNING] Unused pattern binding: x. -//β”‚ β•‘ l.28: pattern PairSum = [x, y] where (let x = true, let y = x, y) +//β”‚ β•‘ l.29: pattern PairSum = [x, y] where (let x = true, let y = x, y) //β”‚ ╙── ^ //β”‚ ╔══[WARNING] Unused pattern binding: y. -//β”‚ β•‘ l.28: pattern PairSum = [x, y] where (let x = true, let y = x, y) +//β”‚ β•‘ l.29: pattern PairSum = [x, y] where (let x = true, let y = x, y) //β”‚ ╙── ^ :w pattern PairSum = [x, y] where (if x is [y] then y) //β”‚ ╔══[WARNING] Unused pattern binding: y. -//β”‚ β•‘ l.38: pattern PairSum = [x, y] where (if x is [y] then y) +//β”‚ β•‘ l.39: pattern PairSum = [x, y] where (if x is [y] then y) //β”‚ ╙── ^ :w pattern PairSum = [x, y] where (class C(y) with { fun test = y }, C(x).test) //β”‚ ╔══[WARNING] Unused pattern binding: y. -//β”‚ β•‘ l.44: pattern PairSum = [x, y] where (class C(y) with { fun test = y }, C(x).test) +//β”‚ β•‘ l.45: pattern PairSum = [x, y] where (class C(y) with { fun test = y }, C(x).test) //β”‚ ╙── ^ :w pattern PairSum = [x, y] where (fun y = 1, print(y), x) //β”‚ ╔══[WARNING] Unused pattern binding: y. -//β”‚ β•‘ l.50: pattern PairSum = [x, y] where (fun y = 1, print(y), x) +//β”‚ β•‘ l.51: pattern PairSum = [x, y] where (fun y = 1, print(y), x) //β”‚ ╙── ^ // Forward reference to `y` should not be counted as a free variable :w pattern PairSum = [x, y] where (print(y), fun y = 1, x) //β”‚ ╔══[WARNING] Unused pattern binding: y. -//β”‚ β•‘ l.57: pattern PairSum = [x, y] where (print(y), fun y = 1, x) +//β”‚ β•‘ l.58: pattern PairSum = [x, y] where (print(y), fun y = 1, x) //β”‚ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/Literals.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/Literals.mls index f564604c99..9da7e685cc 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/Literals.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/Literals.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Option.mls" @@ -77,7 +78,7 @@ fun dual_patterns(x, y) = :fixme // TODO () is () //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern (unit). -//β”‚ β•‘ l.78: () is () +//β”‚ β•‘ l.79: () is () //β”‚ ╙── ^^ //β”‚ = true diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/NamePattern.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/NamePattern.mls index 6f34bf970e..61145a2255 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/NamePattern.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/NamePattern.mls @@ -1,3 +1,4 @@ +:. :js fun id(x) = x @@ -7,7 +8,7 @@ if id(0) is t then t //β”‚ Split with nested patterns: //β”‚ > if //β”‚ > let tmp:scrut = member:id⁰(0) -//β”‚ > tmp:scrut is t then t⁰ +//β”‚ > tmp:scrut is tβ€Ήt⁰› then t⁰ //β”‚ Expanded split with flattened patterns: //β”‚ > β€Ήif|whileβ€Ί //β”‚ > let tmp:scrut = member:id⁰(0) diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/Parameters.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/Parameters.mls index f0f0c45a41..da06f644c5 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/Parameters.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/Parameters.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Option.mls" @@ -9,10 +10,10 @@ class Foo(a: Int, val b: Int) :e fun foo(x) = if x is Foo(p, q) then p + q //β”‚ ╔══[COMPILATION ERROR] This pattern cannot be matched -//β”‚ β•‘ l.10: fun foo(x) = if x is Foo(p, q) then p + q +//β”‚ β•‘ l.11: fun foo(x) = if x is Foo(p, q) then p + q //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ because the corresponding parameter `a` is not publicly accessible -//β”‚ β•‘ l.7: class Foo(a: Int, val b: Int) +//β”‚ β•‘ l.8: class Foo(a: Int, val b: Int) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ Suggestion: use a wildcard pattern `_` in this position //β”‚ ╙── Suggestion: mark this parameter with `val` so it becomes accessible @@ -39,13 +40,13 @@ keepSecret(SecretPair(2, 3)) :e fun rebuildSecret(x) = if x is (SecretPair(_, (q => q + 1))) as result then result //β”‚ ╔══[COMPILATION ERROR] This pattern cannot be matched -//β”‚ β•‘ l.40: fun rebuildSecret(x) = if x is (SecretPair(_, (q => q + 1))) as result then result +//β”‚ β•‘ l.41: fun rebuildSecret(x) = if x is (SecretPair(_, (q => q + 1))) as result then result //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ because the corresponding parameter `a` is not publicly accessible -//β”‚ β•‘ l.29: class SecretPair(a: Int, val b: Int) +//β”‚ β•‘ l.30: class SecretPair(a: Int, val b: Int) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ because rebuilding the matched `SecretPair` value requires reading every constructor argument -//β”‚ β•‘ l.40: fun rebuildSecret(x) = if x is (SecretPair(_, (q => q + 1))) as result then result +//β”‚ β•‘ l.41: fun rebuildSecret(x) = if x is (SecretPair(_, (q => q + 1))) as result then result //β”‚ β•‘ ^^^^^^^^^^ //β”‚ ╙── Suggestion: mark this parameter with `val` so it becomes accessible @@ -56,69 +57,72 @@ fun test(x) = if x is Test then "ok" :e fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" //β”‚ ╔══[COMPILATION ERROR] This pattern cannot be matched -//β”‚ β•‘ l.57: fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" +//β”‚ β•‘ l.58: fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ because the corresponding parameter `a` is not publicly accessible -//β”‚ β•‘ l.52: class Test(a, b, c, d, e, f) +//β”‚ β•‘ l.53: class Test(a, b, c, d, e, f) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ Suggestion: use a wildcard pattern `_` in this position //β”‚ ╙── Suggestion: mark this parameter with `val` so it becomes accessible //β”‚ ╔══[COMPILATION ERROR] This pattern cannot be matched -//β”‚ β•‘ l.57: fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" +//β”‚ β•‘ l.58: fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ because the corresponding parameter `b` is not publicly accessible -//β”‚ β•‘ l.52: class Test(a, b, c, d, e, f) +//β”‚ β•‘ l.53: class Test(a, b, c, d, e, f) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ Suggestion: use a wildcard pattern `_` in this position //β”‚ ╙── Suggestion: mark this parameter with `val` so it becomes accessible //β”‚ ╔══[COMPILATION ERROR] This pattern cannot be matched -//β”‚ β•‘ l.57: fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" +//β”‚ β•‘ l.58: fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ because the corresponding parameter `c` is not publicly accessible -//β”‚ β•‘ l.52: class Test(a, b, c, d, e, f) +//β”‚ β•‘ l.53: class Test(a, b, c, d, e, f) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ Suggestion: use a wildcard pattern `_` in this position //β”‚ ╙── Suggestion: mark this parameter with `val` so it becomes accessible //β”‚ ╔══[COMPILATION ERROR] This pattern cannot be matched -//β”‚ β•‘ l.57: fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" +//β”‚ β•‘ l.58: fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ because the corresponding parameter `d` is not publicly accessible -//β”‚ β•‘ l.52: class Test(a, b, c, d, e, f) +//β”‚ β•‘ l.53: class Test(a, b, c, d, e, f) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ Suggestion: use a wildcard pattern `_` in this position //β”‚ ╙── Suggestion: mark this parameter with `val` so it becomes accessible //β”‚ ╔══[COMPILATION ERROR] This pattern cannot be matched -//β”‚ β•‘ l.57: fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" +//β”‚ β•‘ l.58: fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ because the corresponding parameter `e` is not publicly accessible -//β”‚ β•‘ l.52: class Test(a, b, c, d, e, f) +//β”‚ β•‘ l.53: class Test(a, b, c, d, e, f) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ Suggestion: use a wildcard pattern `_` in this position //β”‚ ╙── Suggestion: mark this parameter with `val` so it becomes accessible //β”‚ ╔══[COMPILATION ERROR] This pattern cannot be matched -//β”‚ β•‘ l.57: fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" +//β”‚ β•‘ l.58: fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ because the corresponding parameter `f` is not publicly accessible -//β”‚ β•‘ l.52: class Test(a, b, c, d, e, f) +//β”‚ β•‘ l.53: class Test(a, b, c, d, e, f) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ Suggestion: use a wildcard pattern `_` in this position //β”‚ ╙── Suggestion: mark this parameter with `val` so it becomes accessible :e fun test(x) = if x is Test(_, _, _) then "ok" -//β”‚ ╔══[COMPILATION ERROR] Expected six arguments, but found only three arguments. -//β”‚ β•‘ l.108: fun test(x) = if x is Test(_, _, _) then "ok" -//β”‚ ╙── ^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in constructor +//β”‚ β•‘ l.109: fun test(x) = if x is Test(_, _, _) then "ok" +//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class definition 'Test' expected 6 pattern arguments, but got 3 +//β”‚ β•‘ l.53: class Test(a, b, c, d, e, f) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fun test(x) = if x is Test(_, _, _, _, _, _) then "ok" :e fun test(x) = if x is Test(_, _, _, a, _, _) then "ok" //β”‚ ╔══[COMPILATION ERROR] This pattern cannot be matched -//β”‚ β•‘ l.116: fun test(x) = if x is Test(_, _, _, a, _, _) then "ok" +//β”‚ β•‘ l.120: fun test(x) = if x is Test(_, _, _, a, _, _) then "ok" //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ because the corresponding parameter `d` is not publicly accessible -//β”‚ β•‘ l.52: class Test(a, b, c, d, e, f) +//β”‚ β•‘ l.53: class Test(a, b, c, d, e, f) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ Suggestion: use a wildcard pattern `_` in this position //β”‚ ╙── Suggestion: mark this parameter with `val` so it becomes accessible @@ -141,20 +145,20 @@ object A :e fun foo(x) = if x is A() then 0 //β”‚ ╔══[COMPILATION ERROR] `A` is an object. -//β”‚ β•‘ l.139: object A +//β”‚ β•‘ l.143: object A //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ Its pattern cannot have an argument list. -//β”‚ β•‘ l.142: fun foo(x) = if x is A() then 0 -//β”‚ ╙── ^ +//β”‚ β•‘ l.146: fun foo(x) = if x is A() then 0 +//β”‚ ╙── ^^^ :e fun foo(x) = if x is A(y) then y -//β”‚ ╔══[COMPILATION ERROR] `A` is an object. -//β”‚ β•‘ l.139: object A -//β”‚ β•‘ ^ -//β”‚ β•Ÿβ”€β”€ Its pattern cannot have arguments. -//β”‚ β•‘ l.151: fun foo(x) = if x is A(y) then y -//β”‚ ╙── ^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in constructor +//β”‚ β•‘ l.155: fun foo(x) = if x is A(y) then y +//β”‚ β•‘ ^^^^ +//β”‚ β•Ÿβ”€β”€ Module/object definition 'A' does not take pattern arguments. +//β”‚ β•‘ l.143: object A +//β”‚ ╙── ^^^^^^^^ 0 is Foo //β”‚ = false @@ -208,7 +212,7 @@ if p is Crazy(a, b, c, d, e, f, g) then :re if p is Crazy(a, b, c, d, e, f) then 42 //β”‚ ╔══[COMPILATION ERROR] Expected seven extraction arguments, but found only six arguments. -//β”‚ β•‘ l.209: if p is Crazy(a, b, c, d, e, f) then 42 +//β”‚ β•‘ l.213: if p is Crazy(a, b, c, d, e, f) then 42 //β”‚ ╙── ^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: match error @@ -216,7 +220,7 @@ if p is Crazy(a, b, c, d, e, f) then 42 :re if p is Crazy(all) then 42 //β”‚ ╔══[COMPILATION ERROR] Expected seven extraction arguments, but found only one argument. -//β”‚ β•‘ l.217: if p is Crazy(all) then 42 +//β”‚ β•‘ l.221: if p is Crazy(all) then 42 //β”‚ ╙── ^^^ //β”‚ ═══[RUNTIME ERROR] Error: match error diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/RecordPattern.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/RecordPattern.mls index 54602c4250..897e4f3692 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/RecordPattern.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/RecordPattern.mls @@ -1,3 +1,4 @@ +:. :js @@ -43,7 +44,7 @@ f(a: 1) let f = case { a: 1 } then a //β”‚ ╔══[COMPILATION ERROR] Name not found: a -//β”‚ β•‘ l.44: { a: 1 } then a +//β”‚ β•‘ l.45: { a: 1 } then a //β”‚ ╙── ^ //β”‚ f = fun @@ -63,10 +64,10 @@ let r = {x: 2, y : 4} if r is {x:a, y:a} then a //β”‚ ╔══[COMPILATION ERROR] Duplicated pattern variable. -//β”‚ β•‘ l.64: {x:a, y:a} then a +//β”‚ β•‘ l.65: {x:a, y:a} then a //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ The previous definition is as follows. -//β”‚ β•‘ l.64: {x:a, y:a} then a +//β”‚ β•‘ l.65: {x:a, y:a} then a //β”‚ ╙── ^ //β”‚ = 4 @@ -150,7 +151,7 @@ foo(1 :: Nil) :ge foo(1 :: "this is invalid") //β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Str' at an unrelated type 'Stack.Base' -//β”‚ β•‘ l.151: foo(1 :: "this is invalid") +//β”‚ β•‘ l.152: foo(1 :: "this is invalid") //β”‚ ╙── ^^^^^^^^^^^^^^^^^ //β”‚ = "yeah we're done" diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/Refinement.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/Refinement.mls index 632d31f3bd..1ffa0c6818 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/Refinement.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/Refinement.mls @@ -1,3 +1,4 @@ +:. :ignore :ucs normalized @@ -8,24 +9,18 @@ open Option // OK x => if x is refined(None) then x -//β”‚ ╔══[COMPILATION ERROR] Name not found: refined -//β”‚ β•‘ l.10: refined(None) then x -//β”‚ ╙── ^^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Cannot use this β€Ήerrorβ€Ί as a pattern. -//β”‚ β•‘ l.10: refined(None) then x -//β”‚ ╙── ^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Cannot use this wildcard-open reference as a pattern. +//β”‚ β•‘ l.11: refined(None) then x +//β”‚ ╙── ^^^^^^^^^^^^^ //β”‚ Normalized: //β”‚ > β€Ήif|whileβ€Ί // OK x => if x is refined(Some) then x -//β”‚ ╔══[COMPILATION ERROR] Name not found: refined -//β”‚ β•‘ l.22: refined(Some) then x -//β”‚ ╙── ^^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Cannot use this β€Ήerrorβ€Ί as a pattern. -//β”‚ β•‘ l.22: refined(Some) then x -//β”‚ ╙── ^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Cannot use this wildcard-open reference as a pattern. +//β”‚ β•‘ l.20: refined(Some) then x +//β”‚ ╙── ^^^^^^^^^^^^^ //β”‚ Normalized: //β”‚ > β€Ήif|whileβ€Ί @@ -33,29 +28,20 @@ x => if x is x => if x is refined(None) then x Some then x -//β”‚ ╔══[COMPILATION ERROR] Name not found: refined -//β”‚ β•‘ l.34: refined(None) then x -//β”‚ ╙── ^^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Cannot use this β€Ήerrorβ€Ί as a pattern. -//β”‚ β•‘ l.34: refined(None) then x -//β”‚ ╙── ^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Cannot use this wildcard-open reference as a pattern. +//β”‚ β•‘ l.29: refined(None) then x +//β”‚ ╙── ^^^^^^^^^^^^^ //β”‚ Normalized: -//β”‚ > β€Ήif|whileβ€Ί x is Option.Some then x⁰ +//β”‚ > β€Ήif|whileβ€Ί x is Someβ€Ήopenβ€Ί then x⁰ x => if x is refined(None) then x refined(Some) then x -//β”‚ ╔══[COMPILATION ERROR] Name not found: refined -//β”‚ β•‘ l.46: refined(None) then x -//β”‚ ╙── ^^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Name not found: refined -//β”‚ β•‘ l.47: refined(Some) then x -//β”‚ ╙── ^^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Cannot use this β€Ήerrorβ€Ί as a pattern. -//β”‚ β•‘ l.46: refined(None) then x -//β”‚ ╙── ^^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Cannot use this β€Ήerrorβ€Ί as a pattern. -//β”‚ β•‘ l.47: refined(Some) then x -//β”‚ ╙── ^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Cannot use this wildcard-open reference as a pattern. +//β”‚ β•‘ l.38: refined(None) then x +//β”‚ ╙── ^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Cannot use this wildcard-open reference as a pattern. +//β”‚ β•‘ l.39: refined(Some) then x +//β”‚ ╙── ^^^^^^^^^^^^^ //β”‚ Normalized: //β”‚ > β€Ήif|whileβ€Ί diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/RestTuple.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/RestTuple.mls index fafca822ce..8a8ca18e09 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/RestTuple.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/RestTuple.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/SimpleTuple.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/SimpleTuple.mls index 0e04c45788..9eed01d339 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/SimpleTuple.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/SimpleTuple.mls @@ -1,3 +1,4 @@ +:. :js @@ -15,17 +16,20 @@ sum(1, 2) fun sum([x, y]) = x + y sum([1, 2]) //β”‚ ╔══[COMPILATION ERROR] Expected a valid parameter, found tuple -//β”‚ β•‘ l.15: fun sum([x, y]) = x + y +//β”‚ β•‘ l.16: fun sum([x, y]) = x + y //β”‚ ╙── ^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Name not found: x -//β”‚ β•‘ l.15: fun sum([x, y]) = x + y +//β”‚ β•‘ l.16: fun sum([x, y]) = x + y //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Name not found: y -//β”‚ β•‘ l.15: fun sum([x, y]) = x + y +//β”‚ β•‘ l.16: fun sum([x, y]) = x + y //β”‚ ╙── ^ -//β”‚ ╔══[COMPILATION ERROR] Expected 0 arguments, got 1 -//β”‚ β•‘ l.16: sum([1, 2]) -//β”‚ ╙── ^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.17: sum([1, 2]) +//β”‚ β•‘ ^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'sum' expected 0 arguments, but got 1 +//β”‚ β•‘ l.16: fun sum([x, y]) = x + y +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. fun sum(pair) = diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/SpreadTrailingIndex.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/SpreadTrailingIndex.mls index 794c5f6797..6a7a4d0daa 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/SpreadTrailingIndex.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/SpreadTrailingIndex.mls @@ -1,3 +1,4 @@ +:. :js // Regression test: spread destructuring with different trailing sizes should diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/String.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/String.mls index 2768956a89..243b6ab7de 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/String.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/String.mls @@ -1,3 +1,4 @@ +:. :js fun parseIntegerSimple(str) = if str is diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/And.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/And.mls index 449f9bc182..d82e36b318 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/And.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/And.mls @@ -1,3 +1,4 @@ +:. :parseOnly :pt @@ -133,9 +134,9 @@ if x is B then z B then C //β”‚ ╔══[PARSE ERROR] Expected start of expression in this position; found new line instead -//β”‚ β•‘ l.132: and v is +//β”‚ β•‘ l.133: and v is //β”‚ β•‘ ^ -//β”‚ β•‘ l.133: B then z +//β”‚ β•‘ l.134: B then z //β”‚ ╙── ^^^^ //β”‚ β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”| Parsed tree |β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” //β”‚ IfLike: diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/BadUCSSyntax.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/BadUCSSyntax.mls index 4fe34c676d..e7deda684b 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/BadUCSSyntax.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/BadUCSSyntax.mls @@ -1,3 +1,4 @@ +:. :js @@ -6,10 +7,10 @@ :e if x else z //β”‚ ╔══[COMPILATION ERROR] Unrecognized term split (identifier) -//β”‚ β•‘ l.7: if x else z +//β”‚ β•‘ l.8: if x else z //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Name not found: z -//β”‚ β•‘ l.7: if x else z +//β”‚ β•‘ l.8: if x else z //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -18,7 +19,7 @@ fun f(x, z) = if x else z //β”‚ ╔══[COMPILATION ERROR] Unrecognized term split (identifier) -//β”‚ β•‘ l.18: if x +//β”‚ β•‘ l.19: if x //β”‚ ╙── ^ // β€”β€”β€” β€”β€”β€” β€”β€”β€” @@ -47,14 +48,14 @@ if and true do print("ok") //β”‚ ╔══[COMPILATION ERROR] Unrecognized term split (true literal) -//β”‚ β•‘ l.45: and true +//β”‚ β•‘ l.46: and true //β”‚ ╙── ^^^^ //β”‚ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'do' here -//β”‚ β•‘ l.46: let x = 1 +//β”‚ β•‘ l.47: let x = 1 //β”‚ β•‘ ^ -//β”‚ β•‘ l.47: and true do +//β”‚ β•‘ l.48: and true do //β”‚ β•‘ ^^^^^^^^^^^^^ -//β”‚ β•‘ l.48: print("ok") +//β”‚ β•‘ l.49: print("ok") //β”‚ ╙── ^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/Case.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/Case.mls index b49686c907..63c27351f1 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/Case.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/Case.mls @@ -1,3 +1,4 @@ +:. :js let f = case x then 0 @@ -48,13 +49,13 @@ h'(2) :e case (0 | 1) //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern split (parenthesis section). -//β”‚ β•‘ l.49: case (0 | 1) +//β”‚ β•‘ l.50: case (0 | 1) //β”‚ ╙── ^^^^^^^ //β”‚ = fun :e case 0 | 1 //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern split (infix operator '|'). -//β”‚ β•‘ l.56: case 0 | 1 +//β”‚ β•‘ l.57: case 0 | 1 //β”‚ ╙── ^^^^^ //β”‚ = fun diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/ConjunctMatches.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/ConjunctMatches.mls index dcd99557b3..3359ccacf6 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/ConjunctMatches.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/ConjunctMatches.mls @@ -1,3 +1,4 @@ +:. // ConjunctMatches.mls // =================== // Examine the structure of conjunct matches. diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/Do.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/Do.mls index 3c69b22b0c..508f911be7 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/Do.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/Do.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Option.mls" @@ -68,13 +69,13 @@ fun g(y) = Some(v) and v % 2 == 0 then set x = Some(v / 2) x //β”‚ ╔══[COMPILATION ERROR] Mixed use of `do` and `then` in the `if` expression. -//β”‚ β•‘ l.66: if x is +//β”‚ β•‘ l.67: if x is //β”‚ β•‘ ^^ //β”‚ β•Ÿβ”€β”€ Keyword `then` is used here. -//β”‚ β•‘ l.68: Some(v) and v % 2 == 0 then set x = Some(v / 2) +//β”‚ β•‘ l.69: Some(v) and v % 2 == 0 then set x = Some(v / 2) //β”‚ β•‘ ^^^^ //β”‚ β•Ÿβ”€β”€ Keyword `do` is used here. -//β”‚ β•‘ l.67: Some(0) do set x = None +//β”‚ β•‘ l.68: Some(0) do set x = None //β”‚ ╙── ^^ g(0) @@ -128,7 +129,7 @@ h(41) Some(1) is Some of [process, rest] do "hi" //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern (infix operator 'do'). -//β”‚ β•‘ l.129: Some of [process, rest] do "hi" +//β”‚ β•‘ l.130: Some of [process, rest] do "hi" //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ = true diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/DoProblems.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/DoProblems.mls index 7fbf8c7295..264019e318 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/DoProblems.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/DoProblems.mls @@ -1,3 +1,4 @@ +:. :js // * It looks like trying to support both infix `do` and prefix `do` is a problem; @@ -20,13 +21,13 @@ while do print(arrMatches) //β”‚ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'do' here -//β”‚ β•‘ l.18: let arrMatches = true +//β”‚ β•‘ l.19: let arrMatches = true //β”‚ β•‘ ^^^^^^^^^^^^^^^^^ -//β”‚ β•‘ l.19: arrMatches +//β”‚ β•‘ l.20: arrMatches //β”‚ β•‘ ^^^^^^^^^^^^^^ -//β”‚ β•‘ l.20: do +//β”‚ β•‘ l.21: do //β”‚ β•‘ ^^^^ -//β”‚ β•‘ l.21: print(arrMatches) +//β”‚ β•‘ l.22: print(arrMatches) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -49,7 +50,7 @@ if do () //β”‚ ╔══[COMPILATION ERROR] Unrecognized term split (null) -//β”‚ β•‘ l.48: arrMatches !== null +//β”‚ β•‘ l.49: arrMatches !== null //β”‚ ╙── ^^^^ //β”‚ ═══[RUNTIME ERROR] Error: match error diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/Else.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/Else.mls index 3add0d8c64..7b8d02a6e0 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/Else.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/Else.mls @@ -1,3 +1,4 @@ +:. :js // Else.mls @@ -76,7 +77,7 @@ fun f(x, y, z) = if x then y else z //β”‚ ╔══[PARSE ERROR] Unexpected 'else' keyword here -//β”‚ β•‘ l.77: x then y else z +//β”‚ β•‘ l.78: x then y else z //β”‚ ╙── ^^^^ @@ -107,7 +108,7 @@ else else 1 ) + 3 //β”‚ ╔══[PARSE ERROR] Unexpected 'else' keyword here -//β”‚ β•‘ l.107: else 1 +//β”‚ β•‘ l.108: else 1 //β”‚ ╙── ^^^^ //β”‚ = 8 @@ -117,7 +118,7 @@ else 5 else 1 ) + 3 //β”‚ ╔══[PARSE ERROR] Unexpected 'else' keyword here -//β”‚ β•‘ l.117: 5 else 1 +//β”‚ β•‘ l.118: 5 else 1 //β”‚ ╙── ^^^^ //β”‚ = 8 @@ -132,11 +133,11 @@ fun foo(x) = if x is else "I don't want to tell you what's inside the box." else "I shouldn't tell you what's inside the box." //β”‚ ╔══[WARNING] This catch-all clause makes the following branches unreachable. -//β”‚ β•‘ l.131: else "I can't tell you what's inside the box." +//β”‚ β•‘ l.132: else "I can't tell you what's inside the box." //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ This branch is unreachable. -//β”‚ β•‘ l.132: else "I don't want to tell you what's inside the box." +//β”‚ β•‘ l.133: else "I don't want to tell you what's inside the box." //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ This branch is unreachable. -//β”‚ β•‘ l.133: else "I shouldn't tell you what's inside the box." +//β”‚ β•‘ l.134: else "I shouldn't tell you what's inside the box." //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/Empty.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/Empty.mls index 06438179b9..0315a9e370 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/Empty.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/Empty.mls @@ -1,3 +1,4 @@ +:. :js @@ -28,7 +29,7 @@ if if let x //β”‚ ╔══[COMPILATION ERROR] Unrecognized term split (let) -//β”‚ β•‘ l.29: let x +//β”‚ β•‘ l.30: let x //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] Error: match error diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/IfOpSplit.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/IfOpSplit.mls index ff3ae66288..68795c3d0e 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/IfOpSplit.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/IfOpSplit.mls @@ -1,3 +1,4 @@ +:. :js @@ -27,23 +28,23 @@ if 1 + 2 * 3 then 0 + 4 then 0 //β”‚ ╔══[PARSE ERROR] Operator cannot be used inside this operator split -//β”‚ β•‘ l.28: + 4 then 0 +//β”‚ β•‘ l.29: + 4 then 0 //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ as it has lower precedence than the splitting operator here -//β”‚ β•‘ l.27: * 3 then 0 +//β”‚ β•‘ l.28: * 3 then 0 //β”‚ ╙── ^ //β”‚ ╔══[PARSE ERROR] Unexpected 'then' keyword in this operator split inner position -//β”‚ β•‘ l.28: + 4 then 0 +//β”‚ β•‘ l.29: + 4 then 0 //β”‚ β•‘ ^^^^ //β”‚ β•Ÿβ”€β”€ Note: the operator split starts here -//β”‚ β•‘ l.27: * 3 then 0 +//β”‚ β•‘ l.28: * 3 then 0 //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here -//β”‚ β•‘ l.26: if 1 + 2 +//β”‚ β•‘ l.27: if 1 + 2 //β”‚ β•‘ ^ -//β”‚ β•‘ l.27: * 3 then 0 +//β”‚ β•‘ l.28: * 3 then 0 //β”‚ β•‘ ^^^^^^^^^^^^ -//β”‚ β•‘ l.28: + 4 then 0 +//β”‚ β•‘ l.29: + 4 then 0 //β”‚ ╙── ^^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -70,9 +71,9 @@ if 1 + 2 * 3 then 0 then 0 //β”‚ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here -//β”‚ β•‘ l.70: * 3 then 0 +//β”‚ β•‘ l.71: * 3 then 0 //β”‚ β•‘ ^ -//β”‚ β•‘ l.71: then 0 +//β”‚ β•‘ l.72: then 0 //β”‚ ╙── ^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: match error diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/Is.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/Is.mls index ac01636309..fde098b59b 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/Is.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/Is.mls @@ -1,3 +1,4 @@ +:. :parseOnly diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/NestedOpSplits.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/NestedOpSplits.mls index 81ed717faf..99fe0cd4f5 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/NestedOpSplits.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/NestedOpSplits.mls @@ -1,3 +1,4 @@ +:. :js :ucs desugared @@ -10,13 +11,13 @@ fun f(x) = //β”‚ Split with nested patterns: //β”‚ > if //β”‚ > let tmp:scrut = 1 -//β”‚ > let tmp:scrut = (member:Predef⁰.)equalsβ€Ήmember:equalsβ€Ί(x⁰, builtin:+⁰(tmp:scrut⁰, 2)) +//β”‚ > let tmp:scrut = member:Predef⁰.equals^term:f⁰(x⁰, builtin:+⁰(tmp:scrut⁰, 2)) //β”‚ > tmp:scrut is true then 0 //β”‚ > else 1 //β”‚ Expanded split with flattened patterns: //β”‚ > β€Ήif|whileβ€Ί //β”‚ > let tmp:scrut = 1 -//β”‚ > let tmp:scrut = (member:Predef⁰.)equalsβ€Ήmember:equalsβ€Ί(x⁰, builtin:+⁰(tmp:scrut⁰, 2)) +//β”‚ > let tmp:scrut = member:Predef⁰.equals^term:f⁰(x⁰, builtin:+⁰(tmp:scrut⁰, 2)) //β”‚ > tmp:scrut is true then 0 //β”‚ > else 1 diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/Of.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/Of.mls index d3b79d9968..ced34e0623 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/Of.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/Of.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Option.mls" diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/PlainConditionals.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/PlainConditionals.mls index a0f3d97a8d..eef6e08b8e 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/PlainConditionals.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/PlainConditionals.mls @@ -1,3 +1,4 @@ +:. :js fun f(x) = x @@ -77,14 +78,14 @@ fun foo(x) = x is Pair(a, b) and a > b Int //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern (infix operator 'and'). -//β”‚ β•‘ l.77: Pair(a, b) and a > b +//β”‚ β•‘ l.78: Pair(a, b) and a > b //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^ :e fun foo(x) = x is Pair(a, b) | Int //β”‚ ╔══[COMPILATION ERROR] Unexpected term form in expression position (alternation) -//β”‚ β•‘ l.85: fun foo(x) = x is Pair(a, b) | Int +//β”‚ β•‘ l.86: fun foo(x) = x is Pair(a, b) | Int //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^ // Test how shorthand conditionals work together with lambda functions. diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/SimpleUCS.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/SimpleUCS.mls index 0ad3a832d9..fb9982713c 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/SimpleUCS.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/SimpleUCS.mls @@ -1,3 +1,4 @@ +:. :js abstract class Option[A]: Some[A] | None @@ -181,7 +182,7 @@ data class IntVal(value: Int) extends ValBase data class BoolVal(value: Bool) extends ValBase data class Lit(value: ValBase) -fun p(e, context) = +fun p(e, context: {get: Str -> Option[ValBase]}) = if e is Var(x) and context.get(x) is Some(IntVal(v)) then Left(v) @@ -199,9 +200,9 @@ fun f(x) = 0 :: Nil() then "oh" //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern split (operator application). -//β”‚ β•‘ l.199: 0 :: +//β”‚ β•‘ l.200: 0 :: //β”‚ β•‘ ^^^^ -//β”‚ β•‘ l.200: Nil() then "oh" +//β”‚ β•‘ l.201: Nil() then "oh" //β”‚ ╙── ^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/Split.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/Split.mls index 936ec87454..1d6f5a7412 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/Split.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/Split.mls @@ -1,3 +1,4 @@ +:. :parseOnly :pt diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/TupleRest.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/TupleRest.mls index d02c7c20c1..f5e13a1a14 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/TupleRest.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/TupleRest.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Stack.mls" diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/WithBraces.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/WithBraces.mls index 2634c5bf7d..da7d8cecc6 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/WithBraces.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/WithBraces.mls @@ -1,3 +1,4 @@ +:. // :elt x => if x is { 0 then false, 1 then true } diff --git a/hkmc2/shared/src/test/mlscript/ups/.mls b/hkmc2/shared/src/test/mlscript/ups/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ups/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ups/BadPatterns.mls b/hkmc2/shared/src/test/mlscript/ups/BadPatterns.mls index 2aa64eff58..4dd012109c 100644 --- a/hkmc2/shared/src/test/mlscript/ups/BadPatterns.mls +++ b/hkmc2/shared/src/test/mlscript/ups/BadPatterns.mls @@ -1,31 +1,32 @@ +:. :js :e pattern P //β”‚ ╔══[COMPILATION ERROR] Pattern definitions must have a body. -//β”‚ β•‘ l.5: pattern P +//β”‚ β•‘ l.6: pattern P //β”‚ ╙── ^^^^^^^^^ :e pattern Foo1[A]: ... //β”‚ ╔══[COMPILATION ERROR] Pattern definitions must have a body. -//β”‚ β•‘ l.11: pattern Foo1[A]: ... +//β”‚ β•‘ l.12: pattern Foo1[A]: ... //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^ :fixme pattern Foo2[pattern A]: ... //β”‚ ╔══[COMPILATION ERROR] Unsupported type parameter type definition -//β”‚ β•‘ l.17: pattern Foo2[pattern A]: ... +//β”‚ β•‘ l.18: pattern Foo2[pattern A]: ... //β”‚ ╙── ^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Pattern definitions must have a body. -//β”‚ β•‘ l.17: pattern Foo2[pattern A]: ... +//β”‚ β•‘ l.18: pattern Foo2[pattern A]: ... //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :e pattern Foo(pattern T) //β”‚ ╔══[COMPILATION ERROR] Pattern definitions must have a body. -//β”‚ β•‘ l.26: pattern Foo(pattern T) +//β”‚ β•‘ l.27: pattern Foo(pattern T) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ups/BasicStackPatterns.mls b/hkmc2/shared/src/test/mlscript/ups/BasicStackPatterns.mls index d7adcc017b..2ee9a6b6ee 100644 --- a/hkmc2/shared/src/test/mlscript/ups/BasicStackPatterns.mls +++ b/hkmc2/shared/src/test/mlscript/ups/BasicStackPatterns.mls @@ -1,3 +1,4 @@ +:. :js @@ -9,12 +10,12 @@ open Stack :e :w pattern P = x :: xs => [x, xs] -//β”‚ ╔══[COMPILATION ERROR] Name not found: x -//β”‚ β•‘ l.11: pattern P = x :: xs => [x, xs] -//β”‚ ╙── ^ //β”‚ ╔══[WARNING] Unused pattern binding: x. -//β”‚ β•‘ l.11: pattern P = x :: xs => [x, xs] +//β”‚ β•‘ l.12: pattern P = x :: xs => [x, xs] //β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] Name not found in wildcard opens: x +//β”‚ β•‘ l.12: pattern P = x :: xs => [x, xs] +//β”‚ ╙── ^ pattern P = (x :: xs) => [x, xs] diff --git a/hkmc2/shared/src/test/mlscript/ups/EmptyJunctions.mls b/hkmc2/shared/src/test/mlscript/ups/EmptyJunctions.mls index 1046092387..cd3452b0b2 100644 --- a/hkmc2/shared/src/test/mlscript/ups/EmptyJunctions.mls +++ b/hkmc2/shared/src/test/mlscript/ups/EmptyJunctions.mls @@ -1,3 +1,4 @@ +:. :js open annotations diff --git a/hkmc2/shared/src/test/mlscript/ups/JoinPatterns.mls b/hkmc2/shared/src/test/mlscript/ups/JoinPatterns.mls index 2114f0349f..4358eafc10 100644 --- a/hkmc2/shared/src/test/mlscript/ups/JoinPatterns.mls +++ b/hkmc2/shared/src/test/mlscript/ups/JoinPatterns.mls @@ -1,3 +1,4 @@ +:. :js pattern SimpleJoin = ("abc" | "xyz") ~ ("def" | "") diff --git a/hkmc2/shared/src/test/mlscript/ups/RangePatterns.mls b/hkmc2/shared/src/test/mlscript/ups/RangePatterns.mls index a3a7b6b833..c13359515a 100644 --- a/hkmc2/shared/src/test/mlscript/ups/RangePatterns.mls +++ b/hkmc2/shared/src/test/mlscript/ups/RangePatterns.mls @@ -1,3 +1,4 @@ +:. :js pattern Lower = "a"..="z" @@ -50,19 +51,16 @@ pattern UnsignedShort = 0 ..< 65536 // Don't forget to add a space between numbers and the range operator. pattern UnsignedByte = 0..< 256 //β”‚ ╔══[LEXICAL ERROR] Expected at least one digit after the decimal point -//β”‚ β•‘ l.51: pattern UnsignedByte = 0..< 256 +//β”‚ β•‘ l.52: pattern UnsignedByte = 0..< 256 //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Name not found: .< -//β”‚ β•‘ l.51: pattern UnsignedByte = 0..< 256 -//β”‚ ╙── ^^ -//β”‚ ╔══[COMPILATION ERROR] Cannot use this β€Ήerrorβ€Ί as a pattern. -//β”‚ β•‘ l.51: pattern UnsignedByte = 0..< 256 +//β”‚ β•‘ l.52: pattern UnsignedByte = 0..< 256 //β”‚ ╙── ^^ :e pattern BadRange = "s"..=0 //β”‚ ╔══[COMPILATION ERROR] The upper and lower bounds of range patterns should be literals of the same type. -//β”‚ β•‘ l.63: pattern BadRange = "s"..=0 +//β”‚ β•‘ l.61: pattern BadRange = "s"..=0 //β”‚ ╙── ^^^^^^^ // It becomes an absurd pattern. @@ -72,14 +70,14 @@ pattern BadRange = "s"..=0 :e pattern BadRange = 0 ..= "s" //β”‚ ╔══[COMPILATION ERROR] The upper and lower bounds of range patterns should be literals of the same type. -//β”‚ β•‘ l.73: pattern BadRange = 0 ..= "s" +//β”‚ β•‘ l.71: pattern BadRange = 0 ..= "s" //β”‚ ╙── ^^^^^^^^^ :e pattern BadRange = "yolo" ..= "swag" //β”‚ ╔══[COMPILATION ERROR] The lower bound of character ranges must be a single character. -//β”‚ β•‘ l.79: pattern BadRange = "yolo" ..= "swag" +//β”‚ β•‘ l.77: pattern BadRange = "yolo" ..= "swag" //β”‚ β•‘ ^^^^^^ //β”‚ β•Ÿβ”€β”€ The upper bound of character ranges must be a single character. -//β”‚ β•‘ l.79: pattern BadRange = "yolo" ..= "swag" +//β”‚ β•‘ l.77: pattern BadRange = "yolo" ..= "swag" //β”‚ ╙── ^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ups/RecursiveTransformations.mls b/hkmc2/shared/src/test/mlscript/ups/RecursiveTransformations.mls index 52f6a8c865..eaba4ce345 100644 --- a/hkmc2/shared/src/test/mlscript/ups/RecursiveTransformations.mls +++ b/hkmc2/shared/src/test/mlscript/ups/RecursiveTransformations.mls @@ -1,3 +1,4 @@ +:. :js @@ -64,14 +65,14 @@ arr is MultiSet(3, MultiSet(0, _)) :fixme assert arr is MultiSet(1, rest), rest //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'rest' -//β”‚ β•‘ l.65: assert arr is MultiSet(1, rest), rest +//β”‚ β•‘ l.66: assert arr is MultiSet(1, rest), rest //β”‚ ╙── ^^^^ //β”‚ ═══[RUNTIME ERROR] ReferenceError: rest is not defined :e assert arr is MultiSet as res, res //β”‚ ═══[COMPILATION ERROR] Expected two pattern arguments, but found only zero pattern arguments. -//β”‚ ═══[RUNTIME ERROR] Error: Assertion failed (RecursiveTransformations.mls:73) +//β”‚ ═══[RUNTIME ERROR] Error: Assertion failed (RecursiveTransformations.mls:74) pattern Id = _ @@ -82,7 +83,7 @@ if [1, 2, 3] is //β”‚ ═══[COMPILATION ERROR] No definition found in scope for member 'arg$Id$0$' //β”‚ ═══[COMPILATION ERROR] No definition found in scope for member 'arg$Id$0$' //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' -//β”‚ β•‘ l.81: MultiSet(2, MultiSet(1, Id(x))) do print(x) +//β”‚ β•‘ l.82: MultiSet(2, MultiSet(1, Id(x))) do print(x) //β”‚ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/ups/SimpleConjunction.mls b/hkmc2/shared/src/test/mlscript/ups/SimpleConjunction.mls index 1dabbc9f85..fbd5b72b65 100644 --- a/hkmc2/shared/src/test/mlscript/ups/SimpleConjunction.mls +++ b/hkmc2/shared/src/test/mlscript/ups/SimpleConjunction.mls @@ -1,3 +1,4 @@ +:. :js import "../../mlscript-compile/Option.mls" diff --git a/hkmc2/shared/src/test/mlscript/ups/TransformFree.mls b/hkmc2/shared/src/test/mlscript/ups/TransformFree.mls index 4f54ac9876..978a236311 100644 --- a/hkmc2/shared/src/test/mlscript/ups/TransformFree.mls +++ b/hkmc2/shared/src/test/mlscript/ups/TransformFree.mls @@ -1,3 +1,4 @@ +:. :js open annotations diff --git a/hkmc2/shared/src/test/mlscript/ups/UpsBugsBacklog.mls b/hkmc2/shared/src/test/mlscript/ups/UpsBugsBacklog.mls index 75f3fbb801..0fe2bc0547 100644 --- a/hkmc2/shared/src/test/mlscript/ups/UpsBugsBacklog.mls +++ b/hkmc2/shared/src/test/mlscript/ups/UpsBugsBacklog.mls @@ -1,3 +1,4 @@ +:. :js open annotations @@ -47,7 +48,7 @@ if tokens is @compile Parse as stack do print(stack) if tokens is @compile Parse as stack do print(stack) //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'stack' -//β”‚ β•‘ l.48: @compile Parse as stack do print(stack) +//β”‚ β•‘ l.49: @compile Parse as stack do print(stack) //β”‚ ╙── ^^^^^ // What's actually intended: diff --git a/hkmc2/shared/src/test/mlscript/ups/examples/.mls b/hkmc2/shared/src/test/mlscript/ups/examples/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ups/examples/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ups/examples/BadRecStackParse.mls b/hkmc2/shared/src/test/mlscript/ups/examples/BadRecStackParse.mls index e436d25b5b..527f11a138 100644 --- a/hkmc2/shared/src/test/mlscript/ups/examples/BadRecStackParse.mls +++ b/hkmc2/shared/src/test/mlscript/ups/examples/BadRecStackParse.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/ups/examples/BasicSeqStackParse.mls b/hkmc2/shared/src/test/mlscript/ups/examples/BasicSeqStackParse.mls index d02fb5fa98..2effecf635 100644 --- a/hkmc2/shared/src/test/mlscript/ups/examples/BasicSeqStackParse.mls +++ b/hkmc2/shared/src/test/mlscript/ups/examples/BasicSeqStackParse.mls @@ -1,3 +1,4 @@ +:. :js @@ -20,7 +21,7 @@ data class Expr with Op(lhs: Expr, op: Str, rhs: Expr) -fun go(stack, input) = if stack is +fun go(stack: Array[Any], input: Array[Str]) = if stack is [..rest, Expr as e1, "+", Expr as e2] then go([..rest, Op(e1, "+", e2)], input) [..rest, Ident as id] then @@ -38,7 +39,7 @@ fun go(stack, input) = if stack is Error: stack let Space = new RegExp of "(\\s+)" -fun parse(input) = +fun parse(input: Str) = let input = input.split(Space) \filtering of (_ !== "") \filtering of (_ \every of (_ !== " ")) diff --git a/hkmc2/shared/src/test/mlscript/ups/examples/BasicStackParse.mls b/hkmc2/shared/src/test/mlscript/ups/examples/BasicStackParse.mls index 59f7c24d47..0bcaff6622 100644 --- a/hkmc2/shared/src/test/mlscript/ups/examples/BasicStackParse.mls +++ b/hkmc2/shared/src/test/mlscript/ups/examples/BasicStackParse.mls @@ -1,3 +1,4 @@ +:. :js @@ -70,10 +71,10 @@ pattern Parse = ((Expr as e) :: "+" :: (Expr as e) :: rest) => Op(e, "+", e) :: rest ) //β”‚ ╔══[COMPILATION ERROR] Unexpected record property pattern. -//β”‚ β•‘ l.69: ((Str as name) :: rest) => Id(name) :: rest +//β”‚ β•‘ l.70: ((Str as name) :: rest) => Id(name) :: rest //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Unexpected record property pattern. -//β”‚ β•‘ l.70: ((Expr as e) :: "+" :: (Expr as e) :: rest) => Op(e, "+", e) :: rest +//β”‚ β•‘ l.71: ((Expr as e) :: "+" :: (Expr as e) :: rest) => Op(e, "+", e) :: rest //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ups/examples/DnfCnf.mls b/hkmc2/shared/src/test/mlscript/ups/examples/DnfCnf.mls index 8f80b027ce..a239449e03 100644 --- a/hkmc2/shared/src/test/mlscript/ups/examples/DnfCnf.mls +++ b/hkmc2/shared/src/test/mlscript/ups/examples/DnfCnf.mls @@ -1,3 +1,4 @@ +:. :js open annotations diff --git a/hkmc2/shared/src/test/mlscript/ups/examples/DoubleOrSum.mls b/hkmc2/shared/src/test/mlscript/ups/examples/DoubleOrSum.mls index 2573afb86c..37d1e8b3da 100644 --- a/hkmc2/shared/src/test/mlscript/ups/examples/DoubleOrSum.mls +++ b/hkmc2/shared/src/test/mlscript/ups/examples/DoubleOrSum.mls @@ -1,3 +1,4 @@ +:. :js open annotations diff --git a/hkmc2/shared/src/test/mlscript/ups/examples/DoubleTripleList.mls b/hkmc2/shared/src/test/mlscript/ups/examples/DoubleTripleList.mls index e8610ea98a..ec867c5b61 100644 --- a/hkmc2/shared/src/test/mlscript/ups/examples/DoubleTripleList.mls +++ b/hkmc2/shared/src/test/mlscript/ups/examples/DoubleTripleList.mls @@ -1,3 +1,4 @@ +:. :js open annotations @@ -102,8 +103,9 @@ let testList = 1 :: 2 :: Nil //β”‚ testList = Cons(1, Cons(2, Nil)) withLog of () => - testList.head - testList.tail + // Observe the installed JS getters dynamically; Cons's val reads are pure. + testList!head + testList!tail () //β”‚ > access the `head` of [1, 2] //β”‚ > access the `tail` of [1, 2] diff --git a/hkmc2/shared/src/test/mlscript/ups/examples/EvaluationContext.mls b/hkmc2/shared/src/test/mlscript/ups/examples/EvaluationContext.mls index 57e4af7a1b..2fd23eb40f 100644 --- a/hkmc2/shared/src/test/mlscript/ups/examples/EvaluationContext.mls +++ b/hkmc2/shared/src/test/mlscript/ups/examples/EvaluationContext.mls @@ -1,3 +1,4 @@ +:. :js // This is an interesting experiment in evaluation context. @@ -21,7 +22,7 @@ data class Term with let _freeVars = None // Compute free variables. Retrieve the cache if it exists. - fun freeVars = if _freeVars is + fun freeVars: Set[Str] = if _freeVars is Some(fv) then fv None then let fv = if this is @@ -149,7 +150,7 @@ id_x <~> succ zero <~> false_ //β”‚ = true -fun subst(term, name, target) = if term is +fun subst(term, name, target: Term) = if term is Var(name') and name === name' then target else term @@ -231,7 +232,7 @@ redex of id_x redex of Var("a") //β”‚ ═══[RUNTIME ERROR] Error: match error -fun step(term) = +fun step(term): Term = if term is ByValue.Context(ByValue.Redex) as [subterm, remake] then remake(subterm) :re @@ -258,7 +259,7 @@ step(App(App(id_a, id_b), id_x)).show step(App(id_x, id_y)).show //β”‚ = "Ξ»y. y" -fun evalByValue(term) = +fun evalByValue(term: Term): Term = while term is ByValue.Context(ByValue.Redex) as [subterm, remake] then set term = remake(subterm) @@ -281,16 +282,16 @@ fun evalByValue'(term) = set term = remake(subterm) else term //β”‚ ╔══[COMPILATION ERROR] Pattern chaining is not supported in pattern compilation. -//β”‚ β•‘ l.214: App(Abs as lhs, Context(Hole) as [rhs, remake]) => +//β”‚ β•‘ l.215: App(Abs as lhs, Context(Hole) as [rhs, remake]) => //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Pattern chaining is not supported in pattern compilation. -//β”‚ β•‘ l.217: App(Context(Hole) as [lhs, remake], Term as rhs) => +//β”‚ β•‘ l.218: App(Context(Hole) as [lhs, remake], Term as rhs) => //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.282: else term +//β”‚ β•‘ l.283: else term //β”‚ ╙── ^^^^ //β”‚ ╔══[WARNING] Pure expression in statement position -//β”‚ β•‘ l.282: else term +//β”‚ β•‘ l.283: else term //β”‚ ╙── ^^^^ object ByName with @@ -317,7 +318,14 @@ print of t.show import "../../../mlscript-compile/Runtime.mls" -fun stepByStep(system, term, maxSteps) = +// An evaluation system supplies a redex pattern and a context matcher. Its +// successful output is a reduced subterm together with a reconstruction function. +type EvaluationSystem = { + Redex: Runtime.Pattern[Term, Term], + Context: { unapply: (Runtime.Pattern[Term, Term], Term) -> Runtime.MatchResult[[Term, Term -> Term]] } +} + +fun stepByStep(system: EvaluationSystem, term: Term, maxSteps: Int) = let numSteps = 0 print of "Term: " + term.show while numSteps < maxSteps and system.Context.unapply(system.Redex, term) is diff --git a/hkmc2/shared/src/test/mlscript/ups/examples/Extraction.mls b/hkmc2/shared/src/test/mlscript/ups/examples/Extraction.mls index 1f0025923c..d7b08b6084 100644 --- a/hkmc2/shared/src/test/mlscript/ups/examples/Extraction.mls +++ b/hkmc2/shared/src/test/mlscript/ups/examples/Extraction.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Option.mls" diff --git a/hkmc2/shared/src/test/mlscript/ups/examples/Flatten.mls b/hkmc2/shared/src/test/mlscript/ups/examples/Flatten.mls index 8e8e9f90dd..9641eb82d7 100644 --- a/hkmc2/shared/src/test/mlscript/ups/examples/Flatten.mls +++ b/hkmc2/shared/src/test/mlscript/ups/examples/Flatten.mls @@ -1,3 +1,4 @@ +:. :js open annotations diff --git a/hkmc2/shared/src/test/mlscript/ups/examples/Negation.mls b/hkmc2/shared/src/test/mlscript/ups/examples/Negation.mls index 0482b5c0ae..42982fcf68 100644 --- a/hkmc2/shared/src/test/mlscript/ups/examples/Negation.mls +++ b/hkmc2/shared/src/test/mlscript/ups/examples/Negation.mls @@ -1,3 +1,4 @@ +:. :js pattern NonZero = ~0 diff --git a/hkmc2/shared/src/test/mlscript/ups/examples/PrecedenceClimbStackParse.mls b/hkmc2/shared/src/test/mlscript/ups/examples/PrecedenceClimbStackParse.mls index 81362e3792..0175a9b9bd 100644 --- a/hkmc2/shared/src/test/mlscript/ups/examples/PrecedenceClimbStackParse.mls +++ b/hkmc2/shared/src/test/mlscript/ups/examples/PrecedenceClimbStackParse.mls @@ -1,3 +1,4 @@ +:. :js @@ -139,12 +140,9 @@ f("*") // * Note: this fails due to `,` terminating the function scope... :e fun f(x) = assert x is str as AtPrec as prec, [str, prec] -//β”‚ ╔══[COMPILATION ERROR] Name not found: str -//β”‚ β•‘ l.141: fun f(x) = assert x is str as AtPrec as prec, [str, prec] +//β”‚ ╔══[COMPILATION ERROR] Name not found in wildcard opens: str +//β”‚ β•‘ l.142: fun f(x) = assert x is str as AtPrec as prec, [str, prec] //β”‚ ╙── ^^^ -//β”‚ ╔══[COMPILATION ERROR] Name not found: prec -//β”‚ β•‘ l.141: fun f(x) = assert x is str as AtPrec as prec, [str, prec] -//β”‚ ╙── ^^^^ //β”‚ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/ups/examples/Record.mls b/hkmc2/shared/src/test/mlscript/ups/examples/Record.mls index d32ccc54b9..20e6f755b5 100644 --- a/hkmc2/shared/src/test/mlscript/ups/examples/Record.mls +++ b/hkmc2/shared/src/test/mlscript/ups/examples/Record.mls @@ -1,3 +1,4 @@ +:. :js open annotations @@ -38,7 +39,7 @@ fun bmi(record) = if record is MatchOrNone(RoundedBMI) as value then value // alternatively? fun bmi'(record) = if record is MatchOrNone[RoundedBMI] as value then value //β”‚ ╔══[COMPILATION ERROR] Unrecognized pattern (application). -//β”‚ β•‘ l.39: fun bmi'(record) = if record is MatchOrNone[RoundedBMI] as value then value +//β”‚ β•‘ l.40: fun bmi'(record) = if record is MatchOrNone[RoundedBMI] as value then value //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ :expect Some(23) @@ -51,7 +52,7 @@ bmi of height: 1.75 :todo // Make it happen! fun bmi'(record) = if record is (@compile MatchOrNone(RoundedBMI)) as value then value //β”‚ ╔══[COMPILATION ERROR] Pattern chaining is not supported in pattern compilation. -//β”‚ β•‘ l.20: pattern RoundedBMI = CalculateBMI as Rounded +//β”‚ β•‘ l.21: pattern RoundedBMI = CalculateBMI as Rounded //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ // Calculate the area of different shapes diff --git a/hkmc2/shared/src/test/mlscript/ups/examples/TupleSpread.mls b/hkmc2/shared/src/test/mlscript/ups/examples/TupleSpread.mls index fc56f338d4..572349ad53 100644 --- a/hkmc2/shared/src/test/mlscript/ups/examples/TupleSpread.mls +++ b/hkmc2/shared/src/test/mlscript/ups/examples/TupleSpread.mls @@ -1,3 +1,4 @@ +:. :js // Extract the first and last elements of a list diff --git a/hkmc2/shared/src/test/mlscript/ups/fixpoint/.mls b/hkmc2/shared/src/test/mlscript/ups/fixpoint/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ups/fixpoint/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ups/fixpoint/Diagnostics.mls b/hkmc2/shared/src/test/mlscript/ups/fixpoint/Diagnostics.mls index 565ba234cd..fd087d6ac6 100644 --- a/hkmc2/shared/src/test/mlscript/ups/fixpoint/Diagnostics.mls +++ b/hkmc2/shared/src/test/mlscript/ups/fixpoint/Diagnostics.mls @@ -1,3 +1,4 @@ +:. :js // Diagnostics parity: warnings produced by the other compilation paths must @@ -21,7 +22,7 @@ pattern Steps = UnBox as Steps | _ :expect "matched" if Box(0) is @compile Steps(CaptureBit(ZeroToOne)) then "matched" else "no match" //β”‚ ╔══[WARNING] This extraction argument's transformation result is discarded by pattern `CaptureBit`. -//β”‚ β•‘ l.22: if Box(0) is @compile Steps(CaptureBit(ZeroToOne)) then "matched" else "no match" +//β”‚ β•‘ l.23: if Box(0) is @compile Steps(CaptureBit(ZeroToOne)) then "matched" else "no match" //β”‚ β•‘ ^^^^^^^^^ //β”‚ ╙── Write `... as _` to discard it explicitly. //β”‚ = "matched" @@ -35,9 +36,9 @@ if Box(0) is @compile Steps(CaptureBit(ZeroToOne)) then "matched" else "no match pattern FastSteps = @compile ((Box(CaptureBit(ZeroToOne)) => 0) as FastSteps | _) //β”‚ ╔══[COMPILATION ERROR] Pattern `CaptureBit` has zero pattern parameters. //β”‚ β•Ÿβ”€β”€ But one pattern argument were provided. -//β”‚ β•‘ l.35: pattern FastSteps = @compile ((Box(CaptureBit(ZeroToOne)) => 0) as FastSteps | _) -//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.36: pattern FastSteps = @compile ((Box(CaptureBit(ZeroToOne)) => 0) as FastSteps | _) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[WARNING] This extraction argument's transformation result is discarded by pattern `CaptureBit`. -//β”‚ β•‘ l.35: pattern FastSteps = @compile ((Box(CaptureBit(ZeroToOne)) => 0) as FastSteps | _) +//β”‚ β•‘ l.36: pattern FastSteps = @compile ((Box(CaptureBit(ZeroToOne)) => 0) as FastSteps | _) //β”‚ β•‘ ^^^^^^^^^ //β”‚ ╙── Write `... as _` to discard it explicitly. diff --git a/hkmc2/shared/src/test/mlscript/ups/fixpoint/FixedPointPatterns.mls b/hkmc2/shared/src/test/mlscript/ups/fixpoint/FixedPointPatterns.mls index 162d72150a..23773c2e37 100644 --- a/hkmc2/shared/src/test/mlscript/ups/fixpoint/FixedPointPatterns.mls +++ b/hkmc2/shared/src/test/mlscript/ups/fixpoint/FixedPointPatterns.mls @@ -1,3 +1,4 @@ +:. :js // Fixed-point patterns are patterns that pipe their own output back into @@ -374,7 +375,7 @@ if term3 is EvalAnnotated(r) and r is Lit(n) then n // Only annotating the chained pattern doesn't work. pattern EvalAnnotatedStep = @compile (Ctx(Redex) as EvalAnnotatedStep) | _ //β”‚ ╔══[WARNING] This fixed-point pattern is not supported by pattern compilation. -//β”‚ β•‘ l.375: pattern EvalAnnotatedStep = @compile (Ctx(Redex) as EvalAnnotatedStep) | _ +//β”‚ β•‘ l.376: pattern EvalAnnotatedStep = @compile (Ctx(Redex) as EvalAnnotatedStep) | _ //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ `EvalAnnotatedStep` is recursive and must be compiled as a whole (rather than just part of it). //β”‚ ╙── Falling back to the naive translation. diff --git a/hkmc2/shared/src/test/mlscript/ups/fixpoint/IndirectRecursion.mls b/hkmc2/shared/src/test/mlscript/ups/fixpoint/IndirectRecursion.mls index 6df9fa28a8..a854e68c44 100644 --- a/hkmc2/shared/src/test/mlscript/ups/fixpoint/IndirectRecursion.mls +++ b/hkmc2/shared/src/test/mlscript/ups/fixpoint/IndirectRecursion.mls @@ -1,3 +1,4 @@ +:. :js // Fixed-point patterns may recurse indirectly through other definitions: @@ -78,13 +79,13 @@ pattern PeelB = UnTag as PeelA | _ :w fun runPeelA(x) = if x is @compile PeelA(y) then y //β”‚ ╔══[WARNING] This fixed-point pattern is not supported by pattern compilation. -//β”‚ β•‘ l.79: fun runPeelA(x) = if x is @compile PeelA(y) then y -//β”‚ β•‘ ^^^^^^^ +//β”‚ β•‘ l.80: fun runPeelA(x) = if x is @compile PeelA(y) then y +//β”‚ β•‘ ^^^^^^^^ //β”‚ β•Ÿβ”€β”€ One of its recursive parts accepts any term -//β”‚ β•‘ l.71: pattern PeelB = UnTag as PeelA | _ +//β”‚ β•‘ l.72: pattern PeelB = UnTag as PeelA | _ //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ while another can fail. -//β”‚ β•‘ l.70: pattern PeelA = UnBox as PeelB | 42 +//β”‚ β•‘ l.71: pattern PeelA = UnBox as PeelB | 42 //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^ //β”‚ ╙── Falling back to the naive translation. @@ -160,8 +161,8 @@ fun atLeastOneSlow(x) = if x is AtLeastOneA(r) then r else "" :w fun atLeastOneFast(x) = if x is @compile AtLeastOneA(r) then r else "" //β”‚ ╔══[WARNING] This fixed-point pattern is not supported by pattern compilation. -//β”‚ β•‘ l.161: fun atLeastOneFast(x) = if x is @compile AtLeastOneA(r) then r else "" -//β”‚ β•‘ ^^^^^^^^^^^^^ +//β”‚ β•‘ l.162: fun atLeastOneFast(x) = if x is @compile AtLeastOneA(r) then r else "" +//β”‚ β•‘ ^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ `AtLeastOneA` does not match unless its first pattern does, which is not supported for mutually recursive definitions. //β”‚ ╙── Falling back to the naive translation. diff --git a/hkmc2/shared/src/test/mlscript/ups/fixpoint/ListFusion.mls b/hkmc2/shared/src/test/mlscript/ups/fixpoint/ListFusion.mls index 938de67bec..5c1aa264e5 100644 --- a/hkmc2/shared/src/test/mlscript/ups/fixpoint/ListFusion.mls +++ b/hkmc2/shared/src/test/mlscript/ups/fixpoint/ListFusion.mls @@ -1,3 +1,4 @@ +:. :js // Integer list pipelines fused by a fixed-point pattern. `MapInc` and @@ -32,26 +33,28 @@ fun withCounting(f) = print("Constructed " + (numObjectConstructed - before) + " object(s)") result +type Printable = {toString: () -> Str} + object Nil -data class Cons(val head, val tail) with +data class Cons(val head, val tail: Printable) with print("new: " + this.toString()) set numObjectConstructed += 1 - fun toString() = render(head) + " :: " + tail.toString() + fun toString(): Str = render(head) + " :: " + tail.toString() -data class MapInc(val source) with +data class MapInc(val source: Printable) with print("new: " + this.toString()) set numObjectConstructed += 1 - fun toString() = "map (+ 1) (" + source.toString() + ")" + fun toString(): Str = "map (+ 1) (" + source.toString() + ")" -data class FilterNonZero(val source) with +data class FilterNonZero(val source: Printable) with print("new: " + this.toString()) set numObjectConstructed += 1 - fun toString() = "filter (!= 0) (" + source.toString() + ")" + fun toString(): Str = "filter (!= 0) (" + source.toString() + ")" data class Item(val value) with print("new: " + this.toString()) set numObjectConstructed += 1 - fun toString() = render(value) + fun toString(): Str = render(value) pattern Ctx(pattern Hole) = | Hole diff --git a/hkmc2/shared/src/test/mlscript/ups/fixpoint/MoreAlternatives.mls b/hkmc2/shared/src/test/mlscript/ups/fixpoint/MoreAlternatives.mls index 8dcfd1418d..7c19007b01 100644 --- a/hkmc2/shared/src/test/mlscript/ups/fixpoint/MoreAlternatives.mls +++ b/hkmc2/shared/src/test/mlscript/ups/fixpoint/MoreAlternatives.mls @@ -1,3 +1,4 @@ +:. :js // Fixed-point patterns may list extra non-recursive alternatives between the diff --git a/hkmc2/shared/src/test/mlscript/ups/fixpoint/NonCatchAll.mls b/hkmc2/shared/src/test/mlscript/ups/fixpoint/NonCatchAll.mls index 66594299cf..72c6dae94d 100644 --- a/hkmc2/shared/src/test/mlscript/ups/fixpoint/NonCatchAll.mls +++ b/hkmc2/shared/src/test/mlscript/ups/fixpoint/NonCatchAll.mls @@ -1,3 +1,4 @@ +:. :js // A fixed-point pattern need not end in a catch-all wildcard: @@ -138,13 +139,13 @@ if 42 is KeepOriginalInt(r) then r else "" :expect 42 if 42 is @compile KeepOriginalInt(r) then r else "" //β”‚ ╔══[WARNING] This fixed-point pattern is not supported by pattern compilation. -//β”‚ β•‘ l.139: if 42 is @compile KeepOriginalInt(r) then r else "" -//β”‚ β•‘ ^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.140: if 42 is @compile KeepOriginalInt(r) then r else "" +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ A term this pattern can still rewrite -//β”‚ β•‘ l.131: pattern KeepOriginalInt = IntToStr as KeepOriginalInt | Num +//β”‚ β•‘ l.132: pattern KeepOriginalInt = IntToStr as KeepOriginalInt | Num //β”‚ β•‘ ^^^^^^^^ //β”‚ β•Ÿβ”€β”€ can also be matched by a trailing alternative. -//β”‚ β•‘ l.131: pattern KeepOriginalInt = IntToStr as KeepOriginalInt | Num +//β”‚ β•‘ l.132: pattern KeepOriginalInt = IntToStr as KeepOriginalInt | Num //β”‚ β•‘ ^^^ //β”‚ ╙── Falling back to the naive translation. //β”‚ = 42 @@ -247,14 +248,14 @@ pattern AddStep = :w pattern AddSteps = @compile ((AddStep as AddSteps) | Add(Lit, Lit)) //β”‚ ╔══[WARNING] This fixed-point pattern is not supported by pattern compilation. -//β”‚ β•‘ l.248: pattern AddSteps = @compile ((AddStep as AddSteps) | Add(Lit, Lit)) -//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.249: pattern AddSteps = @compile ((AddStep as AddSteps) | Add(Lit, Lit)) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ A term this pattern can still rewrite -//β”‚ β•‘ l.248: pattern AddSteps = @compile ((AddStep as AddSteps) | Add(Lit, Lit)) +//β”‚ β•‘ l.249: pattern AddSteps = @compile ((AddStep as AddSteps) | Add(Lit, Lit)) //β”‚ β•‘ ^^^^^^^ //β”‚ β•Ÿβ”€β”€ can also be matched by a trailing alternative. -//β”‚ β•‘ l.248: pattern AddSteps = @compile ((AddStep as AddSteps) | Add(Lit, Lit)) -//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•‘ l.249: pattern AddSteps = @compile ((AddStep as AddSteps) | Add(Lit, Lit)) +//β”‚ β•‘ ^^^^^^^^^^^^^ //β”‚ ╙── Falling back to the naive translation. diff --git a/hkmc2/shared/src/test/mlscript/ups/fixpoint/RecursionAlternatives.mls b/hkmc2/shared/src/test/mlscript/ups/fixpoint/RecursionAlternatives.mls index fa04257392..e9ad048763 100644 --- a/hkmc2/shared/src/test/mlscript/ups/fixpoint/RecursionAlternatives.mls +++ b/hkmc2/shared/src/test/mlscript/ups/fixpoint/RecursionAlternatives.mls @@ -1,3 +1,4 @@ +:. :js // A fixed-point pattern may have several recursive alternatives: @@ -176,13 +177,13 @@ fun peelOrRewrapSlow(x) = if x is PeelOrRewrap(r) then r else "" :w fun peelOrRewrapFast(x) = if x is @compile PeelOrRewrap(r) then r else "" //β”‚ ╔══[WARNING] This fixed-point pattern is not supported by pattern compilation. -//β”‚ β•‘ l.177: fun peelOrRewrapFast(x) = if x is @compile PeelOrRewrap(r) then r else "" -//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•‘ l.178: fun peelOrRewrapFast(x) = if x is @compile PeelOrRewrap(r) then r else "" +//β”‚ β•‘ ^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ Some recursive alternatives can rewrite the same term in more than one way, including this one -//β”‚ β•‘ l.170: | UnBox as PeelOrRewrap +//β”‚ β•‘ l.171: | UnBox as PeelOrRewrap //β”‚ β•‘ ^^^^^ //β”‚ β•Ÿβ”€β”€ and this one. -//β”‚ β•‘ l.171: | Rewrap as PeelOrRewrap +//β”‚ β•‘ l.172: | Rewrap as PeelOrRewrap //β”‚ β•‘ ^^^^^^ //β”‚ ╙── Falling back to the naive translation. @@ -255,13 +256,13 @@ fun mixedSlow(x) = if x is MixedSteps(r) then r else "" :w fun mixedFast(x) = if x is @compile MixedSteps(r) then r else "" //β”‚ ╔══[WARNING] This fixed-point pattern is not supported by pattern compilation. -//β”‚ β•‘ l.256: fun mixedFast(x) = if x is @compile MixedSteps(r) then r else "" -//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•‘ l.257: fun mixedFast(x) = if x is @compile MixedSteps(r) then r else "" +//β”‚ β•‘ ^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ Some recursive alternatives can rewrite the same term in more than one way, including this one -//β”‚ β•‘ l.249: | (UnTag | UnBox) as MixedSteps +//β”‚ β•‘ l.250: | (UnTag | UnBox) as MixedSteps //β”‚ β•‘ ^^^^^ //β”‚ β•Ÿβ”€β”€ and this one. -//β”‚ β•‘ l.250: | Rewrap as MixedSteps +//β”‚ β•‘ l.251: | Rewrap as MixedSteps //β”‚ β•‘ ^^^^^^ //β”‚ ╙── Falling back to the naive translation. @@ -310,12 +311,12 @@ pattern WildStep = _ as WildStep | UnTag as WildStep | Tag(_) :w fun wildStep(x) = if x is @compile WildStep(r) then r else "" //β”‚ ╔══[WARNING] This fixed-point pattern is not supported by pattern compilation. -//β”‚ β•‘ l.311: fun wildStep(x) = if x is @compile WildStep(r) then r else "" -//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•‘ l.312: fun wildStep(x) = if x is @compile WildStep(r) then r else "" +//β”‚ β•‘ ^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ Some recursive alternatives can rewrite the same term in more than one way, including this one -//β”‚ β•‘ l.308: pattern WildStep = _ as WildStep | UnTag as WildStep | Tag(_) +//β”‚ β•‘ l.309: pattern WildStep = _ as WildStep | UnTag as WildStep | Tag(_) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ and this one. -//β”‚ β•‘ l.308: pattern WildStep = _ as WildStep | UnTag as WildStep | Tag(_) +//β”‚ β•‘ l.309: pattern WildStep = _ as WildStep | UnTag as WildStep | Tag(_) //β”‚ β•‘ ^^^^^ //β”‚ ╙── Falling back to the naive translation. diff --git a/hkmc2/shared/src/test/mlscript/ups/fixpoint/SimpleExample.mls b/hkmc2/shared/src/test/mlscript/ups/fixpoint/SimpleExample.mls index b367d6e756..cf2f37fcaf 100644 --- a/hkmc2/shared/src/test/mlscript/ups/fixpoint/SimpleExample.mls +++ b/hkmc2/shared/src/test/mlscript/ups/fixpoint/SimpleExample.mls @@ -1,3 +1,4 @@ +:. :js open annotations { compile } diff --git a/hkmc2/shared/src/test/mlscript/ups/fixpoint/UnsupportedShapes.mls b/hkmc2/shared/src/test/mlscript/ups/fixpoint/UnsupportedShapes.mls index 96aa843a09..236cdf37fb 100644 --- a/hkmc2/shared/src/test/mlscript/ups/fixpoint/UnsupportedShapes.mls +++ b/hkmc2/shared/src/test/mlscript/ups/fixpoint/UnsupportedShapes.mls @@ -1,3 +1,4 @@ +:. :js // Fixed-point-shaped patterns that the machine compilation does not support @@ -32,8 +33,8 @@ if Box(Lit(7)) is PeelOnce(r) then r else "" :expect Lit(7) if Box(Lit(7)) is @compile PeelOnce(r) then r else "" //β”‚ ╔══[WARNING] This fixed-point pattern is not supported by pattern compilation. -//β”‚ β•‘ l.33: if Box(Lit(7)) is @compile PeelOnce(r) then r else "" -//β”‚ β•‘ ^^^^^^^^^^ +//β”‚ β•‘ l.34: if Box(Lit(7)) is @compile PeelOnce(r) then r else "" +//β”‚ β•‘ ^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ `Rest` does not recurse back, so this pattern applies once rather than repeatedly. //β”‚ ╙── Falling back to the naive translation. //β”‚ = Lit(7) @@ -57,9 +58,9 @@ if nested is SumLits(r) then r else "" :expect Lit(10) if nested is @compile SumLits(r) then r else "" //β”‚ ╔══[WARNING] The recursive context must occur as exactly one direct constructor argument. -//β”‚ β•‘ l.46: pattern TwoHoles(pattern H) = H | Pair(TwoHoles(H), TwoHoles(H)) +//β”‚ β•‘ l.47: pattern TwoHoles(pattern H) = H | Pair(TwoHoles(H), TwoHoles(H)) //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//β”‚ β•‘ l.47: pattern SumLits = TwoHoles(Pair(Lit(a), Lit(b)) => Lit(a + b)) as SumLits | _ +//β”‚ β•‘ l.48: pattern SumLits = TwoHoles(Pair(Lit(a), Lit(b)) => Lit(a + b)) as SumLits | _ //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ = Lit(10) @@ -92,8 +93,8 @@ reduce(Pair(Lit(1), Pair(Lit(3), Lit(2)))) :expect Pair(Lit(1), Lit(2)) if Pair(Lit(1), Lit(2)) is @compile Reduce(r) then r else "" //β”‚ ╔══[WARNING] The recursive context has no redex alternative, so no rewriting step can apply. -//β”‚ β•‘ l.81: pattern Reduce = NoRedex(Sum) as Reduce | _ -//β”‚ ╙── ^^^^^^^^^^^ +//β”‚ β•‘ l.82: pattern Reduce = NoRedex(Sum) as Reduce | _ +//β”‚ ╙── ^^^^^^^^^^^^ //β”‚ = Pair(Lit(1), Lit(2)) @@ -116,8 +117,8 @@ if Box(Box(7)) is OnlyRecursive(r) then r else "" :expect "" if Box(Box(7)) is @compile OnlyRecursive(r) then r else "" //β”‚ ╔══[WARNING] This fixed-point pattern is not supported by pattern compilation. -//β”‚ β•‘ l.117: if Box(Box(7)) is @compile OnlyRecursive(r) then r else "" -//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.118: if Box(Box(7)) is @compile OnlyRecursive(r) then r else "" +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ It has only recursive alternatives, so a match can never finish. //β”‚ ╙── Falling back to the naive translation. //β”‚ = "" @@ -130,8 +131,8 @@ pattern EarlyWildcard = UnBox as EarlyWildcard | _ | Lit(_) :expect 7 if Box(7) is @compile EarlyWildcard(r) then r else "" //β”‚ ╔══[WARNING] This fixed-point pattern is not supported by pattern compilation. -//β”‚ β•‘ l.131: if Box(7) is @compile EarlyWildcard(r) then r else "" -//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.132: if Box(7) is @compile EarlyWildcard(r) then r else "" +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ A wildcard alternative makes the alternatives after it unreachable. //β”‚ ╙── Falling back to the naive translation. //β”‚ = 7 @@ -144,8 +145,8 @@ pattern LateRecursion = UnBox as LateRecursion | UnTag as Rest | _ :expect 7 if Box(7) is @compile LateRecursion(r) then r else "" //β”‚ ╔══[WARNING] This fixed-point pattern is not supported by pattern compilation. -//β”‚ β•‘ l.145: if Box(7) is @compile LateRecursion(r) then r else "" -//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.146: if Box(7) is @compile LateRecursion(r) then r else "" +//β”‚ β•‘ ^^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ Its recursive alternatives must come before its other alternatives. //β”‚ ╙── Falling back to the naive translation. //β”‚ = 7 @@ -159,8 +160,8 @@ pattern ToExtracting = UnBox as Extracting | _ :expect Box(7) if Box(7) is @compile ToExtracting(r) then r else "" //β”‚ ╔══[WARNING] This fixed-point pattern is not supported by pattern compilation. -//β”‚ β•‘ l.160: if Box(7) is @compile ToExtracting(r) then r else "" -//β”‚ β•‘ ^^^^^^^^^^^^^^ +//β”‚ β•‘ l.161: if Box(7) is @compile ToExtracting(r) then r else "" +//β”‚ β•‘ ^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ The recursion goes through `Extracting`, which is not a parameterless pattern definition. //β”‚ ╙── Falling back to the naive translation. //β”‚ = Box(7) @@ -175,8 +176,8 @@ pattern WanderC = UnBox as WanderB | _ :expect 7 if Box(7) is @compile WanderA(r) then r else "" //β”‚ ╔══[WARNING] This fixed-point pattern is not supported by pattern compilation. -//β”‚ β•‘ l.176: if Box(7) is @compile WanderA(r) then r else "" -//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•‘ l.177: if Box(7) is @compile WanderA(r) then r else "" +//β”‚ β•‘ ^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ The recursion goes through `WanderC` but never comes back to `WanderA`. //β”‚ ╙── Falling back to the naive translation. //β”‚ = 7 @@ -188,7 +189,7 @@ if Box(7) is @compile WanderA(r) then r else "" pattern PartOfCycleA = @compile (UnBox as PartOfCycleB) | _ pattern PartOfCycleB = UnTag as PartOfCycleA | _ //β”‚ ╔══[WARNING] This fixed-point pattern is not supported by pattern compilation. -//β”‚ β•‘ l.188: pattern PartOfCycleA = @compile (UnBox as PartOfCycleB) | _ +//β”‚ β•‘ l.189: pattern PartOfCycleA = @compile (UnBox as PartOfCycleB) | _ //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ `@compile` has to be placed on the whole body of a definition taking part in the recursion through `PartOfCycleB`. //β”‚ ╙── Falling back to the naive translation. @@ -215,11 +216,11 @@ pattern SideCtx(pattern H) = H | Pair(SideCtx(H), SideUnBox) pattern SideGo = SideCtx(SideSum) as SideGo | _ fun sideGo(x) = if x is @compile SideGo(r) then r else "" //β”‚ ╔══[WARNING] Side patterns of a context alternative must be transform-free. -//β”‚ β•‘ l.212: pattern SideUnBox = Box(x) => x +//β”‚ β•‘ l.213: pattern SideUnBox = Box(x) => x //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^ -//β”‚ β•‘ l.213: pattern SideSum = Pair(Lit(a), Lit(b)) => Lit(a + b) +//β”‚ β•‘ l.214: pattern SideSum = Pair(Lit(a), Lit(b)) => Lit(a + b) //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//β”‚ β•‘ l.214: pattern SideCtx(pattern H) = H | Pair(SideCtx(H), SideUnBox) +//β”‚ β•‘ l.215: pattern SideCtx(pattern H) = H | Pair(SideCtx(H), SideUnBox) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -231,9 +232,9 @@ pattern TupleCtx(pattern H) = H | [TupleCtx(H), _] pattern TupleGo = TupleCtx(TupleSum) as TupleGo | _ fun tupleGo(x) = if x is @compile TupleGo(r) then r else "" //β”‚ ╔══[WARNING] This alternative is not supported by fixed-point pattern compilation. -//β”‚ β•‘ l.229: pattern TupleSum = Pair(Lit(a), Lit(b)) => Lit(a + b) +//β”‚ β•‘ l.230: pattern TupleSum = Pair(Lit(a), Lit(b)) => Lit(a + b) //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//β”‚ β•‘ l.230: pattern TupleCtx(pattern H) = H | [TupleCtx(H), _] +//β”‚ β•‘ l.231: pattern TupleCtx(pattern H) = H | [TupleCtx(H), _] //β”‚ ╙── ^^^^^^^^^^^^^^^^ @@ -245,20 +246,23 @@ pattern AritySum = Pair(Lit(a), Lit(b)) => Lit(a + b) pattern ArityCtx(pattern H) = H | Pair(ArityCtx(H)) pattern ArityGo = ArityCtx(AritySum) as ArityGo | _ fun arityGo(x) = if x is @compile ArityGo(r) then r else "" +//β”‚ ╔══[COMPILATION ERROR] Resolution error in constructor +//β”‚ β•‘ l.246: pattern ArityCtx(pattern H) = H | Pair(ArityCtx(H)) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class definition 'Pair' expected 2 pattern arguments, but got 1 +//β”‚ β•‘ l.14: data class Pair(val first, val second) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Class `Pair` has 2 parameters. -//β”‚ β•‘ l.13: data class Pair(val first, val second) +//β”‚ β•‘ l.14: data class Pair(val first, val second) //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ But 1 arguments were provided. -//β”‚ β•‘ l.245: pattern ArityCtx(pattern H) = H | Pair(ArityCtx(H)) -//β”‚ ╙── ^^^^^^^^^^ +//β”‚ β•‘ l.246: pattern ArityCtx(pattern H) = H | Pair(ArityCtx(H)) +//β”‚ ╙── ^^^^^^^^^^^ //β”‚ ╔══[WARNING] Cannot rebuild `Pair` because not all of its parameters are accessible. -//β”‚ β•‘ l.244: pattern AritySum = Pair(Lit(a), Lit(b)) => Lit(a + b) +//β”‚ β•‘ l.245: pattern AritySum = Pair(Lit(a), Lit(b)) => Lit(a + b) //β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//β”‚ β•‘ l.245: pattern ArityCtx(pattern H) = H | Pair(ArityCtx(H)) +//β”‚ β•‘ l.246: pattern ArityCtx(pattern H) = H | Pair(ArityCtx(H)) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Expected two arguments, but found only one argument. -//β”‚ β•‘ l.245: pattern ArityCtx(pattern H) = H | Pair(ArityCtx(H)) -//β”‚ ╙── ^^^^^^^^^^ // A rewriting alternative placed after one that descends. @@ -268,8 +272,8 @@ pattern OrderCtx(pattern H) = H | Pair(OrderCtx(H), _) | Lit(_) pattern OrderGo = OrderCtx(OrderSum) as OrderGo | _ fun orderGo(x) = if x is @compile OrderGo(r) then r else "" //β”‚ ╔══[WARNING] Redex alternatives must precede all recursive context alternatives. -//β”‚ β•‘ l.268: pattern OrderGo = OrderCtx(OrderSum) as OrderGo | _ -//β”‚ ╙── ^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.272: pattern OrderGo = OrderCtx(OrderSum) as OrderGo | _ +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^ // Splitting those same definitions across blocks crashes while building the diff --git a/hkmc2/shared/src/test/mlscript/ups/nondeterminism/.mls b/hkmc2/shared/src/test/mlscript/ups/nondeterminism/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ups/nondeterminism/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ups/nondeterminism/BitArithmetic.mls b/hkmc2/shared/src/test/mlscript/ups/nondeterminism/BitArithmetic.mls index 3b6f35ebab..c997309ae2 100644 --- a/hkmc2/shared/src/test/mlscript/ups/nondeterminism/BitArithmetic.mls +++ b/hkmc2/shared/src/test/mlscript/ups/nondeterminism/BitArithmetic.mls @@ -1,3 +1,4 @@ +:. :js open annotations { compile } diff --git a/hkmc2/shared/src/test/mlscript/ups/nondeterminism/EvenOddTree.mls b/hkmc2/shared/src/test/mlscript/ups/nondeterminism/EvenOddTree.mls index 83c1444e38..bc97cb7985 100644 --- a/hkmc2/shared/src/test/mlscript/ups/nondeterminism/EvenOddTree.mls +++ b/hkmc2/shared/src/test/mlscript/ups/nondeterminism/EvenOddTree.mls @@ -1,3 +1,4 @@ +:. :js open annotations { compile } diff --git a/hkmc2/shared/src/test/mlscript/ups/nondeterminism/LaRbTree.mls b/hkmc2/shared/src/test/mlscript/ups/nondeterminism/LaRbTree.mls index 1b1d07d16b..c2c5218c6b 100644 --- a/hkmc2/shared/src/test/mlscript/ups/nondeterminism/LaRbTree.mls +++ b/hkmc2/shared/src/test/mlscript/ups/nondeterminism/LaRbTree.mls @@ -1,3 +1,4 @@ +:. :js open annotations { compile } diff --git a/hkmc2/shared/src/test/mlscript/ups/parametric/.mls b/hkmc2/shared/src/test/mlscript/ups/parametric/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ups/parametric/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ups/parametric/EtaConversion.mls b/hkmc2/shared/src/test/mlscript/ups/parametric/EtaConversion.mls index 0044b48129..019303aeb4 100644 --- a/hkmc2/shared/src/test/mlscript/ups/parametric/EtaConversion.mls +++ b/hkmc2/shared/src/test/mlscript/ups/parametric/EtaConversion.mls @@ -1,3 +1,4 @@ +:. :js pattern Nullable(pattern T) = T | null @@ -23,8 +24,8 @@ import "../../../mlscript-compile/Char.mls" fun foo(x) = x is Nullable(Char.Letter) //β”‚ Normalized: //β”‚ > β€Ήif|whileβ€Ί -//β”‚ > let tmp:patternArgument0$ = member:Char⁰.Letterβ€Ήmember:Letterβ€Ί -//β”‚ > let tmp:unapplyResult = (member:Nullable⁰.)unapply(tmp:patternArgument0$ΒΉ, x⁰) +//β”‚ > let tmp:patternArgument0$ = member:Char⁰^term:foo⁰.Letter +//β”‚ > let tmp:unapplyResult = (member:Nullable⁰^term:foo⁰.)unapply(tmp:patternArgument0$ΒΉ, x⁰) //β”‚ > tmp:unapplyResult is (tmp:runtime⁰.)MatchSuccessβ€Ήclass:MatchSuccessβ€Ί(output, bindings) then true //β”‚ > else false diff --git a/hkmc2/shared/src/test/mlscript/ups/parametric/HigherOrderPattern.mls b/hkmc2/shared/src/test/mlscript/ups/parametric/HigherOrderPattern.mls index 27d8f6eb6c..10d64202aa 100644 --- a/hkmc2/shared/src/test/mlscript/ups/parametric/HigherOrderPattern.mls +++ b/hkmc2/shared/src/test/mlscript/ups/parametric/HigherOrderPattern.mls @@ -1,3 +1,4 @@ +:. :js open annotations { compile } @@ -9,21 +10,21 @@ object A :e null is @compile Nullable //β”‚ ╔══[COMPILATION ERROR] Pattern `Nullable` has one pattern parameter. -//β”‚ β•‘ l.5: pattern Nullable(pattern T) = null | T +//β”‚ β•‘ l.6: pattern Nullable(pattern T) = null | T //β”‚ β•‘ ^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ But zero pattern arguments were provided. -//β”‚ β•‘ l.10: null is @compile Nullable +//β”‚ β•‘ l.11: null is @compile Nullable //β”‚ ╙── ^^^^^^^^ //β”‚ = false :e null is @compile Nullable(A, A) //β”‚ ╔══[COMPILATION ERROR] Pattern `Nullable` has one pattern parameter. -//β”‚ β•‘ l.5: pattern Nullable(pattern T) = null | T +//β”‚ β•‘ l.6: pattern Nullable(pattern T) = null | T //β”‚ β•‘ ^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ But two pattern arguments were provided. -//β”‚ β•‘ l.20: null is @compile Nullable(A, A) -//β”‚ ╙── ^^^^^^^^^^^^^ +//β”‚ β•‘ l.21: null is @compile Nullable(A, A) +//β”‚ ╙── ^^^^^^^^^^^^^^ //β”‚ = false pattern Nullable(pattern T) = null | T diff --git a/hkmc2/shared/src/test/mlscript/ups/parametric/ListLike.mls b/hkmc2/shared/src/test/mlscript/ups/parametric/ListLike.mls index dfcf9b16f1..cebe68dc5b 100644 --- a/hkmc2/shared/src/test/mlscript/ups/parametric/ListLike.mls +++ b/hkmc2/shared/src/test/mlscript/ups/parametric/ListLike.mls @@ -1,3 +1,4 @@ +:. :js open annotations diff --git a/hkmc2/shared/src/test/mlscript/ups/parametric/Nullable.mls b/hkmc2/shared/src/test/mlscript/ups/parametric/Nullable.mls index 23aec5fb13..4ff8a290cd 100644 --- a/hkmc2/shared/src/test/mlscript/ups/parametric/Nullable.mls +++ b/hkmc2/shared/src/test/mlscript/ups/parametric/Nullable.mls @@ -1,3 +1,4 @@ +:. :js open annotations @@ -59,10 +60,10 @@ null is Optional(Int) as Some(42) // Test the error message. fun toIntOption(x) = if x is (@compile Optional) as o then o //β”‚ ╔══[COMPILATION ERROR] Pattern `Optional` has one pattern parameter. -//β”‚ β•‘ l.47: pattern Optional(pattern T) = (null => None) | ((T as x) => Some(x)) +//β”‚ β•‘ l.48: pattern Optional(pattern T) = (null => None) | ((T as x) => Some(x)) //β”‚ β•‘ ^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ But zero pattern arguments were provided. -//β”‚ β•‘ l.60: fun toIntOption(x) = if x is (@compile Optional) as o then o +//β”‚ β•‘ l.61: fun toIntOption(x) = if x is (@compile Optional) as o then o //β”‚ ╙── ^^^^^^^^ fun toIntOption(x) = if x is (@compile Optional(Int)) as o then o diff --git a/hkmc2/shared/src/test/mlscript/ups/recursion/.mls b/hkmc2/shared/src/test/mlscript/ups/recursion/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ups/recursion/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ups/recursion/BitSeq.mls b/hkmc2/shared/src/test/mlscript/ups/recursion/BitSeq.mls index 13bc3a5c48..fa62ba3635 100644 --- a/hkmc2/shared/src/test/mlscript/ups/recursion/BitSeq.mls +++ b/hkmc2/shared/src/test/mlscript/ups/recursion/BitSeq.mls @@ -1,3 +1,4 @@ +:. :js open annotations { compile } diff --git a/hkmc2/shared/src/test/mlscript/ups/recursion/BitTree.mls b/hkmc2/shared/src/test/mlscript/ups/recursion/BitTree.mls index 9fa478ee7d..63d7ebb51a 100644 --- a/hkmc2/shared/src/test/mlscript/ups/recursion/BitTree.mls +++ b/hkmc2/shared/src/test/mlscript/ups/recursion/BitTree.mls @@ -1,3 +1,4 @@ +:. :js open annotations { compile } diff --git a/hkmc2/shared/src/test/mlscript/ups/recursion/LeafEvenOddTree.mls b/hkmc2/shared/src/test/mlscript/ups/recursion/LeafEvenOddTree.mls index 3ce40c8248..aee6194305 100644 --- a/hkmc2/shared/src/test/mlscript/ups/recursion/LeafEvenOddTree.mls +++ b/hkmc2/shared/src/test/mlscript/ups/recursion/LeafEvenOddTree.mls @@ -1,3 +1,4 @@ +:. :js open annotations { compile } diff --git a/hkmc2/shared/src/test/mlscript/ups/recursion/NatBox.mls b/hkmc2/shared/src/test/mlscript/ups/recursion/NatBox.mls index 25a2c59916..cd07bb6c19 100644 --- a/hkmc2/shared/src/test/mlscript/ups/recursion/NatBox.mls +++ b/hkmc2/shared/src/test/mlscript/ups/recursion/NatBox.mls @@ -1,3 +1,4 @@ +:. :js open annotations { compile } diff --git a/hkmc2/shared/src/test/mlscript/ups/recursion/NullTree.mls b/hkmc2/shared/src/test/mlscript/ups/recursion/NullTree.mls index 1915b61150..318e0eb5e5 100644 --- a/hkmc2/shared/src/test/mlscript/ups/recursion/NullTree.mls +++ b/hkmc2/shared/src/test/mlscript/ups/recursion/NullTree.mls @@ -1,3 +1,4 @@ +:. :js open annotations { compile } diff --git a/hkmc2/shared/src/test/mlscript/ups/recursion/SignBox.mls b/hkmc2/shared/src/test/mlscript/ups/recursion/SignBox.mls index a040bbd9f2..d863a5578b 100644 --- a/hkmc2/shared/src/test/mlscript/ups/recursion/SignBox.mls +++ b/hkmc2/shared/src/test/mlscript/ups/recursion/SignBox.mls @@ -1,3 +1,4 @@ +:. :js open annotations { compile } diff --git a/hkmc2/shared/src/test/mlscript/ups/regex/.mls b/hkmc2/shared/src/test/mlscript/ups/regex/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ups/regex/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ups/regex/EmailAddress.mls b/hkmc2/shared/src/test/mlscript/ups/regex/EmailAddress.mls index 08a8e6af0c..4bf03c2c19 100644 --- a/hkmc2/shared/src/test/mlscript/ups/regex/EmailAddress.mls +++ b/hkmc2/shared/src/test/mlscript/ups/regex/EmailAddress.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Char.mls" diff --git a/hkmc2/shared/src/test/mlscript/ups/regex/EmptyString.mls b/hkmc2/shared/src/test/mlscript/ups/regex/EmptyString.mls index c04ae45652..00b60978bc 100644 --- a/hkmc2/shared/src/test/mlscript/ups/regex/EmptyString.mls +++ b/hkmc2/shared/src/test/mlscript/ups/regex/EmptyString.mls @@ -1,3 +1,4 @@ +:. :js pattern Oops = "" ~ (Oops | "") diff --git a/hkmc2/shared/src/test/mlscript/ups/regex/Identifier.mls b/hkmc2/shared/src/test/mlscript/ups/regex/Identifier.mls index dd52222cdf..7c18e5096f 100644 --- a/hkmc2/shared/src/test/mlscript/ups/regex/Identifier.mls +++ b/hkmc2/shared/src/test/mlscript/ups/regex/Identifier.mls @@ -1,3 +1,4 @@ +:. :js open annotations @@ -138,7 +139,7 @@ pattern Word = Letter ~ (Word | "") :e fun isWord(x) = x is @compile Word //β”‚ ╔══[COMPILATION ERROR] String concatenation is not supported in pattern compilation. -//β”‚ β•‘ l.120: pattern Word = Letter ~ (Word | "") +//β”‚ β•‘ l.121: pattern Word = Letter ~ (Word | "") //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^ // Unsupported patterns are equivalent to `Never`. diff --git a/hkmc2/shared/src/test/mlscript/ups/regex/Number.mls b/hkmc2/shared/src/test/mlscript/ups/regex/Number.mls index 603d9342c9..9d095efa10 100644 --- a/hkmc2/shared/src/test/mlscript/ups/regex/Number.mls +++ b/hkmc2/shared/src/test/mlscript/ups/regex/Number.mls @@ -1,3 +1,4 @@ +:. :js pattern Digits = ("0"..="9") ~ (Digits | "") diff --git a/hkmc2/shared/src/test/mlscript/ups/regex/Simplification.mls b/hkmc2/shared/src/test/mlscript/ups/regex/Simplification.mls index 2f4bebc00d..986f05b541 100644 --- a/hkmc2/shared/src/test/mlscript/ups/regex/Simplification.mls +++ b/hkmc2/shared/src/test/mlscript/ups/regex/Simplification.mls @@ -1,3 +1,4 @@ +:. :js pattern X = "x" diff --git a/hkmc2/shared/src/test/mlscript/ups/specialization/.mls b/hkmc2/shared/src/test/mlscript/ups/specialization/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ups/specialization/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ups/specialization/SimpleList.mls b/hkmc2/shared/src/test/mlscript/ups/specialization/SimpleList.mls index 5b1200824f..6143d1d1b9 100644 --- a/hkmc2/shared/src/test/mlscript/ups/specialization/SimpleList.mls +++ b/hkmc2/shared/src/test/mlscript/ups/specialization/SimpleList.mls @@ -1,3 +1,4 @@ +:. :js open annotations { compile } @@ -151,10 +152,10 @@ fun (::) cons(head, tail) = Pair(head, tail) :e pattern List(a) = null | Pair(a, List) //β”‚ ╔══[COMPILATION ERROR] Found an inconsistent variable in disjunction patterns. -//β”‚ β•‘ l.152: pattern List(a) = null | Pair(a, List) +//β”‚ β•‘ l.153: pattern List(a) = null | Pair(a, List) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ The variable is missing from this sub-pattern. -//β”‚ β•‘ l.152: pattern List(a) = null | Pair(a, List) +//β”‚ β•‘ l.153: pattern List(a) = null | Pair(a, List) //β”‚ ╙── ^^^^ null is @compile List diff --git a/hkmc2/shared/src/test/mlscript/ups/specialization/SimpleLiterals.mls b/hkmc2/shared/src/test/mlscript/ups/specialization/SimpleLiterals.mls index 02aebb83b8..9bd4cfe01a 100644 --- a/hkmc2/shared/src/test/mlscript/ups/specialization/SimpleLiterals.mls +++ b/hkmc2/shared/src/test/mlscript/ups/specialization/SimpleLiterals.mls @@ -1,3 +1,4 @@ +:. :js open annotations { compile } @@ -52,6 +53,6 @@ pattern ManyZero = ("0" ~ (ManyZero | "")) | "" :todo let res = "1" is @compile ManyZero //β”‚ ╔══[COMPILATION ERROR] String concatenation is not supported in pattern compilation. -//β”‚ β•‘ l.49: pattern ManyZero = ("0" ~ (ManyZero | "")) | "" +//β”‚ β•‘ l.50: pattern ManyZero = ("0" ~ (ManyZero | "")) | "" //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^ //β”‚ res = false diff --git a/hkmc2/shared/src/test/mlscript/ups/syntax/.mls b/hkmc2/shared/src/test/mlscript/ups/syntax/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ups/syntax/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ups/syntax/CrossCompilation.mls b/hkmc2/shared/src/test/mlscript/ups/syntax/CrossCompilation.mls index 4561923e4c..7dce3a4882 100644 --- a/hkmc2/shared/src/test/mlscript/ups/syntax/CrossCompilation.mls +++ b/hkmc2/shared/src/test/mlscript/ups/syntax/CrossCompilation.mls @@ -1,3 +1,4 @@ +:. :js open annotations diff --git a/hkmc2/shared/src/test/mlscript/ups/syntax/Declaration.mls b/hkmc2/shared/src/test/mlscript/ups/syntax/Declaration.mls index 7fbd5ff943..05422a2b9b 100644 --- a/hkmc2/shared/src/test/mlscript/ups/syntax/Declaration.mls +++ b/hkmc2/shared/src/test/mlscript/ups/syntax/Declaration.mls @@ -1,13 +1,14 @@ +:. :js :e pattern Foo(val T) = null | T //β”‚ ╔══[COMPILATION ERROR] Unexpected pattern parameter T with modifiers: val -//β”‚ β•‘ l.5: pattern Foo(val T) = null | T +//β”‚ β•‘ l.6: pattern Foo(val T) = null | T //β”‚ ╙── ^ -//β”‚ ╔══[COMPILATION ERROR] Pattern name not found: T. -//β”‚ β•‘ l.5: pattern Foo(val T) = null | T +//β”‚ ╔══[COMPILATION ERROR] Name not found: T +//β”‚ β•‘ l.6: pattern Foo(val T) = null | T //β”‚ ╙── ^ @@ -17,7 +18,7 @@ pattern Foo(pattern T) = null | T :checkIR pattern Foo(pattern T) = null | T //β”‚ ╔══[INTERNAL ERROR] [BlockChecker] Invalid IR: symbol T⁰ is bound more than once -//β”‚ β•‘ l.18: pattern Foo(pattern T) = null | T +//β”‚ β•‘ l.19: pattern Foo(pattern T) = null | T //β”‚ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/ups/syntax/InterestingPatterns.mls b/hkmc2/shared/src/test/mlscript/ups/syntax/InterestingPatterns.mls index de83df9939..c9381f3ba2 100644 --- a/hkmc2/shared/src/test/mlscript/ups/syntax/InterestingPatterns.mls +++ b/hkmc2/shared/src/test/mlscript/ups/syntax/InterestingPatterns.mls @@ -10,7 +10,7 @@ pattern DropZ = ((:x, :y, :z)) => (x: x, y: y) :ucs ups pattern SumList = (Nil => 0) | ((Int as hd) :: (SumList as tl)) => hd + tl -//β”‚ elaborated pattern body: Nil => 0 ∨ Cons(Int as hd, SumList as tl) => builtin:+⁰(hd⁰, tl⁰) +//β”‚ elaborated pattern body: Nil => 0 ∨ Cons(Int as hdβ€Ήhd⁰›, SumList as tlβ€Ήtl⁰›) => builtin:+⁰(hdΒΉ, tlΒΉ) // Flatten the nested tuples. Note that it makes use of biased union. pattern Flatten = ([Flatten as hd, ...Flatten as tl] => Array.concat(hd, tl)) | _ @@ -28,12 +28,12 @@ pattern CountTree(pattern P) = // Match a 2x2 matrix and return its transpose. :ucs ups pattern Transpose = (null => null) | (([[a, b], [c, d]]) => [[a, c], [b, d]]) -//β”‚ elaborated pattern body: null => null ∨ [[a, b], [c, d]] => [[a⁰, c⁰], [b⁰, d⁰]] +//β”‚ elaborated pattern body: null => null ∨ [[aβ€Ήa⁰›, bβ€Ήb⁰›], [cβ€Ήc⁰›, dβ€Ήd⁰›]] => [[aΒΉ, cΒΉ], [bΒΉ, dΒΉ]] // Return the middle element of a list. It won't match if the length is even. :ucs ups pattern Middle = ([x] => x) | ([x, ...(Middle as m), y] => m) -//β”‚ elaborated pattern body: [x] => x⁰ ∨ [x, ...Middle as m, y] => m⁰ +//β”‚ elaborated pattern body: [xβ€Ήx⁰›] => xΒΉ ∨ [xβ€ΉxΒ²β€Ί, ...Middle as mβ€Ήm⁰›, yβ€Ήy⁰›] => mΒΉ // Return the leaves of a tree. pattern Leaves = (null => Nil) | ([x] => x :: Nil) | ([Leaves as left, Leaves as right] => left ::: right) diff --git a/hkmc2/shared/src/test/mlscript/ups/syntax/MixedParameters.mls b/hkmc2/shared/src/test/mlscript/ups/syntax/MixedParameters.mls index 5564b20025..5163ff307a 100644 --- a/hkmc2/shared/src/test/mlscript/ups/syntax/MixedParameters.mls +++ b/hkmc2/shared/src/test/mlscript/ups/syntax/MixedParameters.mls @@ -1,3 +1,4 @@ +:. :js // The semantics of mixed pattern parameters and extraction parameters. @@ -23,7 +24,7 @@ pattern P'(pattern Q) = [Q as a, Q as b] => [a, b] // is that we either provide all of them or none at all. if [0, 0] is P(Bit, a) then a //β”‚ ╔══[COMPILATION ERROR] Expected two extraction arguments, but found only one argument. -//β”‚ β•‘ l.24: if [0, 0] is P(Bit, a) then a +//β”‚ β•‘ l.25: if [0, 0] is P(Bit, a) then a //β”‚ ╙── ^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: match error @@ -33,7 +34,7 @@ if [0, 0] is P(Bit, a) then a // then none can be provided. if [0, 0] is P'(Bit, a, b) then "values: " + a + ", " + b //β”‚ ╔══[COMPILATION ERROR] Expected zero extraction arguments, but found two arguments. -//β”‚ β•‘ l.34: if [0, 0] is P'(Bit, a, b) then "values: " + a + ", " + b +//β”‚ β•‘ l.35: if [0, 0] is P'(Bit, a, b) then "values: " + a + ", " + b //β”‚ ╙── ^^^^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: match error @@ -42,7 +43,7 @@ if [0, 0] is P'(Bit, a, b) then "values: " + a + ", " + b // reporting correctly. if [0, 0] is P(a, Bit, b) then "values: " + a + ", " + b //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'a' -//β”‚ β•‘ l.43: if [0, 0] is P(a, Bit, b) then "values: " + a + ", " + b +//β”‚ β•‘ l.44: if [0, 0] is P(a, Bit, b) then "values: " + a + ", " + b //β”‚ ╙── ^ //β”‚ ═══[RUNTIME ERROR] ReferenceError: a is not defined @@ -57,7 +58,7 @@ f([0, 0]) // Order actually matters. fun f(x) = if x is S(Bit, a, b) then [a, b] //β”‚ ╔══[COMPILATION ERROR] No definition found in scope for member 'a' -//β”‚ β•‘ l.58: fun f(x) = if x is S(Bit, a, b) then [a, b] +//β”‚ β•‘ l.59: fun f(x) = if x is S(Bit, a, b) then [a, b] //β”‚ ╙── ^ :re @@ -85,7 +86,7 @@ if 0 is Q'(Bit) as y then y // Similarly, it cannot be written like this: if 0 is Q'(Bit, y) then y //β”‚ ╔══[COMPILATION ERROR] Expected zero extraction arguments, but found one argument. -//β”‚ β•‘ l.86: if 0 is Q'(Bit, y) then y +//β”‚ β•‘ l.87: if 0 is Q'(Bit, y) then y //β”‚ ╙── ^^^^^^ //β”‚ ═══[RUNTIME ERROR] Error: match error @@ -98,8 +99,8 @@ pattern CaptureBit(value) = Bit as value // discarded unless the user makes the discard explicit with `as _`. if 0 is CaptureBit(ZeroToOne) then "discarded implicitly" //β”‚ ╔══[WARNING] This extraction argument's transformation result is discarded by pattern `CaptureBit`. -//β”‚ β•‘ l.99: if 0 is CaptureBit(ZeroToOne) then "discarded implicitly" -//β”‚ β•‘ ^^^^^^^^^ +//β”‚ β•‘ l.100: if 0 is CaptureBit(ZeroToOne) then "discarded implicitly" +//β”‚ β•‘ ^^^^^^^^^ //β”‚ ╙── Write `... as _` to discard it explicitly. //β”‚ = "discarded implicitly" diff --git a/hkmc2/shared/src/test/mlscript/ups/syntax/PatternBody.mls b/hkmc2/shared/src/test/mlscript/ups/syntax/PatternBody.mls index ce2c404682..2d804de251 100644 --- a/hkmc2/shared/src/test/mlscript/ups/syntax/PatternBody.mls +++ b/hkmc2/shared/src/test/mlscript/ups/syntax/PatternBody.mls @@ -1,9 +1,10 @@ +:. :js :todo // Patterns at parameter position are not supported yet. 0 => false //β”‚ ╔══[COMPILATION ERROR] Expected a valid parameter, found integer literal -//β”‚ β•‘ l.4: 0 => false +//β”‚ β•‘ l.5: 0 => false //β”‚ ╙── ^ //β”‚ = fun @@ -19,67 +20,67 @@ class Pair[A, B](val fst: A, val snd: B) :ucs ups pattern PlainList(pattern T) = (null => Nil) | (Pair(T as hd, PlainList(T) as tl) => hd :: tl) -//β”‚ elaborated pattern body: null => (member:Stack⁰.)Nilβ€Ήmember:Nilβ€Ί ∨ Pair(T as hd, PlainList(T) as tl) => (member:Stack⁰.)Consβ€Ήmember:Consβ€Ί(hd⁰, tl⁰) +//β”‚ elaborated pattern body: null => Nilβ€Ήopenβ€Ί ∨ member:Pair⁰(T⁰ as hdβ€Ήhd⁰›, member:PlainList⁰(T⁰) as tlβ€Ήtl⁰›) => ::β€Ήopenβ€Ί(hdΒΉ, tlΒΉ) :ucs ups // TODO: Decide on the relative operator precedence between `as` and `=>`. // This is elaborated into `T as (x => x)`, which might not be what we want. pattern Identity(pattern T) = T as x => x -//β”‚ elaborated pattern body: T as (x => x⁰) +//β”‚ elaborated pattern body: TΒΉ >> (xβ€Ήx⁰› => xΒΉ) :ucs ups pattern Identity(pattern T) = (T as x) => x -//β”‚ elaborated pattern body: (T as x) => xΒΉ +//β”‚ elaborated pattern body: (TΒ² as xβ€ΉxΒ²β€Ί) => xΒ³ pattern Identity = x => x :ucs ups pattern Sick = (0 as a as b as c as d as e as f) => a + b + c + d + e + f -//β”‚ elaborated pattern body: ((((((0 as a) as b) as c) as d) as e) as f) => builtin:+⁰(builtin:+⁰(builtin:+⁰(builtin:+⁰(builtin:+⁰(a⁰, b⁰), c⁰), d⁰), e⁰), f⁰) +//β”‚ elaborated pattern body: ((((((0 as aβ€Ήa⁰›) as bβ€Ήb⁰›) as cβ€Ήc⁰›) as dβ€Ήd⁰›) as eβ€Ήe⁰›) as fβ€Ήf⁰›) => builtin:+⁰(builtin:+⁰(builtin:+⁰(builtin:+⁰(builtin:+⁰(aΒΉ, bΒΉ), cΒΉ), dΒΉ), eΒΉ), fΒΉ) :ucs ups pattern PairLike = [fst, snd] => Pair(fst, snd) -//β”‚ elaborated pattern body: [fst, snd] => member:Pair⁰(fst⁰, snd⁰) +//β”‚ elaborated pattern body: [fstβ€Ήfst⁰›, sndβ€Ήsnd⁰›] => member:Pair⁰(fstΒΉ, sndΒΉ) :ucs ups pattern PairLike(fst, snd) = Pair(fst, snd) | [fst, snd] -//β”‚ elaborated pattern body: Pair(fst, snd) ∨ [fst, snd] +//β”‚ elaborated pattern body: member:Pair⁰(fstβ€ΉfstΒ²β€Ί, sndβ€ΉsndΒ²β€Ί) ∨ [fstβ€ΉfstΒ²β€Ί, sndβ€ΉsndΒ²β€Ί] :w pattern UselessParameter = x //β”‚ ╔══[WARNING] Unused pattern binding: x. -//β”‚ β•‘ l.49: pattern UselessParameter = x +//β”‚ β•‘ l.50: pattern UselessParameter = x //β”‚ ╙── ^ :ucs ups pattern PlainList(pattern T) = (null => Nil) | ([T as hd, PlainList(T) as tl] => hd :: tl) -//β”‚ elaborated pattern body: null => (member:Stack⁰.)Nilβ€Ήmember:Nilβ€Ί ∨ [T as hd, PlainList(T) as tl] => (member:Stack⁰.)Consβ€Ήmember:Consβ€Ί(hdΒΉ, tlΒΉ) +//β”‚ elaborated pattern body: null => Nilβ€Ήopenβ€Ί ∨ [TΒ³ as hdβ€ΉhdΒ²β€Ί, member:PlainListΒΉ(TΒ³) as tlβ€ΉtlΒ²β€Ί] => ::β€Ήopenβ€Ί(hdΒ³, tlΒ³) :ucs ups pattern Consistent = ((Int as x) | (Str as x)) => x.toString() -//β”‚ elaborated pattern body: (Int as x ∨ Str as x) => xΒ².toString() +//β”‚ elaborated pattern body: (member:Int⁰ as xβ€Ήx⁴› ∨ member:Str⁰ as xβ€Ήx⁴›) => x⁡.toString() :e pattern Inconsistent = ~(~(Int as x) | Str) => x.toString() //β”‚ ╔══[COMPILATION ERROR] This pattern cannot be bound. -//β”‚ β•‘ l.63: pattern Inconsistent = ~(~(Int as x) | Str) => x.toString() +//β”‚ β•‘ l.64: pattern Inconsistent = ~(~(Int as x) | Str) => x.toString() //β”‚ β•‘ ^^^ //β”‚ β•Ÿβ”€β”€ Because the pattern it belongs to is negated. -//β”‚ β•‘ l.63: pattern Inconsistent = ~(~(Int as x) | Str) => x.toString() +//β”‚ β•‘ l.64: pattern Inconsistent = ~(~(Int as x) | Str) => x.toString() //β”‚ ╙── ^^^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Name not found: x -//β”‚ β•‘ l.63: pattern Inconsistent = ~(~(Int as x) | Str) => x.toString() -//β”‚ ╙── ^ :e pattern Inconsistent = (~(~((Int as x) => x)) | Str) => x.toString() -//β”‚ ╔══[COMPILATION ERROR] Name not found: x -//β”‚ β•‘ l.75: pattern Inconsistent = (~(~((Int as x) => x)) | Str) => x.toString() -//β”‚ ╙── ^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'toString' has no resolved target +//β”‚ β•‘ l.73: pattern Inconsistent = (~(~((Int as x) => x)) | Str) => x.toString() +//β”‚ ╙── ^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] This selection of member 'toString' has no resolved target +//β”‚ β•‘ l.73: pattern Inconsistent = (~(~((Int as x) => x)) | Str) => x.toString() +//β”‚ ╙── ^^^^^^^^^^ :ucs ups pattern Negated = ~(Int | Str) -//β”‚ elaborated pattern body: Β¬(Int ∨ Str) +//β”‚ elaborated pattern body: Β¬(member:Int⁰ ∨ member:Str⁰) // FIXME: The precedence of `=>` is higher than `~`? :pt @@ -105,33 +106,33 @@ pattern BadNegated = ~(Int as x) => x :e pattern BadNegated = (~(Int as x)) => x //β”‚ ╔══[COMPILATION ERROR] This pattern cannot be bound. -//β”‚ β•‘ l.106: pattern BadNegated = (~(Int as x)) => x +//β”‚ β•‘ l.107: pattern BadNegated = (~(Int as x)) => x //β”‚ β•‘ ^^^ //β”‚ β•Ÿβ”€β”€ Because the pattern it belongs to is negated. -//β”‚ β•‘ l.106: pattern BadNegated = (~(Int as x)) => x +//β”‚ β•‘ l.107: pattern BadNegated = (~(Int as x)) => x //β”‚ ╙── ^^^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Name not found: x -//β”‚ β•‘ l.106: pattern BadNegated = (~(Int as x)) => x +//β”‚ elaborated pattern body: Β¬(member:Int⁰ as xβ€Ήβ€Ί) => xβ€Ήopenβ€Ί +//β”‚ ╔══[COMPILATION ERROR] Name not found in wildcard opens: x +//β”‚ β•‘ l.107: pattern BadNegated = (~(Int as x)) => x //β”‚ ╙── ^ -//β”‚ elaborated pattern body: Β¬(Int as x) => :ucs ups :e pattern BadNegated = (~Pair(Int, x)) => x //β”‚ ╔══[COMPILATION ERROR] This variable cannot be accessed. -//β”‚ β•‘ l.120: pattern BadNegated = (~Pair(Int, x)) => x +//β”‚ β•‘ l.121: pattern BadNegated = (~Pair(Int, x)) => x //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ Because the pattern it belongs to is negated. -//β”‚ β•‘ l.120: pattern BadNegated = (~Pair(Int, x)) => x -//β”‚ ╙── ^^^^^^^^^^^ -//β”‚ ╔══[COMPILATION ERROR] Name not found: x -//β”‚ β•‘ l.120: pattern BadNegated = (~Pair(Int, x)) => x +//β”‚ β•‘ l.121: pattern BadNegated = (~Pair(Int, x)) => x +//β”‚ ╙── ^^^^^^^^^^^^ +//β”‚ elaborated pattern body: Β¬member:Pair⁰(member:Int⁰, xβ€Ή?β€Ί) => xβ€Ήopenβ€Ί +//β”‚ ╔══[COMPILATION ERROR] Name not found in wildcard opens: x +//β”‚ β•‘ l.121: pattern BadNegated = (~Pair(Int, x)) => x //β”‚ ╙── ^ -//β”‚ elaborated pattern body: Β¬Pair(Int, x) => :ucs ups pattern DoubleNegated = ~(~Int) -//β”‚ elaborated pattern body: ¬¬Int +//β”‚ elaborated pattern body: ¬¬member:Int⁰ :ucs ups pattern Nothing = ~_ @@ -147,11 +148,11 @@ pattern Origin = (x: 0, y: 0, z: 0) :ucs ups pattern PointLike = ((x: x, y: y)) => x + y -//β”‚ elaborated pattern body: {x: x, y: y} => builtin:+⁰(xΒ³, y⁰) +//β”‚ elaborated pattern body: {x: xβ€Ήx⁢›, y: yβ€Ήy⁰›} => builtin:+⁰(x⁷, yΒΉ) :ucs ups pattern PointLike = ((:x, :y, :z)) => x + y -//β”‚ elaborated pattern body: {x: x, y: y, z: z} => builtin:+⁰(x⁴, yΒΉ) +//β”‚ elaborated pattern body: {x: xβ€Ήx⁸›, y: yβ€ΉyΒ²β€Ί, z: zβ€Ήz⁰›} => builtin:+⁰(x⁹, yΒ³) :ucs ups pattern NestedTuple = [[[]]] @@ -166,18 +167,18 @@ class SecretPair(a: Int, val b: Int) :ucs ups pattern KeepSecret(b) = SecretPair(_, b) -//β”‚ elaborated pattern body: SecretPair(_, b) +//β”‚ elaborated pattern body: member:SecretPair⁰(_, bβ€ΉbΒ²β€Ί) :e pattern RebuildSecret = SecretPair(_, b => b + 1) //β”‚ ╔══[COMPILATION ERROR] This pattern cannot be matched -//β”‚ β•‘ l.172: pattern RebuildSecret = SecretPair(_, b => b + 1) +//β”‚ β•‘ l.173: pattern RebuildSecret = SecretPair(_, b => b + 1) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ because the corresponding parameter `a` is not publicly accessible -//β”‚ β•‘ l.165: class SecretPair(a: Int, val b: Int) +//β”‚ β•‘ l.166: class SecretPair(a: Int, val b: Int) //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ because rebuilding the matched `SecretPair` value requires reading every constructor argument -//β”‚ β•‘ l.172: pattern RebuildSecret = SecretPair(_, b => b + 1) +//β”‚ β•‘ l.173: pattern RebuildSecret = SecretPair(_, b => b + 1) //β”‚ β•‘ ^^^^^^^^^^ //β”‚ ╙── Suggestion: mark this parameter with `val` so it becomes accessible @@ -193,10 +194,10 @@ module HiddenCorner with class CantYouSeeMe(val wow: Int) val k = 0 //β”‚ ╔══[COMPILATION ERROR] This pattern cannot be matched -//β”‚ β•‘ l.188: pattern Naughty = HiddenCorner.m.m.YouCantSeeMe(n) => n + HiddenCorner.m.m.k +//β”‚ β•‘ l.189: pattern Naughty = HiddenCorner.m.m.YouCantSeeMe(n) => n + HiddenCorner.m.m.k //β”‚ β•‘ ^ //β”‚ β•Ÿβ”€β”€ because the corresponding parameter `wow` is not publicly accessible -//β”‚ β•‘ l.192: class YouCantSeeMe(wow: Int) +//β”‚ β•‘ l.193: class YouCantSeeMe(wow: Int) //β”‚ β•‘ ^^^ //β”‚ β•Ÿβ”€β”€ Suggestion: use a wildcard pattern `_` in this position //β”‚ ╙── Suggestion: mark this parameter with `val` so it becomes accessible diff --git a/hkmc2/shared/src/test/mlscript/ups/syntax/Precedence.mls b/hkmc2/shared/src/test/mlscript/ups/syntax/Precedence.mls index f5e5b923e1..8d2440050f 100644 --- a/hkmc2/shared/src/test/mlscript/ups/syntax/Precedence.mls +++ b/hkmc2/shared/src/test/mlscript/ups/syntax/Precedence.mls @@ -1,3 +1,4 @@ +:. :ucs ups // The right binding power of `=>` is lower than the left binding power of `|`. @@ -26,19 +27,19 @@ pattern Test = (1 => 2) | 4 => 8 // The precedence of `as` is lower than `=>`. pattern Test = 0 as x => 2 -//β”‚ elaborated pattern body: 0 as (x => 2) +//β”‚ elaborated pattern body: 0 >> (xβ€Ήx⁰› => 2) pattern Test = (0 as x) => 2 -//β”‚ elaborated pattern body: (0 as x) => 2 +//β”‚ elaborated pattern body: (0 as xβ€ΉxΒΉβ€Ί) => 2 pattern Test(x) = 0 as x | 1 as x -//β”‚ elaborated pattern body: 0 as x ∨ 1 as x +//β”‚ elaborated pattern body: 0 as xβ€ΉxΒ²β€Ί ∨ 1 as xβ€ΉxΒ²β€Ί pattern Test(x) = 0 as x | (1 as x) -//β”‚ elaborated pattern body: 0 as x ∨ 1 as x +//β”‚ elaborated pattern body: 0 as xβ€ΉxΒ³β€Ί ∨ 1 as xβ€ΉxΒ³β€Ί pattern Test(x) = 0 as x | (1 as x) -//β”‚ elaborated pattern body: 0 as x ∨ 1 as x +//β”‚ elaborated pattern body: 0 as xβ€Ήx⁴› ∨ 1 as xβ€Ήx⁴› pattern Test(x) = (0 as x) | (1 as x) -//β”‚ elaborated pattern body: 0 as x ∨ 1 as x +//β”‚ elaborated pattern body: 0 as xβ€Ήx⁡› ∨ 1 as xβ€Ήx⁡› diff --git a/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArguments.mls b/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArguments.mls index 84d392023d..3f455cdb04 100644 --- a/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArguments.mls +++ b/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArguments.mls @@ -1,3 +1,4 @@ +:. :js class Foo(val bar: Int) @@ -15,17 +16,17 @@ pattern Baz(pattern A) = A :e pattern Baz(pattern A) = A() //β”‚ ╔══[COMPILATION ERROR] `A` is a pattern parameter. -//β”‚ β•‘ l.16: pattern Baz(pattern A) = A() +//β”‚ β•‘ l.17: pattern Baz(pattern A) = A() //β”‚ β•‘ ^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ It cannot be applied. -//β”‚ β•‘ l.16: pattern Baz(pattern A) = A() -//β”‚ ╙── ^ +//β”‚ β•‘ l.17: pattern Baz(pattern A) = A() +//β”‚ ╙── ^^^ :e pattern Baz(pattern A) = A(1, 2, 3) //β”‚ ╔══[COMPILATION ERROR] `A` is a pattern parameter. -//β”‚ β•‘ l.25: pattern Baz(pattern A) = A(1, 2, 3) +//β”‚ β•‘ l.26: pattern Baz(pattern A) = A(1, 2, 3) //β”‚ β•‘ ^^^^^^^^^ //β”‚ β•Ÿβ”€β”€ It cannot be applied to any arguments. -//β”‚ β•‘ l.25: pattern Baz(pattern A) = A(1, 2, 3) -//β”‚ ╙── ^^^^^^^^^ +//β”‚ β•‘ l.26: pattern Baz(pattern A) = A(1, 2, 3) +//β”‚ ╙── ^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArity.mls b/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArity.mls index e45fe7be13..c0dccc581e 100644 --- a/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArity.mls +++ b/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArity.mls @@ -1,15 +1,50 @@ +:. :js data class Pair[A, B](val first: A, val second: B) :e pattern LessArity = Pair("meow") -//β”‚ ╔══[COMPILATION ERROR] Expected two arguments, but found only one argument. -//β”‚ β•‘ l.6: pattern LessArity = Pair("meow") -//β”‚ ╙── ^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in constructor +//β”‚ β•‘ l.7: pattern LessArity = Pair("meow") +//β”‚ β•‘ ^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class definition 'Pair' expected 2 pattern arguments, but got 1 +//β”‚ β•‘ l.4: data class Pair[A, B](val first: A, val second: B) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :e pattern MoreArity = Pair("meow", "woof", "moo") -//β”‚ ╔══[COMPILATION ERROR] Expected two arguments, but found three arguments. -//β”‚ β•‘ l.12: pattern MoreArity = Pair("meow", "woof", "moo") -//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in constructor +//β”‚ β•‘ l.16: pattern MoreArity = Pair("meow", "woof", "moo") +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class definition 'Pair' expected 2 pattern arguments, but got 3 +//β”‚ β•‘ l.4: data class Pair[A, B](val first: A, val second: B) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// Nested modifiers and annotations belong to the whole declaration span. +open annotations +@untyped +public data class AnnotatedPair(val first, val second) + +:e +pattern WrongAnnotatedArity = AnnotatedPair("one") +//β”‚ ╔══[COMPILATION ERROR] Resolution error in constructor +//β”‚ β•‘ l.30: pattern WrongAnnotatedArity = AnnotatedPair("one") +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class definition 'AnnotatedPair' expected 2 pattern arguments, but got 1 +//β”‚ β•‘ l.26: @untyped +//β”‚ β•‘ ^^^^^^^^ +//β”‚ β•‘ l.27: public data class AnnotatedPair(val first, val second) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// A following declaration must not inherit the preceding annotation's span. +data class NextPair(val first, val second) + +:e +pattern WrongNextArity = NextPair("one") +//β”‚ ╔══[COMPILATION ERROR] Resolution error in constructor +//β”‚ β•‘ l.44: pattern WrongNextArity = NextPair("one") +//β”‚ β•‘ ^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Class definition 'NextPair' expected 2 pattern arguments, but got 1 +//β”‚ β•‘ l.41: data class NextPair(val first, val second) +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ups/transformation/.mls b/hkmc2/shared/src/test/mlscript/ups/transformation/.mls new file mode 100644 index 0000000000..e3f9030862 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ups/transformation/.mls @@ -0,0 +1 @@ +:.. diff --git a/hkmc2/shared/src/test/mlscript/ups/transformation/BasicTransformations.mls b/hkmc2/shared/src/test/mlscript/ups/transformation/BasicTransformations.mls index b3935c69ac..5b75473255 100644 --- a/hkmc2/shared/src/test/mlscript/ups/transformation/BasicTransformations.mls +++ b/hkmc2/shared/src/test/mlscript/ups/transformation/BasicTransformations.mls @@ -1,3 +1,4 @@ +:. :js diff --git a/hkmc2/shared/src/test/mlscript/ups/transformation/BindingLess.mls b/hkmc2/shared/src/test/mlscript/ups/transformation/BindingLess.mls index 704f8fa2af..5d84dfd77d 100644 --- a/hkmc2/shared/src/test/mlscript/ups/transformation/BindingLess.mls +++ b/hkmc2/shared/src/test/mlscript/ups/transformation/BindingLess.mls @@ -1,3 +1,4 @@ +:. :js import "../../../mlscript-compile/Char.mls" diff --git a/hkmc2/shared/src/test/mlscript/wasm/.mls b/hkmc2/shared/src/test/mlscript/wasm/.mls new file mode 100644 index 0000000000..6168956255 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/wasm/.mls @@ -0,0 +1,2 @@ +:.. +:wasm diff --git a/hkmc2/shared/src/test/mlscript/wasm/Basics.mls b/hkmc2/shared/src/test/mlscript/wasm/Basics.mls index 89b20fcc17..ed5b634513 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Basics.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Basics.mls @@ -1,3 +1,4 @@ +:. :global :wasm @@ -33,7 +34,7 @@ x fun foo() = 42 foo //β”‚ ╔══[COMPILATION ERROR] Returning function instances is not supported -//β”‚ β•‘ l.34: foo +//β”‚ β•‘ l.35: foo //β”‚ ╙── ^^^ :ge @@ -41,7 +42,7 @@ class Foo with val x = 1 let f = Foo //β”‚ ╔══[COMPILATION ERROR] Plain class references are not supported in Wasm; instantiate the class instead. -//β”‚ β•‘ l.42: let f = Foo +//β”‚ β•‘ l.43: let f = Foo //β”‚ ╙── ^^^ @@ -437,7 +438,7 @@ fun bar() = 42 fun foo() = bar foo()() //β”‚ ╔══[COMPILATION ERROR] Returning function instances is not supported -//β”‚ β•‘ l.437: fun foo() = bar +//β”‚ β•‘ l.438: fun foo() = bar //β”‚ ╙── ^^^ @@ -449,8 +450,8 @@ fun takeTop(m: Int32) = m takeTop(1) //β”‚ ═══[COMPILATION ERROR] Cannot use a value of type 'Int32' at an unrelated type 'Unknown' //β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Int' at an unrelated type 'Int32' -//β”‚ β•‘ l.449: takeTop(1) +//β”‚ β•‘ l.450: takeTop(1) //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Wasm function `takeTop` expects result to have type `(ref null any)`, but got `i32` -//β”‚ β•‘ l.448: fun takeTop(m: Int32) = m +//β”‚ β•‘ l.449: fun takeTop(m: Int32) = m //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/wasm/Binaryen.mls b/hkmc2/shared/src/test/mlscript/wasm/Binaryen.mls index 59fcbf3c8f..125fc76c6b 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Binaryen.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Binaryen.mls @@ -1,3 +1,4 @@ +:.. :js :silent diff --git a/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls b/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls index b956bb7886..075deb7daf 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls @@ -1,6 +1,5 @@ +:. -:global -:wasm // An `Int32`-annotated `val` binds the unboxed result; `ref.i31` boxes it back into a reference. val k: Int32 = wasm.i32.const(1) @@ -43,27 +42,33 @@ wasm.ref.i31(wasm.i32.add(wasm.i32.const(1), wasm.i32.const(2))) :ge wasm.i32.add(1, 2) //β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Int' at an unrelated type 'Int32' -//β”‚ β•‘ l.44: wasm.i32.add(1, 2) +//β”‚ β•‘ l.43: wasm.i32.add(1, 2) //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Int' at an unrelated type 'Int32' -//β”‚ β•‘ l.44: wasm.i32.add(1, 2) +//β”‚ β•‘ l.43: wasm.i32.add(1, 2) //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Operand #1 of wasm intrinsic 'i32.add' must be of type 'Int32' -//β”‚ β•‘ l.44: wasm.i32.add(1, 2) +//β”‚ β•‘ l.43: wasm.i32.add(1, 2) //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Operand #2 of wasm intrinsic 'i32.add' must be of type 'Int32' -//β”‚ β•‘ l.44: wasm.i32.add(1, 2) +//β”‚ β•‘ l.43: wasm.i32.add(1, 2) //β”‚ ╙── ^ // Mismatching arity is reported by both the elaborator and the backend. :ge wasm.i32.const(1, 2) -//β”‚ ╔══[COMPILATION ERROR] Expected 1 arguments, got 2 -//β”‚ β•‘ l.61: wasm.i32.const(1, 2) -//β”‚ ╙── ^^^^^^ +//β”‚ ╔══[COMPILATION ERROR] Resolution error in application +//β”‚ β•‘ l.60: wasm.i32.const(1, 2) +//β”‚ β•‘ ^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•Ÿβ”€β”€ Function 'const' expected 1 argument, but got 2 +//β”‚ β•‘ in ../decls/Prelude.mls +//β”‚ β•‘ l.448: fun +//β”‚ β•‘ ^^^ +//β”‚ β•‘ l.449: const(c: Int): Int32 +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Wasm intrinsic 'i32.const' called with incorrect arity (2) -//β”‚ β•‘ l.61: wasm.i32.const(1, 2) +//β”‚ β•‘ l.60: wasm.i32.const(1, 2) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +77,7 @@ wasm.i32.const(1, 2) :ge fun constOf(x: Int) = wasm.ref.i31(wasm.i32.const(x)) //β”‚ ╔══[COMPILATION ERROR] Wasm intrinsic 'i32.const' expects an integer literal immediate -//β”‚ β•‘ l.73: fun constOf(x: Int) = wasm.ref.i31(wasm.i32.const(x)) +//β”‚ β•‘ l.78: fun constOf(x: Int) = wasm.ref.i31(wasm.i32.const(x)) //β”‚ ╙── ^ @@ -82,7 +87,7 @@ fun constOf(x: Int) = wasm.ref.i31(wasm.i32.const(x)) :ge wasm.i32.const(-1) //β”‚ ╔══[COMPILATION ERROR] Wasm intrinsic 'i32.const' expects an integer literal immediate -//β”‚ β•‘ l.83: wasm.i32.const(-1) +//β”‚ β•‘ l.88: wasm.i32.const(-1) //β”‚ ╙── ^^ diff --git a/hkmc2/shared/src/test/mlscript/wasm/Casts.mls b/hkmc2/shared/src/test/mlscript/wasm/Casts.mls index 4f24c3a927..2003d6debd 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Casts.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Casts.mls @@ -1,6 +1,5 @@ +:. -:global -:wasm // In this example, the function call is equivalent to `useBox((id(new Box(42) as!! Box)))`. @@ -53,9 +52,8 @@ useBox(id(new Box(42))) //β”‚ (local.get $a))) //β”‚ (func $useBox (export "useBox") (type $useBox) (param $b (ref $Box)) (result (ref null i31)) //β”‚ (return -//β”‚ (ref.cast (ref i31) -//β”‚ (struct.get $Box $v -//β”‚ (local.get $b))))) +//β”‚ (struct.get $Box $v +//β”‚ (local.get $b)))) //β”‚ (func $entry (export "entry") (type $entry) (result (ref null i31)) //β”‚ (local $tmp (ref $Box)) //β”‚ (local $tmp1 (ref null any)) @@ -151,10 +149,10 @@ wasm.ref.i31(wasm.i32.const(7)) :noInline wasm.ref.i31(id(wasm.i32.const(7))) //β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Int32' at an unrelated type 'Unknown' -//β”‚ β•‘ l.152: wasm.ref.i31(id(wasm.i32.const(7))) +//β”‚ β•‘ l.150: wasm.ref.i31(id(wasm.i32.const(7))) //β”‚ ╙── ^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Unknown' at an unrelated type 'Int32' -//β”‚ β•‘ l.152: wasm.ref.i31(id(wasm.i32.const(7))) +//β”‚ β•‘ l.150: wasm.ref.i31(id(wasm.i32.const(7))) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[COMPILATION ERROR] Cannot cast a Wasm value of type `i32` to `(ref null any)` in a different type hierarchy //β”‚ ═══[COMPILATION ERROR] Operand #1 of wasm intrinsic 'ref.i31' must be of type 'Int32' @@ -179,7 +177,7 @@ unpack(toObj()) :e fun test: Function = id //β”‚ ╔══[COMPILATION ERROR] Returning function instances is not supported -//β”‚ β•‘ l.180: fun test: Function = id +//β”‚ β•‘ l.178: fun test: Function = id //β”‚ ╙── ^^ :fixme @@ -221,7 +219,7 @@ fun test: Function = castToA(123) :e castToA(wasm.i32.const(123)) //β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Int32' at an unrelated type 'Unknown' -//β”‚ β•‘ l.222: castToA(wasm.i32.const(123)) +//β”‚ β•‘ l.220: castToA(wasm.i32.const(123)) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^ //β”‚ Wasm result: //β”‚ = 123 diff --git a/hkmc2/shared/src/test/mlscript/wasm/ClassInheritance.mls b/hkmc2/shared/src/test/mlscript/wasm/ClassInheritance.mls index a326fbc0b8..1a8b877b10 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/ClassInheritance.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/ClassInheritance.mls @@ -1,6 +1,5 @@ +:. -:global -:wasm class A(val x) @@ -266,8 +265,11 @@ class B :ge class A extends B class B extends A +//β”‚ ╔══[COMPILATION ERROR] Inheritance cycle involving 'A' +//β”‚ β•‘ l.266: class A extends B +//β”‚ ╙── ^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Inheritance cycles are not supported. -//β”‚ β•‘ l.267: class A extends B +//β”‚ β•‘ l.266: class A extends B //β”‚ ╙── ^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/wasm/ClassMethods.mls b/hkmc2/shared/src/test/mlscript/wasm/ClassMethods.mls index fc1ccad316..d2409532df 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/ClassMethods.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/ClassMethods.mls @@ -1,6 +1,5 @@ +:. -:global -:wasm :wat @@ -181,18 +180,18 @@ let d = Counter(c.Counter#get()) :todo d.Counter#inc().Counter#double().Counter#get() //β”‚ ╔══[COMPILATION ERROR] Cannot find variable `d` (VarSymbol) in local or global scope. -//β”‚ β•‘ l.178: let d = Counter(c.Counter#get()) +//β”‚ β•‘ l.177: let d = Counter(c.Counter#get()) //β”‚ ╙── ^ -//β”‚ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Missing type definition for symbol `member:Counter` +//β”‚ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Missing field `term:Counter/inc` in struct `member:Counter` with type `(type $Counter (sub $Object (struct (field $$typeinfo (mut (ref $TypeInfoBase))) (field $x (mut (ref null any))))))` :todo c.Counter#get() -//β”‚ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Missing type definition for symbol `member:Counter` +//β”‚ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Missing field `term:Counter/get` in struct `member:Counter` with type `(type $Counter (sub $Object (struct (field $$typeinfo (mut (ref $TypeInfoBase))) (field $x (mut (ref null any))))))` class GetterOnly() with fun f = 1 -GetterOnly().f() +GetterOnly().f //β”‚ Wasm result: //β”‚ = 1 @@ -202,7 +201,7 @@ class A(val x) with fun get() = x A(1).get //β”‚ ╔══[COMPILATION ERROR] `member:get` is neither a field access nor a callable method -//β”‚ β•‘ l.203: A(1).get +//β”‚ β•‘ l.202: A(1).get //β”‚ ╙── ^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls b/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls index 3f77ba0d91..468cf8a296 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls @@ -1,6 +1,5 @@ +:. -:global -:wasm if 1 === 2 do 2 + 2 diff --git a/hkmc2/shared/src/test/mlscript/wasm/DeadConstructorElim.mls b/hkmc2/shared/src/test/mlscript/wasm/DeadConstructorElim.mls index 508a3f7f7e..4ac5ffbb26 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/DeadConstructorElim.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/DeadConstructorElim.mls @@ -1,3 +1,4 @@ +:. :noInline :global diff --git a/hkmc2/shared/src/test/mlscript/wasm/DeadParamElim.mls b/hkmc2/shared/src/test/mlscript/wasm/DeadParamElim.mls index f338c5e93e..1513f84d04 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/DeadParamElim.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/DeadParamElim.mls @@ -1,3 +1,4 @@ +:. :noInline :global diff --git a/hkmc2/shared/src/test/mlscript/wasm/Exceptions.mls b/hkmc2/shared/src/test/mlscript/wasm/Exceptions.mls index 2bdfda52c3..5f1311cab4 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Exceptions.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Exceptions.mls @@ -1,6 +1,5 @@ +:. -:global -:wasm :wat diff --git a/hkmc2/shared/src/test/mlscript/wasm/MainFunctions.mls b/hkmc2/shared/src/test/mlscript/wasm/MainFunctions.mls index ac631f4d3d..5b5c95fa58 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/MainFunctions.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/MainFunctions.mls @@ -1,6 +1,5 @@ +:. -:global -:wasm let x = 0 diff --git a/hkmc2/shared/src/test/mlscript/wasm/Matching.mls b/hkmc2/shared/src/test/mlscript/wasm/Matching.mls index a9ec477d25..b1b366209d 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Matching.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Matching.mls @@ -1,6 +1,5 @@ +:. -:global -:wasm if true then 0 else 2 //β”‚ Wasm result: diff --git a/hkmc2/shared/src/test/mlscript/wasm/NumericIntrinsicReview.mls b/hkmc2/shared/src/test/mlscript/wasm/NumericIntrinsicReview.mls index f0ad695ab5..de63470ff9 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/NumericIntrinsicReview.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/NumericIntrinsicReview.mls @@ -1,7 +1,6 @@ +:. // [Codex] Unboxed results returned from branches must determine the entry result type. -:global -:wasm // Both branches return i32, so the entry must return i32 too. @@ -24,7 +23,7 @@ if true then wasm.f64.const(1.5) else wasm.f64.const(2.5) fun chooseFloat(b: Bool): Float64 = if b then 1.5 else wasm.f64.const(2.5) //β”‚ ═══[COMPILATION ERROR] Cannot use a value of type 'Num' at an unrelated type 'Float64' //β”‚ ╔══[COMPILATION ERROR] WatBackend::result for Lit expression not implemented yet -//β”‚ β•‘ l.24: fun chooseFloat(b: Bool): Float64 = if b then 1.5 else wasm.f64.const(2.5) +//β”‚ β•‘ l.23: fun chooseFloat(b: Bool): Float64 = if b then 1.5 else wasm.f64.const(2.5) //β”‚ ╙── ^^^ // A declared function result keeps the type available when its call is not inlined. @@ -94,7 +93,7 @@ if false do wasm.i32.const(1) class ResultBase class ResultLeft extends ResultBase class ResultRight extends ResultBase -if true then ResultLeft() else ResultRight() +if true then new ResultLeft else new ResultRight //β”‚ Wasm result: //β”‚ = {} @@ -109,14 +108,14 @@ if true then throw 1 else wasm.i64.const(9) // Joining a nullable unit reference with a class reference must keep nullability. // :wat class NullableResult -if false then () else NullableResult() +if false then () else new NullableResult //β”‚ Wasm result: //β”‚ = {} // Distinct reference kinds still have a common Wasm reference supertype. class MixedResult -if true then 7 else MixedResult() +if true then 7 else new MixedResult //β”‚ Wasm result: //β”‚ = 7 diff --git a/hkmc2/shared/src/test/mlscript/wasm/NumericTypes.mls b/hkmc2/shared/src/test/mlscript/wasm/NumericTypes.mls index 6b0dd8da5c..fe69899422 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/NumericTypes.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/NumericTypes.mls @@ -1,8 +1,7 @@ +:. // This file covers the three unboxed numeric types other than `Int32`: `Int64`, `Float32` and `Float64`. // See `BuiltinOperators.mls` for the `Int32` intrinsics, whose shape these mirror. -:global -:wasm // `Int64` lowers to `i64`, and an `i64` result is handed back to JS as a `BigInt`. @@ -32,7 +31,7 @@ wasm.i64.const(9223372036854775807) :ge wasm.i64.const(9223372036854775808) //β”‚ ╔══[COMPILATION ERROR] Immediate #1 of wasm intrinsic 'i64.const' is outside the signed 64-bit range -//β”‚ β•‘ l.33: wasm.i64.const(9223372036854775808) +//β”‚ β•‘ l.32: wasm.i64.const(9223372036854775808) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^ @@ -98,10 +97,10 @@ wasm.i64.eqz(wasm.i64.const(0)) :ge wasm.i64.add(wasm.i32.const(1), wasm.i32.const(2)) //β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Int32' at an unrelated type 'Int64' -//β”‚ β•‘ l.99: wasm.i64.add(wasm.i32.const(1), wasm.i32.const(2)) +//β”‚ β•‘ l.98: wasm.i64.add(wasm.i32.const(1), wasm.i32.const(2)) //β”‚ ╙── ^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Int32' at an unrelated type 'Int64' -//β”‚ β•‘ l.99: wasm.i64.add(wasm.i32.const(1), wasm.i32.const(2)) +//β”‚ β•‘ l.98: wasm.i64.add(wasm.i32.const(1), wasm.i32.const(2)) //β”‚ ╙── ^^^^^^^^^^^^^^^^^ //β”‚ ═══[COMPILATION ERROR] Operand #1 of wasm intrinsic 'i64.add' must be of type 'Int64' //β”‚ ═══[COMPILATION ERROR] Operand #2 of wasm intrinsic 'i64.add' must be of type 'Int64' @@ -211,7 +210,7 @@ x :ge wasm.f32.const(1.0e39) //β”‚ ╔══[COMPILATION ERROR] Immediate #1 of wasm intrinsic 'f32.const' is outside the f32 range -//β”‚ β•‘ l.212: wasm.f32.const(1.0e39) +//β”‚ β•‘ l.211: wasm.f32.const(1.0e39) //β”‚ ╙── ^^^^^^ @@ -219,7 +218,7 @@ wasm.f32.const(1.0e39) :ge wasm.f64.const(1.0e400) //β”‚ ╔══[COMPILATION ERROR] Immediate #1 of wasm intrinsic 'f64.const' is outside the f64 range -//β”‚ β•‘ l.220: wasm.f64.const(1.0e400) +//β”‚ β•‘ l.219: wasm.f64.const(1.0e400) //β”‚ ╙── ^^^^^^^ @@ -227,7 +226,7 @@ wasm.f64.const(1.0e400) :ge fun constOf(x: Num): Float64 = wasm.f64.const(x) //β”‚ ╔══[COMPILATION ERROR] Wasm intrinsic 'f64.const' expects a numeric literal immediate -//β”‚ β•‘ l.228: fun constOf(x: Num): Float64 = wasm.f64.const(x) +//β”‚ β•‘ l.227: fun constOf(x: Num): Float64 = wasm.f64.const(x) //β”‚ ╙── ^ @@ -235,10 +234,10 @@ fun constOf(x: Num): Float64 = wasm.f64.const(x) :ge wasm.f64.add(wasm.f32.const(1.5), wasm.f32.const(2.25)) //β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Float32' at an unrelated type 'Float64' -//β”‚ β•‘ l.236: wasm.f64.add(wasm.f32.const(1.5), wasm.f32.const(2.25)) +//β”‚ β•‘ l.235: wasm.f64.add(wasm.f32.const(1.5), wasm.f32.const(2.25)) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Float32' at an unrelated type 'Float64' -//β”‚ β•‘ l.236: wasm.f64.add(wasm.f32.const(1.5), wasm.f32.const(2.25)) +//β”‚ β•‘ l.235: wasm.f64.add(wasm.f32.const(1.5), wasm.f32.const(2.25)) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^ //β”‚ ═══[COMPILATION ERROR] Operand #1 of wasm intrinsic 'f64.add' must be of type 'Float64' //β”‚ ═══[COMPILATION ERROR] Operand #2 of wasm intrinsic 'f64.add' must be of type 'Float64' diff --git a/hkmc2/shared/src/test/mlscript/wasm/ReplImports.mls b/hkmc2/shared/src/test/mlscript/wasm/ReplImports.mls index b5f8486fd3..2101e73d44 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/ReplImports.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/ReplImports.mls @@ -1,6 +1,5 @@ +:. -:global -:wasm object Foo with val x = 1 diff --git a/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls b/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls index de2a1845c5..5c7ab591a7 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls @@ -1,6 +1,5 @@ +:. -:global -:wasm :wat diff --git a/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls b/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls index 31c795cc25..c0b6cfcc90 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls @@ -1,6 +1,5 @@ +:. -:global -:wasm :wat () diff --git a/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls b/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls index 8a3faeb224..3664c2ae24 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Singletons.mls @@ -1,6 +1,5 @@ +:. -:global -:wasm :wat diff --git a/hkmc2/shared/src/test/mlscript/wasm/Strings.mls b/hkmc2/shared/src/test/mlscript/wasm/Strings.mls index 9b4df994de..5aac241761 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Strings.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Strings.mls @@ -1,6 +1,5 @@ +:. -:global -:wasm "Hello World!" //β”‚ Wasm result: diff --git a/hkmc2/shared/src/test/mlscript/wasm/TailRecOptCasts.mls b/hkmc2/shared/src/test/mlscript/wasm/TailRecOptCasts.mls index e5d5033b96..db8ce21077 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/TailRecOptCasts.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/TailRecOptCasts.mls @@ -1,6 +1,5 @@ +:. -:global -:wasm // Fixtures shared by the coercion tests below. @@ -273,27 +272,27 @@ fun sccF(): Int31 = wasm.ref.i31(sccG()) fun sccG(): Int32 = wasm.i31.get_s(sccF()) + wasm.i32.const(1) //β”‚ ═══[COMPILATION ERROR] Cannot use a value of type 'Unknown' at an unrelated type 'Int32' //β”‚ ╔══[COMPILATION ERROR] Types 'Int31' and 'Int32' have no common representation -//β”‚ β•‘ l.273: fun sccG(): Int32 = wasm.i31.get_s(sccF()) + wasm.i32.const(1) +//β”‚ β•‘ l.272: fun sccG(): Int32 = wasm.i31.get_s(sccF()) + wasm.i32.const(1) //β”‚ ╙── ^^^^ //β”‚ ═══[COMPILATION ERROR] Types 'Int31' and 'Int32' have no common representation //β”‚ ╔══[COMPILATION ERROR] Types 'Int31' and 'Int32' have no common representation -//β”‚ β•‘ l.272: fun sccF(): Int31 = wasm.ref.i31(sccG()) +//β”‚ β•‘ l.271: fun sccF(): Int31 = wasm.ref.i31(sccG()) //β”‚ ╙── ^^^^ //β”‚ ═══[COMPILATION ERROR] Types 'Int31' and 'Int32' have no common representation //β”‚ ╔══[COMPILATION ERROR] Types 'Int31' and 'Int32' have no common representation -//β”‚ β•‘ l.272: fun sccF(): Int31 = wasm.ref.i31(sccG()) +//β”‚ β•‘ l.271: fun sccF(): Int31 = wasm.ref.i31(sccG()) //β”‚ ╙── ^^^^ //β”‚ ╔══[COMPILATION ERROR] Types 'Int31' and 'Int32' have no common representation -//β”‚ β•‘ l.273: fun sccG(): Int32 = wasm.i31.get_s(sccF()) + wasm.i32.const(1) +//β”‚ β•‘ l.272: fun sccG(): Int32 = wasm.i31.get_s(sccF()) + wasm.i32.const(1) //β”‚ ╙── ^^^^ //β”‚ ═══[COMPILATION ERROR] Types 'Int31' and 'Int32' have no common representation //β”‚ ═══[COMPILATION ERROR] Types 'Int31' and 'Int32' have no common representation //β”‚ ═══[COMPILATION ERROR] Cannot cast a Wasm value of type `(ref null any)` to `i32` in a different type hierarchy //β”‚ ╔══[COMPILATION ERROR] Wasm function `sccF` expects result to have type `(ref null i31)`, but got `(ref null any)` -//β”‚ β•‘ l.272: fun sccF(): Int31 = wasm.ref.i31(sccG()) +//β”‚ β•‘ l.271: fun sccF(): Int31 = wasm.ref.i31(sccG()) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //β”‚ ╔══[COMPILATION ERROR] Wasm function `sccG` expects result to have type `i32`, but got `(ref null any)` -//β”‚ β•‘ l.273: fun sccG(): Int32 = wasm.i31.get_s(sccF()) + wasm.i32.const(1) +//β”‚ β•‘ l.272: fun sccG(): Int32 = wasm.i31.get_s(sccF()) + wasm.i32.const(1) //β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -311,6 +310,6 @@ fun bar(m: Int32) = foo(wasm.ref.i31(m)) //β”‚ ═══[COMPILATION ERROR] Cannot cast a Wasm value of type `i32` to `(ref null any)` in a different type hierarchy //β”‚ ╔══[COMPILATION ERROR] Operand #1 of wasm intrinsic 'ref.i31' must be of type 'Int32' -//β”‚ β•‘ l.311: foo(wasm.ref.i31(m)) +//β”‚ β•‘ l.310: foo(wasm.ref.i31(m)) //β”‚ ╙── ^ //β”‚ ═══[COMPILATION ERROR] Cannot cast a Wasm value of type `i32` to `(ref null any)` in a different type hierarchy diff --git a/hkmc2/shared/src/test/mlscript/wasm/Tuples.mls b/hkmc2/shared/src/test/mlscript/wasm/Tuples.mls index 001ec4af34..4dfc9ab194 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/Tuples.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/Tuples.mls @@ -1,6 +1,5 @@ +:. -:global -:wasm [] //β”‚ Wasm result: diff --git a/hkmc2/shared/src/test/mlscript/wasm/TypeResolution.mls b/hkmc2/shared/src/test/mlscript/wasm/TypeResolution.mls new file mode 100644 index 0000000000..e5872581fa --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/wasm/TypeResolution.mls @@ -0,0 +1,10 @@ +:. + +// The annotation precedes both the alias and its unboxed representation. +fun increment(n: Number): Number = wasm.i32.add(n, wasm.i32.const(1)) +type Number = LaterNumber +type LaterNumber = Int32 +increment(wasm.i32.const(41)) +//β”‚ Wasm result: +//β”‚ = 42 + diff --git a/hkmc2/shared/src/test/mlscript/wasm/VirtualMethods.mls b/hkmc2/shared/src/test/mlscript/wasm/VirtualMethods.mls index 62cb45d746..057b84255a 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/VirtualMethods.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/VirtualMethods.mls @@ -1,13 +1,12 @@ +:. -:global -:wasm class A() with - virtual fun f(x) = x + 1 + open fun f(x) = x + 1 class B() extends A() with - virtual fun g() = 0 - fun f(x) = x * 2 + open fun g() = 0 + override fun f(x) = x * 2 B().f(5) //β”‚ Wasm result: //β”‚ = 10 @@ -15,9 +14,9 @@ B().f(5) :wat class A() with - virtual fun f(x) = x + 1 + open fun f(x) = x + 1 class B() extends A() with - fun f(x) = x * 2 + override fun f(x) = x * 2 fun callF(a: A) = a.f(5) callF(B()) //β”‚ Wat: @@ -146,37 +145,37 @@ callF(B()) class A(val a) with - virtual fun f(x) = x + a + open fun f(x) = x + a class B(val b) extends A(b) with - virtual fun f(x) = x * b + open override fun f(x) = x * b class C(val c) extends B(c) with - fun f(x) = x - c + override fun f(x) = x - c C(3).f(5) //β”‚ Wasm result: //β”‚ = 2 class A() with - virtual fun f() = 1 + open fun f() = 1 class B() extends A() with - virtual fun g() = 2 + open fun g() = 2 class C() extends B() with - fun f() = 10 - fun g() = 20 + override fun f() = 10 + override fun g() = 20 fun callF(a: A) = a.f() fun callG(b: B) = b.g() callF(C()) + callG(C()) //β”‚ Wasm result: -//β”‚ = 21 +//β”‚ = 30 // A virtual slot's Wasm type carries the result type of the method that introduced it, so overrides share it :wat val n32: Int32 = wasm.i32.const(7) class V() with - virtual fun f(m: Int32): Int32 = m + open fun f(m: Int32): Int32 = m class W() extends V() with - fun f(m: Int32): Int32 = m + override fun f(m: Int32): Int32 = m fun callPrim(a: V): Int32 = a.f(n32) callPrim(W()) //β”‚ Wat: @@ -301,36 +300,36 @@ callPrim(W()) // Overrides share the slot's Wasm function type, so primitives must match exactly - here in the result. :ge class M1() with - virtual fun f(m: Int32): Int32 = m + open fun f(m: Int32): Int32 = m class M2() extends M1() with - fun f(m: Int32): Int = 0 + override fun f(m: Int32): Int = 0 M2().f(1) //β”‚ ╔══[COMPILATION ERROR] Cannot use a value of type 'Int' at an unrelated type 'Int32' -//β”‚ β•‘ l.307: M2().f(1) +//β”‚ β•‘ l.306: M2().f(1) //β”‚ ╙── ^ //β”‚ ╔══[COMPILATION ERROR] Overriding method `f` declares result as `(ref null i31)`, but the method it overrides declares `i32` -//β”‚ β•‘ l.306: fun f(m: Int32): Int = 0 -//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.305: override fun f(m: Int32): Int = 0 +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ // The same rule on a parameter: `g`'s `m` is unannotated, so it erases to `anyref` rather than `i32`. :ge class N1() with - virtual fun g(m: Int32): Int32 = m + open fun g(m: Int32): Int32 = m class N2() extends N1() with - fun g(m): Int32 = 0 + override fun g(m): Int32 = 0 N2().g(1) //β”‚ ═══[COMPILATION ERROR] Cannot use a value of type 'Int' at an unrelated type 'Int32' //β”‚ ╔══[COMPILATION ERROR] Overriding method `g` declares parameter 1 as `(ref null any)`, but the method it overrides declares `i32` -//β”‚ β•‘ l.321: fun g(m): Int32 = 0 -//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^ +//β”‚ β•‘ l.320: override fun g(m): Int32 = 0 +//β”‚ ╙── ^^^^^^^^^^^^^^^^^^^ // Boxed types are bridged by casts, so they need not match class R1() with - virtual fun h(x: Int): Anything = x + open fun h(x: Int): Anything = x class R2() extends R1() with - fun h(x) = x + override fun h(x) = x R2().h(3) //β”‚ Wasm result: //β”‚ = 3 @@ -342,9 +341,9 @@ R2().h(3) :noInline :wat class Parent() with - virtual fun get(): Object = new Parent() + open fun get(): Object = new Parent() class Child() extends Parent() with - fun get(): Child = new Child() + override fun get(): Child = new Child() fun readChild(c: Child): Child = c.get() readChild(new Child()) //β”‚ Wat: diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/DiffTestRunner.scala b/hkmc2DiffTests/src/test/scala/hkmc2/DiffTestRunner.scala index be999a181a..b0f3232dc6 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/DiffTestRunner.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/DiffTestRunner.scala @@ -42,11 +42,9 @@ object DiffTestRunner: // To be overridden in subproject-specific State classes def testDir: os.Path = dir - val validExt = Set("mls") - val allFiles = os.walk(testDir) .filter(_.toIO.isFile) - .filter(_.ext in validExt) + .filter(TestFolders.isTestSource) def filter(file: os.RelPath): Bool = true @@ -68,7 +66,7 @@ object DiffTestRunner: val filePath = os.RelPath(gitStr.drop(3)) if prefix =:= "A " || prefix =:= "M " || prefix =:= "R " || prefix =:= "D " then N // * Disregard modified files that are staged - else if filePath.ext =/= "mls" then N + else if !TestFolders.isTestSource(filePath) then N else S(filePath) }.toSet catch case err: Throwable => diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/InvalmlDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/InvalmlDiffMaker.scala index 5c1f705e54..a7d02f5aa2 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/InvalmlDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/InvalmlDiffMaker.scala @@ -37,8 +37,10 @@ abstract class InvalMLDiffMaker extends JSBackendDiffMaker: super.processTerm(trm, inImport) if invalmlOpt.isSet then given Scope = Scope.empty(Scope.Cfg.default) + given Elaborator.Ctx = curCtx + // Typing may lower patterns even when executable code generation is disabled. + given codegen.Erasure = codegen.Erasure(trm) if invalmlTyper.isEmpty then - given Elaborator.Ctx = curCtx invalmlTyper = S(InvalTyper()) given hkmc2.invalml.InvalCtx = invalCtx.copy(raise = summon) val typer = invalmlTyper.get diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index b20bcb78ec..e463cc7dcd 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -20,7 +20,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val debugLowering = NullaryCommand("dl") val noCodeGen = NullaryCommand("noCodeGen") - val js = NullaryCommand("js") + val js = FlagCommand(false, "js") val showSanitizedJS = NullaryCommand("ssjs") val showJS = NullaryCommand("sjs") val showRepl = NullaryCommand("showRepl") @@ -124,6 +124,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: if noCodeGen.isUnset then given Elaborator.Ctx = curCtx + given codegen.Erasure = codegen.Erasure(blk) val low = ltl.givenIn: new codegen.Lowering()(using summon[Config], ltl, summon[Raise], loweringState, curCtx, summon[SymbolPrinter]) with codegen.LoweringTraceLog(traceJS.isSet) diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala index a7cb944188..b1a8283e23 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala @@ -20,7 +20,8 @@ abstract class MLsDiffMaker extends DiffMaker: val runtimeSourceFile: io.Path = predefFile.up / "Runtime.mls" // * Contains MLscript runtime sources val termFile: io.Path = predefFile.up / "Term.mjs" // * Contains MLscript runtime term definitions val blockFile: io.Path = predefFile.up / "Block.mjs" // * Contains MLscript runtime block definitions - val optionFile: io.Path = predefFile.up / "Option.mjs" // * Contains MLscipt runtime option definition + // Reflection must use the same option constructors as Block.mls. + val optionFile: io.Path = predefFile.up / "Option.mjs" val wd = file.up @@ -47,6 +48,7 @@ abstract class MLsDiffMaker extends DiffMaker: val showResolve = NullaryCommand("r") val showResolvedTree = NullaryCommand("rt") val showFlows = FlagCommand(false, "sf") + val showResl = FlagCommand(false, "sr") val showLoweredTree = NullaryCommand("lot") val ppLoweredTreeOld = NullaryCommand("slot", () => output("Option ':slot' is deprecated, use ':sir' instead.")) val showIR = NullaryCommand("sir") @@ -234,7 +236,7 @@ abstract class MLsDiffMaker extends DiffMaker: given Elaborator.State = new Elaborator.State: override def dbg: Bool = dbgParsing.isSet - || dbgElab.isSet + // || dbgElab.isSet || dbgResolving.isSet || debug.isSet @@ -302,6 +304,11 @@ abstract class MLsDiffMaker extends DiffMaker: val preludeArtifact = cctx.getPrelude(preludeFile) curCtx = preludeArtifact.ctx prelude = preludeArtifact.ctx + else + // The compiler loads the prelude as one compilation unit. Its mutually + // referring host signatures must see declarations across blank lines here + // too; worksheet block boundaries are not part of the prelude's semantics. + consumeEmptyLines.setCurrentValue(()) super.run() @@ -314,7 +321,9 @@ abstract class MLsDiffMaker extends DiffMaker: () if file != preludeFile then val cfg = mkConfig - given Config = cfg.copy(optimizer = cfg.optimizer.copy( + // The synthetic Predef import bootstraps the host environment; it is not + // a WASM test block, even when :wasm is enabled at the top of the file. + given Config = cfg.copy(target = CompilationTarget.JS, optimizer = cfg.optimizer.copy( deforest = cfg.deforest.map: d => d.copy(config = d.config.copy( debug = false, @@ -356,7 +365,7 @@ abstract class MLsDiffMaker extends DiffMaker: case (syntax.IDENT(":..", true), _) :: rest => doImportUp(file.up) dropCrap(rest.dropWhile(_._1 isnt syntax.NEWLINE).drop(1)) - case (syntax.IDENT(":", true), _) :: (syntax.IDENT(nme, false), _) :: rest => + case (syntax.IDENT(prefix @ (":" | ":!"), true), _) :: (syntax.IDENT(nme, false), _) :: rest => if includeDirectives then val ln = rest.takeWhile(_._1 isnt syntax.NEWLINE) def render(ts: Ls[syntax.Stroken -> Loc]): Str = ts match @@ -367,7 +376,7 @@ abstract class MLsDiffMaker extends DiffMaker: case _ => TODO(st) str + render(rest) case Nil => "" - processLines(s":$nme ${render(ln)}" :: Nil, reprintCommands = false) + processLines(s"$prefix$nme ${render(ln)}" :: Nil, reprintCommands = false) dropCrap(rest.dropWhile(_._1 isnt syntax.NEWLINE).drop(1)) case _ => ts @@ -386,7 +395,7 @@ abstract class MLsDiffMaker extends DiffMaker: val elab = Elaborator(etl, wd, Ctx.empty) try val resBlk = new syntax.Tree.Block(res) - val (e, newCtx) = elab.importFrom(resBlk) + val (e, newCtx) = elab.importFrom(resBlk, resBlk.definedSymbols.iterator.map(_._2).toList) if file.toString === runtimeSourceFile.toString then summon[Elaborator.State].initRuntimeSymbolsFromBlock(e) val ctxWithImports = newCtx.withMembers(resBlk.definedSymbols) @@ -452,14 +461,20 @@ abstract class MLsDiffMaker extends DiffMaker: case _ => () def processTrees(trees: Ls[syntax.Tree])(using Config, Raise): Unit = - val elab = Elaborator(etl, file.up, prelude) + val blk = new syntax.Tree.Block(trees) + val elaborationConfig = Config.elaborationConfig(blk) // val blockSymbol = // semantics.TopLevelSymbol("block#"+blockNum) blockNum += 1 // given Elaborator.Ctx = curCtx.nest(S(blockSymbol)) given Elaborator.Ctx = curCtx.nestLocal(s"block:${blockNum}") - val blk = new syntax.Tree.Block(trees) - val (e, newCtx) = elab.topLevel(blk) + // Match the compiler's prelude bootstrap: builtin lookup uses the original + // declaration symbols, including while those declarations are elaborated. + if file == preludeFile then prelude = summon[Elaborator.Ctx].withMembers(blk.definedSymbols) + val elab = + given Config = elaborationConfig + Elaborator(etl, file.up, prelude) + val (e, newCtx) = elaborationConfig.givenIn(elab.topLevel(blk)) curCtx = newCtx extractConfig(e.stats) @@ -471,6 +486,19 @@ abstract class MLsDiffMaker extends DiffMaker: outputSeparator(s"Elaborated tree") output(e.showAsTree) + if showResl.isSet then + import semantics.ShowCfg + given ShowCfg = ShowCfg( + showErasedTypes = showIRErasedTypes.isSet, + showExpansionMappings = true, + showFlowSymbols = true, + debug = debug.isSet, + ) + outputSeparator(s"Resolved") + output: + import document.* + doc" #{ ${e.showTopLevel(using flowScp)} #} ".mkString(output.ColWidth) + processTerm(e, inImport = false) @@ -481,8 +509,9 @@ abstract class MLsDiffMaker extends DiffMaker: if file.toString =/= runtimeSourceFile.toString && file.toString =/= preludeFile.toString then summon[Elaborator.State].initRuntimeSymbolsFromFile(runtimeSourceFile, prelude)( using summon[TL], summon[Raise], cctx) - val resolver = Resolver(rtl) - curICtx = resolver.traverseBlock(trm)(using curICtx) + if !config.language.useNewResolution then + val resolver = Resolver(rtl) + curICtx = resolver.traverseBlock(trm)(using curICtx) if showResolve.isSet then output(s"Resolved: ${trm.showDbg}") diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/WasmDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/WasmDiffMaker.scala index 38daf778f6..de8a7059be 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/WasmDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/WasmDiffMaker.scala @@ -51,6 +51,12 @@ abstract class WasmDiffMaker extends InvalMLDiffMaker: lazy val prettifyBinaryenWat = (content: Str) => content.substring(2, content.length() - 2).replace("\\\\n", "\n").replace("\\\\\"", "\"") + override def processTrees(trees: Ls[syntax.Tree])(using Config, Raise): Unit = + if trees.nonEmpty && js.isSet && wasm.isSet && config.target == CompilationTarget.Wasm then + raise(WarningReport( + msg"Enabling both :js and :wasm is not currently supported; disable one before compiling this block." -> N :: Nil)) + super.processTrees(trees) + override def processIRBlock( pgrm: Program, definedValues: ComputeDefinedValues, @@ -61,7 +67,7 @@ abstract class WasmDiffMaker extends InvalMLDiffMaker: val outerRaise: Raise = summon - if wasm.isSet then + if wasm.isSet && config.target == CompilationTarget.Wasm then val reportedMessages = mutable.Set.empty[Str] @@ -73,29 +79,8 @@ abstract class WasmDiffMaker extends InvalMLDiffMaker: errored = true outerRaise(d) case d => outerRaise(d) - val sessionImportSymbols = mutable.LinkedHashSet.from[Symbol](pgrm.main.freeVars) - new BlockTraverser: - override def applyResult(r: Result): Unit = - // Cast targets are type dependencies rather than value references, so freeVars omits them. - r match - case Cast(_, target, _) => target.canonicalize match - case ErasedType.AnyRef(_, tpeSym) => sessionImportSymbols += tpeSym.bms.get - case _ => () - case _ => () - super.applyResult(r) - override def applyPath(p: Path): Unit = p match - // ValDefn traverses its RHS as a Path, whereas Return and Assign traverse a Result. - case c: Cast => applyResult(c) - case sel: Select => - sel.symbol.foreach: - case sym: ModuleOrObjectSymbol => sessionImportSymbols += sym - case _ => () - super.applyPath(sel) - case _ => - super.applyPath(p) - .applyBlock(pgrm.main) val sessionImports = mutable.LinkedHashMap.empty[Str, SessionBinding] - sessionImportSymbols.iterator.foreach: sym => + SessionBinding.referencedSymbols(pgrm.main).foreach: sym => sessionImportsBySymbol.get(sym).foreach: bindings => bindings.foreach: (bindingKey, binding) => sessionImports.update(bindingKey, binding) diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/Watcher.scala b/hkmc2DiffTests/src/test/scala/hkmc2/Watcher.scala index 7bb1f2117d..97f95cfdb6 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/Watcher.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/Watcher.scala @@ -95,12 +95,12 @@ class Watcher(dirs: Ls[File]): def go(file: File) = // println(s"go $file") - val isMls = file.toString.endsWith(".mls") + val path = os.Path(file.pathAsString) + val isMls = TestFolders.isTestSource(path) if file.toString.endsWith(".scala") then watcher.close() else if isMls || file.toString.endsWith(".cmd") then Thread.sleep(100) - val path = os.Path(file.pathAsString) val basePath = path.segments.drop(dirPaths.head.segmentCount).toList.init val relativeName = basePath.map(_ + "/").mkString + path.baseName val rootPath = os.pwd/os.up