Scope and status
LinkTopaz 5.21 language mode. The rules cite SELF source at af88afbc1d99. Examples target the public Topaz 5.21.x CLI.
Use this page to choose type annotations and understand compatibility and inference. Bindings and scope covers declarations and assignment. Functions and generics covers argument binding, specialization, and return inference.
Nominal value construction, protocol conformance, module lookup, literal spelling, and API operations belong to their respective references.
Type forms
LinkType = PrimaryType ("|" PrimaryType)* ;PrimaryType = NamedType | LiteralType | UnitType | GroupedType | RecordType | FunctionType ;NamedType = Identifier ("." Identifier)? TypeArguments? ;TypeArguments = "<" Type ("," Type)* ">" ;UnitType = "(" ")" ;GroupedType = "(" Type ")" ;RecordType = "{" (FieldName ":" Type ("," FieldName ":" Type)* ","?)? "}" ;FunctionType = "(" FunctionTypeParameters? ")" "->" Type ;
Hand-transcribed source grammar excerpt. Literal spellings, function-type parameter sequence restrictions, and alias declaration syntax are omitted. Checking conditions follow below.
Source evidence
- parseTypeArguments / parseNamedType at
af88afbc1d99· 77-143 - parsePrimaryType at
af88afbc1d99· 254-303 - parseParenthesizedType at
af88afbc1d99· 219-251 - parseFunctionType at
af88afbc1d99· 185-216 - parseRecordType at
af88afbc1d99· 145-182 - parseType at
af88afbc1d99· 305-328 - pull/41 at
271329b482bc - parseRecordType currently rejects zero fields at
271329b482bc· 145-184
Literal type forms
Literal type syntax admits int, float, bool, null, string literals. A string literal type must contain no interpolation; interpolation is rejected during parsing with TPZ2001.
Why
Literal types describe exact values, which supports finite choices and discriminants. Excluding interpolation keeps their identity independent of expression evaluation.
Source evidence
- parsePrimaryType at
af88afbc1d99· 254-303 - formLiteralType at
af88afbc1d99· 251-272
Unit and grouped types
() denotes the unit type. (T) denotes the same type as T.
Why
The unit type describes computations with no result value. Grouping changes how a type expression is parsed without creating another type.
Source evidence
- parseParenthesizedType at
af88afbc1d99· 219-251
The zero-field structural record type
A structural record type may have zero fields. In a type position, {} denotes the zero-field structural record and may appear in an annotation, alias, parameter, or function result. Structural compatibility still requires the same field-name set. This type is distinct from the unit type ().
The ordinary expression {} remains an empty block returning unit. No empty structural record literal is introduced. An empty concurrent {} produces the zero-field structural record value.
Why
A structural record type describes its field set, including the empty set. Naming that type explicitly keeps annotations, parameters, and function results consistent with the type inferred for an empty concurrent group. Unit has its existing distinct type and value. The empty expression block keeps its existing unit meaning; this rule introduces no alternative record literal.
Source evidence
- pull/41 at
271329b482bc - parseRecordType currently rejects zero fields at
271329b482bc· 145-184 - unit is primitive and the zero-field structural record is a distinct kind at
271329b482bc· 111-159 - assignable at
af88afbc1d99· 388-407 - structural at
af88afbc1d99· 632-675 - formRecordType at
af88afbc1d99· 298-341
Type identity source observations
LinkAlias expansion
A resolved type alias denotes its body with the supplied type arguments substituted for the corresponding declared type parameters. The alias creates no new nominal identity.
Why
An alias gives an existing type a reusable name. Keeping its identity transparent avoids conversions whose sole purpose would be changing that name.
Source evidence
- formNamedType at
af88afbc1d99· 585-609 - fillAliasNominal at
af88afbc1d99· 936-962
Nominal type identity
Named records, enums, and newtypes retain their declaration kind, name, originating module, and type arguments. Matching fields or a matching name from another module do not establish the same type identity.
Why
Named declarations represent distinct domain concepts even when their fields match. Including the defining module prevents unrelated declarations from becoming interchangeable.
Source evidence
- formNamedType at
af88afbc1d99· 563-581 - typeEqual at
af88afbc1d99· 246-259 - assignable at
af88afbc1d99· 358-416 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
Compiler-owned names and type arguments
| Type names | Type arguments |
|---|---|
int, float, string, bool | 0 |
template, JSON, File, Bytes, Byte, Path, Regex, Match, TOML, URL, Date, Big, Decimal, Rounding, Duration | 0 |
Array, Set, Option | 1 |
Map, Result | 2 |
Each terminal name denotes a distinct type. An incorrect type argument count for a listed name produces TPZ5022.
Why
A type constructor needs a fixed set of arguments to determine the values it describes. Arity checks catch missing or unrelated parameters before values are used.
Source evidence
- primitiveNamedType / terminalNamedType / builtinTypeConstructorArity at
af88afbc1d99· 73-110 - formNamedType at
af88afbc1d99· 448-519 - builtinTypeConstructorArity at
af88afbc1d99· 104-110 - pull/41 at
271329b482bc
Length unit markers
Length takes one compiler-owned marker from Pt, Mm, Cm, In and Em.
The marker is part of type identity and differs from the unit type (). Invalid markers and argument counts produce TPZ5001.
Physical lengths and relative lengths define their stored values and conversions.
Why
A unit belongs to a length's meaning. Keeping it in the type prevents document geometry from mixing physical and font-relative lengths without conversion.
Source evidence
- formNamedType at
af88afbc1d99· 404-417 - typeTextInModule at
af88afbc1d99· 112-117
Finite alias normalization and declaration dependencies
A transparent type alias substitutes its arguments into its body and denotes that resulting type. Alias expansion must terminate; a direct or indirect cycle is a static error identified by TPZ5023. A reference to another alias must retain the body's type constraints. An internal unresolved placeholder does not turn an alias into a type that accepts every value. The current source-order normalization defects are recorded below, including a later int alias whose constraint is lost.
Why
A transparent alias needs a finite meaning. Rejecting cycles and preserving forward constraints prevents declaration order or unresolved placeholders from changing that meaning.
Source evidence
- predeclareNominals at
151141d7bb44· 732-746 - formNamedType at
151141d7bb44· 585-609 - fillAliasNominal; fillNominals at
151141d7bb44· 936-991 - runCheckerPhases at
151141d7bb44· 148-164 - hasHole at
151141d7bb44· 289-297 - assignable at
151141d7bb44· 358-361 - expand_alias at
151141d7bb44· 872-894 - Read the input and result types at
151141d7bb44· 53-59
Compatibility source observations
LinkLiteral values and primitive targets
An inferred literal type in int, float, string, bool is compatible with its corresponding primitive type.
Why
An exact literal value is also a member of its primitive family. This lets precise values flow into ordinary APIs without a separate conversion.
Source evidence
- assignable at
af88afbc1d99· 358-376 - widen at
af88afbc1d99· 543-554
Primitive compatibility
Resolved primitive types are compatible when their names match. An int value does not implicitly convert to float, and a float value does not implicitly convert to int.
Why
Implicit numeric promotion can change precision and the operations a value supports. Explicit conversion keeps those changes visible at the use site.
Source evidence
- assignable at
af88afbc1d99· 358-376 - assignable at
af88afbc1d99· 358-416
Literal type targets
A literal type target requires the same inferred literal kind and value from a resolved non-union source type. A broad primitive type does not narrow to a literal type during this comparison.
Why
A literal target promises one exact value. A broader primitive type does not establish that promise without further checking.
Source evidence
- typeEqual at
af88afbc1d99· 246-259 - assignable at
af88afbc1d99· 358-416
The null literal type
null has its own literal type. Scalar widening preserves that type.
Why
Absence carries information that a primitive type cannot express. Preserving null keeps that information available to union checks and narrowing.
Source evidence
- widen at
af88afbc1d99· 543-554 - formLiteralType at
af88afbc1d99· 251-272
Union compatibility
Every member of an actual union must be compatible with the expected type. A non-union actual type is compatible with an expected union when at least one expected member accepts it. Different actual union members can match different expected members.
Why
A value of a union can belong to any member. Every possible actual member must therefore be accepted by the destination.
Source evidence
- assignable at
af88afbc1d99· 358-416
Union normalization
Union construction flattens nested unions and removes exactly equal members.
Why
Nesting and repeated members do not change the values a union admits. Normalization keeps those spellings from creating different type identities.
Source evidence
- unionType at
af88afbc1d99· 784-844
Option and Result payload compatibility
For the same Option, Result constructor, each actual payload type must be compatible with the corresponding expected payload type.
Why
A payload accepted by the destination remains safe when wrapped in the same constructor. Checking each position preserves that relationship.
Source evidence
- assignable at
af88afbc1d99· 377-383 - covariantArgumentsAssignable at
af88afbc1d99· 297-318 - covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387
Typed collection compatibility
An already typed Array, Map, Set value requires exactly equal corresponding type arguments when compared with the same expected collection constructor.
Why
An already typed collection retains its element, key, and value types. Exact corresponding type arguments preserve that contract without an implicit conversion between collection types.
Source evidence
- assignable at
af88afbc1d99· 377-383 - typeArraysEqual at
af88afbc1d99· 216-229 - builtinTypeConstructorArity at
af88afbc1d99· 104-110
Structural record compatibility
Structural record types require the same set of field names and a compatible actual type for every expected field. Field order does not affect compatibility. An extra or missing field fails this comparison.
Why
Field names identify the parts of a structural record. Comparing them as a set avoids making declaration order part of its meaning.
Source evidence
- assignable at
af88afbc1d99· 388-407 - structural at
af88afbc1d99· 632-675 - formRecordType at
af88afbc1d99· 298-341 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969
Function type compatibility
Function types require the same fixed parameter count and variadic shape. Each actual parameter type must accept the corresponding expected parameter type, including a variadic element type when present. The actual result type must be compatible with the expected result type.
Why
A substitute function must accept every argument its caller may pass and return a value the caller expects.
Source evidence
- functionAssignable at
af88afbc1d99· 320-356 - assignable at
af88afbc1d99· 385-387 - covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387
Type boundaries across functions, payloads, and aggregates
Function parameter compatibility runs from the expected parameter type to the actual function parameter type. The result runs in the opposite direction. The same parameter direction applies to a variadic element, and fixed arity and variadic shape must match. Both Result payload positions follow the result direction.
| Boundary | Accepted form | Rejected form |
|---|---|---|
| Function parameters | A function accepting int or string used where callers pass int | An int function used where callers may pass string |
| Variadic parameters | Wider accepted elements used through a narrower variadic type | Narrower accepted elements promised as a wider variadic type |
| Result payloads | Widening either payload argument | Narrowing a declared payload union |
| Nominal identity | The same declaration from the same module | Same-spelled declarations from different modules |
| Structural fields | Distinct names in any source order | Repeating a name in a structural type or literal |
| Map and Set inference | Widened element unions, with Map keys and values inferred separately | Empty bare initializers with no matching expected type |
| Aggregate joins | Matching components completed by another branch or an expected type | Unresolved Result components hidden inside different record shapes |
Joining aggregate branches retains their structure while solving corresponding inference variables. A missing type argument remains unresolved until another branch or the surrounding expectation supplies it. An unannotated binding whose non-function result still contains an inference variable is rejected with TPZ5020.
Why
Type compatibility must remain safe through callbacks, payloads, and aggregates. Solving every missing component prevents unresolved types from admitting incompatible values.
Source evidence
- covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684 - record-join at
151141d7bb44· 9-15 - join-callback-context at
151141d7bb44· 96-102 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
Shared inference source observations
LinkBinding annotations supply context
An explicit binding type annotation supplies the expected type to its initializer.
Why
The declaration already states the required type. Passing it into inference resolves values whose types cannot be chosen from their syntax alone.
Source evidence
- bindingAnnotationType / inferBindingValue at
af88afbc1d99· 100-193 - checkBindingStatement at
af88afbc1d99· 422-475
Unannotated binding types
Without an annotation, const retains the inferred initializer type. A let binding widens a top-level int, float, string, bool literal type to its primitive type. Aggregate types retain the result of aggregate inference.
Why
Widening a let literal allows later operations to use its primitive family. Retaining a constant's literal type lets it name an exact permitted value.
Source evidence
- checkBindingStatement at
af88afbc1d99· 422-475 - widen at
af88afbc1d99· 543-554
Checking an expected type
An expected type guides expression inference and refinement before compatibility is checked. Incompatible fully resolved types produce TPZ5001.
Why
The surrounding use can supply information an expression lacks. Inference uses that information before checking whether the resulting value fits.
Source evidence
- checkExpressionExpectedResult at
af88afbc1d99· 1716-1748 - reportExpectedType at
af88afbc1d99· 1660-1684 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684
Array and Set element inference
For a nonempty Array, Set literal without element context, ordinary elements contribute their inferred types after scalar literal widening. Their union becomes the element type.
Why
Every element contributes to the collection's possible contents. Widening scalar literals avoids tying ordinary collections to the exact values used to create them.
Source evidence
- inferSequenceLiteralExpression at
af88afbc1d99· 682-751 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819
Map key and value inference
For a nonempty Map literal without collection context, keys and values are inferred separately. Scalar literal types widen, then key types form one union and value types form another.
Why
Map keys and values have different roles. Inferring each domain separately retains which types may be looked up and which may be returned.
Source evidence
- inferMapLiteralExpression at
af88afbc1d99· 773-819 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819
Structural record field inference
During ordinary expression checking, each distinct structural record field uses its expected field type when one is available. A field without that expectation uses its widened inferred type.
Why
Each field has its own meaning and can need a different expected type. Fieldwise inference keeps that information when the surrounding record type is known.
Source evidence
- inferRecordLiteralExpression at
af88afbc1d99· 900-969
Expected Array element types
A nonempty Array literal checked against a fully resolved Array<T> expectation uses T to check its ordinary elements and retains T as the resulting element type.
Why
An expected element type describes what the array may hold. Checking each element against it constructs the intended collection without an extra conversion.
Source evidence
- inferSequenceLiteralExpression at
af88afbc1d99· 682-736 - inferSequenceLiteralExpression at
af88afbc1d99· 682-751
Empty collections in a binding
An empty Array, Map, Set literal adopts a matching expected collection type, including a generic parameter inside that constructor. A bare binding initializer without matching context is rejected with TPZ5020. Nested operands do not independently count as bare bindings.
Why
An empty literal contains no element from which to infer a type. Requiring context prevents a guessed type from affecting later reads or mutations.
Source evidence
- inferSequenceLiteralExpression at
af88afbc1d99· 659-681 - inferMapLiteralExpression at
af88afbc1d99· 754-772
Types contributed by if branches
For an if expression with an else branch, value-producing branches contribute their types to the join. A terminating branch contributes no value type.
Why
The result type must account for every branch that can produce a value. A branch that leaves the expression cannot contribute a result.
Source evidence
- inferIfExpression at
af88afbc1d99· 107-212
The unit type of if without else
An if expression without an else arm has the unit type in both value and statement positions. On normal completion it produces (). The selected then branch runs for its effects and its value is discarded.
Why
Without an else branch there is no result on the false path. Giving both paths unit keeps the result independent of whether the condition succeeds.
Source evidence
- issues/31 at
570530e69141
Fresh branch literal types
Fresh scalar literal results in an if-with-else join widen before joining when there is no fully resolved expected type or literal-preserving context.
Why
A conditional chooses between values at runtime. Widening fresh literals avoids assigning an ordinary result a separate exact-value type for each branch.
Source evidence
- inferIfExpression at
af88afbc1d99· 107-212 - joinedTypes at
af88afbc1d99· 1148-1187 - inferBranchExpression at
af88afbc1d99· 456-477 - widenedUnion at
af88afbc1d99· 846-852
Existing union domains in a join
At the widening step of a branch join, an existing fully resolved union retains its member types.
Why
An existing union may encode a deliberate set of allowed values. Widening its members would discard that constraint when control flow joins.
Source evidence
- joinedTypes at
af88afbc1d99· 1148-1187 - widenedUnion at
af88afbc1d99· 846-852 - widen at
af88afbc1d99· 543-554 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190
Map lookup and optional fallback
For Map. and a fallback supplied by ??, apply the Map lookup contract.
The Map lookup contract defines migration from get. The eager-effects example preserves receiver, key and default evaluation through explicit bindings.
Why
Map lookup represents absence with Option. The same coalescing operator handles that absence for every Option value. Keeping fallback evaluation in the operator gives lookup one spelling and makes its effects explicit.
Source evidence
- mapMemberType at
271329b482bc· 871-890 - emitBinary at
271329b482bc· 483-503 - issues/31 at
a545e8811f8f
Examples and expected results
LinkEach example is a complete example. file. The expected success or rejection is independent of future observed output. All examples remain Pending. A source-backed expectation is not an executed result. Negative examples specify the intended cause and phase; their exact diagnostic locations remain unmeasured.
Aliases, literals, unit, and a branch join
Linktype Count = intlet count: Count = 3let chosen = if (true) { 1 } else { "one" }let accepted: int | string = chosenconst status = "ready"let exact: "ready" = statuslet absent: null = nulllet finished: () = ()
No stdout.
Expected result
Expected result
Checking accepts the program. Exit code 0.
Observed assertions · passed · 5.21.3
Exit code 0.
No stdout.
No stderr.
- Passed · expected 0; observed 0
- Passed · stdout matches the authored expectation
- Passed · stderr matches the authored expectation
- Passed · expected 0; observed 0
- Passed · expected 0; observed 0
- Passed · expected 0; observed 0
- Passed · expected 0; observed 0
- Passed · expected 0; observed 0
- Passed · expected 0; observed 0
Source evidence
- formNamedType at
af88afbc1d99· 585-609 - fillAliasNominal at
af88afbc1d99· 936-962 - formNamedType at
af88afbc1d99· 563-581 - typeEqual at
af88afbc1d99· 246-259 - assignable at
af88afbc1d99· 358-416 - assignable at
af88afbc1d99· 358-376 - widen at
af88afbc1d99· 543-554 - typeEqual at
af88afbc1d99· 246-259 - assignable at
af88afbc1d99· 358-416 - formLiteralType at
af88afbc1d99· 251-272 - assignable at
af88afbc1d99· 358-416 - unionType at
af88afbc1d99· 784-844 - inferIfExpression at
af88afbc1d99· 107-212 - joinedTypes at
af88afbc1d99· 1148-1187 - inferBranchExpression at
af88afbc1d99· 456-477 - checkExpressionExpectedResult at
af88afbc1d99· 1716-1748 - reportExpectedType at
af88afbc1d99· 1660-1684 - bindingAnnotationType / inferBindingValue at
af88afbc1d99· 100-193 - checkBindingStatement at
af88afbc1d99· 422-475 - widen at
af88afbc1d99· 543-554
An integer is not implicitly a float
Linklet value: float = 1
No stdout.
error[TPZ5001]: expected `float`, found `int` --> example.tpz:1:20 | 1 | let value: float = 1 | ^ example.tpz: 1 type diagnostic
Expected result
Observed assertions · passed · 5.21.3
Exit code 1.
No stdout.
error[TPZ5001]: expected `float`, found `int`--> example.tpz:1:20|1 | let value: float = 1| ^example.tpz: 1 type diagnostic
- Passed · expected nonzero; observed 1
- Passed · stdout matches the authored expectation
- Passed · checking TPZ5001; cause types-int-not-float; primary location asserted; additional diagnostics allow
- Passed · checking TPZ5001; cause types-int-not-float; primary location asserted; additional diagnostics allow
Source evidence
- assignable at
af88afbc1d99· 358-376 - widen at
af88afbc1d99· 543-554 - typeEqual at
af88afbc1d99· 246-259 - assignable at
af88afbc1d99· 358-416 - formLiteralType at
af88afbc1d99· 251-272 - checkExpressionExpectedResult at
af88afbc1d99· 1716-1748 - reportExpectedType at
af88afbc1d99· 1660-1684 - bindingAnnotationType / inferBindingValue at
af88afbc1d99· 100-193 - checkBindingStatement at
af88afbc1d99· 422-475 - widen at
af88afbc1d99· 543-554
Structural fields can be written in another order
Linktype Point = { x: int, y: int }let point: Point = { y: 2, x: 1 }
No stdout.
Expected result
Expected result
Checking accepts the program. Exit code 0.
Observed assertions · passed · 5.21.3
Exit code 0.
No stdout.
No stderr.
- Passed · expected 0; observed 0
- Passed · stdout matches the authored expectation
- Passed · stderr matches the authored expectation
- Passed · expected 0; observed 0
- Passed · expected 0; observed 0
Source evidence
- assignable at
af88afbc1d99· 388-407 - structural at
af88afbc1d99· 632-675 - formRecordType at
af88afbc1d99· 298-341 - formNamedType at
af88afbc1d99· 585-609 - fillAliasNominal at
af88afbc1d99· 936-962 - formNamedType at
af88afbc1d99· 563-581 - typeEqual at
af88afbc1d99· 246-259 - assignable at
af88afbc1d99· 358-416
An extra structural field is a different shape
Linklet point: { x: int } = { x: 1, y: 2 }
No stdout.
error[TPZ5001]: expected `{ x: int }`, found `{ x: int, y: int }`
--> example.tpz:1:25
|
1 | let point: { x: int } = { x: 1, y: 2 }
| ^^^^^^^^^^^^^^
example.tpz: 1 type diagnostic
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001types-record-extra-field atexample.tpz:1:25
Observed assertions · passed · 5.21.3
Exit code 1.
No stdout.
error[TPZ5001]: expected `{ x: int }`, found `{ x: int, y: int }`--> example.tpz:1:25|1 | let point: { x: int } = { x: 1, y: 2 }| ^^^^^^^^^^^^^^example.tpz: 1 type diagnostic
- Passed · expected nonzero; observed 1
- Passed · stdout matches the authored expectation
- Passed · checking TPZ5001; cause types-record-extra-field; primary location asserted; additional diagnostics allow
- Passed · checking TPZ5001; cause types-record-extra-field; primary location asserted; additional diagnostics allow
Source evidence
- assignable at
af88afbc1d99· 388-407 - structural at
af88afbc1d99· 632-675 - formRecordType at
af88afbc1d99· 298-341 - checkExpressionExpectedResult at
af88afbc1d99· 1716-1748 - reportExpectedType at
af88afbc1d99· 1660-1684 - bindingAnnotationType / inferBindingValue at
af88afbc1d99· 100-193 - checkBindingStatement at
af88afbc1d99· 422-475 - widen at
af88afbc1d99· 543-554
Annotations provide empty collection types
Linklet numbers: Array<int> = []let lookup: Map<string, int> = map {}let names: Set<string> = set {}
No stdout.
Expected result
Expected result
Checking accepts the program. Exit code 0.
Observed assertions · passed · 5.21.3
Exit code 0.
No stdout.
No stderr.
- Passed · expected 0; observed 0
- Passed · stdout matches the authored expectation
- Passed · stderr matches the authored expectation
- Passed · expected 0; observed 0
Source evidence
- inferSequenceLiteralExpression at
af88afbc1d99· 682-751 - inferMapLiteralExpression at
af88afbc1d99· 773-819 - inferRecordLiteralExpression at
af88afbc1d99· 900-969 - inferSequenceLiteralExpression at
af88afbc1d99· 659-681 - inferMapLiteralExpression at
af88afbc1d99· 754-772 - checkExpressionExpectedResult at
af88afbc1d99· 1716-1748 - reportExpectedType at
af88afbc1d99· 1660-1684 - bindingAnnotationType / inferBindingValue at
af88afbc1d99· 100-193 - checkBindingStatement at
af88afbc1d99· 422-475 - widen at
af88afbc1d99· 543-554
A bare empty array lacks an element type
Linklet values = []
No stdout.
error[TPZ5020]: `[]` needs a contextual type (§22.1): annotate the binding or the expected element type --> example.tpz:1:14 | 1 | let values = [] | ^^ example.tpz: 1 type diagnostic
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5020types-empty-array-needs-context atexample.tpz:1:14
Observed assertions · passed · 5.21.3
Exit code 1.
No stdout.
error[TPZ5020]: `[]` needs a contextual type (§22.1): annotate the binding or the expected element type--> example.tpz:1:14|1 | let values = []| ^^example.tpz: 1 type diagnostic
- Passed · expected nonzero; observed 1
- Passed · stdout matches the authored expectation
- Passed · checking TPZ5020; cause types-empty-array-needs-context; primary location asserted; additional diagnostics allow
Source evidence
- inferSequenceLiteralExpression at
af88afbc1d99· 682-751 - inferMapLiteralExpression at
af88afbc1d99· 773-819 - inferRecordLiteralExpression at
af88afbc1d99· 900-969 - inferSequenceLiteralExpression at
af88afbc1d99· 659-681 - inferMapLiteralExpression at
af88afbc1d99· 754-772
An existing Array keeps its element type
Linklet integers: Array<int> = [1]let values: Array<int | string> = integers
No stdout.
error[TPZ5001]: expected `Array<int | string>`, found `Array<int>` --> example.tpz:2:35 | 2 | let values: Array<int | string> = integers | ^^^^^^^^ example.tpz: 1 type diagnostic
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001types-invariant-array-argument atexample.tpz:2:35
Observed assertions · passed · 5.21.3
Exit code 1.
No stdout.
error[TPZ5001]: expected `Array<int | string>`, found `Array<int>`--> example.tpz:2:35|2 | let values: Array<int | string> = integers| ^^^^^^^^example.tpz: 1 type diagnostic
- Passed · expected nonzero; observed 1
- Passed · stdout matches the authored expectation
- Passed · checking TPZ5001; cause types-invariant-array-argument; primary location asserted; additional diagnostics allow
- Passed · checking TPZ5001; cause types-invariant-array-argument; primary location asserted; additional diagnostics allow
Source evidence
- assignable at
af88afbc1d99· 377-383 - covariantArgumentsAssignable at
af88afbc1d99· 297-318 - assignable at
af88afbc1d99· 377-383 - typeArraysEqual at
af88afbc1d99· 216-229 - builtinTypeConstructorArity at
af88afbc1d99· 104-110 - inferSequenceLiteralExpression at
af88afbc1d99· 682-736 - checkExpressionExpectedResult at
af88afbc1d99· 1716-1748 - reportExpectedType at
af88afbc1d99· 1660-1684 - bindingAnnotationType / inferBindingValue at
af88afbc1d99· 100-193 - checkBindingStatement at
af88afbc1d99· 422-475 - widen at
af88afbc1d99· 543-554
Payload and function types use their own compatibility rules
Linklet narrow: Option<int> = Some(1)let wide: Option<int | string> = narrowfunction acceptsEither(value: int | string) -> int {1}let callback: (int) -> (int | string) = acceptsEither
No stdout.
Expected result
Expected result
Checking accepts the program. Exit code 0.
Observed assertions · passed · 5.21.3
Exit code 0.
No stdout.
No stderr.
- Passed · expected 0; observed 0
- Passed · stdout matches the authored expectation
- Passed · stderr matches the authored expectation
- Passed · expected 0; observed 0
Source evidence
- assignable at
af88afbc1d99· 377-383 - covariantArgumentsAssignable at
af88afbc1d99· 297-318 - assignable at
af88afbc1d99· 377-383 - typeArraysEqual at
af88afbc1d99· 216-229 - builtinTypeConstructorArity at
af88afbc1d99· 104-110 - inferSequenceLiteralExpression at
af88afbc1d99· 682-736 - functionAssignable at
af88afbc1d99· 320-356 - assignable at
af88afbc1d99· 385-387
Finite aliases substitute their type arguments
Linkimport std.iotype Element = inttype Bag<T> = Array<T>type Counts = Bag<Element>let counts: Counts = [2, 5]io.writeLine("{counts[0]}:{counts[1]}")
2:5
Expected result
Expected result
Execution completes. Exit code 0.
2:5
Source evidence
- predeclareNominals at
151141d7bb44· 732-746 - formNamedType at
151141d7bb44· 585-609 - fillAliasNominal; fillNominals at
151141d7bb44· 936-991 - runCheckerPhases at
151141d7bb44· 148-164 - hasHole at
151141d7bb44· 289-297 - assignable at
151141d7bb44· 358-361 - expand_alias at
151141d7bb44· 872-894 - Read the input and result types at
151141d7bb44· 53-59
Recursive alias expansion retains its intended rejection
Linktype First = Secondtype Second = Firstlet value: First = 1
No stdout.
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5023types-alias-cycle
The required primary-location contract is Pending.
Source evidence
- predeclareNominals at
151141d7bb44· 732-746 - formNamedType at
151141d7bb44· 585-609 - fillAliasNominal; fillNominals at
151141d7bb44· 936-991 - runCheckerPhases at
151141d7bb44· 148-164 - hasHole at
151141d7bb44· 289-297 - assignable at
151141d7bb44· 358-361 - expand_alias at
151141d7bb44· 872-894 - Read the input and result types at
151141d7bb44· 53-59
A later alias body must not erase an int constraint
Linktype First = Secondtype Second = intlet value: First = "bad"
No stdout.
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001types-alias-body-mismatch atexample.tpz:3:20
Source evidence
- predeclareNominals at
151141d7bb44· 732-746 - formNamedType at
151141d7bb44· 585-609 - fillAliasNominal; fillNominals at
151141d7bb44· 936-991 - runCheckerPhases at
151141d7bb44· 148-164 - hasHole at
151141d7bb44· 289-297 - assignable at
151141d7bb44· 358-361 - expand_alias at
151141d7bb44· 872-894 - Read the input and result types at
151141d7bb44· 53-59
A function with wider inputs fits a narrower input contract
Linkimport std.iofunction acceptsEither(value: int | string) -> int { 7 }let callback: (int) -> (int | string) = acceptsEitherio.writeLine("{callback(1)}")
7
Expected result
Expected result
Execution completes. Exit code 0.
7
Source evidence
- covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684 - record-join at
151141d7bb44· 9-15 - join-callback-context at
151141d7bb44· 96-102 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
A function accepting int cannot promise to accept string
Linkfunction acceptsInt(value: int) -> int { value }let callback: (int | string) -> int = acceptsInt
No stdout.
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001types-function-parameter-variance atexample.tpz:2:39
Source evidence
- covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684 - record-join at
151141d7bb44· 9-15 - join-callback-context at
151141d7bb44· 96-102 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
Variadic element types follow the parameter direction
Linkimport std.iofunction count(...values: int | string) -> int { values.length }let callback: (...int) -> int = countio.writeLine("{callback(1, 2)}")
2
Expected result
Expected result
Execution completes. Exit code 0.
2
Source evidence
- covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684 - record-join at
151141d7bb44· 9-15 - join-callback-context at
151141d7bb44· 96-102 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
A variadic int function cannot accept string elements
Linkfunction count(...values: int) -> int { values.length }let callback: (...int | string) -> int = count
No stdout.
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001types-variadic-parameter-variance atexample.tpz:2:42
Source evidence
- covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684 - record-join at
151141d7bb44· 9-15 - join-callback-context at
151141d7bb44· 96-102 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
Both Result payload positions may widen
Linklet narrow: Result<int, string> = Ok(3)let wider: Result<int | string, string | bool> = narrowlet error: Result<int, string> = Err("missing")let widerError: Result<int | string, string | bool> = error
No stdout.
Expected result
Expected result
Checking accepts the program. Exit code 0.
Source evidence
- covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684 - record-join at
151141d7bb44· 9-15 - join-callback-context at
151141d7bb44· 96-102 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
A wider Result payload cannot be narrowed by assignment
Linklet broad: Result<int | string, string> = Ok("bad")let narrow: Result<int, string> = broad
No stdout.
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001types-result-payload-narrowing atexample.tpz:2:35
Source evidence
- covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684 - record-join at
151141d7bb44· 9-15 - join-callback-context at
151141d7bb44· 96-102 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
A nominal value keeps its declaring module
Linkimport aimport bfunction same(value: b.Book) -> b.Book { value }
No stdout.
export record Book { title: string }
export record Book { title: string }
Expected result
Expected result
Checking accepts the program. Exit code 0.
Source evidence
- covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684 - record-join at
151141d7bb44· 9-15 - join-callback-context at
151141d7bb44· 96-102 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
Same-named records from different modules remain distinct
Linkimport aimport bfunction wrong(value: b.Book) -> a.Book { value }
No stdout.
export record Book { title: string }
export record Book { title: string }
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001types-cross-module-nominal atexample.tpz:3:43
Source evidence
- covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684 - record-join at
151141d7bb44· 9-15 - join-callback-context at
151141d7bb44· 96-102 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
Distinct structural fields may be written in either order
Linkimport std.iolet item: { value: int, label: string } = { label: "x", value: 1 }io.writeLine("{item.value}:{item.label}")
1:x
Expected result
Expected result
Execution completes. Exit code 0.
1:x
Source evidence
- covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684 - record-join at
151141d7bb44· 9-15 - join-callback-context at
151141d7bb44· 96-102 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
A structural type cannot repeat a field name
Linklet item: { value: int, value: string } = { value: 1 }
No stdout.
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5022types-repeated-record-type-field atexample.tpz:1:25
Source evidence
- covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684 - record-join at
151141d7bb44· 9-15 - join-callback-context at
151141d7bb44· 96-102 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
A structural literal cannot repeat a field name
Linklet item = { value: 1, value: 2 }
No stdout.
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5022types-repeated-record-literal-field atexample.tpz:1:24
Source evidence
- covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684 - record-join at
151141d7bb44· 9-15 - join-callback-context at
151141d7bb44· 96-102 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
Map and Set infer unions and accept empty typed contexts
Linklet mapping = map { 1: "one", "two": 2 }let checkedMap: Map<int | string, int | string> = mappinglet members = set { 1, "two" }let checkedSet: Set<int | string> = memberslet emptyMap: Map<string, int> = map {}let emptySet: Set<int> = set {}
No stdout.
Expected result
Expected result
Checking accepts the program. Exit code 0.
Source evidence
- covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684 - record-join at
151141d7bb44· 9-15 - join-callback-context at
151141d7bb44· 96-102 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
An unannotated empty Map leaves its arguments unresolved
Linklet values = map {}
No stdout.
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5020types-empty-map-needs-context atexample.tpz:1:14
Source evidence
- covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684 - record-join at
151141d7bb44· 9-15 - join-callback-context at
151141d7bb44· 96-102 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
An unannotated empty Set leaves its element unresolved
Linklet values = set {}
No stdout.
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5020types-empty-set-needs-context atexample.tpz:1:14
Source evidence
- covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684 - record-join at
151141d7bb44· 9-15 - join-callback-context at
151141d7bb44· 96-102 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
Matching record branches complete Result payload arguments
Linklet joined = if (true) { { result: Ok(1) } } else { { result: Err("missing") } }let checked: { result: Result<int, string> } = joined
No stdout.
Expected result
Expected result
Checking accepts the program. Exit code 0.
Source evidence
- covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684 - record-join at
151141d7bb44· 9-15 - join-callback-context at
151141d7bb44· 96-102 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
A callback expectation supplies both Result payloads
Linkfunction take(f: () -> { a: Result<int, string> } | { b: Result<int, string> }) -> int { 0 }let count = take(() => if (true) { { a: Ok(1) } } else { { b: Err("e") } })
No stdout.
Expected result
Expected result
Checking accepts the program. Exit code 0.
Source evidence
- covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684 - record-join at
151141d7bb44· 9-15 - join-callback-context at
151141d7bb44· 96-102 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
Different record shapes do not erase unsolved Result arguments
Linklet got = if (true) { { a: Ok(1) } } else { { b: Err("e") } }
No stdout.
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5020types-unresolved-record-join atexample.tpz:1:11
Source evidence
- covariantArgumentsAssignable; functionAssignable; assignable at
151141d7bb44· 297-387 - typeEqual at
151141d7bb44· 246-259 - formNamedType at
151141d7bb44· 563-581 - formRecordType at
151141d7bb44· 298-341 - inferRecordLiteralExpression at
151141d7bb44· 900-969 - inferSequenceLiteralExpression; inferMapLiteralExpression at
151141d7bb44· 659-819 - unifyPartialTypes; joinedTypes at
151141d7bb44· 1027-1187 - inferIfExpression at
151141d7bb44· 107-212 - inferBindingValue at
151141d7bb44· 169-190 - reportExpectedType at
151141d7bb44· 1660-1684 - record-join at
151141d7bb44· 9-15 - join-callback-context at
151141d7bb44· 96-102 - diagnostic-distinct-nominals at
151141d7bb44· 437-452
An omitted else discards values and preserves selected effects
Linkimport std.iolet taken = if (true) {io.writeLine("then")7}let skipped = if (false) {io.writeLine("unexpected")9}io.writeLine("{taken}")io.writeLine("{skipped}")
then () ()
Expected result
Expected result
Execution completes. Exit code 0.
then()()
Source evidence
- issues/31 at
570530e69141 - inferIfExpression joins then and implicit unit at
570530e69141· 107-211 - emitIf discards a then value when else is absent at
570530e69141· 1309-1339
A unit context accepts discarded non-unit branch values
Linklet taken: () = if (true) { 7 }let skipped: () = if (false) { "discarded" }
No stdout.
Expected result
Expected result
Checking accepts the program. Exit code 0.
Source evidence
- issues/31 at
570530e69141 - inferIfExpression joins then and implicit unit at
570530e69141· 107-211 - emitIf discards a then value when else is absent at
570530e69141· 1309-1339
The inferred result can be assigned to unit
Linklet inferred = if (true) { 7 }let checked: () = inferred
No stdout.
Expected result
Expected result
Checking accepts the program. Exit code 0.
Source evidence
- issues/31 at
570530e69141 - inferIfExpression joins then and implicit unit at
570530e69141· 107-211 - emitIf discards a then value when else is absent at
570530e69141· 1309-1339
An omitted else cannot produce an int result
Linklet value: int = if (true) { 7 }
No stdout.
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001if-without-else-result-type-mismatch atexample.tpz:1:18
Source evidence
- issues/31 at
570530e69141 - inferIfExpression joins then and implicit unit at
570530e69141· 107-211 - emitIf discards a then value when else is absent at
570530e69141· 1309-1339 - reportExpectedType and checkExpressionExpectedResult at
570530e69141· 1660-1748
Evaluate a Map fallback when its key is absent
Linkimport std.iofunction makeMap() -> Map<string, int> {io.writeLine("map")map { "present": 7 }}function makeKey(name: string) -> string {io.writeLine("key")name}function makeDefault() -> int {io.writeLine("fallback")99}let present = makeMap().get(makeKey("present")) ?? makeDefault()io.writeLine("present={present}")let absent = makeMap().get(makeKey("missing")) ?? makeDefault()io.writeLine("absent={absent}")let inner: Option<int> = None()let nested = map { "present": inner }let payload: Option<int> = nested.get("present") ?? Some(99)io.writeLine("inner={payload ?? 77}")
map key present=7 map key fallback absent=99 inner=77
Expected result
Expected result
Execution completes. Exit code 0.
mapkeypresent=7mapkeyfallbackabsent=99inner=77
Source evidence
- mapMemberType at
271329b482bc· 871-890 - emitBinary at
271329b482bc· 483-503
Preserve eager fallback effects with ordered bindings
Linkimport std.iofunction makeMap() -> Map<string, int> {io.writeLine("map")map { "present": 7 }}function makeKey(name: string) -> string {io.writeLine("key")name}function makeDefault() -> int {io.writeLine("fallback")99}let receiver = makeMap()let key = makeKey("present")let default = makeDefault()let present = receiver.get(key) ?? defaultio.writeLine("present={present}")let absent = receiver.get("missing") ?? defaultio.writeLine("absent={absent}")
map key fallback present=7 absent=99
Expected result
Expected result
Execution completes. Exit code 0.
mapkeyfallbackpresent=7absent=99
Source evidence
- mapMemberType at
271329b482bc· 871-890 - emitBinary at
271329b482bc· 483-503
Use the empty structural type in aliases, parameters, and results
Linktype Empty = {}function keep(value: Empty) -> {} { value }
No stdout.
Expected result
Expected result
Checking accepts the program. Exit code 0.
Source evidence
- pull/41 at
271329b482bc - parseConcurrentArms rejects zero arms at
271329b482bc· 801-841 - inferConcurrentExpression constructs structural(names, values) at
271329b482bc· 601-681 - ConcurrentJoin returns a record when no pending arms remain at
271329b482bc· 135-170 - empty pending collection skips the expired-deadline branch at
271329b482bc· 240-270 - issues/31 at
570530e69141 - parseRecordType currently rejects zero fields at
271329b482bc· 145-184 - unit is primitive and the zero-field structural record is a distinct kind at
271329b482bc· 111-159 - assignable at
af88afbc1d99· 388-407 - structural at
af88afbc1d99· 632-675 - formRecordType at
af88afbc1d99· 298-341 - reportExpectedType diagnoses the initializer expression at
271329b482bc· 422-475
An empty concurrent record passes through an explicit record signature
Linkimport std.iofunction keep(value: {}) -> {} { value }let joined: {} = keep(concurrent {})io.writeLine("completed")
completed
Expected result
Expected result
Execution completes. Exit code 0.
completed
Source evidence
- pull/41 at
271329b482bc - parseConcurrentArms rejects zero arms at
271329b482bc· 801-841 - inferConcurrentExpression constructs structural(names, values) at
271329b482bc· 601-681 - ConcurrentJoin returns a record when no pending arms remain at
271329b482bc· 135-170 - empty pending collection skips the expired-deadline branch at
271329b482bc· 240-270 - issues/31 at
570530e69141 - parseRecordType currently rejects zero fields at
271329b482bc· 145-184 - unit is primitive and the zero-field structural record is a distinct kind at
271329b482bc· 111-159 - assignable at
af88afbc1d99· 388-407 - structural at
af88afbc1d99· 632-675 - formRecordType at
af88afbc1d99· 298-341 - reportExpectedType diagnoses the initializer expression at
271329b482bc· 422-475
The zero-field record is distinct from unit
Linklet joined: () = concurrent {}
No stdout.
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001concurrent-empty-result-is-not-unit atexample.tpz:1:18
Source evidence
- pull/41 at
271329b482bc - parseConcurrentArms rejects zero arms at
271329b482bc· 801-841 - inferConcurrentExpression constructs structural(names, values) at
271329b482bc· 601-681 - ConcurrentJoin returns a record when no pending arms remain at
271329b482bc· 135-170 - empty pending collection skips the expired-deadline branch at
271329b482bc· 240-270 - issues/31 at
570530e69141 - parseRecordType currently rejects zero fields at
271329b482bc· 145-184 - unit is primitive and the zero-field structural record is a distinct kind at
271329b482bc· 111-159 - assignable at
af88afbc1d99· 388-407 - structural at
af88afbc1d99· 632-675 - formRecordType at
af88afbc1d99· 298-341 - reportExpectedType diagnoses the initializer expression at
271329b482bc· 422-475