Skip to content
Topaz6.0 · Before release
Download

Search documentation

Specification 6.0 · Not released · Tested product 5.21.3

Use arrow keys to choose a result. Press Enter to open.

Type an API name, diagnostic number, rule, or project topic.

    Language · chapter 05 · 14 rules

    Expressions and operators

    Defines expression formation, grouping, and operator behavior.

    Scope and ownership

    Link

    This page defines Topaz 6.0 expressions. Examples run against public Topaz 5.21.3 and keep the expected 6.0 results. Source evidence identifies implementation behavior. Each example records execution separately.

    This page owns expression grouping, operators, ranges, and member and index access. It also specifies the retirement of pipeline, composition, and expression-placeholder syntax. Functions and generics owns call argument binding. Collections and comprehensions owns collection construction. Type and library entries own individual members. Pattern matching owns wildcard patterns. Failure and propagation owns postfix ? behavior.

    Selected source forms

    Link
    EBNF
    1. Grouped = "(" Expression ")" ;
    2. Unary = ("-" | "!") Unary | Power ;
    3. Power = Postfix ["**" Unary] ;
    4. RangeShape = Additive (".." | "..<") Additive ["by" Additive] ;
    5. IndexSuffix = "[" Expression "]" ;
    6. MemberSuffix = "." MemberName ;
    7. OptionalMemberSuffix = "?." MemberName ;

    This handwritten excerpt shows selected source forms. The precedence record supplies the grouping between operator families. It omits call argument lists, collection construction, and other page-owned syntax; parsing alone does not establish operand legality.

    Source evidence
    Ruletopaz.rule.expressions-and-operators.grouping

    Grouping and postfix forms

    Empty parentheses form the unit literal (). Parentheses around one expression explicitly group that expression. A parenthesized comma list is rejected during parsing with TPZ2001. Postfix extensions attach to the preceding expression in source order.

    Parentheses select a grouping for an expression. The unit literal () is documented in Literals and templates. Function argument binding, collection construction, propagation, and nominal updates keep their own owners; their suffixes appear here only to locate their grouping.

    Why

    Explicit grouping makes operand relationships visible. An ordered postfix chain gives each extension one receiver.

    Source evidence

    Explicit control operands

    A control operand has a required outer pair of parentheses. The construct consumes that pair and parses its contents with the ordinary expression grammar. Record construction, record update, and lambdas keep the same forms they have in a binding or call argument. Their result types are checked separately.

    PositionCanonical form
    if testif (test) { body }
    if-let valueif let Pattern = (value) { body }
    while testwhile (test) { body }
    while-let valuewhile let Pattern = (value) { body }
    for iterablefor Pattern in (iterable) { body }
    using valueusing name = (value) { body }
    match scrutineematch (value) { cases }
    match guardcase Pattern if (test) => body
    comprehension iterable[for Pattern in (iterable) => value]
    comprehension filter[for Pattern in (iterable) if (test) => value]

    Omitting the required pair is rejected during parsing with TPZ2001. Extra parentheses inside the pair retain ordinary grouping. The existing concurrent(timeout: expression) argument list already delimits its timeout expression. A loop has no operand.

    Why

    An undelimited input and the same input inside the required parentheses supply the same condition, matched value, iteration source, or resource. The boundary preserves that intent and lets each position use ordinary expression grammar. Record construction and updates retain their existing forms, and lambdas retain one parenthesized parameter list. Parentheses inside an operand group that expression. The explicit boundary prevents a record body or a lambda arrow from taking the place of the following control body or case arrow.

    Source evidence

    Precedence and associativity

    Link
    Ruletopaz.rule.expressions-and-operators.precedence

    Precedence and associativity

    Higher rows bind more tightly. These rows specify parser grouping. Operand-type acceptance is checked separately.

    RankFormsAssociationBinary parser level
    1call suffix, index suffix, member suffix, optional-member suffix, propagation suffix, record-update suffixSuccessive postfix extensionsSeparate parser production
    2**RightSeparate parser production
    3prefix -, !Recursive prefixSeparate parser production
    4*, /, %Left9
    5+, -Left8
    6.., ..<Left7
    7==, !=, <, <=, >, >=, inLeft6
    8&&Left5
    9\|\|Left4
    10??Left3

    A prefix on the left applies outside power. The right operand of ** admits unary syntax. The range row accepts the by step clause.

    ExpressionGrouping
    -2 ** 2-(2 ** 2)
    2 ** 3 ** 22 ** (3 ** 2)
    a ?? b ?? c(a ?? b) ?? c

    The structured table describes grouping. A parsed combination can still fail type checking. Equality, ordering, and in share one level, and ?? groups to the left.

    Power accepts a unary expression on its right. Thus -2 ** 2 groups as -(2 ** 2), while 2 ** -1 parses with a negative exponent. The integer exponent restriction is a separate runtime condition.

    Why

    A fixed precedence table makes expression grouping independent of whitespace. The same grouping applies before operand types are checked.

    Source evidence

    Unary, binary, and logical operators

    Link
    Ruletopaz.rule.expressions-and-operators.unary

    Unary operators

    OperatorsOperand typesResultOperation
    !boolboolLogical negation
    -int, float, BigInt, DecimalOperand type- negates the value

    Prefix + is rejected during parsing with TPZ2001. Use the operand directly. Binary + remains addition.

    Literal sign rules are defined by Literals and templates. Other operand families remain Pending.

    Literal bounds are covered by Literals and templates. This rule covers operators applied to values. Unary minus negates a value and does not introduce another numeric type.

    Why

    An ordinary expression already denotes its value, so an identity prefix would add another spelling. Numeric negation preserves the operand type.

    Source evidence
    Ruletopaz.rule.expressions-and-operators.arithmetic

    Arithmetic operands and results

    The following operand pairs produce the listed result types.

    OperatorsLeft operandRight operandResultOperation
    +, -, *, /, **intintintArithmetic
    +, -, *, /, **floatfloatfloatArithmetic
    %intintintArithmetic
    +stringstringstringConcatenation
    +, -, *BigIntBigIntBigIntArithmetic
    +, -, *DecimalDecimalDecimalArithmetic

    Mixed int/float operands are rejected during checking. Integer and Length value boundaries are specified below.

    Arithmetic examples use small integers so grouping is visible independently of overflow boundaries. BigInt and Decimal division APIs belong to their library entries. The Types and Literals pages own type forms, literal bounds, and units.

    The Decimal value contract defines exact decimal arithmetic and canonical scale. Explicit BigInt division and remainder follow the BigInt contract. Duration arithmetic and range failures follow the Duration value rule.

    Evaluation follows the operand and assignment order.

    Why

    Requiring matching numeric types avoids implicit changes in precision. Explicit result types let arithmetic participate in the same type checks as other expressions.

    Source evidence

    Integer and Length value boundaries

    Integer arithmetic stays within -9223372036854775808 through 9223372036854775807. Division truncates toward zero. A nonzero remainder has the dividend's sign. Integer 0 ** 0 is 1.

    BoundaryRuntime diagnostic
    Integer or Length division by zero, or integer remainder by zeroTPZ4002
    Integer overflow, including minimum int divided or reduced modulo -1TPZ4004
    Integer power with a negative exponentTPZ4005
    Integer exponent greater than 4294967295, even with base 0 or 1TPZ4004
    Unrepresentable Length result or a non-finite scaling operandTPZ4004

    Length operations use the following operand pairs. U denotes the same unit on both Length operands. Mixed units are rejected during checking.

    OperatorsLeftRightResult
    +, -Length<U>Length<U>Length<U>
    /Length<U>Length<U>float
    *Length<U>int-or-floatLength<U>
    *int-or-floatLength<U>Length<U>
    /Length<U>intLength<U>

    Length multiplication and integer division round to the nearest representable fixed-precision value, with ties choosing the even value. Dividing two Length values gives their floating-point ratio.

    When a constant initializer reaches a numeric fault, checking rejects it with TPZ5001 and a message beginning constant expression error:.

    The physical length contract defines stored scaled points and explicit unit conversions.

    Why

    Checked arithmetic prevents silent wraparound in computed sizes. Matching length units prevents accidental mixing of document measurements. Ties-to-even rounding avoids a consistent bias at exact halfway values.

    Source evidence

    Comparison and membership

    ==, != compare equality, and <, <=, >, >= compare order. The operands must have compatible, comparable types. These operations produce bool. Comparison capabilities are defined by Protocols and implementations. A declared nominal Eq or Order conformance supplies both its protocol-qualified method and the corresponding operators. Nested derived comparisons use that same conformance.

    Write element membership as element in container. in produces bool for Array<T>, Set<T>, Range<int>. The value must be compatible with the container's element type. Range membership requires both bounds and step alignment. Map, string are rejected as membership containers during checking.

    Substring search and Map key lookup follow their distinct library contracts.

    The comparison operators produce Boolean results after the relevant type and capability checks. General compatibility and protocol resolution are documented by Types and Protocols and implementations. For a range, membership also considers its step.

    The Decimal comparison contract defines exact equality and ordering independently of input padding.

    Why

    Set.contains and in ask the same question about whether an equal element belongs to a set. The in operator expresses that membership intent for arrays, sets, and integer ranges. Range membership includes step alignment so it agrees with iteration. Substring search and Map key lookup retain their distinct input and result contracts.

    Source evidence

    Logical operators and coalescing

    The left operand is evaluated first. The following cases skip the right operand and produce the indicated value. Otherwise, the right operand is evaluated and supplies the result.

    OperatorLeft typeRight typeSkip right when left isResult when skippedEvaluate right when left is
    &&boolboolfalsefalsetrue
    \|\|boolbooltruetruefalse
    ??Option<T>TSome(value)valueNone()
    ??T \| nullTnon-nullleftnull

    The short-circuit decision follows evaluation of the left operand. In the example, any call to mark would print an extra line; that line is absent from the independent expected output.

    Why

    Skipping an unused operand avoids its effects and failures. This lets a guard or fallback depend on the value already computed.

    Source evidence

    Derived equality and operators use the inner manual equality

    Link
    example.tpzDerived equality and operators use the inner manual equality
    1. import std.io { writeLine }
    2. record Token { key: int, noise: int }
    3. impl Eq<Token> {
    4. function equals(a: Token, b: Token) -> bool { a.key == b.key }
    5. }
    6. record Box derives Eq { item: Token }
    7. let a = Box { item: Token { key: 1, noise: 10 } }
    8. let b = Box { item: Token { key: 1, noise: 20 } }
    9. let c = Box { item: Token { key: 2, noise: 10 } }
    10. writeLine("{Eq.equals(a, b)}")
    11. writeLine("{a == b}")
    12. writeLine("{Eq.equals(a, c)}")
    13. writeLine("{a != c}")
    Expected outputexit 0
    true
    true
    false
    true
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. true
    2. true
    3. false
    4. true
    Source evidence

    A manual equality hides an opaque field from outer operators

    Link
    example.tpzA manual equality hides an opaque field from outer operators
    1. import std.io { writeLine }
    2. record Callback { key: int, run: () -> int }
    3. impl Eq<Callback> {
    4. function equals(a: Callback, b: Callback) -> bool { a.key == b.key }
    5. }
    6. record Box derives Eq { item: Callback }
    7. let a = Box { item: Callback { key: 1, run: () => 7 } }
    8. let b = Box { item: Callback { key: 1, run: () => 9 } }
    9. writeLine("{a == b}")
    10. writeLine("{Eq.equals(a, b)}")
    Expected outputexit 0
    true
    true
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. true
    2. true
    Source evidence

    Derived ordering and operators use the inner manual ordering

    Link
    example.tpzDerived ordering and operators use the inner manual ordering
    1. import std.io { writeLine }
    2. record Rank { score: int }
    3. impl Order<Rank> {
    4. function compare(a: Rank, b: Rank) -> int {
    5. if (a.score > b.score) { -1 } else if (a.score < b.score) { 1 } else { 0 }
    6. }
    7. }
    8. record Box derives Order { item: Rank }
    9. let a = Box { item: Rank { score: 9 } }
    10. let b = Box { item: Rank { score: 3 } }
    11. writeLine("{Order.compare(a, b)}")
    12. writeLine("{a < b}")
    13. writeLine("{Order.compare(b, a)}")
    14. writeLine("{b > a}")
    Expected outputexit 0
    -1
    true
    1
    true
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. -1
    2. true
    3. 1
    4. true
    Source evidence

    Operand and assignment evaluation order

    Binary expressions evaluate their operands from left to right. The same order applies within nested operands. The first runtime fault terminates evaluation, so later operands do not run. The short-circuit rule still determines whether a right operand runs.

    Assignment formEvaluation order
    x = value()Evaluate the right value, then write x.
    a[index()] = value()Evaluate the receiver, evaluate the index, validate the slot, evaluate the right value, then write the slot.
    record.field = value()Evaluate the right value, then read the named record root, update the field, and write the root.
    a[index()].field = value()Evaluate the receiver and index, validate the slot, evaluate the right value, then read the record, update the field, and write the slot.
    x += value() and other compound arithmetic assignmentsEvaluate the target and read its old value, evaluate the right value, apply the operation, then write the result.

    A fault in an earlier assignment step prevents later steps. Assignment does not give a named record field a read before its right-hand value.

    The short-circuit rule determines which operands run. Function calls keep their argument evaluation order.

    Why

    One operand order makes effects and the first fault reproducible. Existing assignment forms retain their evaluation order.

    Source evidence

    Ranges and access

    Link
    Ruletopaz.rule.expressions-and-operators.range

    Ranges and steps

    Both endpoints are required and have type int. The result has type Range<int>.

    OperatorUpper bound
    ..inclusive
    ..<exclusive

    The by clause supplies an int step. Its default is 1. The sequence begins at the first endpoint and advances by adding the step. A positive step advances toward an increasing end value. A negative step advances toward a decreasing end value. Bounds in the wrong direction produce an empty range.

    Membership requires bounds and step alignment. A zero step is invalid. A statically detected zero step is rejected during checking. A zero step that reaches evaluation causes a runtime fault. Iteration is defined by Blocks and control flow.

    The by clause is part of the range expression. An inclusive upper bound is included only if the sequence reaches it. Blocks and control flow describes how for consumes a range; collection construction is documented separately.

    Why

    Explicit bounds and steps define the visited integers without materializing an array. Membership and iteration use the same sequence.

    Source evidence
    Ruletopaz.rule.expressions-and-operators.access

    Member, index, and optional access

    object.member follows the selected type or library entry's member contract. array[index] accepts an int index into Array<T> and produces T. Indices start at 0. Other index types are rejected during checking. Negative or out-of-bounds indices cause a runtime fault.

    object?.member accepts Option<T>, T | null.

    ReceiverMember access and result
    None()Skip access and produce None()
    nullSkip access and produce null
    Some(value)Access the wrapped value and produce one Option layer, flattening an existing Option result
    non-null TAccess the value and produce the member result
    Receiver typeResult type
    Option<T>Option<member-type> with one-layer flattening
    T \| nullmember-type \| null

    A member name selects a field or member whose individual contract belongs to its type or library entry. The access example uses an existing array's length property only to show optional access. Array construction, mutation, and the property's API contract are outside this page.

    Evaluation follows the operand and assignment order.

    Why

    Optional access skips a member lookup when its receiver is absent. Bounds checks prevent an invalid array position from producing an ordinary element value.

    Source evidence

    Choose Unicode scalars or grapheme clusters explicitly

    text.scalars() returns Array<string> in source order. Each element contains one Unicode scalar value. Empty text returns an empty array. The operation preserves the stored scalars without normalization. Joining its elements with an empty separator reproduces the original string.

    text.graphemes() follows the Unicode grapheme contract. Use the returned array's length and indexing operations for the chosen text unit.

    Loop inputs follow the iteration rule. Choose scalar values or grapheme clusters explicitly before iterating over text.

    Why

    A scalar and a grapheme cluster are different text units. Explicit names preserve scalar algorithms while allowing document editing to keep combining marks, Hangul syllables, and emoji sequences together. Unicode 16 matches the language character data and gives every host the same boundaries. Returning an Array reuses its count and index operations without additional string spellings.

    Source evidence

    Calls and function composition

    Link
    Ruletopaz.rule.expressions-and-operators.pipeline

    Ordinary calls and saved values

    Write calls as callee(arguments) and members as value.member. Bind an intermediate value with let when it must be evaluated once or reused.

    Method chains and successive let bindings read from the input through its transformations in order.

    |> is rejected during parsing with TPZ2001. This includes bare-callable, inserted-argument, placeholder, and member-shortcut stages. A standalone expression _ is also rejected during parsing.

    When migrating a pipeline with effects, bind the input first, then evaluate the callee and written arguments in their required order. The resulting ordinary call follows Functions and generics.

    Use a named binding when one computed value is needed more than once. A wildcard _ belongs to a pattern and has no expression meaning. Ordinary calls use the argument binding rules in Functions and generics.

    Why

    A pipeline and a method chain both present a value followed by the transformations that consume it. When the transformations are ordinary functions, successive let bindings present the same input-to-result reading order. These forms preserve the intended data flow and make evaluation and reuse explicit. Written call arguments keep each consumed value visible. Bind effectful input, callee, and argument expressions in their required order when replacing a pipeline.

    Source evidence
    Ruletopaz.rule.expressions-and-operators.composition

    Compose calls in a function body

    Compose calls in a function body, for example (value: T) => second(first(value)). Input, intermediate, and result types follow ordinary function and call rules.

    The body can name successive results with let in data-flow order.

    The >> composition operator is rejected during parsing with TPZ2001. Bind function values before constructing the lambda when their evaluation or capture must happen once.

    To preserve composition-time evaluation, first bind each function value once and in the desired order. Capture those immutable bindings in the function body. Later calls then reuse the same function values.

    Why

    A composition operator and a function body both describe a transformation whose result becomes the next transformation input. Successive let bindings inside the body read in that same data-flow order, and ordinary call typing preserves the intermediate types. Binding function values before constructing the body preserves evaluation and capture that must happen once.

    Source evidence

    Examples and expected results

    Link

    Each example is a complete source file with an independent expected result and an assertion plan. No execution receipt is attached. Missing boundary coverage remains explicit; a future observation that differs from an intended result requires triage and an implementation limitation when appropriate, rather than replacement of the expectation.

    Grouping arithmetic and power

    Link
    example.tpzGrouping arithmetic and powerOpen in Playground
    1. import std.io
    2. io.writeLine("{2 + 3 * 4}")
    3. io.writeLine("{(2 + 3) * 4}")
    4. io.writeLine("{20 - 5 - 3}")
    5. io.writeLine("{2 ** 3 ** 2}")
    6. io.writeLine("{-2 ** 2}")
    7. io.writeLine("{(-2) ** 2}")
    Recorded outputexit 0
    14
    20
    12
    512
    -4
    4
    
    $ topaz run example.tpz --compiler self · recorded with topaz 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 14
    2. 20
    3. 12
    4. 512
    5. -4
    6. 4
    Observed assertions · passed · 5.21.3

    Exit code 0.

    Observed stdout
    1. 14
    2. 20
    3. 12
    4. 512
    5. -4
    6. 4

    No stderr.

    • Passed · expected 0; observed 0
    • Passed · stdout matches the authored expectation
    • Passed · stderr matches the authored expectation
    • Passed · stdout matches the authored expectation
    • Passed · stdout matches the authored expectation

    The expected result is specified from the source contract. This exact fixture has not been run on a public Topaz 5.21.x CLI.

    Source evidence

    Skipping a right operand

    Link
    example.tpzSkipping a right operandOpen in Playground
    1. import std.io
    2. function mark() -> bool {
    3. io.writeLine("evaluated")
    4. true
    5. }
    6. io.writeLine("{false && mark()}")
    7. io.writeLine("{true || mark()}")
    8. let present: Option<int> = Some(7)
    9. let absent: Option<int> = None()
    10. io.writeLine("{present ?? 9}")
    11. io.writeLine("{absent ?? 9}")
    Recorded outputexit 0
    false
    true
    7
    9
    
    $ topaz run example.tpz --compiler self · recorded with topaz 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. false
    2. true
    3. 7
    4. 9
    Observed assertions · passed · 5.21.3

    Exit code 0.

    Observed stdout
    1. false
    2. true
    3. 7
    4. 9

    No stderr.

    • Passed · expected 0; observed 0
    • Passed · stdout matches the authored expectation
    • Passed · stderr matches the authored expectation

    The expected result is specified from the source contract. This exact fixture has not been run on a public Topaz 5.21.x CLI.

    Source evidence

    Apply ordinary calls and a function body

    Link
    example.tpzApply ordinary calls and a function bodyOpen in Playground
    1. import std.io
    2. function increment(n: int) -> int { n + 1 }
    3. function double(n: int) -> int { n * 2 }
    4. function subtract(a: int, b: int) -> int { a - b }
    5. let composed = (n: int) => double(increment(n))
    6. io.writeLine("{double(increment(3))}")
    7. io.writeLine("{composed(3)}")
    8. io.writeLine("{subtract(10, 3)}")
    9. io.writeLine("{subtract(3, 10)}")
    Expected outputexit 0
    8
    8
    7
    -7
    
    $ topaz run example.tpz --compiler self
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 8
    2. 8
    3. 7
    4. -7

    This fixture uses ordinary calls and a lambda. Its expected values are unchanged. Product execution of this revised fixture remains pending.

    Source evidence

    Membership respects a range's step

    Link
    example.tpzMembership respects a range's stepOpen in Playground
    1. import std.io
    2. let odd = 1..<6 by 2
    3. io.writeLine("{1 in odd}")
    4. io.writeLine("{2 in odd}")
    5. io.writeLine("{5 in odd}")
    6. io.writeLine("{6 in odd}")
    Recorded outputexit 0
    true
    false
    true
    false
    
    $ topaz run example.tpz --compiler self · recorded with topaz 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. true
    2. false
    3. true
    4. false
    Observed assertions · passed · 5.21.3

    Exit code 0.

    Observed stdout
    1. true
    2. false
    3. true
    4. false

    No stderr.

    • Passed · expected 0; observed 0
    • Passed · stdout matches the authored expectation
    • Passed · stderr matches the authored expectation
    • Passed · stdout matches the authored expectation

    The expected result is specified from the source contract. This exact fixture has not been run on a public Topaz 5.21.x CLI.

    Source evidence

    Indexing and optional member access

    Link
    example.tpzIndexing and optional member accessOpen in Playground
    1. import std.io
    2. let values = [4, 5]
    3. let present: Option<Array<int>> = Some(values)
    4. let absent: Option<Array<int>> = None()
    5. io.writeLine("{values[0]}")
    6. io.writeLine("{present?.length ?? 0}")
    7. io.writeLine("{absent?.length ?? 0}")
    Recorded outputexit 0
    4
    2
    0
    
    $ topaz run example.tpz --compiler self · recorded with topaz 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 4
    2. 2
    3. 0
    Observed assertions · passed · 5.21.3

    Exit code 0.

    Observed stdout
    1. 4
    2. 2
    3. 0

    No stderr.

    • Passed · expected 0; observed 0
    • Passed · stdout matches the authored expectation
    • Passed · stderr matches the authored expectation

    The expected result is specified from the source contract. This exact fixture has not been run on a public Topaz 5.21.x CLI.

    Source evidence

    Rejecting a parenthesized comma list

    Link
    example.tpzRejecting a parenthesized comma listOpen in Playground
    1. let value = (1, 2)
    Recorded outputexit 1
    No stdout.
    error[TPZ2001]: parentheses group a single expression; `(a, b)` comma lists are not Topaz syntax
     --> example.tpz:1:15
      |
    1 | let value = (1, 2)
      |               ^
    
    example.tpz: 1 diagnostic
    
    $ topaz check example.tpz --compiler self · recorded with topaz 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 parenthesized-comma-list at example.tpz:1:15
    Observed assertions · passed · 5.21.3

    Exit code 1.

    No stdout.

    Observed stderr
    1. error[TPZ2001]: parentheses group a single expression; `(a, b)` comma lists are not Topaz syntax
    2. --> example.tpz:1:15
    3. |
    4. 1 | let value = (1, 2)
    5. | ^
    6. example.tpz: 1 diagnostic
    • Passed · expected nonzero; observed 1
    • Passed · checking TPZ2001; cause parenthesized-comma-list; primary location asserted; additional diagnostics allow

    This fixture expects rejection at the comma because parentheses group a single expression. It is run through check so parsing is reached, and requires the parsing diagnostic and its location. The exact public CLI observation is pending.

    Source evidence

    Signed division, zero power, and Length arithmetic

    Link
    example.tpzSigned division, zero power, and Length arithmeticOpen in Playground
    1. import std.io
    2. let quotient = -7 / 3
    3. let remainder = -7 % 3
    4. let zeroPower = 0 ** 0
    5. io.writeLine("{quotient} {remainder} {zeroPower}")
    6. io.writeLine("{(1pt + 2pt) == 3pt}")
    7. io.writeLine("{(1pt * 2) == 2pt}")
    8. io.writeLine("{(2 * 1pt) == 2pt}")
    9. io.writeLine("{(1pt / 2) == 0.5pt}")
    10. io.writeLine("{3pt / 2pt}")
    Expected outputexit 0
    -2 -1 1
    true
    true
    true
    true
    1.5
    
    $ topaz run example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. -2 -1 1
    2. true
    3. true
    4. true
    5. true
    6. 1.5

    The expected result follows from current SELF source. Execution against public Topaz 5.21.3 is pending CI.

    Source evidence

    Integer division by zero is rejected in a constant

    Link
    example.tpzInteger division by zero is rejected in a constantOpen in Playground
    1. const bad = 1 / 0
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5001 constant-integer-division-zero at example.tpz:1:13

    The expected result follows from current SELF source. Execution against public Topaz 5.21.3 is pending CI.

    Source evidence

    An overflowing integer constant is rejected

    Link
    example.tpzAn overflowing integer constant is rejectedOpen in Playground
    1. const bad = 9223372036854775807 + 1
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5001 constant-integer-addition-overflow at example.tpz:1:13

    The expected result follows from current SELF source. Execution against public Topaz 5.21.3 is pending CI.

    Source evidence

    An integer exponent must be non-negative

    Link
    example.tpzAn integer exponent must be non-negativeOpen in Playground
    1. const bad = 2 ** -1
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5001 constant-integer-negative-exponent at example.tpz:1:13

    The expected result follows from current SELF source. Execution against public Topaz 5.21.3 is pending CI.

    Source evidence

    Length division by zero is rejected in a constant

    Link
    example.tpzLength division by zero is rejected in a constantOpen in Playground
    1. const bad = 1pt / 0
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5001 constant-length-division-zero at example.tpz:1:13

    The expected result follows from current SELF source. Execution against public Topaz 5.21.3 is pending CI.

    Source evidence

    An unrepresentable Length constant is rejected

    Link
    example.tpzAn unrepresentable Length constant is rejectedOpen in Playground
    1. const bad = 1pt * 9223372036854775807
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5001 constant-length-unrepresentable at example.tpz:1:13

    The expected result follows from current SELF source. Execution against public Topaz 5.21.3 is pending CI.

    Source evidence

    Rejecting unary plus before an integer

    Link
    example.tpzRejecting unary plus before an integerOpen in Playground
    1. let value = +1
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self --format json
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 removed-unary-plus at example.tpz:1:13

    Write 1 to use the integer value.

    Source evidence

    Rejecting unary plus before a bound value

    Link
    example.tpzRejecting unary plus before a bound valueOpen in Playground
    1. let value = 1
    2. let copy = +value
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self --format json
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 removed-unary-plus at example.tpz:2:12

    Use the bound value directly.

    Source evidence

    Rejecting unary plus before a float

    Link
    example.tpzRejecting unary plus before a floatOpen in Playground
    1. let value = +1.5
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self --format json
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 removed-unary-plus at example.tpz:1:13

    Write 1.5 to use the floating value.

    Source evidence

    Using ordinary values, negation, and binary addition

    Link
    example.tpzUsing ordinary values, negation, and binary additionOpen in Playground
    1. import std.io
    2. let value = 3
    3. io.writeLine("{1}")
    4. io.writeLine("{1.5}")
    5. io.writeLine("{value}")
    6. io.writeLine("{-value}")
    7. io.writeLine("{value + 2}")
    Expected outputexit 0
    1
    1.5
    3
    -3
    5
    
    $ topaz run example.tpz --compiler self
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 1
    2. 1.5
    3. 3
    4. -3
    5. 5

    Ordinary values need no identity prefix. Unary minus and binary addition retain their arithmetic meanings.

    Source evidence

    Use in for arrays, sets, and ranges

    Link
    example.tpzUse in for arrays, sets, and rangesOpen in Playground
    1. import std.io
    2. let values = [1, 3]
    3. let members = set { 1, 3 }
    4. let odds = 1..<5 by 2
    5. io.writeLine("{1 in values}:{2 in values}")
    6. io.writeLine("{1 in members}:{2 in members}")
    7. io.writeLine("{1 in odds}:{2 in odds}")
    Expected outputexit 0
    true:false
    true:false
    true:false
    
    $ topaz run example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. true:false
    2. true:false
    3. true:false
    Source evidence

    A Map is not an element-membership container

    Link
    example.tpzA Map is not an element-membership containerOpen in Playground
    1. let found = "key" in map { "key": 1 }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    The required primary-location contract is Pending.

    Source evidence

    Substring search is not element membership

    Link
    example.tpzSubstring search is not element membershipOpen in Playground
    1. let found = "a" in "abc"
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5001 string-not-element-membership

    The required primary-location contract is Pending.

    Source evidence

    Keep combining marks, Hangul, and emoji in their grapheme clusters

    Link
    example.tpzKeep combining marks, Hangul, and emoji in their grapheme clustersOpen in Playground
    1. import std.io
    2. let text = "é가👩‍💻🇰🇷"
    3. let clusters = text.graphemes()
    4. io.writeLine("{text.scalars().length}:{clusters.length}")
    5. io.writeLine("{clusters[0] == "é"}")
    6. io.writeLine("{clusters[1] == "가"}")
    7. io.writeLine("{clusters[2] == "👩‍💻"}")
    8. io.writeLine("{clusters[3] == "🇰🇷"}")
    9. io.writeLine("{clusters.join("") == text}")
    Expected outputexit 0
    9:4
    true
    true
    true
    true
    true
    
    $ topaz run example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 9:4
    2. true
    3. true
    4. true
    5. true
    6. true
    Source evidence

    Segment empty text, CRLF, leading marks, and regional indicators

    Link
    example.tpzSegment empty text, CRLF, leading marks, and regional indicatorsOpen in Playground
    1. import std.io
    2. let empty = "".graphemes()
    3. let lineBreak = "\r\n".graphemes()
    4. let marks = "́̂".graphemes()
    5. let flags = "🇰🇷🇯".graphemes()
    6. io.writeLine("{empty.length}:{lineBreak.length}:{marks.length}:{flags.length}")
    7. io.writeLine("{lineBreak[0] == "\r\n"}")
    8. io.writeLine("{marks[0].scalars().length}")
    9. io.writeLine("{flags[0] == "🇰🇷"}:{flags[1] == "🇯"}")
    Expected outputexit 0
    0:1:1:2
    true
    2
    true:true
    
    $ topaz run example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 0:1:1:2
    2. true
    3. 2
    4. true:true
    Source evidence

    Reject a pipeline into a bare callable

    Link
    example.tpzReject a pipeline into a bare callableOpen in Playground
    1. function increment(n: int) -> int { n + 1 }
    2. let value = 3 |> increment
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    The required primary-location contract is Pending.

    Source evidence

    Reject implicit pipeline argument insertion

    Link
    example.tpzReject implicit pipeline argument insertionOpen in Playground
    1. function add(a: int, b: int) -> int { a + b }
    2. let value = 3 |> add(4)
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    The required primary-location contract is Pending.

    Source evidence

    Reject a pipeline placeholder

    Link
    example.tpzReject a pipeline placeholderOpen in Playground
    1. function subtract(a: int, b: int) -> int { a - b }
    2. let value = 3 |> subtract(10, _)
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    The required primary-location contract is Pending.

    Source evidence

    Reject a pipeline member shortcut

    Link
    example.tpzReject a pipeline member shortcutOpen in Playground
    1. let item = { answer: 7 }
    2. let value = item |> .answer
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    The required primary-location contract is Pending.

    Source evidence

    Reject the composition operator

    Link
    example.tpzReject the composition operatorOpen in Playground
    1. function increment(n: int) -> int { n + 1 }
    2. function double(n: int) -> int { n * 2 }
    3. let composed = increment >> double
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    The required primary-location contract is Pending.

    Source evidence

    Reject a standalone expression placeholder

    Link
    example.tpzReject a standalone expression placeholderOpen in Playground
    1. let value = _
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 removed-expression-placeholder

    The required primary-location contract is Pending.

    Source evidence

    Save values to preserve call and composition effects

    Link
    example.tpzSave values to preserve call and composition effectsOpen in Playground
    1. import std.io
    2. function input() -> int {
    3. io.writeLine("input")
    4. 3
    5. }
    6. function subtract(a: int, b: int) -> int { a - b }
    7. function stage() -> (int, int) -> int {
    8. io.writeLine("callee")
    9. subtract
    10. }
    11. function argument() -> int {
    12. io.writeLine("argument")
    13. 10
    14. }
    15. let saved = input()
    16. let callee = stage()
    17. let written = argument()
    18. io.writeLine("{callee(written, saved)}")
    19. function increment(n: int) -> int { n + 1 }
    20. function double(n: int) -> int { n * 2 }
    21. function first() -> (int) -> int {
    22. io.writeLine("first")
    23. increment
    24. }
    25. function second() -> (int) -> int {
    26. io.writeLine("second")
    27. double
    28. }
    29. let left = first()
    30. let right = second()
    31. let composed = (n: int) => right(left(n))
    32. io.writeLine("{composed(3)}:{composed(5)}")
    Expected outputexit 0
    input
    callee
    argument
    7
    first
    second
    8:12
    
    $ topaz run example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. input
    2. callee
    3. argument
    4. 7
    5. first
    6. second
    7. 8:12
    Source evidence

    Reject an undelimited if operand

    Link
    example.tpzReject an undelimited if operandOpen in Playground
    1. let value = if true { 1 } else { 0 }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 control-operand-parentheses at example.tpz:1:16
    Source evidence

    Reject an undelimited if let operand

    Link
    example.tpzReject an undelimited if let operandOpen in Playground
    1. let value = if let Some(item) = Some(1) { item } else { 0 }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 control-operand-parentheses at example.tpz:1:33
    Source evidence

    Reject an undelimited while operand

    Link
    example.tpzReject an undelimited while operandOpen in Playground
    1. while false { () }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 control-operand-parentheses at example.tpz:1:7
    Source evidence

    Reject an undelimited while let operand

    Link
    example.tpzReject an undelimited while let operandOpen in Playground
    1. let mut value: Option<int> = None()
    2. while let Some(item) = value { value = None() }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 control-operand-parentheses at example.tpz:2:24
    Source evidence

    Reject an undelimited for operand

    Link
    example.tpzReject an undelimited for operandOpen in Playground
    1. for item in [1] { let _ = item }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 control-operand-parentheses at example.tpz:1:13
    Source evidence

    Reject an undelimited using operand

    Link
    example.tpzReject an undelimited using operandOpen in Playground
    1. function manage(file: File) -> () {
    2. using resource = file {}
    3. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 control-operand-parentheses at example.tpz:2:20
    Source evidence

    Reject an undelimited match operand

    Link
    example.tpzReject an undelimited match operandOpen in Playground
    1. let value = match 1 { case _ => 2 }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 control-operand-parentheses at example.tpz:1:19
    Source evidence

    Reject an undelimited match guard operand

    Link
    example.tpzReject an undelimited match guard operandOpen in Playground
    1. let value = match (1) {
    2. case 1 if true => 1
    3. case _ => 0
    4. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 control-operand-parentheses at example.tpz:2:13
    Source evidence

    Reject an undelimited comprehension iterable operand

    Link
    example.tpzReject an undelimited comprehension iterable operandOpen in Playground
    1. let values = [for item in [1] => item]
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 control-operand-parentheses at example.tpz:1:27
    Source evidence

    Reject an undelimited comprehension filter operand

    Link
    example.tpzReject an undelimited comprehension filter operandOpen in Playground
    1. let values = [for item in ([1]) if true => item]
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 control-operand-parentheses at example.tpz:1:36
    Source evidence

    Delimited tests, iterators, scrutinees, and guards

    Link
    example.tpzDelimited tests, iterators, scrutinees, and guardsOpen in Playground
    1. import std.io
    2. let mut count = 0
    3. if (true) { count += 1 }
    4. if let Some(value) = (Some(1)) { count += value }
    5. while (count < 3) { count += 1 }
    6. let mut pending: Option<int> = Some(1)
    7. while let Some(value) = (pending) { pending = None() }
    8. for value in ([1, 2]) { count += value }
    9. let chosen = match (count) {
    10. case 6 if (((flag: bool) => flag)(true)) => 6
    11. case _ => 0
    12. }
    13. let collected = [for value in ([1, 2]) if (((flag: bool) => flag)(value > 0)) => value]
    14. io.writeLine("{chosen}")
    15. io.writeLine("{collected.length}")
    Expected outputexit 0
    6
    2
    
    $ topaz run example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 6
    2. 2
    Source evidence

    Record construction and update use ordinary operand grammar

    Link
    example.tpzRecord construction and update use ordinary operand grammarOpen in Playground
    1. import std.io
    2. record Flag { enabled: bool }
    3. let state = { enabled: true }
    4. let first = if (Flag { enabled: true }.enabled) { 1 } else { 0 }
    5. let second = if (state { enabled: false }.enabled) { 1 } else { 2 }
    6. let third = match (Flag { enabled: true }) {
    7. case Flag { enabled: true } => true
    8. case _ => false
    9. }
    10. io.writeLine("{first},{second},{third}")
    Expected outputexit 0
    1,2,true
    
    $ topaz run example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 1,2,true
    Source evidence

    The using expression has the same required boundary

    Link
    example.tpzThe using expression has the same required boundaryOpen in Playground
    1. function manage(file: File) -> () {
    2. using resource = (file) {
    3. let handle: File = resource
    4. }
    5. }
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    Read transformations in data-flow order

    Link
    example.tpzRead transformations in data-flow orderOpen in Playground
    1. import std.io
    2. function increment(value: int) -> int { value + 1 }
    3. function double(value: int) -> int { value * 2 }
    4. function transform(value: int) -> int {
    5. let increased = increment(value)
    6. double(increased)
    7. }
    8. let values = [1, 2]
    9. let chained = values.map(increment).map(double)
    10. let increased = values.map(increment)
    11. let staged = increased.map(double)
    12. let composed = values.map(transform)
    13. io.writeLine("{chained[0]},{chained[1]}")
    14. io.writeLine("{staged[0]},{staged[1]}")
    15. io.writeLine("{composed[0]},{composed[1]}")
    Expected outputexit 0
    4,6
    4,6
    4,6
    
    $ topaz run example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 4,6
    2. 4,6
    3. 4,6
    Source evidence

    Binary and nested operands print from left to right

    Link
    example.tpzBinary and nested operands print from left to rightOpen in Playground
    1. import std.io
    2. function value(label: string, number: int) -> int {
    3. io.writeLine(label)
    4. number
    5. }
    6. let result = value("left", 1) + value("middle", 2) * value("right", 3)
    7. io.writeLine("{result}")
    Expected outputexit 0
    left
    middle
    right
    7
    
    $ topaz run example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. left
    2. middle
    3. right
    4. 7
    Source evidence

    The left operand faults before the right operand runs

    Link
    example.tpzThe left operand faults before the right operand runsOpen in Playground
    1. import std.io
    2. function left(divisor: int) -> int {
    3. io.writeLine("left")
    4. 1 / divisor
    5. }
    6. function right(number: int) -> int {
    7. io.writeLine("right")
    8. 9223372036854775807 + number
    9. }
    10. left(0) + right(1)
    Expected outputNonzero exit
    left
    
    $ topaz run example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Rejection during run. Nonzero exit.

    • TPZ4002 diagnostic-tpz4002 at example.tpz:4:5
    Expected stdout
    1. left

    Both calls would fault if evaluated. The left call produces TPZ4002, and the right call produces no output.

    Source evidence

    Index assignment evaluates the index before its value

    Link
    example.tpzIndex assignment evaluates the index before its valueOpen in Playground
    1. import std.io
    2. function index() -> int {
    3. io.writeLine("index")
    4. 0
    5. }
    6. function value() -> int {
    7. io.writeLine("value")
    8. 7
    9. }
    10. let mut values = [1]
    11. values[index()] = value()
    12. io.writeLine("{values[0]}")
    13. let mut number = 1
    14. number += value()
    15. io.writeLine("{number}")
    Expected outputexit 0
    index
    value
    7
    value
    8
    
    $ topaz run example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. index
    2. value
    3. 7
    4. value
    5. 8
    Source evidence

    Pending contracts and verification

    Link

    Related contracts and diagnostic

    Link

    Functions and generics

    Unexpected token

    TPZ5001

    Unknown member or field

    Division or remainder by zero

    Menu

    Download