Skip to content
CryoCryo home
LanguageErrors

26Error Codes

Every diagnostic the compiler reports carries a code: E followed by four digits for an error, W for a warning. The code is the stable name for the situation — messages get reworded, spans move, suggestions are added — so it is the thing to search for, and the thing this page is indexed by. A diagnostic looks like this:

error[E0214]: mismatched types
 --> src/main.cryo:6:15
   |
 6 |     takes_int("bad");
   |               ^~~~~ expected `i32`, found `string`
   |
 note: argument 1 doesn't match the parameter's declared type

Errors fail the build. Warnings do not, and each one names what would silence it. Codes are grouped by the phase of the compiler that reports them, which is also roughly the order the phases run in: a file with a lexical error never reaches the type checker, so fixing the first error in the output is usually the right place to start.

RangeCategoryCodes
E0001E0099Lexical analysis12 (3 reserved)
E0050E0069AST validation15
E0100E0199Syntax17 (6 reserved)
E0150E0169Directives4
E0154E0169Symbol resolution2
E0200E0399Type checking42 (14 reserved)
E0300E0349Generics and traits11 (5 reserved)
E0350E0399Structs and classes16 (4 reserved)
E0400E0449Control flow7 (2 reserved)
E0450E0499Memory and ownership8 (2 reserved)
E0500E0549Modules and imports6 (1 reserved)
E0600E0699Code generation46 (27 reserved)
E0700E0799Linking5 (5 reserved)
E0800E0899System and I/O7 (3 reserved)
E0900E0999Internal compiler errors10 (8 reserved)
W0001W9999Warnings15 (6 reserved)

Not every code in a range is in use. E0000 is the code a note: or help: line carries internally, and is never printed. The compiler's ErrorCode enum reserves numbers for situations it does not currently report — some describe checks that are planned, some belong to a design that was abandoned (the borrow-check codes E0450E0451 were removed outright, because Cryo has no borrow checker and a code that implied one was a promise the compiler does not make). Those are listed as reserved and have no entry; if you see one in real output, that is a bug worth reporting.

Every entry below is checked against the compiler. The example programs live in the site's repository; each sync from the compiler repository runs them through cryo check and records what the compiler printed, which is the output shown under each one. An example that stops producing its code fails the sync rather than going stale. Where the compiler's own test suite pins a code with a compile-fail test, the entry links to it — those tests are the authoritative statement of what the code means, and a good place to look when the entry here is not enough.

Lexical analysis

The lexer turns source text into tokens. Its errors are about characters and literals: a string that never closes, a number that does not fit, a suffix that contradicts the literal it is on.

CodeTitle
E0001Unexpected Character
E0002Unterminated String
E0003Unterminated Character
E0004Invalid Numberreserved
E0005Invalid Escape Sequencereserved
E0006Invalid Unicodereserved
E0007Invalid Hexadecimal
E0008Invalid Binary
E0009Invalid Octal
E0010Number Too Large
E0011Lexing Exception
E0012Unterminated Block Comment

E0001 Unexpected Character

A character the lexer has no token for. In practice most stray characters are reported by the parser instead, as an unexpected token (E0100); this code covers the ones the lexer cannot even begin a token from.

E0002 Unterminated String

A string literal (or an f"..." literal) is opened and never closed. The error is anchored where the lexer gave up looking — the end of the file, if nothing else closed it first — with a suggestion to close the literal.

function main() -> int {
    const greeting: string = "hello;
    return 0;
}
error[E0002]: Unterminated string literal
 --> main.cryo:5:1
   |
 3 |     return 0;
 4 | }
 5 | 
   | ^ Unterminated string literal
 6 | 
   |
 suggestion [machine-applicable]: close the literal with `"`
    | "

aborting due to 1 error

Pinned by E0002_unterminated_string.

E0003 Unterminated Character

A character literal holds exactly one codepoint. 'AB' is not a two-character char, and the lexer says so rather than guessing which quote you meant; use a string if you meant a string.

function main() -> int {
    const c: char = 'AB';
    return 0;
}
error[E0003]: character literal may only contain one codepoint
 --> main.cryo:2:25
   |
 1 | function main() -> int {
 2 |     const c: char = 'AB';
   |                         ^ character literal may only contain one codepoint
 3 |     return 0;
 4 | }
   |
 note: a `'...'` literal holds a single character; use double quotes for a string

aborting due to 1 error

Pinned by E0003_multichar_char_literal.

E0007 Invalid Hexadecimal

A hexadecimal literal (0x...) with no digits. A separator on its own (0x_) does not count as a digit, so it is refused rather than silently read as zero.

function main() -> int {
    const mask: i32 = 0x_;
    return 0;
}
error[E0007]: Invalid hexadecimal number
 --> main.cryo:2:26
   |
 1 | function main() -> int {
 2 |     const mask: i32 = 0x_;
   |                          ^ Invalid hexadecimal number
 3 |     return 0;
 4 | }
   |

aborting due to 1 error

Pinned by E0007_empty_hex_underscore.

E0008 Invalid Binary

A binary literal (0b...) with no digits, the same way.

function main() -> int {
    const bits: i32 = 0b_;
    return 0;
}
error[E0008]: Invalid binary number
 --> main.cryo:2:26
   |
 1 | function main() -> int {
 2 |     const bits: i32 = 0b_;
   |                          ^ Invalid binary number
 3 |     return 0;
 4 | }
   |

aborting due to 1 error

Pinned by E0008_empty_binary_underscore.

E0009 Invalid Octal

An octal literal (0o...) with no digits, the same way.

function main() -> int {
    const mode: i32 = 0o_;
    return 0;
}
error[E0009]: Invalid octal number
 --> main.cryo:2:26
   |
 1 | function main() -> int {
 2 |     const mode: i32 = 0o_;
   |                          ^ Invalid octal number
 3 |     return 0;
 4 | }
   |

aborting due to 1 error

Pinned by E0009_empty_octal_underscore.

E0010 Number Too Large

An integer literal that does not fit the type it has. The type comes from wherever the literal gets it — an annotation, a parameter, a suffix — and the bound is checked where the literal is written, so the message names the literal rather than reporting a mismatch somewhere downstream. There is no silent truncation: 300 into a u8 is an error, not 44.

function main() -> int {
    const x: u8 = 300;
    return 0;
}
error[E0010]: integer literal `300` is out of range for type `u8`
 --> main.cryo:2:19
   |
 1 | function main() -> int {
 2 |     const x: u8 = 300;
   |                   ^~~ integer literal `300` is out of range for type `u8`
 3 |     return 0;
 4 | }
   |

aborting due to 1 error

A suffix names the literal's type outright, so the bound is the suffix's even when the surrounding context would have admitted the value:

function main() -> int {
    // The bound is checked against the suffix the author wrote, not the
    // `i32` the binding would have admitted.
    const x: i32 = 300u8;
    return 0;
}
error[E0010]: integer literal `300u8` is out of range for type `u8`
 --> main.cryo:4:20
   |
 2 |     // The bound is checked against the suffix the author wrote, not the
 3 |     // `i32` the binding would have admitted.
 4 |     const x: i32 = 300u8;
   |                    ^~~~~ integer literal `300u8` is out of range for type `u8`
 5 |     return 0;
 6 | }
   |

aborting due to 1 error

A literal argument takes the parameter's type, and is checked against it the same way:

function set_alpha(a: u8) -> void {}

function main() -> int {
    set_alpha(300);
    return 0;
}
error[E0010]: integer literal `300` is out of range for type `u8`
 --> main.cryo:4:15
   |
 2 | 
 3 | function main() -> int {
 4 |     set_alpha(300);
   |               ^~~ integer literal `300` is out of range for type `u8`
 5 |     return 0;
 6 | }
   |

aborting due to 1 error

A fixed array's size is an integer literal too, and one that does not fit 64 bits is reported here rather than wrapping.

Pinned by E0010_array_size_too_large, E0010_generic_owner_arg_overflow, E0010_number_too_large, E0010_suffix_width_overflow, E0214_arg_literal_overflow, static_call_hint_leaf_collision.

E0011 Lexing Exception

A numeric literal that is malformed rather than merely out of range: an exponent with no digits (1e, 2e+), or an integer suffix on a literal that has a fractional part or exponent. Only f32 and f64 suffix a float.

function main() -> int {
    const ratio: f32 = 1.5u8;
    return 0;
}
error[E0011]: integer type suffix on a floating-point literal; use `f32`/`f64` or drop the suffix
 --> main.cryo:2:29
   |
 1 | function main() -> int {
 2 |     const ratio: f32 = 1.5u8;
   |                             ^ integer type suffix on a floating-point literal; use `f32`/`f64` or drop the suffix
 3 |     return 0;
 4 | }
   |

aborting due to 1 error
function main() -> int {
    const big: f64 = 1e;
    return 0;
}
error[E0011]: malformed float literal: the exponent has no digits
 --> main.cryo:2:24
   |
 1 | function main() -> int {
 2 |     const big: f64 = 1e;
   |                        ^ malformed float literal: the exponent has no digits
 3 |     return 0;
 4 | }
   |

aborting due to 1 error

Pinned by E0011_int_suffix_on_float, E0011_malformed_float_exponent.

E0012 Unterminated Block Comment

A /* block comment with no */ before the end of the file. Without the diagnostic the comment would swallow the rest of the file — including main — and the build would appear to succeed.

function main() -> int {
    return 0;
}

/* this comment is never closed, so it swallows the rest of the file
error[E0012]: Unterminated block comment (expected `*/` before end of file)
 --> main.cryo:6:1
   |
 4 | 
 5 | /* this comment is never closed, so it swallows the rest of the file
 6 | 
   | ^ Unterminated block comment (expected `*/` before end of file)
 7 | 
   |

aborting due to 1 error

Pinned by E0012_unterminated_block_comment.

AST validation

After parsing, the tree is checked for shapes that are well-formed as syntax but not as a program. Most of these codes guard the compiler against its own parser — a null node where a declaration should be — and a user will only see them if the parser has a bug. The two that describe something an author can write are below.

CodeTitle
E0050Null AST Node
E0051Missing Function Name
E0052Missing Type Name
E0053Null Parameter
E0054Null Field
E0055Null Method
E0056Method Missing Function
E0057Enum Has No Variants
E0058Null Enum Variant
E0059Null Block Statement
E0060Empty Program
E0061Type Alias Missing Target
E0062Impl Block Missing Target
E0063Import Missing Path
E0064Duplicate Enum Discriminant

E0054 Null Field

A union with no fields. A union is one-of-N storage; with N = 0 there is nothing to store.

type union Nothing { }

function main() -> int { return 0; }
error[E0054]: union 'Nothing' must declare at least one field
 --> main.cryo:1:1
   |
 1 | type union Nothing { }
   | ^~~~~~~~~~~~~~~~~~~~~~ union 'Nothing' must declare at least one field
 2 | 
 3 | function main() -> int { return 0; }
   |

aborting due to 1 error

E0057 Enum Has No Variants

An enum with no variants. There is no value of such a type, so nothing could ever be matched or constructed.

type enum Empty { }

function main() -> int { return 0; }
error[E0057]: enum 'Empty' has no variants
 --> main.cryo:1:1
   |
 1 | type enum Empty { }
   | ^~~~~~~~~~~~~~~~~~~ enum 'Empty' has no variants
 2 | 
 3 | function main() -> int { return 0; }
   |

aborting due to 1 error

E0064 Duplicate Enum Discriminant

Two variants of an enum share a discriminant, whether written explicitly or reached by counting on from an explicit one. The help names the fix: give one of them a unique tag.

type enum Level {
    Low  = 1;
    High = 1;
}

function main() -> int { return 0; }
error[E0064]: variant `High` shares discriminant 1 with `Low` in enum `Level`
 --> main.cryo:3:5
   |
 1 | type enum Level {
 2 |     Low  = 1;
 3 |     High = 1;
   |     ^~~~~~~~~ duplicate discriminant 1
 4 | }
   |
 help: give `High` a unique tag (e.g. `= 0`) or remove the conflicting `= 1` on the earlier variant

aborting due to 1 error

Pinned by E0064_duplicate_enum_discriminant.

Syntax

The parser's errors. E0100 and E0101 between them cover most of what a typo produces; the rest name a specific construct the parser recognised and refused.

CodeTitle
E0100Expected Token
E0101Unexpected Token
E0102Expected Expression
E0103Expected Statementreserved
E0104Expected Type
E0105Expected Identifier
E0106Expected Semicolonreserved
E0107Expected Parenthesisreserved
E0108Expected Brace
E0109Expected Bracketreserved
E0110Mismatched Delimitersreserved
E0111Invalid Syntax
E0112Unexpected End of File
E0113Invalid Pattern
E0114Duplicate Default
E0115Parse Recovery Failedreserved
E0116Parse Exception

E0100 Expected Token

The parser expected one token and found another. The message names both, and where the fix is a single insertion — a missing ;, most often — it comes with a machine-applicable suggestion.

function main() -> int {
    const x: i32 = 1
    return x;
}
error[E0100]: expected ';', found 'return'
 --> main.cryo:3:5
   |
 1 | function main() -> int {
 2 |     const x: i32 = 1
 3 |     return x;
   |     ^~~~~~ expected ';', found 'return'
 4 | }
   |
 suggestion [machine-applicable]: insert `;` here
   |
 2 |     const x: i32 = 1;
   |                     +

aborting due to 1 error

One place this catches people coming from other languages: enum variants are separated with ;, not ,.

// Variants are separated by `;`, not `,`.
type enum Shape { Circle(i32), Square }

function main() -> int { return 0; }
error[E0100]: expected ';', found ','
 --> main.cryo:2:30
   |
 1 | // Variants are separated by `;`, not `,`.
 2 | type enum Shape { Circle(i32), Square }
   |                              ^ expected ';', found ','
 3 | 
 4 | function main() -> int { return 0; }
   |
 suggestion [machine-applicable]: insert `;` here
   |
 2 | type enum Shape { Circle(i32);, Square }
   |                              +

error[E0100]: expected variant name, found ','
 --> main.cryo:2:30
   |
 1 | // Variants are separated by `;`, not `,`.
 2 | type enum Shape { Circle(i32), Square }
   |                              ^ expected variant name, found ','
 3 | 
 4 | function main() -> int { return 0; }
   |

error[E0100]: expected ';', found '}'
 --> main.cryo:2:39
   |
 1 | // Variants are separated by `;`, not `,`.
 2 | type enum Shape { Circle(i32), Square }
   |                                       ^ expected ';', found '}'
 3 | 
 4 | function main() -> int { return 0; }
   |
 suggestion [machine-applicable]: insert `;` here
   |
 2 | type enum Shape { Circle(i32), Square; }
   |                                      +

aborting due to 3 errors

Pinned by E0100_enum_comma_separator, E0100_missing_semicolon.

E0101 Unexpected Token

A token the parser did not expect at all. The one everyone hits once is ->: Cryo has no arrow operator, because . auto-dereferences, so ptr.field is the spelling whether ptr is a value or a pointer.

type struct Point { x: i32; y: i32; }

function main() -> int {
    const p: Point = Point { x: 3, y: 4 };
    const pp: Point* = &p;
    return pp->x;
}
error[E0101]: `->` is not a valid operator; use `.` for member access
 --> main.cryo:6:14
   |
 4 |     const p: Point = Point { x: 3, y: 4 };
 5 |     const pp: Point* = &p;
 6 |     return pp->x;
   |              ^~ `->` is not a valid operator; use `.` for member access
 7 | }
   |
 note: Cryo has no `->` operator: `.` auto-dereferences, so `foo.bar` resolves the member whether `foo` is a value or a pointer
 suggestion: replace `->` with `.`
   |
 6 |     return pp.x;
   |              ~

aborting due to 1 error

Pinned by E0101_arrow_member_access, inline_generic_bound.

E0102 Expected Expression

An expression was required and there is none — an initializer left empty, an operator with no right-hand side.

function main() -> int {
    const x: i32 = ;
    return 0;
}
error[E0102]: expected expression, found ';'
 --> main.cryo:2:20
   |
 1 | function main() -> int {
 2 |     const x: i32 = ;
   |                    ^ expected expression, found ';'
 3 |     return 0;
 4 | }
   |

aborting due to 1 error

Pinned by E0102_expected_expression.

E0104 Expected Type

A type was required and there is none, or what is there is not a type. Parameters need an annotation, and so does a binding with no initializer or one whose initializer produces no value.

function scale(factor: ) -> void {}

function main() -> int { return 0; }
error[E0104]: expected type, found ')'
 --> main.cryo:1:24
   |
 1 | function scale(factor: ) -> void {}
   |                        ^ expected type, found ')'
 2 | 
 3 | function main() -> int { return 0; }
   |

aborting due to 1 error

Lambda parameters are not inferred, so each one needs its type written:

function apply(f: (i32) -> i32, v: i32) -> i32 {
    return f(v);
}

function main() -> int {
    // Lambda parameters are not inferred; each needs an annotation.
    return apply((x) -> { return x + 1; }, 5);
}
error[E0104]: lambda parameters need explicit type annotations
 --> main.cryo:7:19
   |
 5 | function main() -> int {
 6 |     // Lambda parameters are not inferred; each needs an annotation.
 7 |     return apply((x) -> { return x + 1; }, 5);
   |                   ^ lambda parameters need explicit type annotations
 8 | }
   |
 note: annotate each parameter, e.g. `(x: T) -> ...`; Cryo does not infer lambda parameter types

aborting due to 1 error

The pre-1.0 bracket spelling of a tuple type, [T, U], was removed in favour of (T, U); a [ in type position is reported here with the migration.

function pair() -> [i32, i32] { return (1, 2); }

function main() -> int { return 0; }
error[E0104]: bracket tuple types '[T, U]' are no longer supported; use '(T, U)'
 --> main.cryo:1:20
   |
 1 | function pair() -> [i32, i32] { return (1, 2); }
   |                    ^ bracket tuple types '[T, U]' are no longer supported; use '(T, U)'
 2 | 
 3 | function main() -> int { return 0; }
   |

error[E0100]: expected ';' or '{', found '['
 --> main.cryo:1:20
   |
 1 | function pair() -> [i32, i32] { return (1, 2); }
   |                    ^ expected ';' or '{', found '['
 2 | 
 3 | function main() -> int { return 0; }
   |
 suggestion [machine-applicable]: insert `;` here
   |
 1 | function pair() ->; [i32, i32] { return (1, 2); }
   |                   +

aborting due to 2 errors

Pinned by E0104_bracket_tuple_type, E0104_expected_type, E0104_non_identifier_after_colons, E0104_untyped_lambda_param.

E0105 Expected Identifier

An identifier was required. The one form this reports today is a directive with no name, ![].

![]
function main() -> int { return 0; }
error[E0105]: expected directive name, found ']'
 --> main.cryo:1:3
   |
 1 | ![]
   |   ^ expected directive name, found ']'
 2 | function main() -> int { return 0; }
   |

error[E0100]: expected ']', found 'function'
 --> main.cryo:2:1
   |
 1 | ![]
 2 | function main() -> int { return 0; }
   | ^~~~~~~~ expected ']', found 'function'
 3 | 
   |
 suggestion [machine-applicable]: insert `]` here
   |
 1 | ![]]
   |    +

aborting due to 2 errors

E0108 Expected Brace

A { was required. A lambda's body must be a braced block — the brace-less single-expression form (n: i32) -> i32 n * 2 is not supported in v1.0, so write { return n * 2; }.

function main() -> int {
    const twice = (n: i32) -> i32 n * 2;
    return twice(21);
}
error[E0108]: expected '{' to begin lambda body; the brace-less single-expression form is not supported in v1.0 - wrap the body in `{ return expr; }`
 --> main.cryo:2:35
   |
 1 | function main() -> int {
 2 |     const twice = (n: i32) -> i32 n * 2;
   |                                   ^ expected '{' to begin lambda body; the brace-less single-expression form is not supported in v1.0 - wrap the body in `{ return expr; }`
 3 |     return twice(21);
 4 | }
   |

error[E0100]: expected ';', found 'n'
 --> main.cryo:2:35
   |
 1 | function main() -> int {
 2 |     const twice = (n: i32) -> i32 n * 2;
   |                                   ^ expected ';', found 'n'
 3 |     return twice(21);
 4 | }
   |
 suggestion [machine-applicable]: insert `;` here
   |
 2 |     const twice = (n: i32) -> i32; n * 2;
   |                                  +

aborting due to 2 errors

Pinned by E0108_lambda_missing_brace.

E0111 Invalid Syntax

A construct the parser recognised but which is not valid as written. Three situations report it:

  • An asm { ... } block with no ![arch(<arch>, <dialect>)] directive above it. The directive selects the target the block is for and the assembly dialect it is written in, and there is no default.
  • Expression nesting past the parser's recursion limit (256 levels of parentheses, or of right-nested =, ??, or ? :). The limit exists so that pathological input produces a diagnostic instead of a stack overflow.
  • export Module::*; — an export names what it grants; there is no glob form.
function main() -> int {
    mut a: i64 = 1;
    asm { add ${+a}, ${a} }
    return 0;
}
error[E0111]: `asm` block requires an `![arch(<arch>, <dialect>)]` directive above it
 --> main.cryo:3:5
   |
 1 | function main() -> int {
 2 |     mut a: i64 = 1;
 3 |     asm { add ${+a}, ${a} }
   |     ^~~~~~~~~~~~~~~~~~~~~~~ `asm` block requires an `![arch(<arch>, <dialect>)]` directive above it
 4 |     return 0;
 5 | }
   |

aborting due to 1 error

Pinned by E0111_asm_missing_arch, E0111_assignment_nesting_too_deep, E0111_coalesce_nesting_too_deep, E0111_expression_nesting_too_deep, E0111_ternary_nesting_too_deep, reexport_glob_rejected.

E0112 Unexpected End of File

The file ended in the middle of a construct. Most unclosed blocks are reported as an E0100 expecting }; this code is the parser's word for running out of tokens somewhere it cannot name a specific expectation.

E0113 Invalid Pattern

A pattern the parser cannot make sense of: a range whose two bounds are of different kinds, a - before something other than an integer bound, a .. with no high bound after it.

function main() -> int {
    const x: i32 = 1;
    match (x) {
        1..'z' => {}
        _      => {}
    }
    return 0;
}
error[E0113]: range pattern bounds must be the same kind (both char or both integer)
 --> main.cryo:4:12
   |
 2 |     const x: i32 = 1;
 3 |     match (x) {
 4 |         1..'z' => {}
   |            ^ range pattern bounds must be the same kind (both char or both integer)
 5 |         _      => {}
 6 |     }
   |

error[E0100]: expected '=>', found 'z'
 --> main.cryo:4:12
   |
 2 |     const x: i32 = 1;
 3 |     match (x) {
 4 |         1..'z' => {}
   |            ^ expected '=>', found 'z'
 5 |         _      => {}
 6 |     }
   |
 suggestion [machine-applicable]: insert `=>` here
   |
 4 |         1..=>'z' => {}
   |            ++

error[E0113]: expected pattern, found '{'
 --> main.cryo:4:19
   |
 2 |     const x: i32 = 1;
 3 |     match (x) {
 4 |         1..'z' => {}
   |                   ^ expected pattern, found '{'
 5 |         _      => {}
 6 |     }
   |

error[E0100]: expected '=>', found '{'
 --> main.cryo:4:19
   |
 2 |     const x: i32 = 1;
 3 |     match (x) {
 4 |         1..'z' => {}
   |                   ^ expected '=>', found '{'
 5 |         _      => {}
 6 |     }
   |
 suggestion [machine-applicable]: insert `=>` here
   |
 4 |         1..'z' =>=> {}
   |                  ++

error[E0113]: expected pattern, found '{'
 --> main.cryo:5:19
   |
 3 |     match (x) {
 4 |         1..'z' => {}
 5 |         _      => {}
   |                   ^ expected pattern, found '{'
 6 |     }
 7 |     return 0;
   |

error[E0100]: expected '=>', found '{'
 --> main.cryo:5:19
   |
 3 |     match (x) {
 4 |         1..'z' => {}
 5 |         _      => {}
   |                   ^ expected '=>', found '{'
 6 |     }
 7 |     return 0;
   |
 suggestion [machine-applicable]: insert `=>` here
   |
 5 |         _      =>=> {}
   |                  ++

aborting due to 6 errors

E0114 Duplicate Default

Two _ arms in one match. A match has at most one catch-all, and a second could never be reached.

function main() -> int {
    const x: i32 = 1;
    match (x) {
        _ => {}
        _ => {}
    }
    return 0;
}
error[E0114]: duplicate `_` arm: a match may have at most one wildcard arm
 --> main.cryo:5:9
   |
 3 |     match (x) {
 4 |         _ => {}
 5 |         _ => {}
   |         ^~~~~~~ duplicate `_` arm: a match may have at most one wildcard arm
 6 |     }
 7 |     return 0;
   |

aborting due to 1 error

Pinned by E0114_duplicate_default_arm.

E0116 Parse Exception

The parser produced no tree at all — usually because the lexer failed first, in which case the lexer's own error is the one to read.

Directives

![...] directives are validated after parsing: the right number of arguments, arguments of the right shape, and placement on the kind of declaration the directive applies to. Section 17 of the reference lists every directive and what it may be attached to.

CodeTitle
E0150Directive Bad Arity
E0151Directive Misplaced
E0152Directive Bad Argument
E0153Unknown Directive

E0150 Directive Bad Arity

A directive given the wrong number of arguments. ![inline] takes none; ![align] takes one.

![inline(3)]
function hot() -> void {}

function main() -> int { return 0; }
error[E0150]: directive `![inline]` expects 0 argument(s) but got 1
 --> main.cryo:1:1
   |
 1 | ![inline(3)]
   | ^~~~~~~~~~~~ directive `![inline]` expects 0 argument(s) but got 1
 2 | function hot() -> void {}
   |

aborting due to 1 error

Pinned by E0150_directive_bad_arity.

E0151 Directive Misplaced

A directive on a declaration it does not apply to, or on one that does not meet its requirements. ![repr] applies to structs, unions, classes, and enums; ![sink] needs a receiver, so it cannot mark a static method; ![config(testing)] may only appear on a namespace under tests/.

![repr(C)]
function main() -> int { return 0; }
error[E0151]: directive `![repr]` only applies to struct/union/class/enum, not function
 --> main.cryo:1:1
   |
 1 | ![repr(C)]
   | ^~~~~~~~~~ directive `![repr]` only applies to struct/union/class/enum, not function
 2 | function main() -> int { return 0; }
   |
 suggestion [machine-applicable]: remove the misplaced `![repr]` directive
   |
 1 | 
   | -

aborting due to 1 error

![naked] promises that the function body provides its own prologue, epilogue, and return, so the body must be a single asm { } block:

![naked]
function entry() -> void {
    return;
}

function main() -> int { return 0; }
error[E0151]: directive `![naked]` requires the function body to be a single `asm { }` block
 --> main.cryo:1:1
   |
 1 | ![naked]
   | ^~~~~~~~ directive `![naked]` requires the function body to be a single `asm { }` block
 2 | function entry() -> void {
 3 |     return;
   |

aborting due to 1 error

E0152 Directive Bad Argument

A directive whose argument is the wrong kind or value: an alignment that is not a power of two, an ![arch] dialect other than intel or att, a ![config] flavour the compiler does not know, or two directives on one declaration that contradict each other.

![align(3)]
type struct Packet { a: u8; }

function main() -> int { return 0; }
error[E0152]: directive `![align]`: alignment must be a power of two between 1 and 65536, got 3
 --> main.cryo:1:1
   |
 1 | ![align(3)]
   | ^~~~~~~~~~~ directive `![align]`: alignment must be a power of two between 1 and 65536, got 3
 2 | type struct Packet { a: u8; }
   |

aborting due to 1 error

E0153 Unknown Directive

A directive the compiler has no built-in for. The directive is recorded — user directives are legal and inspectable — but nothing acts on it, so a misspelt built-in would otherwise silently do nothing. This is the one E-code the compiler reports at warning severity: the program still builds.

![frobnicate]
function main() -> int { return 0; }
warning[E0153]: unknown directive `![frobnicate]`: no built-in with that name; the directive is recorded but has no effect
 --> main.cryo:1:1
   |
 1 | ![frobnicate]
   | ^~~~~~~~~~~~~ unknown directive `![frobnicate]`: no built-in with that name; the directive is recorded but has no effect
 2 | function main() -> int { return 0; }
   |

1 warning emitted

Symbol resolution

Name resolution is scoped: a bare name means what the imports and declarations in scope say it means, and a name two of them claim means nothing until the author says which. These two codes are the refusals.

CodeTitle
E0154Ambiguous Call
E0155Ambiguous Bare Name

E0154 Ambiguous Call

A name that is in scope twice. Two import lines can each bring in a function called alloc; two traits implemented for a type can each provide fmt; two reachable modules can both be called traits. The name is refused where it is used, with the note naming the full paths that would resolve it.

import std::alloc::heap;
import std::alloc::heap::{ alloc };
import std::alloc::allocator;
import std::alloc::allocator::{ alloc };

function main() -> int {
    // Both imports bring an `alloc` into scope; the bare name picks neither.
    const p: void* = alloc(64, 8);
    return 0;
}
error[E0154]: `alloc` is ambiguous: it is imported from both `std::alloc::heap` and `std::alloc::allocator`
 --> main.cryo:8:22
    |
  6 | function main() -> int {
  7 |     // Both imports bring an `alloc` into scope; the bare name picks neither.
  8 |     const p: void* = alloc(64, 8);
    |                      ^~~~~ ambiguous name
  9 |     return 0;
 10 | }
    |
 note: write the path out to say which one is meant, `std::alloc::heap::alloc` or `std::alloc::allocator::alloc`

aborting due to 1 error

A method call is ambiguous when two traits the receiver implements provide the same method and nothing — a bound on the receiver's type, say — picks one:

import std::fmt::display::{ Display, Debug };

type struct Point { x: i32; }

implement trait Display for Point {
    fmt<W>(&this, w: &W) -> i32 { return 1; }
}
implement trait Debug for Point {
    fmt<W>(&this, w: &W) -> i32 { return 2; }
}

function main() -> int {
    const p: Point = Point { x: 1 };
    mut sink: i32 = 0;
    // `fmt` comes from two traits, and nothing picks one.
    return p.fmt(&sink);
}
error[E0154]: call to `fmt` is ambiguous: it is provided by multiple traits implemented for this type
 --> main.cryo:16:12
    |
 14 |     mut sink: i32 = 0;
 15 |     // `fmt` comes from two traits, and nothing picks one.
 16 |     return p.fmt(&sink);
    |            ^~~~~ `fmt` is defined by both `std::fmt::display::Display` and `std::fmt::display::Debug`
 17 | }
    |
 note: disambiguate by calling the trait method explicitly, e.g. `Trait::method(receiver, args)`

aborting due to 1 error

Pinned by E0154_ambiguous_bare_call, E0154_ambiguous_module_prefix_annotation, E0154_ambiguous_trait_method, resolution_ambiguous_module.

E0155 Ambiguous Bare Name

A bare name that two declarations in different modules claim, with neither of them in scope where it is written. It is distinct from E0154, where both candidates are in scope and the author is asked to choose, and from E0203, which says no such name exists: here two exist, and the name would only pick one of them if the compiler searched the whole program and picked a winner the source never named. The fix is an import of the one meant. This can only arise across modules, so it is pinned by a multi-module test rather than a single file.

Pinned by plural_leaf_gate, reexport_plural_function.

Type checking

The largest range. Most of these are what a type error looks like in any statically typed language; the entries note where Cryo's rules differ from what a reader might expect — no implicit narrowing, no implicit integer-to-pointer conversion, ? only inside a Result-returning function.

CodeTitle
E0200Type Mismatch
E0201Undefined Variable
E0202Undefined Function
E0203Undefined Type
E0204Undefined Field
E0205Redefined Symbol
E0206Redefined Function
E0207Redefined Type
E0208Invalid Cast
E0209Invalid Operation
E0210Invalid Assignmentreserved
E0211Incompatible Typesreserved
E0212Void Value Usedreserved
E0213Non-Callablereserved
E0214Argument Mismatch
E0215Too Many Arguments
E0216Too Few Arguments
E0217Const Violationreserved
E0218Immutable Assignment
E0219Uninitialized Variablereserved
E0220Unreachable Codereserved
E0221Circular Dependencyreserved
E0222Invalid Dereferencereserved
E0223Invalid Address-Of
E0224Invalid Indexreserved
E0225Index Out of Boundsreserved
E0226Division by Zero
E0227Overflowreserved
E0228Underflowreserved
E0229Invalid Binary Operation
E0230Invalid Unary Operation
E0231Non-Callable Typereserved
E0232Invalid Assignment Target
E0233Undefined Symbol
E0234Invalid ? Operand
E0235? Outside Result/Option Function
E0236Recursive Type
E0237Static Assertion Failed
E0238Branch Type Mismatch
E0239Non-Constant Array Size
E0240Namespace Not Reachable
E0241Export Not Grantable

E0200 Type Mismatch

A value of one type where another was required. Assignment, initialization, return, array elements, and the two sides of an if expression are all checked with the same rule, and the rule has no implicit conversions: an i64 does not become a u8, an integer does not become a pointer, and a suffix that names one type does not yield to an annotation that names another. Write the cast.

function main() -> int {
    mut count: i32 = 0;
    count = "a string";
    return 0;
}
error[E0200]: mismatched types
 --> main.cryo:3:13
   |
 1 | function main() -> int {
 2 |     mut count: i32 = 0;
 3 |     count = "a string";
   |     -----   ^^^^^^^^^^ expected `i32`, found `string`
   |     |
   |     expected due to this
 4 |     return 0;
 5 | }
   |
 note: the value assigned here doesn't match the type of the assignment target

aborting due to 1 error
function main() -> int {
    mut small: u8 = 0;
    const big: i64 = 300;
    // `i64 -> u8` would truncate; the cast has to be written.
    small = big;
    return 0;
}
error[E0200]: mismatched types
 --> main.cryo:5:13
   |
 3 |     const big: i64 = 300;
 4 |     // `i64 -> u8` would truncate; the cast has to be written.
 5 |     small = big;
   |     -----   ^^^ expected `u8`, found `i64`
   |     |
   |     expected due to this
 6 |     return 0;
 7 | }
   |
 note: the value assigned here doesn't match the type of the assignment target

aborting due to 1 error

Every element of an array literal must be assignable to the array's element type:

function main() -> int {
    const xs: i32[] = [1, 2, "three"];
    return xs[0];
}
error[E0200]: mismatched types
 --> main.cryo:2:30
   |
 1 | function main() -> int {
 2 |     const xs: i32[] = [1, 2, "three"];
   |                              ^~~~~~~ expected `i32`, found `string`
 3 |     return xs[0];
 4 | }
   |
 note: array elements must all have the same type

aborting due to 1 error

Operators that desugar to traits report the missing trait under this code too — [] needs Index:

type struct Grid { cells: i32; }

function main() -> int {
    const g: Grid = Grid { cells: 5 };
    // `Grid` does not implement `Index`.
    const x: i32 = g[0];
    return x;
}
error[E0200]: `main::Grid` cannot be indexed with `[]`; add `implement trait Index<Idx, Output> ... for main::Grid` (its `index` must return `Output*`)
 --> main.cryo:6:20
   |
 4 |     const g: Grid = Grid { cells: 5 };
 5 |     // `Grid` does not implement `Index`.
 6 |     const x: i32 = g[0];
   |                    ^~~~ `main::Grid` cannot be indexed with `[]`; add `implement trait Index<Idx, Output> ... for main::Grid` (its `index` must return `Output*`)
 7 |     return x;
 8 | }
   |

aborting due to 1 error

And an implement Trait binding requires the initializer's concrete type to implement the trait:

import std::core::iter;
import std::core::option;

type struct Counter { n: i32; }

function main() -> int {
    const c: Counter = Counter { n: 1 };
    // `Counter` does not implement `Iterator`.
    mut it: implement Iterator<i32> = c;
    return 0;
}
error[E0200]: `it` is declared `implement Iterator`, but its initializer has type `main::Counter`, which does not implement `Iterator`
 --> main.cryo:9:13
    |
  7 |     const c: Counter = Counter { n: 1 };
  8 |     // `Counter` does not implement `Iterator`.
  9 |     mut it: implement Iterator<i32> = c;
    |             ^~~~~~~~~~~~~~~~~~~~~~~ this opaque type requires the initializer to implement the trait
 10 |     return 0;
 11 | }
    |

aborting due to 1 error

Pinned by E0200_array_literal_element_mismatch, E0200_array_literal_widening_element, E0200_array_pop_value_use, E0200_assign_incompatible_type, E0200_assign_int_to_pointer, E0200_assign_narrowing, E0200_char_range_on_int, E0200_contradictory_numeric_suffix, E0200_fn_pointer_field_assign, E0200_impl_trait_unmet_bound, E0200_index_no_impl, E0200_opaque_assoc_item_binding, E0200_opaque_assoc_item_return, compile_fail_typeerror.

E0201 Undefined Variable

A value name that is not declared anywhere in scope.

function main() -> int {
    return total;
}
error[E0201]: cannot find value `total` in this scope
 --> main.cryo:2:12
   |
 1 | function main() -> int {
 2 |     return total;
   |            ^~~~~ not found in this scope
 3 | }
   |

aborting due to 1 error

Pinned by E0201_undefined_variable.

E0202 Undefined Function

A call to a function name that is not declared anywhere in scope.

function main() -> int {
    initialise();
    return 0;
}
error[E0202]: cannot find function `initialise` in this scope
 --> main.cryo:2:5
   |
 1 | function main() -> int {
 2 |     initialise();
   |     ^~~~~~~~~~ not found in this scope
 3 |     return 0;
 4 | }
   |

aborting due to 1 error

Pinned by E0202_undefined_function.

E0203 Undefined Type

A type name that resolves to nothing. This is the code for a name that does not exist; a name that exists but is not reachable from the current module is E0240, and one that exists but is private is E0503. A path with a module prefix is resolved from its first segment — Probe::Lib::Widget must name a Widget declared in Probe::Lib itself, and trailing segments are never dropped to make a match.

function render(w: Widget) -> void {}

function main() -> int { return 0; }
error[E0203]: cannot find type `Widget` in this scope
 --> main.cryo:1:20
   |
 1 | function render(w: Widget) -> void {}
   |                    ^~~~~~ not found in this scope
 2 | 
 3 | function main() -> int { return 0; }
   |

aborting due to 1 error

Pinned by E0203_path_drops_trailing_segment, E0203_undefined_base_class, E0203_undefined_type, E0203_unknown_local_var_type, E0203_unknown_module_prefix.

E0204 Undefined Field

A field or method access on a type that has no member of that name. The receiver may be a value or a pointer; . reaches through either.

type struct Point { x: i32; }

function main() -> int {
    const p: Point = Point { x: 1 };
    return p.y;
}
error[E0204]: no field or method `y` on type `main::Point`
 --> main.cryo:5:14
   |
 3 | function main() -> int {
 4 |     const p: Point = Point { x: 1 };
 5 |     return p.y;
   |              ^ unknown field or method
 6 | }
   |
 help: a field or method with a similar name exists: `x`
 suggestion [machine-applicable]: did you mean `x`?
   |
 5 |     return p.x;
   |              ~

aborting due to 1 error

Pinned by E0204_undefined_field, E0204_undefined_field_pointer.

E0205 Redefined Symbol

A name declared twice in the same scope: two locals, or two parameters of one function. Shadowing in a nested block is a different scope and is allowed.

function main() -> int {
    const x: i32 = 1;
    const x: i32 = 2;
    return x;
}
error[E0205]: the name `x` is already declared in this scope
 --> main.cryo:3:5
   |
 1 | function main() -> int {
 2 |     const x: i32 = 1;
 3 |     const x: i32 = 2;
   |     ^~~~~~~~~~~~~~~~~ the name `x` is already declared in this scope
 4 |     return x;
 5 | }
   |

aborting due to 1 error
function add(x: i32, x: i32) -> i32 {
    return x;
}

function main() -> int { return add(1, 2); }
error[E0205]: the parameter `x` is already declared in this function
 --> main.cryo:1:22
   |
 1 | function add(x: i32, x: i32) -> i32 {
   |                      ^~~~~~ the parameter `x` is already declared in this function
 2 |     return x;
 3 | }
   |

aborting due to 1 error

Pinned by E0205_duplicate_local, E0205_duplicate_param.

E0206 Redefined Function

A function defined twice with the same signature. Overloads with different parameter lists are allowed; a second definition of the same one is not.

function reset() -> void {}
function reset() -> void {}

function main() -> int { return 0; }
error[E0206]: function `reset` is defined multiple times with the same signature
 --> main.cryo:2:1
   |
 1 | function reset() -> void {}
 2 | function reset() -> void {}
   | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ function `reset` is defined multiple times with the same signature
 3 | 
 4 | function main() -> int { return 0; }
   |

aborting due to 1 error

Pinned by E0206_redefined_function.

E0207 Redefined Type

A type defined twice in one module.

type struct Point { x: i32; }
type struct Point { y: i32; }

function main() -> int { return 0; }
error[E0207]: type `main::Point` is defined multiple times
 --> main.cryo:2:1
   |
 1 | type struct Point { x: i32; }
 2 | type struct Point { y: i32; }
   | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ type `main::Point` is defined multiple times
 3 | 
 4 | function main() -> int { return 0; }
   |

aborting due to 1 error

Pinned by E0207_redefined_type.

E0208 Invalid Cast

A cast between types that have no conversion. A value aggregate — a struct, tuple, or fixed array — has no scalar representation, so it cannot be cast to an integer. (A class is a heap pointer, so class-to-pointer and class-to-integer casts remain legal.)

type struct Point { x: i32; y: i32; }

function main() -> int {
    const p: Point = Point { x: 1, y: 2 };
    const n: i64 = p as i64;
    return 0;
}
error[E0208]: invalid cast between an aggregate type (struct/class/tuple/array) and a scalar or pointer: there is no valid representation for this conversion
 --> main.cryo:5:20
   |
 3 | function main() -> int {
 4 |     const p: Point = Point { x: 1, y: 2 };
 5 |     const n: i64 = p as i64;
   |                    ^~~~~~~~~ invalid cast between an aggregate type (struct/class/tuple/array) and a scalar or pointer: there is no valid representation for this conversion
 6 |     return 0;
 7 | }
   |

aborting due to 1 error

Pinned by E0208_aggregate_scalar_cast.

E0209 Invalid Operation

An operation that is not valid in that position. The one reported today: typeof(x) is a type-level construct and only makes sense where a type is expected, as in const y: typeof(x).

function main() -> int {
    const x: i32 = 42;
    // `typeof` names a type; it is not a value.
    const t: string = typeof(x);
    return 0;
}
error[E0209]: `typeof` may only be used in type position (e.g. `const y: typeof(x)`)
 --> main.cryo:4:23
   |
 2 |     const x: i32 = 42;
 3 |     // `typeof` names a type; it is not a value.
 4 |     const t: string = typeof(x);
   |                       ^~~~~~~~~ `typeof` may only be used in type position (e.g. `const y: typeof(x)`)
 5 |     return 0;
 6 | }
   |

aborting due to 1 error

Pinned by E0209_typeof_value_position.

E0214 Argument Mismatch

A call argument of the wrong type for its parameter. The rule is the same one assignment uses — no narrowing, no sign reinterpretation, no dropping const from a pointer — applied at every kind of call: free functions, methods, static methods, trait methods, and function-pointer parameters.

function takes_int(x: i32) -> void {}

function main() -> int {
    takes_int("bad");
    return 0;
}
error[E0214]: mismatched types
 --> main.cryo:4:15
   |
 2 | 
 3 | function main() -> int {
 4 |     takes_int("bad");
   |               ^~~~~ expected `i32`, found `string`
 5 |     return 0;
 6 | }
   |
 note: argument 1 doesn't match the parameter's declared type

aborting due to 1 error
type struct Counter {
    v: i32;
    bump(mut &this, by: i32) -> void { this.v = this.v + by; }
}

function main() -> int {
    mut c: Counter = Counter { v: 0 };
    c.bump("one");
    return 0;
}
error[E0214]: mismatched types
 --> main.cryo:8:12
    |
  6 | function main() -> int {
  7 |     mut c: Counter = Counter { v: 0 };
  8 |     c.bump("one");
    |            ^~~~~ expected `i32`, found `string`
  9 |     return 0;
 10 | }
    |
 note: argument 1 doesn't match the parameter's declared type

aborting due to 1 error

Two structs with the same shape are still two types:

type struct Metres { v: i32; }
type struct Seconds { v: i32; }

function walk(d: Metres) -> void {}

function main() -> int {
    const t: Seconds = Seconds { v: 1 };
    walk(t);
    return 0;
}
error[E0214]: mismatched types
 --> main.cryo:8:10
    |
  6 | function main() -> int {
  7 |     const t: Seconds = Seconds { v: 1 };
  8 |     walk(t);
    |          ^ expected `main::Metres`, found `main::Seconds`
  9 |     return 0;
 10 | }
    |
 note: argument 1 doesn't match the parameter's declared type

aborting due to 1 error

A runtime value that would narrow, or a negative literal passed to an unsigned parameter, is refused rather than silently changing value:

function set_alpha(a: u8) -> void {}

function main() -> int {
    const big: i64 = 5;
    // `i64 -> u8` narrows; write `big as u8`.
    set_alpha(big);
    return 0;
}
error[E0214]: mismatched types
 --> main.cryo:6:15
   |
 4 |     const big: i64 = 5;
 5 |     // `i64 -> u8` narrows; write `big as u8`.
 6 |     set_alpha(big);
   |               ^~~ expected `u8`, found `i64`
 7 |     return 0;
 8 | }
   |
 note: argument 1 narrows the value and would silently truncate; add an explicit `as` cast

aborting due to 1 error
function set_count(n: u32) -> void {}

function main() -> int {
    // `-1` would arrive as 4294967295.
    set_count(-1);
    return 0;
}
error[E0214]: mismatched types
 --> main.cryo:5:15
   |
 3 | function main() -> int {
 4 |     // `-1` would arrive as 4294967295.
 5 |     set_count(-1);
   |               ^~ expected `u32`, found `i32`
 6 |     return 0;
 7 | }
   |
 note: argument 1 reinterprets the sign of a same-width integer and would silently change its value; add an explicit `as` cast

aborting due to 1 error

A const T* does not pass where a T* is expected; the cast has to be written:

function write(p: i32*) -> void { *p = 1; }

function relay(p: const i32*) -> void {
    // `const i32*` -> `i32*` drops the const.
    write(p);
}

function main() -> int { return 0; }
error[E0214]: mismatched types
 --> main.cryo:5:11
   |
 3 | function relay(p: const i32*) -> void {
 4 |     // `const i32*` -> `i32*` drops the const.
 5 |     write(p);
   |           ^ expected `i32*`, found `i32*`
 6 | }
   |
 note: argument 1 drops `const` from the pointee; add an explicit `as` cast if this is intended

aborting due to 1 error

A generic call whose arguments pin one type parameter to two different types has no single instantiation:

function second<T>(a: T, b: T) -> T { return b; }

function main() -> int {
    // No single `T` is both `i32` and `string`.
    const r: i32 = second(5, "five");
    return 0;
}
error[E0214]: conflicting types for the type parameter of `second`
 --> main.cryo:5:27
   |
 3 | function main() -> int {
 4 |     // No single `T` is both `i32` and `string`.
 5 |     const r: i32 = second(5, "five");
   |                           ^ expected `string` (inferred from an earlier argument), found `i32`
 6 |     return 0;
 7 | }
   |
 note: a single type parameter cannot be both `string` and `i32`

error[E0200]: mismatched types
 --> main.cryo:5:20
   |
 3 | function main() -> int {
 4 |     // No single `T` is both `i32` and `string`.
 5 |     const r: i32 = second(5, "five");
   |              ---   ^^^^^^^^^^^^^^^^^ expected `i32`, found `T`
   |              |
   |              expected due to this
 6 |     return 0;
 7 | }
   |
 note: `r` was declared with this type; the initializer must match

aborting due to 2 errors

When a type has several same-named methods (two Mul impls, say), a call that matches none of them is reported here too:

import std::core::ops::{ Mul };

type struct Vec3 { x: f32; y: f32; z: f32; }

implement trait Mul<f32, Vec3> for struct Vec3 {
    mul(&this, rhs: &f32) -> Vec3 {
        return Vec3 { x: this.x * *rhs, y: this.y * *rhs, z: this.z * *rhs };
    }
}

implement trait Mul<Vec3, Vec3> for struct Vec3 {
    mul(&this, rhs: &Vec3) -> Vec3 {
        return Vec3 { x: this.x * rhs.x, y: this.y * rhs.y, z: this.z * rhs.z };
    }
}

function main() -> int {
    const a: Vec3 = Vec3 { x: 1.0, y: 2.0, z: 3.0 };
    const k: f64 = 2.0;
    // Neither `mul` takes an `f64`.
    const s: Vec3 = a * k;
    return 0;
}
error[E0214]: no overload of `mul` accepts these arguments
 --> main.cryo:21:21
    |
 19 |     const k: f64 = 2.0;
 20 |     // Neither `mul` takes an `f64`.
 21 |     const s: Vec3 = a * k;
    |                     ^~~~~ no `mul` on `main::Vec3` takes `(&f64)`
 22 |     return 0;
 23 | }
    |
 note: candidates are `mul(&f32)` and `mul(&main::Vec3)`

aborting due to 1 error

Pinned by E0214_arg_narrowing, E0214_const_pointer_drop, E0214_fn_pointer_argument, E0214_free_function, E0214_generic_inference_conflict, E0214_generic_numeric_conflict, E0214_instance_method, E0214_negative_literal_unsigned_arg, E0214_overload_set_no_match, E0214_runtime_sign_reinterpret, E0214_static_method, E0214_trait_method, E0214_wrong_struct.

E0215 Too Many Arguments

More arguments than the function takes. For an overloaded function, no overload takes that many.

type struct Point {
    x: i32;
    y: i32;
    sum(&this) -> i32 { return this.x + this.y; }
}

function main() -> int {
    const p: Point = Point { x: 1, y: 2 };
    return p.sum(5);
}
error[E0215]: method `sum` takes 0 argument(s) but 1 were supplied
 --> main.cryo:9:12
    |
  7 | function main() -> int {
  8 |     const p: Point = Point { x: 1, y: 2 };
  9 |     return p.sum(5);
    |            ^~~~~~~~ unexpected argument
 10 | }
    |

aborting due to 1 error
function add(a: i32) -> i32 { return a; }
function add(a: i32, b: i32) -> i32 { return a + b; }

function main() -> int {
    return add(1, 2, 3);
}
error[E0215]: no overload of function `add` takes 3 argument(s)
 --> main.cryo:5:12
   |
 3 | 
 4 | function main() -> int {
 5 |     return add(1, 2, 3);
   |            ^~~~~~~~~~~~ no matching overload for this argument count
 6 | }
   |

aborting due to 1 error

Pinned by E0215_method_call_arity, E0215_overload_no_matching_arity.

E0216 Too Few Arguments

Fewer arguments than the function takes.

function add(a: i32, b: i32) -> i32 { return a + b; }

function main() -> int {
    return add(1);
}
error[E0216]: function `add` takes 2 argument(s) but 1 were supplied
 --> main.cryo:4:12
   |
 2 | 
 3 | function main() -> int {
 4 |     return add(1);
   |            ^~~~~~ missing 1 argument
 5 | }
   |
 suggestion [has-placeholders]: provide the missing argument
   |
 4 |     return add(1, <i32>);
   |                 +++++++

aborting due to 1 error

Pinned by E0216_too_few_args.

E0218 Immutable Assignment

An assignment to a const binding. Declare it mut if it changes.

function main() -> int {
    const limit: i32 = 10;
    limit = 20;
    return 0;
}
error[E0218]: cannot assign to immutable variable `limit`
 --> main.cryo:3:5
   |
 1 | function main() -> int {
 2 |     const limit: i32 = 10;
 3 |     limit = 20;
   |     ^~~~~ this binding is immutable
 4 |     return 0;
 5 | }
   |
 suggestion [machine-applicable]: make `limit` mutable
   |
 2 |     mut limit: i32 = 10;
   |     ~~~

aborting due to 1 error

Pinned by E0218_assign_to_const.

E0223 Invalid Address-Of

& applied to something that is not a place. A literal has no address; & needs a variable, field, or index expression.

function main() -> int {
    const p: i32* = &5;
    return 0;
}
error[E0223]: cannot take the address of a literal; `&` requires a place (variable, field, or index)
 --> main.cryo:2:21
   |
 1 | function main() -> int {
 2 |     const p: i32* = &5;
   |                     ^~ cannot take the address of a literal; `&` requires a place (variable, field, or index)
 3 |     return 0;
 4 | }
   |

aborting due to 1 error

Pinned by E0223_address_of_literal.

E0226 Division by Zero

A division by a constant zero, caught at compile time.

function main() -> int {
    const x: i32 = 1 / 0;
    return x;
}
error[E0226]: division by zero
 --> main.cryo:2:20
   |
 1 | function main() -> int {
 2 |     const x: i32 = 1 / 0;
   |                    ^~~~~ division by zero
 3 |     return x;
 4 | }
   |

aborting due to 1 error

Pinned by E0226_division_by_zero.

E0229 Invalid Binary Operation

A binary operator applied to types it is not defined for. Arithmetic and comparison on user types desugar to the core::ops and core::cmp traits — + to Add, < to Ord, *x on a struct to Deref — and the message names the trait that is missing.

type struct Money { cents: i32; }

function main() -> int {
    const a: Money = Money { cents: 1 };
    const b: Money = Money { cents: 2 };
    const c: Money = a + b;
    return 0;
}
error[E0229]: `main::Money` does not implement `Add`; add `implement trait Add ... for main::Money` to use the `+` operator on it
 --> main.cryo:6:22
   |
 4 |     const a: Money = Money { cents: 1 };
 5 |     const b: Money = Money { cents: 2 };
 6 |     const c: Money = a + b;
   |                      ^~~~~ `main::Money` does not implement `Add`; add `implement trait Add ... for main::Money` to use the `+` operator on it
 7 |     return 0;
 8 | }
   |

aborting due to 1 error
type struct Tag { v: i32; }

function main() -> int {
    const a: Tag = Tag { v: 1 };
    const b: Tag = Tag { v: 2 };
    const lt: boolean = a < b;
    return 0;
}
error[E0229]: `main::Tag` does not implement `Ord`; add `implement trait Ord ... for main::Tag` to use the `<` operator on it
 --> main.cryo:6:25
   |
 4 |     const a: Tag = Tag { v: 1 };
 5 |     const b: Tag = Tag { v: 2 };
 6 |     const lt: boolean = a < b;
   |                         ^~~~~ `main::Tag` does not implement `Ord`; add `implement trait Ord ... for main::Tag` to use the `<` operator on it
 7 |     return 0;
 8 | }
   |

aborting due to 1 error
type struct Handle { v: i32; }

function main() -> int {
    const h: Handle = Handle { v: 5 };
    const x: i32 = *h;
    return x;
}
error[E0229]: `main::Handle` does not implement `Deref`; add `implement trait Deref ... for main::Handle` to use the `*` operator on it
 --> main.cryo:5:20
   |
 3 | function main() -> int {
 4 |     const h: Handle = Handle { v: 5 };
 5 |     const x: i32 = *h;
   |                    ^~ `main::Handle` does not implement `Deref`; add `implement trait Deref ... for main::Handle` to use the `*` operator on it
 6 |     return x;
 7 | }
   |

error[E0200]: mismatched types
 --> main.cryo:5:20
   |
 3 | function main() -> int {
 4 |     const h: Handle = Handle { v: 5 };
 5 |     const x: i32 = *h;
   |              ---   ^^ expected `i32`, found `main::Handle`
   |              |
   |              expected due to this
 6 |     return x;
 7 | }
   |
 note: `x` was declared with this type; the initializer must match

aborting due to 2 errors

Compound assignment is checked the same way as the operator it expands to:

function main() -> int {
    mut s: string = "hello";
    s *= 3;
    return 0;
}
error[E0229]: Cannot apply '*' to String and Int
 --> main.cryo:3:5
   |
 1 | function main() -> int {
 2 |     mut s: string = "hello";
 3 |     s *= 3;
   |     ^~~~~~ Cannot apply '*' to String and Int
 4 |     return 0;
 5 | }
   |

aborting due to 1 error

Pinned by E0229_deref_no_impl, E0229_invalid_binary_op, E0229_invalid_compound_op, E0229_numeric_plus_string, E0229_relational_no_ord.

E0230 Invalid Unary Operation

A unary operator applied to a type it is not defined for: most often * on something that is not a pointer.

function main() -> int {
    const x: i32 = 5;
    const y: i32 = *x;
    return y;
}
error[E0230]: Dereference requires pointer or reference operand
 --> main.cryo:3:20
   |
 1 | function main() -> int {
 2 |     const x: i32 = 5;
 3 |     const y: i32 = *x;
   |                    ^~ Dereference requires pointer or reference operand
 4 |     return y;
 5 | }
   |

aborting due to 1 error

Pinned by E0230_deref_non_pointer.

E0232 Invalid Assignment Target

An assignment whose left-hand side is not a place.

function main() -> int {
    5 = 3;
    return 0;
}
error[E0232]: cannot assign to a literal; the left-hand side of an assignment must be a place (variable, field, or index)
 --> main.cryo:2:5
   |
 1 | function main() -> int {
 2 |     5 = 3;
   |     ^ cannot assign to a literal; the left-hand side of an assignment must be a place (variable, field, or index)
 3 |     return 0;
 4 | }
   |

aborting due to 1 error

Pinned by E0232_invalid_assignment_target.

E0233 Undefined Symbol

A Type::name path that names nothing on the type — not a static method, not a variant, not a nested type.

type struct Config { v: i32; }

function main() -> int {
    const c: Config = Config::load();
    return 0;
}
error[E0233]: cannot find `Config::load`: no matching static method, variant, or type
 --> main.cryo:4:23
   |
 2 | 
 3 | function main() -> int {
 4 |     const c: Config = Config::load();
   |                       ^~~~~~~~~~~~ cannot find `Config::load`: no matching static method, variant, or type
 5 |     return 0;
 6 | }
   |

aborting due to 1 error

Pinned by E0233_undefined_static.

E0234 Invalid `?` Operand

? applied to a value that is not a Result or Option. A plain value has no error channel to propagate.

function main() -> int {
    const x: i32 = 5;
    const y: i32 = x?;
    return y;
}
error[E0234]: the `?` operator requires a `Result` or `Option` value, but the operand has type 'i32'
 --> main.cryo:3:20
   |
 1 | function main() -> int {
 2 |     const x: i32 = 5;
 3 |     const y: i32 = x?;
   |                    ^~ the `?` operator requires a `Result` or `Option` value, but the operand has type 'i32'
 4 |     return y;
 5 | }
   |

aborting due to 1 error

Pinned by E0234_invalid_try_operand.

E0235 `?` Outside Result/Option Function

? used in a function whose return type is not a Result or Option. ? returns the Err (or None) from the enclosing function, so the enclosing function has to be able to return one.

import std::core::result;

function parse() -> Result<i32, string> {
    return Result::Ok(1);
}

function main() -> int {
    // `main` returns `int`, so there is nowhere for an `Err` to go.
    const v: i32 = parse()?;
    return v;
}
error[E0235]: the `?` operator can only be used in a function that returns `Result`
 --> main.cryo:9:20
    |
  7 | function main() -> int {
  8 |     // `main` returns `int`, so there is nowhere for an `Err` to go.
  9 |     const v: i32 = parse()?;
    |                    ^~~~~~~~ propagates a `Result`, but the enclosing function doesn't return one
 10 |     return v;
 11 | }
    |
 suggestion [has-placeholders]: change the return type to `Result<i32, string>`
   |
 7 | function main() -> Result<i32, string> {
   |                    ~~~~~~~~~~~~~~~~~~~

aborting due to 1 error

Pinned by E0235_try_outside_result_fn.

E0236 Recursive Type

A struct that contains itself by value, and so has no finite size. Indirection through a pointer (next: Node*) is fine.

// `Node` contains itself by value, so it has no finite size.
// `next: Node*` would be fine.
type struct Node {
    value: i32;
    next: Node;
}

function main() -> int { return 0; }
error[E0236]: recursive type `main::Node` has infinite size
 help: break the cycle with a pointer or `Box` indirection (store the self-referential field behind a `*`)

aborting due to 1 error

Pinned by E0236_recursive_struct.

E0237 Static Assertion Failed

A static_assert whose condition is false. The condition is evaluated at compile time; the usual use is checking a struct's layout against what a C header promises.

static_assert(sizeof(i32) == 8);

function main() -> int { return 0; }
error[E0237]: static assertion failed
 --> main.cryo:1:1
   |
 1 | static_assert(sizeof(i32) == 8);
   | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ this condition is false
 2 | 
 3 | function main() -> int { return 0; }
   |

aborting due to 1 error

Pinned by E0237_static_assert.

E0238 Branch Type Mismatch

The branches of an if expression produce types that are incompatible in both directions, so the expression has no result type. Branches that differ but convert (i32 and i64) are accepted, as is a branch that diverges.

function main() -> int {
    const c: boolean = true;
    const x = if (c) { 7 } else { "seven" };
    return 0;
}
error[E0238]: mismatched types
 --> main.cryo:3:35
   |
 1 | function main() -> int {
 2 |     const c: boolean = true;
 3 |     const x = if (c) { 7 } else { "seven" };
   |                        -          ^^^^^^^ expected `i32`, found `string`
   |                        |
   |                        expected due to this
 4 |     return 0;
 5 | }
   |
 note: both branches of an `if` expression must produce the same type

aborting due to 1 error

Pinned by E0238_if_expr_branch_mismatch.

E0239 Non-Constant Array Size

A fixed array's size is not a compile-time constant. A mut global is a runtime value; sizing an array with it would leave the array with no storage behind it.

mut runtime_len: i64 = 8;

type struct Buffer { bytes: u8[runtime_len]; }

function main() -> int { return 0; }
error[E0239]: array size must be a compile-time integer constant
 --> main.cryo:3:44
   |
 1 | mut runtime_len: i64 = 8;
 2 | 
 3 | type struct Buffer { bytes: u8[runtime_len]; }
   |                                            ^ array size must be a compile-time integer constant
 4 | 
 5 | function main() -> int { return 0; }
   |
 help: a compile-time integer constant is an integer literal, a named constant, an enum variant, or arithmetic over them

aborting due to 1 error

Pinned by E0239_non_const_array_size.

E0240 Namespace Not Reachable

A name that exists but is not reachable from the module using it: nothing in scope binds it, and no import brings it in. This is distinct from E0203, which means the name exists nowhere. The message names the module that declares it and suggests the import. A name declared private in its module is reported here too when named from outside, with the note that no import can make it reachable. It only arises across modules, so it is pinned by multi-module tests.

Pinned by const_unimported_gate, namespace_gate, namespace_gate_methods, reexport_private_module, resolution_unreachable_module.

E0241 Export Not Grantable

An export that names something it cannot grant: an item the exported path does not declare, or one it declares private. An export cannot widen visibility, and the error is reported where the export is written, because that is the only place it can be fixed — at an importer the name would simply look absent.

Pinned by reexport_private.

Generics and traits

CodeTitle
E0300Generic Instantiation Failedreserved
E0301Generic Type Resolution Failedreserved
E0302Generic Parameter Mismatch
E0303Invalid Generic Constraintreserved
E0304Ambiguous Genericreserved
E0305Recursive Genericreserved
E0306Trait Bound Not Satisfied
E0307Cannot Infer Type Argument
E0308Conflicting Trait Implementation
E0309Associated Type Not Bound
E0310Positional Associated Type With Generic Parameters

E0302 Generic Parameter Mismatch

An implement head that names some but not all of a template's parameters. Buf<A = Tag> defaults its parameter at a use, never in an impl head, so a bare implement trait Show for Buf has two readings — an impl for every Buf<A>, or one for Buf<Tag> — and the writer has to say which.

type struct Tag { n: i32; }
type struct Buf<A = Tag> { a: A; }
type trait Show { show(&this) -> int; }

// Is this an impl for every `Buf<A>`, or for `Buf<Tag>`? Say which:
// `implement<A> trait Show for Buf<A>` or `implement trait Show for Buf<Tag>`.
implement trait Show for Buf {
    show(&this) -> int { return 1; }
}

function main() -> int { return 0; }
error[E0302]: `Buf` is a template with 1 parameter, and this impl head names 0 of them
 --> main.cryo:7:26
   |
 5 | // Is this an impl for every `Buf<A>`, or for `Buf<Tag>`? Say which:
 6 | // `implement<A> trait Show for Buf<A>` or `implement trait Show for Buf<Tag>`.
 7 | implement trait Show for Buf {
   |                          ^~~ every parameter is written in an impl head
 8 |     show(&this) -> int { return 1; }
 9 | }
   |
 note: write the parameters: `implement<A> trait Show for Buf<A>`
 note: or a concrete instantiation: `implement trait Show for Buf<Tag>`

aborting due to 1 error

Pinned by E0302_impl_head_elides_all_params, E0302_impl_head_elides_trailing_param.

E0306 Trait Bound Not Satisfied

A where bound that the concrete type does not satisfy. Traits the compiler reasons about by identity are included: a type with a destructor is not Copy, whether the destructor comes from implement trait Drop or a drop in a separate implement block.

type trait Show { show(&this) -> i32; }

function display<T>(x: T) -> i32 where T: Show { return x.show(); }

function main() -> int {
    return display<i32>(5);
}
error[E0306]: the trait bound `i32: Show` is not satisfied
 --> main.cryo:6:12
   |
 1 | type trait Show { show(&this) -> i32; }
 2 |
 3 | function display<T>(x: T) -> i32 where T: Show { return x.show(); }
   |                                           ---- required by this bound in `display`
 4 |
 5 | function main() -> int {
 6 |     return display<i32>(5);
   |            ^^^^^^^^^^^^^^^ the trait bound `i32: Show` is not satisfied
 7 | }
 8 |
   |
 note: the trait `Show` is not implemented for `i32`
 help: implement `Show` for `i32`, or pass an argument of a type that already does

aborting due to 1 error

A bound on an associated type (type Item: Copy;) is checked against every impl's binding:

type trait Seq {
    type Item: Copy;
    next_one(&this) -> i32;
}

type struct NotCopy { p: i32*; }
implement trait Drop for struct NotCopy {
    destroy(&this) -> void {}
}

type struct Holder { v: i32; }

// `Item = NotCopy` violates the trait's `Item: Copy` bound.
implement trait Seq<NotCopy> for struct Holder {
    next_one(&this) -> i32 { return this.v; }
}

function main() -> int { return 0; }
error[E0306]: associated type `Item` of trait `main::Seq` is bound to `main::NotCopy`, which does not satisfy the declared bound `Item: Copy`
 --> main.cryo:14:41
    |
 12 | 
 13 | // `Item = NotCopy` violates the trait's `Item: Copy` bound.
 14 | implement trait Seq<NotCopy> for struct Holder {
    |                                         ^~~~~~ associated type `Item` of trait `main::Seq` is bound to `main::NotCopy`, which does not satisfy the declared bound `Item: Copy`
 15 |     next_one(&this) -> i32 { return this.v; }
 16 | }
    |

aborting due to 1 error

Pinned by E0306_assoc_decl_bound, E0306_separate_block_drop_not_copy, E0306_trait_bound.

E0307 Cannot Infer Type Argument

A type argument that cannot be recovered from context. An ![implicit] function recovers its type arguments from the expected type of the call, so a call in statement position — with no expected type — has nothing to go on. A generic owner's static constructor called with only a polymorphic literal (Wrap::new(0)) is the other case: the literal cannot pick T, and defaulting it to i32 was the trap this code replaced.

import std::core::default;
import std::core::default::{ Default };

![implicit]
function make<T>() -> T where T: Default {
    return T::default();
}

function main() -> int {
    // Nothing says what `T` is: write `make<i32>()` or bind to a typed local.
    make();
    return 0;
}
error[E0307]: cannot infer the type argument for `make`
 --> main.cryo:11:5
    |
  9 | function main() -> int {
 10 |     // Nothing says what `T` is: write `make<i32>()` or bind to a typed local.
 11 |     make();
    |     ^~~~~~ cannot infer the type argument for `make`
 12 |     return 0;
 13 | }
    |
 note: `![implicit]` recovers the type argument from the expected type, but this call is not in a typed context (e.g. a `const x: T = ...` initializer)
 help: write the type argument explicitly, e.g. `make<T>(...)`

aborting due to 1 error
type struct Wrap<T> {
    v: T;
    static new(x: T) -> Wrap<T> { return Wrap<T> { v: x }; }
}

function main() -> int {
    // `0` alone cannot pick `T`: write `Wrap<i64>::new(0)`.
    Wrap::new(0);
    return 0;
}
error[E0307]: cannot infer the owner type argument for `Wrap::new` from a polymorphic literal
 --> main.cryo:8:5
    |
  6 | function main() -> int {
  7 |     // `0` alone cannot pick `T`: write `Wrap<i64>::new(0)`.
  8 |     Wrap::new(0);
    |     ^~~~~~~~~~~~ cannot infer the owner type argument for `Wrap::new` from a polymorphic literal
  9 |     return 0;
 10 | }
    |
 note: the owner type argument would default to `i32` from an unsuffixed literal; a bare literal is no longer accepted as the only source for a generic owner's type argument
 help: write the owner type argument explicitly, e.g. `Wrap<i64>::new(...)`, or give the call an expected type (`const x: Wrap<i64> = ...`)

aborting due to 1 error

Pinned by E0307_cannot_infer_type_arg, E0307_owner_literal_default.

E0308 Conflicting Trait Implementation

A trait implemented twice for the same type. Coherence keys on the whole impl head — trait and its arguments, target and its arguments — resolved to type identity, so int and i32 collide, while Conv<i8> and Conv<i16> for one type are two distinct impls.

type trait Greet { greet(&this) -> int; }
type struct Person { age: int; }

implement trait Greet for struct Person { greet(&this) -> int { return 1; } }
implement trait Greet for struct Person { greet(&this) -> int { return 2; } }

function main() -> int { return 0; }
error[E0308]: conflicting implementations of trait `Greet` for type `main::Person`: a trait may be implemented at most once per type, including across modules
 --> main.cryo:5:1
   |
 3 | 
 4 | implement trait Greet for struct Person { greet(&this) -> int { return 1; } }
 5 | implement trait Greet for struct Person { greet(&this) -> int { return 2; } }
   | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ conflicting implementations of trait `Greet` for type `main::Person`: a trait may be implemented at most once per type, including across modules
 6 | 
 7 | function main() -> int { return 0; }
   |

aborting due to 1 error
type trait Show { show(&this) -> int; }

// `int` is an alias of `i32`, so these are the same impl twice.
implement trait Show for int { show(&this) -> int { return 1; } }
implement trait Show for i32 { show(&this) -> int { return 2; } }

function main() -> int { return 0; }
error[E0308]: conflicting implementations of trait `Show` for type `i32`: a trait may be implemented at most once per type, including across modules
 --> main.cryo:5:1
   |
 3 | // `int` is an alias of `i32`, so these are the same impl twice.
 4 | implement trait Show for int { show(&this) -> int { return 1; } }
 5 | implement trait Show for i32 { show(&this) -> int { return 2; } }
   | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ conflicting implementations of trait `Show` for type `i32`: a trait may be implemented at most once per type, including across modules
 6 | 
 7 | function main() -> int { return 0; }
   |

aborting due to 1 error

Pinned by E0308_conflicting_trait_impl, E0308_generic_trait_duplicate, E0308_primitive_alias_duplicate.

E0309 Associated Type Not Bound

An impl of a trait that declares an associated type binds it nowhere — neither positionally (implement trait Seq<i32> for ..., which is sugar for Seq<Item = i32>) nor with a type Item = i32; line in the body.

type trait Seq {
    type Item;
    next_one(&this) -> i32;
}

type struct Nums { cur: i32; }

// Neither `Seq<i32>` nor a `type Item = i32;` line binds `Item`.
implement trait Seq for struct Nums {
    next_one(&this) -> i32 { return this.cur; }
}

function main() -> int { return 0; }
error[E0309]: associated type `Item` not bound in this impl of trait `Seq`
 --> main.cryo:9:32
    |
  7 | 
  8 | // Neither `Seq<i32>` nor a `type Item = i32;` line binds `Item`.
  9 | implement trait Seq for struct Nums {
    |                                ^~~~ associated type `Item` not bound in this impl of trait `Seq`
 10 |     next_one(&this) -> i32 { return this.cur; }
 11 | }
    |

aborting due to 1 error

Pinned by E0309_assoc_type_not_bound.

E0310 Positional Associated Type With Generic Parameters

A trait with both generic parameters and an associated type has its associated type bound positionally. Positional arguments fill the generic parameters in order, so the associated type has to be bound by name: Conv<i32, Out = i64>.

type trait Conv<G> {
    type Out;
    run(&this) -> i32;
}

type struct Widget { v: i32; }

// `Conv<G>` has a generic parameter, so `Out` must be bound by name:
// `Conv<i32, Out = i64>`.
implement trait Conv<i32, i64> for struct Widget {
    run(&this) -> i32 { return this.v; }
}

function main() -> int { return 0; }
error[E0310]: trait `Conv` has generic parameters; its positional args bind only the generic params, so bind associated type `Out` in the impl body (`type Out = ...;`)
 --> main.cryo:10:43
    |
  8 | // `Conv<G>` has a generic parameter, so `Out` must be bound by name:
  9 | // `Conv<i32, Out = i64>`.
 10 | implement trait Conv<i32, i64> for struct Widget {
    |                                           ^~~~~~ trait `Conv` has generic parameters; its positional args bind only the generic params, so bind associated type `Out` in the impl body (`type Out = ...;`)
 11 |     run(&this) -> i32 { return this.v; }
 12 | }
    |

aborting due to 1 error

Pinned by E0310_positional_assoc_with_generics.

Structs and classes

CodeTitle
E0350Struct Field Not Found
E0351Class Member Not Foundreserved
E0352Constructor Not Foundreserved
E0353Private Access Violation
E0354Abstract Method Callreserved
E0355Missing Field Initialization
E0356Duplicate Field
E0357Invalid Instantiationreserved
E0358Undefined Method Implementation
E0359Protected Not Allowed in Struct
E0360Missing Visibility Block
E0361Destructure on Non-Struct Type
E0362Destructure Pattern Incomplete
E0363Union Literal Must Initialize Exactly One Field
E0364async Not Allowed on This Member
E0365async function main Has an Unsupported Signature

E0350 Struct Field Not Found

A struct literal naming a field the struct does not have. (Reading a missing field is E0204.)

E0353 Private Access Violation

Access to a private or protected member from outside where it is visible. A private field or method is reachable only from the declaring type's own methods — not from free functions in the same module — and a protected member only from the class and its subclasses. Naming a private field in a struct literal counts as access.

type struct Account {
    private: balance: i32;
    public: static open() -> Account { return Account { balance: 0 }; }
}

function main() -> int {
    const a: Account = Account::open();
    return a.balance;
}
error[E0353]: field `balance` of `main::Account` is private
 --> main.cryo:8:14
    |
  6 | function main() -> int {
  7 |     const a: Account = Account::open();
  8 |     return a.balance;
    |              ^~~~~~~ private field accessed outside `main::Account`
  9 | }
    |

aborting due to 1 error
type struct Account {
    private: balance: i32;
    public:  id: i32;
}

function main() -> int {
    // Naming a private field in a literal from outside the type is the
    // same violation as reading it.
    const a: Account = Account { balance: 1, id: 2 };
    return a.id;
}
error[E0353]: field `balance` of `main::Account` is private
 --> main.cryo:9:34
    |
  7 |     // Naming a private field in a literal from outside the type is the
  8 |     // same violation as reading it.
  9 |     const a: Account = Account { balance: 1, id: 2 };
    |                                  ^~~~~~~~~~ private field accessed outside `main::Account`
 10 |     return a.id;
 11 | }
    |

aborting due to 1 error
type class Account {
protected:
    balance: i32;
public:
    Account(b: i32) { this.balance = b; }
}

function main() -> int {
    const a: Account* = new Account(5);
    // `protected` reaches the class and its subclasses only.
    return a.balance;
}
error[E0353]: field `balance` of `main::Account` is protected
 --> main.cryo:11:14
    |
  9 |     const a: Account* = new Account(5);
 10 |     // `protected` reaches the class and its subclasses only.
 11 |     return a.balance;
    |              ^~~~~~~ protected field accessed outside `main::Account`
 12 | }
    |

aborting due to 1 error

The same code covers a private function or static called from another module.

Pinned by E0353_private_field_access, E0353_private_field_async_foreign, E0353_private_field_literal, E0353_protected_outside_subclass, visibility_gate, visibility_import_gate, visibility_static_gate, visibility_value_gate.

E0355 Missing Field Initialization

A struct literal that omits a field. A field with a default may be omitted; one without must be supplied, and a default on one field does not loosen the rule for the others.

type struct Point { x: i32; y: i32; }

function main() -> int {
    const p: Point = Point { x: 1 };
    return p.x;
}
error[E0355]: missing field `y` in initializer of `main::Point`
 --> main.cryo:4:22
   |
 2 | 
 3 | function main() -> int {
 4 |     const p: Point = Point { x: 1 };
   |                      ^~~~~~~~~~~~~~ missing field `y`
 5 |     return p.x;
 6 | }
   |

aborting due to 1 error
type struct Config { retries: i32 = 5; name: string; }

function main() -> int {
    // `retries` has a default; `name` does not, so it must be supplied.
    const c: Config = Config { retries: 1 };
    return c.retries;
}
error[E0355]: missing field `name` in initializer of `main::Config`
 --> main.cryo:5:23
   |
 3 | function main() -> int {
 4 |     // `retries` has a default; `name` does not, so it must be supplied.
 5 |     const c: Config = Config { retries: 1 };
   |                       ^~~~~~~~~~~~~~~~~~~~~ missing field `name`
 6 |     return c.retries;
 7 | }
   |

aborting due to 1 error

Pinned by E0355_missing_field_init, E0355_missing_field_no_default.

E0356 Duplicate Field

Two fields with the same name in one struct.

type struct Pair { a: i32; a: i32; }

function main() -> int { return 0; }
error[E0356]: duplicate field `a` in `Pair`
 --> main.cryo:1:28
   |
 1 | type struct Pair { a: i32; a: i32; }
   |                            ^~~~~~~ duplicate field `a` in `Pair`
 2 | 
 3 | function main() -> int { return 0; }
   |

aborting due to 1 error

Pinned by E0356_duplicate_field.

E0358 Undefined Method Implementation

A method call that resolves to nothing on the receiver's type. Besides the plain typo, this is what a where bound looks like when it is not met: Array::append is only defined where T: Copy, so on an Array<String> there is no append to call. A for (x in expr) loop lowers to .next() on the scrutinee, so a scrutinee that is neither an Iterator nor exposes iter() reports here too.

import std::fmt;

function main() -> int {
    const n: i32 = 42;
    // `for (x in expr)` needs `expr` to be an `Iterator` or expose `iter()`.
    for (i in n) {
        fmt::printf("%d\n", i);
    }
    return 0;
}
error[E0358]: no method named `next` found on type `i32`
 --> main.cryo:6:5
   |
 4 |     const n: i32 = 42;
 5 |     // `for (x in expr)` needs `expr` to be an `Iterator` or expose `iter()`.
 6 |     for (i in n) {
   |     ^ no method named `next` found on type `i32`
 7 |         fmt::printf("%d\n", i);
 8 |     }
   |
 note: the method `next` is defined on trait `std::core::iter::Iterator`
 help: implement `std::core::iter::Iterator` for `i32`, or change the receiver to a type that already does

aborting due to 1 error
import std::collections::string;
import std::collections::string::{ String };
import std::collections::str;

function main() -> int {
    mut dst: Array<String> = Array::new();
    mut src: Array<String> = Array::new();
    // `Array::append` is gated `where T: Copy`, and `String` is not `Copy`.
    dst.append(src.as_slice());
    return 0;
}
error[E0358]: no method named `append` found on type `std::collections::array::Array<std::collections::string::String<std::alloc::allocator::GlobalAlloc>, std::alloc::allocator::GlobalAlloc>`
 --> main.cryo:9:9
    |
  7 |     mut src: Array<String> = Array::new();
  8 |     // `Array::append` is gated `where T: Copy`, and `String` is not `Copy`.
  9 |     dst.append(src.as_slice());
    |         ^~~~~~ no method named `append` found on type `std::collections::array::Array<std::collections::string::String<std::alloc::allocator::GlobalAlloc>, std::alloc::allocator::GlobalAlloc>`
 10 |     return 0;
 11 | }
    |

aborting due to 1 error

Pinned by E0358_for_in_non_iterator, E0358_hashmap_get_noncopy, E0358_iter_min_non_ord, E0358_push_slice_non_copy, projection_bound_leaf_collision, trait_default_leaf_collision.

E0359 Protected Not Allowed in Struct

protected: inside a struct. Protected is a class notion — it is about subclasses, and structs have none.

type struct Account {
protected:
    balance: i32;
}

function main() -> int { return 0; }
error[E0359]: `protected:` is not allowed in structs (structs do not support inheritance)
 --> main.cryo:2:1
   |
 1 | type struct Account {
 2 | protected:
   | ^~~~~~~~~ `protected:` is not allowed in structs (structs do not support inheritance)
 3 |     balance: i32;
 4 | }
   |
 suggestion [maybe]: change to `public:`
   |
 2 | public:
   | ~~~~~~

aborting due to 1 error

Pinned by E0359_protected_in_struct.

E0360 Missing Visibility Block

A class body with members but no visibility block. Unlike a struct, a class lists its members under explicit public:, private:, or protected: sections.

type class Animal {
    name: i32;
}

function main() -> int { return 0; }
error[E0360]: class members must declare visibility (block-form `public:` / `private:` / `protected:`, or inline `public` / `private` / `protected`)
 --> main.cryo:2:5
   |
 1 | type class Animal {
 2 |     name: i32;
   |     ^~~~ class members must declare visibility (block-form `public:` / `private:` / `protected:`, or inline `public` / `private` / `protected`)
 3 | }
   |

aborting due to 1 error

Pinned by E0360_missing_visibility_block.

E0361 Destructure on Non-Struct Type

A destructuring binding whose type is not a struct or class.

function main() -> int {
    const { x, y }: i32 = 5;
    return x;
}
error[E0361]: cannot destructure non-struct type 'i32'
 --> main.cryo:2:5
   |
 1 | function main() -> int {
 2 |     const { x, y }: i32 = 5;
   |     ^~~~~~~~~~~~~~~~~~~~~~~~ cannot destructure non-struct type 'i32'
 3 |     return x;
 4 | }
   |

error[E0200]: mismatched types
 --> main.cryo:3:12
   |
 1 | function main() -> int {
   |                    --- expected due to this
 2 |     const { x, y }: i32 = 5;
 3 |     return x;
   |            ^ expected `i32`, found `void`
 4 | }
 5 |
   |
 note: the function's declared return type doesn't match the value returned here

aborting due to 2 errors

Pinned by E0361_destructure_non_struct.

E0362 Destructure Pattern Incomplete

A destructuring pattern that does not name every field. Bind the ones you do not need to _.

type struct Point { x: i32; y: i32; }

function main() -> int {
    const p: Point = Point { x: 1, y: 2 };
    const { x }: Point = p;
    return x;
}
error[E0362]: destructure of `main::Point` must bind all 2 field(s); missing: y
 --> main.cryo:5:5
   |
 3 | function main() -> int {
 4 |     const p: Point = Point { x: 1, y: 2 };
 5 |     const { x }: Point = p;
   |     ^~~~~~~~~~~~~~~~~~~~~~~ missing 1 field
 6 |     return x;
 7 | }
   |

aborting due to 1 error

E0363 Union Literal Must Initialize Exactly One Field

A union literal that initializes zero fields or more than one. A union is one-of-N storage, so a literal sets exactly one.

type union Number {
    i: i32;
    f: f32;
}

function main() -> int {
    // A union literal initialises exactly one field.
    const n: Number = Number { i: 1, f: 2.0 };
    return 0;
}
error[E0363]: union literal of `main::Number` must initialize exactly one field, found 2
 --> main.cryo:8:23
    |
  6 | function main() -> int {
  7 |     // A union literal initialises exactly one field.
  8 |     const n: Number = Number { i: 1, f: 2.0 };
    |                       ^~~~~~~~~~~~~~~~~~~~~~~ more than one field initialized
  9 |     return 0;
 10 | }
    |

aborting due to 1 error

Pinned by E0363_union_literal_arity.

E0364 `async` Not Allowed on This Member

async on a member it cannot apply to. Only a method can be async, and not a virtual or override one: each implementation's future is a distinct type, so overrides would have no common signature to share a vtable slot. There are no async constructors (the object does not exist until the constructor returns), destructors (drop runs from contexts with no executor), or fields.

import std::future;

type class Worker {
public:
    v: i64;

    public virtual async run(&this) -> i64 { return this.v; }
}

function main() -> int { return 0; }
error[E0364]: a `virtual` or `override` method cannot be `async`: each implementation returns its own future type, so the overrides have no common return type to dispatch through
 --> main.cryo:7:26
   |
 5 |     v: i64;
 6 | 
 7 |     public virtual async run(&this) -> i64 { return this.v; }
   |                          ^~~ a `virtual` or `override` method cannot be `async`: each implementation returns its own future type, so the overrides have no common return type to dispatch through
 8 | }
   |

aborting due to 1 error
import std::future;

type struct Task {
    async n: i64;
}

function main() -> int { return 0; }
error[E0364]: only a method can be `async`; a field holds a value, not a computation to suspend
 --> main.cryo:4:5
   |
 2 | 
 3 | type struct Task {
 4 |     async n: i64;
   |     ^~~~~ only a method can be `async`; a field holds a value, not a computation to suspend
 5 | }
   |

aborting due to 1 error

Pinned by E0364_async_constructor, E0364_async_destructor, E0364_async_field, E0364_async_virtual_method.

E0365 `async function main` Has an Unsupported Signature

An async function main with a signature the entry point cannot have. Its value is the process exit status, so it returns i32 or void; and it cannot be generic, because the runtime calls it with no type arguments.

import std::future;

// The exit status is an `i32`; an async `main` returns `i32` or `void`.
async function main() -> i64 {
    return 0;
}
error[E0365]: `async function main` must return `i32` or `void`; found `i64`
 --> main.cryo:4:7
   |
 2 | 
 3 | // The exit status is an `i32`; an async `main` returns `i32` or `void`.
 4 | async function main() -> i64 {
   |       ^ `async function main` must return `i32` or `void`; found `i64`
 5 |     return 0;
 6 | }
   |

aborting due to 1 error

Pinned by E0365_async_main_bad_return, E0365_async_main_generic.

Control flow

CodeTitle
E0400Invalid Break
E0401Invalid Continue
E0402Invalid Returnreserved
E0403Missing Return
E0404Unreachable Patternreserved
E0405Non-Exhaustive Match
E0406Unknown Enum Variant

E0400 Invalid Break

break outside a loop.

function main() -> int {
    break;
    return 0;
}
error[E0400]: `break` outside of a loop
 --> main.cryo:2:5
   |
 1 | function main() -> int {
 2 |     break;
   |     ^~~~~~ `break` outside of a loop
 3 |     return 0;
 4 | }
   |

aborting due to 1 error

Pinned by E0400_break_outside_loop.

E0401 Invalid Continue

continue outside a loop.

function main() -> int {
    continue;
    return 0;
}
error[E0401]: `continue` outside of a loop
 --> main.cryo:2:5
   |
 1 | function main() -> int {
 2 |     continue;
   |     ^~~~~~~~~ `continue` outside of a loop
 3 |     return 0;
 4 | }
   |

aborting due to 1 error

Pinned by E0401_continue_outside_loop.

E0403 Missing Return

A function declared to return a value that can reach the end of its body without returning one.

function answer() -> i32 { }

function main() -> int { return 0; }
error[E0403]: function `answer` is declared to return a value but reaches the end of its body without returning
 --> main.cryo:1:1
   |
 1 | function answer() -> i32 { }
   | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ function `answer` is declared to return a value but reaches the end of its body without returning
 2 | 
 3 | function main() -> int { return 0; }
   |
 note: every control-flow path through the body must end in `return <expr>`, a divergent call (e.g. `panic(...)`), or an infinite `loop { ... }` with no `break`
 note: if a missing path is unreachable, end it with `return <default>` or `panic("unreachable")`

aborting due to 1 error

Pinned by E0403_missing_return.

E0405 Non-Exhaustive Match

A match that does not cover every variant of its subject. A guarded arm (Red if (...)) matches conditionally and does not count as covering its variant; a nested pattern that narrows a payload (Wrap(Inner::A)) covers only that narrowing. The suggestion adds arms for the missing variants.

type enum Colour { Red; Green; Blue; }

function main() -> int {
    const c: Colour = Colour::Red;
    match (c) {
        Colour::Red   => {}
        Colour::Green => {}
    }
    return 0;
}
error[E0405]: non-exhaustive match: not all variants of `main::Colour` are covered
 --> main.cryo:5:5
   |
 3 | function main() -> int {
 4 |     const c: Colour = Colour::Red;
 5 |     match (c) {
   |     ^      - this subject's enum type has variants not handled below
   |     |
   |     non-exhaustive match: not all variants of `main::Colour` are covered
 6 |         Colour::Red   => {}
 7 |         Colour::Green => {}
   |
 note: missing variant: `Blue`
 note: add explicit arms for the missing variants, or use `_ => { ... }` to catch the rest
 suggestion [has-placeholders]: add an arm for the missing variant
   |
 6 |         Colour::Red   => {}
 7 ~         Colour::Green => {}
   +         Colour::Blue => { }
   |         +++++++++++++++++++
 8 |     }
 9 |     return 0;
   |

aborting due to 1 error
type enum Colour { Red; Green; Blue; }

function main() -> int {
    const c: Colour = Colour::Green;
    match (c) {
        // A guarded arm matches conditionally, so `Red` is not covered.
        Colour::Red if (true) => {}
        Colour::Green         => {}
        Colour::Blue          => {}
    }
    return 0;
}
error[E0405]: non-exhaustive match: not all variants of `main::Colour` are covered
 --> main.cryo:5:5
   |
 3 | function main() -> int {
 4 |     const c: Colour = Colour::Green;
 5 |     match (c) {
   |     ^      - this subject's enum type has variants not handled below
   |     |
   |     non-exhaustive match: not all variants of `main::Colour` are covered
 6 |         // A guarded arm matches conditionally, so `Red` is not covered.
 7 |         Colour::Red if (true) => {}
   |
 note: missing variant: `Red`
 note: add explicit arms for the missing variants, or use `_ => { ... }` to catch the rest
 suggestion [has-placeholders]: add an arm for the missing variant
    |
  8 |         Colour::Green         => {}
  9 ~         Colour::Blue          => {}
    +         Colour::Red => { }
    |         ++++++++++++++++++
 10 |     }
 11 |     return 0;
    |

aborting due to 1 error

Pinned by E0405_guarded_arm_non_exhaustive, E0405_nested_incomplete, E0405_non_exhaustive_match.

E0406 Unknown Enum Variant

A pattern naming a variant the enum does not have. Such an arm could never match, and before this check existed a typo here was silently dead code — with the real variant left uncovered, which is why E0405 follows it in the output.

type enum Shape {
    Circle(int);
    Square(int);
}

function main() -> int {
    const s: Shape = Shape::Circle(1);
    match (s) {
        Shape::Circle(r) => { return 1; }
        Shape::Squre(w)  => { return 2; }
    }
    return 0;
}
error[E0406]: enum `main::Shape` has no variant named `Squre`
 --> main.cryo:10:16
    |
  8 |     match (s) {
  9 |         Shape::Circle(r) => { return 1; }
 10 |         Shape::Squre(w)  => { return 2; }
    |                ^~~~~ not a variant of this enum
 11 |     }
 12 |     return 0;
    |
 help: a variant with a similar name exists: `Square`
 note: a pattern naming a variant that does not exist never matches, so the arm is dead code
 suggestion [machine-applicable]: did you mean `Square`?
    |
 10 |         Shape::Square(w)  => { return 2; }
    |                ~~~~~~

error[E0405]: non-exhaustive match: not all variants of `main::Shape` are covered
 --> main.cryo:8:5
    |
  6 | function main() -> int {
  7 |     const s: Shape = Shape::Circle(1);
  8 |     match (s) {
    |     ^      - this subject's enum type has variants not handled below
    |     |
    |     non-exhaustive match: not all variants of `main::Shape` are covered
  9 |         Shape::Circle(r) => { return 1; }
 10 |         Shape::Squre(w)  => { return 2; }
    |
 note: missing variant: `Square`
 note: add explicit arms for the missing variants, or use `_ => { ... }` to catch the rest
 suggestion [has-placeholders]: add an arm for the missing variant
    |
  9 |         Shape::Circle(r) => { return 1; }
 10 ~         Shape::Squre(w)  => { return 2; }
    +         Shape::Square => { }
    |         ++++++++++++++++++++
 11 |     }
 12 |     return 0;
    |

aborting due to 2 errors

Pinned by E0406_unknown_enum_variant.

Memory and ownership

Cryo's ownership model is deliberately smaller than Rust's — Copy, Drop, and a flow-sensitive move check, with no borrow checker and no lifetimes — but the move check is a hard error. These codes are its refusals, and every one of them stands between the program and a double free or a dangling pointer.

CodeTitle
E0452Use After Move
E0453Double Free
E0454Memory Leakreserved
E0455Dangling Pointer
E0456Conditional Move
E0457Non-Copy Capturereserved
E0458Closure Argument Outside Free-Function Call
E0459Future Moved After Being Polled

E0452 Use After Move

A non-Copy value used after it was moved. Moving happens at a by-value call argument, a by-value constructor argument, an assignment or initializer that takes the value, and an explicit .drop(); afterwards the original binding is gone. Branches merge conservatively — a value moved on one path of an if is possibly-moved after the join, and a value moved inside a loop is moved again on the next iteration — because the compiler tracks whole bindings, not paths through them. unsafe does not switch the check off.

import std::ffi::libc;

type struct Heap { p: void*; }

implement trait Drop for struct Heap {
    drop(mut &this) -> void { libc::free(this.p); }
}

function consume(h: Heap) -> void {}

function main() -> int {
    mut a: Heap = Heap { p: libc::malloc(16) };
    consume(a);        // `a` moves into the callee, which drops it
    mut b: Heap = a;   // and is then read again
    return 0;
}
error[E0452]: use of moved value 'a'
 --> main.cryo:14:19
    |
 11 | function main() -> int {
 12 |     mut a: Heap = Heap { p: libc::malloc(16) };
 13 |     consume(a);        // `a` moves into the callee, which drops it
    |             - value moved here
 14 |     mut b: Heap = a;   // and is then read again
    |                   ^ value used here after move
 15 |     return 0;
 16 | }
    |

aborting due to 1 error
import std::ffi::libc;

type struct Heap { p: void*; }

implement trait Drop for struct Heap {
    drop(mut &this) -> void { libc::free(this.p); }
}

function consume(h: Heap) -> void {}

function main() -> int {
    mut h: Heap = Heap { p: libc::malloc(16) };
    if (1 > 0) { consume(h); }   // moved on one path...
    consume(h);                  // ...so it is possibly-moved after the join
    return 0;
}
error[E0452]: use of moved value 'h'
 --> main.cryo:14:13
    |
 11 | function main() -> int {
 12 |     mut h: Heap = Heap { p: libc::malloc(16) };
 13 |     if (1 > 0) { consume(h); }   // moved on one path...
    |                          - value moved here
 14 |     consume(h);                  // ...so it is possibly-moved after the join
    |             ^ value used here after move
 15 |     return 0;
 16 | }
    |

aborting due to 1 error
import std::ffi::libc;

type struct Heap { p: void*; }

implement trait Drop for struct Heap {
    drop(mut &this) -> void { libc::free(this.p); }
}

function main() -> int {
    mut a: Heap = Heap { p: libc::malloc(16) };
    for (mut i: int = 0; i < 3; i++) {
        mut b: Heap = a;   // moves `a` on every iteration
    }
    return 0;
}
error[E0452]: value 'a' moved inside a loop is used again on the next iteration
 --> main.cryo:12:23
    |
 10 |     mut a: Heap = Heap { p: libc::malloc(16) };
 11 |     for (mut i: int = 0; i < 3; i++) {
 12 |         mut b: Heap = a;   // moves `a` on every iteration
    |                       ^ moved here, then re-read on the next iteration (double free)
 13 |     }
 14 |     return 0;
    |
 help: declare the value inside the loop, reassign it after the move, or move out of a per-iteration binding

aborting due to 1 error

A second .drop() is a use after the first one moved the value:

type class Holder {
public:
    a: i32;
    drop(mut &this) -> void {}
}

function main() -> int {
    mut h: Holder = Holder { a: 1 };
    h.drop();
    h.drop();
    return 0;
}
warning[W0015]: explicit destructor call; the value is released at scope exit, and an aggregate's fields are released after its own `drop` runs. To release EARLY, write `mem::drop(x)`, which also stops the value being used afterwards. This is a warning while the tree is being cleaned up and will become an error
 --> main.cryo:9:5
    |
  7 | function main() -> int {
  8 |     mut h: Holder = Holder { a: 1 };
  9 |     h.drop();
    |     ^~~~~~~~ explicit destructor call; the value is released at scope exit, and an aggregate's fields are released after its own `drop` runs. To release EARLY, write `mem::drop(x)`, which also stops the value being used afterwards. This is a warning while the tree is being cleaned up and will become an error
 10 |     h.drop();
 11 |     return 0;
    |

warning[W0015]: explicit destructor call; the value is released at scope exit, and an aggregate's fields are released after its own `drop` runs. To release EARLY, write `mem::drop(x)`, which also stops the value being used afterwards. This is a warning while the tree is being cleaned up and will become an error
 --> main.cryo:10:5
    |
  8 |     mut h: Holder = Holder { a: 1 };
  9 |     h.drop();
 10 |     h.drop();
    |     ^~~~~~~~ explicit destructor call; the value is released at scope exit, and an aggregate's fields are released after its own `drop` runs. To release EARLY, write `mem::drop(x)`, which also stops the value being used afterwards. This is a warning while the tree is being cleaned up and will become an error
 11 |     return 0;
 12 | }
    |

error[E0452]: use of moved value 'h'
 --> main.cryo:10:5
    |
  7 | function main() -> int {
  8 |     mut h: Holder = Holder { a: 1 };
  9 |     h.drop();
    |     - value moved here
 10 |     h.drop();
    |     ^ value used here after move
 11 |     return 0;
 12 | }
    |

aborting due to 1 error; 2 warnings emitted

Pinned by E0452_assign_rhs_use_after_move, E0452_async_giveaway_then_borrow_same_state, E0452_branch_revive_one_path, E0452_double_drop, E0452_if_branch_move_then_use, E0452_loop_carried_byval_arg, E0452_loop_carried_move, E0452_match_arm_double_move, E0452_straight_line_use_after_move, E0452_use_after_move_in_unsafe, E0452_use_after_move_into_constructor.

E0453 Double Free

A move out of storage that still owns the value, so that both the original owner and the moved copy would run the destructor. Two shapes report it: moving a field out of a value that has a user-written Drop (its drop will free the field again), or in a branch (where the owner's scope-exit glue cannot know whether the field is gone); and moving a value out through a pointer (consume(*p)), where the compiler has no way to know whether the pointer's target is yours to empty. core::mem::swap is the escape hatch for the first; unsafe for the second, as a statement that the storage really is yours.

import std::ffi::libc;

type struct Heap { p: void*; }

implement trait Drop for struct Heap {
    drop(mut &this) -> void { libc::free(this.p); }
}

type struct Owner { h: Heap; tag: i32; }

implement trait Drop for struct Owner {
    drop(mut &this) -> void { }
}

function take(x: Heap) -> void {}

function main() -> int {
    mut o: Owner = Owner { h: Heap { p: libc::malloc(16) }, tag: 1 };
    // `o` still owns `h` and will drop it; so would `take`.
    take(o.h);
    return 0;
}
error[E0453]: cannot move field 'h' out of a value that owns a destructor
 --> main.cryo:20:10
    |
 18 |     mut o: Owner = Owner { h: Heap { p: libc::malloc(16) }, tag: 1 };
 19 |     // `o` still owns `h` and will drop it; so would `take`.
 20 |     take(o.h);
    |          ^~~ moving this field out leaves the source to drop it again at scope exit (double free)
 21 |     return 0;
 22 | }
    |
 help: swap the field out with `core::mem::swap`, leaving a valid value to be dropped in place; or use a `Copy` field type

aborting due to 1 error
type struct Res {
    id: i64;
    drop(mut &this) -> void { }
}

function consume(r: Res) -> i64 { return r.id; }

function main() -> int {
    mut a: Res = Res { id: 7 };
    mut p: Res* = &a;
    // `a` still owns the value; `consume` would drop a bitwise copy of it.
    const n: i64 = consume(*p);
    return n as int;
}
error[E0453]: cannot move a value that owns a destructor out of a pointer
 --> main.cryo:12:28
    |
 10 |     mut p: Res* = &a;
 11 |     // `a` still owns the value; `consume` would drop a bitwise copy of it.
 12 |     const n: i64 = consume(*p);
    |                            ^~ reading the pointee by value copies its owning handle; the storage this points at is still dropped by its owner (double free)
 13 |     return n as int;
 14 | }
    |
 help: borrow it instead (`p.field`, `p.method()`), or clone it; if this really is a take - the storage is yours to empty, or you overwrite it straight after - say so with an `unsafe` block around the sequence that makes it sound

aborting due to 1 error

Pinned by E0453_deref_move_out, E0453_deref_move_out_qualified_call, E0453_match_subject_field_move, E0453_move_field_out_of_aggregate, E0453_move_field_out_of_pattern_binding, E0453_partial_move_conditional, E0453_partial_move_drop_owner.

E0455 Dangling Pointer

A pointer that would outlive what it points at. Returning &local hands back an address into a stack frame that is freed on return (returning &this or &param is fine — those are caller-backed). A match arm's payload binding lives in a temporary, so a pointer to it cannot be returned either.

function dangling() -> i32* {
    mut x: i32 = 42;
    return &x;
}

function main() -> int { return 0; }
error[E0455]: cannot return the address of a local variable: it lives on this function's stack frame and is freed when the function returns. Return the value by move, or a heap allocation, instead.
 --> main.cryo:3:12
   |
 1 | function dangling() -> i32* {
 2 |     mut x: i32 = 42;
 3 |     return &x;
   |            ^~ cannot return the address of a local variable: it lives on this function's stack frame and is freed when the function returns. Return the value by move, or a heap allocation, instead.
 4 | }
   |

aborting due to 1 error
type struct Inner {
    x: i32;
    drop(mut &this) -> void { }
}

type enum Wrap {
    Has(Inner);
    Nothing;
}

implement enum Wrap {
    inner_ptr(&this) -> Inner* {
        match (this) {
            Wrap::Has(p)  => { return p; }   // `p` lives in a match temporary
            Wrap::Nothing => { return null; }
        }
    }
}

function main() -> int { return 0; }
error[E0200]: mismatched types
 --> main.cryo:14:39
    |
 10 |
 11 | implement enum Wrap {
 12 |     inner_ptr(&this) -> Inner* {
    |                                - expected due to this
 13 |         match (this) {
 14 |             Wrap::Has(p)  => { return p; }   // `p` lives in a match temporary
    |                                       ^ expected `main::Inner*`, found `main::Inner`
 15 |             Wrap::Nothing => { return null; }
 16 |         }
    |
 note: the function's declared return type doesn't match the value returned here

error[E0455]: cannot return a pointer or reference to a value bound by `match`: the payload lives in a temporary that is freed when the function returns. Return it by value (move the payload out) instead.
 --> main.cryo:14:39
    |
 12 |     inner_ptr(&this) -> Inner* {
 13 |         match (this) {
 14 |             Wrap::Has(p)  => { return p; }   // `p` lives in a match temporary
    |                                       ^ cannot return a pointer or reference to a value bound by `match`: the payload lives in a temporary that is freed when the function returns. Return it by value (move the payload out) instead.
 15 |             Wrap::Nothing => { return null; }
 16 |         }
    |

aborting due to 2 errors

Inside an async function, each poll runs on a fresh frame, so an address of a local held across an await is dangling by the time a later step uses it. The same code covers an async method awaited on a receiver that names no storage (a temporary or a call result), or whose future is stored and awaited later, since the future holds its receiver's address and has to be able to find it again on every poll.

import std::future;
import std::future::{ PendingThenReady };

async function fill() -> i64 {
    mut buf: u8[8] = [0; 8];
    const p: u8* = &buf[0];
    // Each poll runs on a fresh frame, so `p` dangles after the suspend.
    const _z: i64 = await PendingThenReady<i64>::new(2, 1);
    *p = 42;
    return 0;
}

function main() -> int { return 0; }
error[E0455]: async: `p` holds the address of the local or parameter `buf` and is live across an `await`; each poll runs on a fresh frame, so that address is dangling by the time a later step uses it — have the caller own the storage and pass a pointer parameter, whose pointee the suspend does not move
 --> main.cryo:6:5
   |
 4 | async function fill() -> i64 {
 5 |     mut buf: u8[8] = [0; 8];
 6 |     const p: u8* = &buf[0];
   |     ^~~~~~~~~~~~~~~~~~~~~~~ async: `p` holds the address of the local or parameter `buf` and is live across an `await`; each poll runs on a fresh frame, so that address is dangling by the time a later step uses it — have the caller own the storage and pass a pointer parameter, whose pointee the suspend does not move
 7 |     // Each poll runs on a fresh frame, so `p` dangles after the suspend.
 8 |     const _z: i64 = await PendingThenReady<i64>::new(2, 1);
   |

aborting due to 1 error
import std::future;
import std::future::{ PendingThenReady };

type struct Counter {
    base: i64;

    async bump(&this, n: i64) -> i64 {
        const a: i64 = await PendingThenReady<i64>::new(1, 10);
        return this.base + n + a;
    }
}

function make() -> Counter { return Counter { base: 100 }; }

async function drive() -> i64 {
    // The receiver is a temporary; bind it first: `mut c = make(); await c.bump(5);`
    return await make().bump(5);
}

function main() -> int { return 0; }
error[E0455]: async: this `await` calls an `async` method on a receiver that names no storage (a temporary, or the result of a call), so the receiver dies with the step that built the future; bind it to a local and await on that (`mut r = <expr>; await r.m(...);`)
 --> main.cryo:17:12
    |
 15 | async function drive() -> i64 {
 16 |     // The receiver is a temporary; bind it first: `mut c = make(); await c.bump(5);`
 17 |     return await make().bump(5);
    |            ^~~~~~~~~~~~~~~~~~~~ async: this `await` calls an `async` method on a receiver that names no storage (a temporary, or the result of a call), so the receiver dies with the step that built the future; bind it to a local and await on that (`mut r = <expr>; await r.m(...);`)
 18 | }
    |

aborting due to 1 error

Pinned by E0455_async_address_in_if_expression, E0455_async_address_into_awaited_future, E0455_async_method_value_receiver_address, E0455_async_pointer_outlives_local, E0455_async_stored_method_future, E0455_async_temporary_receiver, E0455_return_byvalue_param_binding, E0455_return_local_address, E0455_return_local_subject_binding, E0455_return_match_payload_borrow, E0455_return_temporary_subject_binding.

E0458 Closure Argument Outside Free-Function Call

A capturing closure passed somewhere other than a free function's (Args) -> Ret parameter. In v1.0 a closure that closes over a local is lowered by specializing the callee for it, and that specialization is wired for free-function calls only — a method's argument, a generic free function's function-typed parameter, or a call through a local function pointer would treat the closure struct as a code pointer. A lambda that captures nothing is a plain function pointer and passes anywhere.

import std::core::option;

function main() -> int {
    const bias: i32 = 10;
    const some: Option<i32> = Option::Some(7);
    // A capturing closure can only be passed to a free function's `(Args) -> Ret` parameter.
    const shifted: Option<i32> = some.map((n: i32) -> i32 { return n + bias; });
    return 0;
}
error[E0458]: passing a capturing closure as a method-call argument is not yet supported
 --> main.cryo:7:43
   |
 5 |     const some: Option<i32> = Option::Some(7);
 6 |     // A capturing closure can only be passed to a free function's `(Args) -> Ret` parameter.
 7 |     const shifted: Option<i32> = some.map((n: i32) -> i32 { return n + bias; });
   |                                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ this is a capturing closure (anonymous struct value)
 8 |     return 0;
 9 | }
   |
 note: v1.0 routes capturing closures through NON-GENERIC free-function `(Args) -> Ret` parameters only; generic free-function, method-call, and scope-resolution-call closure parameters are deferred
 note: workaround: pull the call out into a free function, or pre-extract the closure into a fn-pointer if the capture set is empty

aborting due to 1 error

Pinned by E0458_closure_into_fn_pointer_local, E0458_closure_into_generic_free_fn, E0458_closure_method_arg.

E0459 Future Moved After Being Polled

A future moved after it has been polled. Polling runs the state machine, which may take the address of one of its own fields and keep it across the suspend; moving it afterwards leaves that pointer naming storage the future no longer occupies. Handing an unpolled future to block_on, spawn, or join is how they are meant to be used and stays legal.

import std::future;
import std::future::{ Context, Ready, Waker };

async function work() -> i64 {
    return await Ready<i64>::new(7);
}

function main() -> int {
    mut f = work();
    mut cx: Context = Context::new(Waker::noop());
    const _first = f.poll(&cx);
    mut g = f;   // `f` has run: its address is fixed now
    return 0;
}
error[E0459]: future 'f' is moved after it has been polled
 --> main.cryo:12:13
    |
  9 |     mut f = work();
 10 |     mut cx: Context = Context::new(Waker::noop());
 11 |     const _first = f.poll(&cx);
    |                    ----------- polled here, fixing its address
 12 |     mut g = f;   // `f` has run: its address is fixed now
    |             ^ moved here
 13 |     return 0;
 14 | }
    |
 help: a future may hold a pointer into its own storage once it has run, so it must stay put; hand it over before the first poll (`block_on(f)`, `spawn(f)`, `Futures::join(a, b)`) rather than after

aborting due to 1 error

Pinned by E0459_future_moved_after_poll.

Modules and imports

CodeTitle
E0500Module Not Found
E0501Circular Import
E0502Invalid Import
E0503Private Symbol Access
E0504Namespace Conflictreserved
E0505Experimental Feature Not Enabled

E0500 Module Not Found

An import of a module that does not exist.

import std::nonexistent::thing;

function main() -> int { return 0; }
error[E0500]: cannot find module `std::nonexistent::thing`
 --> main.cryo:1:8
   |
 1 | import std::nonexistent::thing;
   |        ^~~~~~~~~~~~~~~~~~~~~~~ no module named `std::nonexistent::thing` is in scope
 2 | 
 3 | function main() -> int { return 0; }
   |
 help: imports resolve against the project root, the entry-point directory, and the standard library; module paths are case-sensitive

aborting due to 1 error
error: module discovery failed

Pinned by E0500_module_not_found.

E0501 Circular Import

A cycle in the module graph: two modules that import each other, directly or through others, so there is no order to compile them in.

E0502 Invalid Import

A braced import naming something the module does not offer — not a declaration, not a re-export, not a sub-module. It is refused at the import, and the message lists what the module does offer, rather than waiting for a use that may never come.

import std::fmt::{ printn };

function main() -> int { return 0; }
error[E0502]: `std::fmt` does not offer `printn`
 --> main.cryo:1:1
   |
 1 | import std::fmt::{ printn };
   | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ no declaration, re-export or sub-module of that name
 2 | 
 3 | function main() -> int { return 0; }
   |
 help: a name with a similar spelling is offered: `printf`
 note: `std::fmt` offers `format`, `printf`, `print`, `println`, `g_fmt_stderr`, `eprintf`, `eprint`, `eprintln`, `float`, `error`, `write`, `display` and 2 more

aborting due to 1 error

Pinned by E0502_import_names_nothing_offered.

E0503 Private Symbol Access

A private top-level type named from another module — in an annotation, a struct literal, a signature, or as the scope of a Type::member path. A private type remains fully usable inside its own module. This is the module-level axis of visibility; a private field is type-scoped and reports E0353.

Pinned by visibility_type_mask.

E0505 Experimental Feature Not Enabled

import vendor::... — the C++ binding generator — used without the experimental feature enabled.

Code generation

Codes in this range come from the LLVM back end. Most of them are internal: they describe a failure to lower something that the earlier phases had already accepted, and if one appears without any other error before it, that is a compiler bug — the message says so, and the thing to do is report it with the source that triggered it. A few describe situations the compiler can only see once it is instantiating a generic or lowering an async body, and those are the ones with entries.

CodeTitle
E0600Code Generation Failed
E0601LLVM Errorreserved
E0602Invalid LLVM Type
E0603Invalid LLVM Valuereserved
E0604Unimplemented Intrinsicreserved
E0605Optimization Failed
E0606Function Generation Error
E0607Variable Generation Error
E0608Intrinsic Generation Errorreserved
E0609Type Mapping Error
E0610Class Generation Errorreserved
E0611Enum Generation Errorreserved
E0612Struct Generation Errorreserved
E0613Control Flow Errorreserved
E0614Assignment Error
E0615Binary Operation Errorreserved
E0616Unary Operation Errorreserved
E0617Memory Operation Errorreserved
E0618Constructor Generation Errorreserved
E0619Method Generation Error
E0620Module Context Errorreserved
E0621Array Operation Error
E0622Member Access Error
E0623Scope Resolution Errorreserved
E0624Exception Handler Errorreserved
E0625Literal Generation Errorreserved
E0626Cast Operation Errorreserved
E0627Loop Generation Errorreserved
E0628Pattern Matching Error
E0629Template Instantiation Errorreserved
E0630Optimization Errorreserved
E0631Enum Operation Errorreserved
E0632Type Resolution Errorreserved
E0633Function Body Error
E0634Variable Initialization Errorreserved
E0635Type Constructor Undefined
E0636Undefined Function Call
E0637Invalid Generic Instantiationreserved
E0638Invalid Struct Initialization
E0639Invalid Class Instantiationreserved
E0640Invalid Enum Variant Access
E0641Null Cast Expressionreserved
E0642Parameter Type Error
E0643Return Type Error
E0644Field Type Errorreserved
E0645No Matching static match Arm

E0600 Code Generation Failed

A construct that has no lowering. Today these are all async: a future that would contain itself (an async function awaiting itself, directly or through a cycle of other async functions — rewrite the recursion as a loop); an await inside an unsafe block or a static match arm, which the state-machine lowering would split across states; and a match whose arm guard awaits binding an owning payload out of the subject, which a failed guard would then re-test with its contents taken. The message says which, and how to rewrite it.

import std::future;
import std::future::{ PendingThenReady };

async function countdown(n: i64) -> i64 {
    const _z: i64 = await PendingThenReady<i64>::new(1, 1);
    if (n <= 0) { return 0; }
    // The future would have to contain itself.
    const rest: i64 = await countdown(n - 1);
    return rest + 1;
}

function main() -> int { return 0; }
error[E0600]: async: this `await` would make the state machine contain itself, which has no finite size (the awaited future leads back to this one); rewrite the recursion as a loop
 --> main.cryo:8:23
    |
  6 |     if (n <= 0) { return 0; }
  7 |     // The future would have to contain itself.
  8 |     const rest: i64 = await countdown(n - 1);
    |                       ^~~~~~~~~~~~~~~~~~~~~~ async: this `await` would make the state machine contain itself, which has no finite size (the awaited future leads back to this one); rewrite the recursion as a loop
  9 |     return rest + 1;
 10 | }
    |

aborting due to 1 error
import std::future;
import std::future::{ PendingThenReady };

async function ready(v: i64) -> i64 {
    return await PendingThenReady<i64>::new(1, v);
}

async function inside_unsafe() -> i64 {
    unsafe {
        // Lift the `await` out: `const v = await ready(41); unsafe { ... }`.
        const v: i64 = await ready(41);
        return v + 1;
    }
}

function main() -> int { return 0; }
error[E0600]: async: an `await` inside an `unsafe` block is not supported — the block is split at each resume point, so the `unsafe` scope cannot span it; move the `await` ahead of the block
 --> main.cryo:9:5
    |
  7 | 
  8 | async function inside_unsafe() -> i64 {
  9 |     unsafe {
    |     ^ async: an `await` inside an `unsafe` block is not supported — the block is split at each resume point, so the `unsafe` scope cannot span it; move the `await` ahead of the block
 10 |         // Lift the `await` out: `const v = await ready(41); unsafe { ... }`.
 11 |         const v: i64 = await ready(41);
    |

aborting due to 1 error

Pinned by E0600_async_guard_moves_owning_payload, E0600_async_mutually_recursive_futures, E0600_async_recursive_future, E0600_await_in_static_match_arm, E0600_await_in_unsafe_block.

E0628 Pattern Matching Error

A range pattern whose low bound is above its high bound. It matches nothing, so it is treated as the typo it almost always is.

function main() -> int {
    const x: i32 = 1;
    match (x) {
        9..0 => {}
        _    => {}
    }
    return 0;
}
error[E0628]: range pattern lower bound (9) exceeds upper bound (0)
 --> main.cryo:4:9
   |
 2 |     const x: i32 = 1;
 3 |     match (x) {
 4 |         9..0 => {}
   |         ^ range pattern lower bound (9) exceeds upper bound (0)
 5 |         _    => {}
 6 |     }
   |

aborting due to 1 error

Pinned by E0628_reversed_range_pattern.

E0640 Invalid Enum Variant Access

An Enum::Name expression naming a variant the enum does not have. The counterpart of E0406 in expression rather than pattern position.

type enum Colour { Red; Green; Blue; }

function main() -> int {
    const c: Colour = Colour::Bogus;
    return 0;
}
error[E0640]: enum `Colour` has no variant `Bogus`
 --> main.cryo:4:23
   |
 2 | 
 3 | function main() -> int {
 4 |     const c: Colour = Colour::Bogus;
   |                       ^~~~~~~~~~~~~ unknown variant
 5 |     return 0;
 6 | }
   |

aborting due to 1 error

Pinned by E0640_nonexistent_enum_variant.

E0645 No Matching static match Arm

A static match over a type parameter with no arm for the type it is instantiated at, and no _ arm. The absence of an arm is the constraint: instantiating at an unhandled type is the error. The E0900 that follows in the output is monomorphization giving up on that instantiation.

type struct Only<T> {
    value: T;

    code(&this) -> i32 {
        return static match (T) {
            u8  => { 8 }
            u32 => { 32 }
        };
    }
}

function main() -> int {
    // `f32` matches no arm, and there is no `_`.
    mut a: Only<f32> = Only<f32> { value: 1.5 as f32 };
    return a.code();
}
error[E0645]: no `static match` arm matches type `f32`
 --> main.cryo:5:16
   |
 3 | 
 4 |     code(&this) -> i32 {
 5 |         return static match (T) {
   |                ^ no `static match` arm matches type `f32`
 6 |             u8  => { 8 }
 7 |             u32 => { 32 }
   |
 note: add an arm for this type or a `_` default arm

error[E0900]: Monomorphization failed

aborting due to 2 errors

Pinned by E0645_no_static_match_arm.

Linking

None of the linker codes are reported today; a failed link surfaces the linker's own output.

CodeTitle
E0700Link Errorreserved
E0701Undefined Symbol (Linker)reserved
E0702Duplicate Symbol (Linker)reserved
E0703Library Not Foundreserved
E0704Invalid Targetreserved

System and I/O

CodeTitle
E0800File Not Found
E0801File Read Error
E0802File Write Errorreserved
E0803Permission Deniedreserved
E0804Out of Memoryreserved
E0805Internal Error
E0806Header Preprocessor Failed

E0800 File Not Found

A source file named on the command line or reached through an import does not exist.

E0801 File Read Error

A source file exists but could not be read.

E0805 Internal Error

An internal failure setting up a phase — the resolver could not be initialized, or a pass ran with no tree to run on.

E0806 Header Preprocessor Failed

An extern "C" block's #include could not be processed: the header name has characters outside [A-Za-z0-9._/+-], or libclang failed to parse the header.

Internal compiler errors

CodeTitle
E0900Internal Compiler Error
E0901Unexpected Compiler Statereserved
E0902Compiler Assertion Failedreserved
E0903Unhandled Exceptionreserved
E0904Compiler Configuration Error
E0905SRM Manager Unavailablereserved
E0906SRM Type Resolution Failedreserved
E0907Method Not Foundreserved
E0908SRM Method Resolution Failedreserved
E0909SRM Constructor Generation Failedreserved

E0900 Internal Compiler Error

The compiler reached a state it has no diagnostic for: an LLVM module that fails verification, a monomorphization that could not complete, a name that reached a late phase unresolved. It is always a bug. When it follows another error (as after E0645) the earlier error is the cause; when it stands alone, report it with the program that produced it.

Pinned by sizeof_undeclared_type.

E0904 Compiler Configuration Error

The compiler's configuration is unusable: a --target triple it cannot map to a platform.

Warnings

A warning does not fail the build. Several of them describe things that are accepted for now and will become errors — the message says so where that is the case.

CodeTitle
W0001Unused Variable
W0002Unused Function
W0003Unused Importreserved
W0004Shadowed Variablereserved
W0005Implicit Conversion
W0006Lossy Conversionreserved
W0007Deprecatedreserved
W0008Unnecessary Castreserved
W0009Dead Code
W0010Missing Documentationreserved
W0011Duplicate Extern Symbol
W0012Signed Literal Out Of Range
W0013Format String Mismatch
W0014Redundant Numeric Suffix
W0015Explicit Destructor Call

W0001 Unused Variable

A local binding that is never read. Prefix the name with _ to say the value is deliberately unused.

function main() -> int {
    const unused: int = 5;
    return 0;
}
warning[W0001]: unused variable `unused`; prefix it with `_` to silence this warning
 --> main.cryo:2:5
   |
 1 | function main() -> int {
 2 |     const unused: int = 5;
   |     ^~~~~~~~~~~~~~~~~~~~~~ unused variable `unused`; prefix it with `_` to silence this warning
 3 |     return 0;
 4 | }
   |

1 warning emitted

Pinned by W0001_unused_variable.

W0002 Unused Function

A private, non-generic function that is never called.

private function helper() -> int {
    return 7;
}

function main() -> int { return 0; }
warning[W0002]: function `helper` is never used
 --> main.cryo:1:9
   |
 1 | private function helper() -> int {
   |         ^ function `helper` is never used
 2 |     return 7;
 3 | }
   |

1 warning emitted

Pinned by W0002_unused_function.

W0005 Implicit Conversion

An arithmetic operation between integers of different widths. The narrower operand is widened for now; write the conversion with as, since this will become an error before v1.0.0.

function main() -> int {
    const a: i32 = 1;
    const b: i64 = 2;
    const c: i64 = a + b;
    return c as int;
}
warning[W0005]: implicit conversion between `i32` and `i64`; the narrower operand widens to the wider one. Write it with `as` - this is accepted for now and will become an error before v1.0.0
 --> main.cryo:4:20
   |
 2 |     const a: i32 = 1;
 3 |     const b: i64 = 2;
 4 |     const c: i64 = a + b;
   |                    ^~~~~ implicit conversion between `i32` and `i64`; the narrower operand widens to the wider one. Write it with `as` - this is accepted for now and will become an error before v1.0.0
 5 |     return c as int;
 6 | }
   |

1 warning emitted

W0009 Dead Code

A statement that can never execute, such as one after an unconditional return.

function main() -> int {
    return 0;
    return 1;
}
warning[W0009]: unreachable statement: the preceding statement always diverges, so this can never be reached
 --> main.cryo:3:5
   |
 1 | function main() -> int {
 2 |     return 0;
 3 |     return 1;
   |     ^~~~~~~~~ unreachable statement: the preceding statement always diverges, so this can never be reached
 4 | }
   |

1 warning emitted

Pinned by W0009_dead_code.

W0011 Duplicate Extern Symbol

The same C symbol declared in more than one extern block with different signatures. Only one declaration can be right.

extern "C" {
    function compute(x: int) -> int;
}

extern "C" {
    function compute(x: int, y: int) -> int;
}

function main() -> int { return 0; }
warning[W0011]: extern C symbol `compute` is declared in more than one extern block; the unqualified name resolves to the first declaration
 --> main.cryo:6:5
   |
 4 | 
 5 | extern "C" {
 6 |     function compute(x: int, y: int) -> int;
   |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ duplicate extern declaration here
 7 | }
   |
 help: first declared in `main.cryo`; this one is in `main.cryo`. Reach this one through its qualified `Namespace::compute` form, or remove the duplicate.

1 warning emitted

Pinned by W0011_duplicate_extern_symbol.

W0012 Signed Literal Out Of Range

An integer literal outside the range of the signed type it is assigned to. The value is stored two's-complement, so 200 in an i8 is -56; if that is what you meant, write it as -56.

function main() -> int {
    const wrapped: i8 = 200;
    return wrapped as int;
}
warning[W0012]: integer literal `200` is outside the range of signed type `i8`; it is stored as its two's-complement bit pattern (wraps to a negative value)
 --> main.cryo:2:25
   |
 1 | function main() -> int {
 2 |     const wrapped: i8 = 200;
   |                         ^~~ integer literal `200` is outside the range of signed type `i8`; it is stored as its two's-complement bit pattern (wraps to a negative value)
 3 |     return wrapped as int;
 4 | }
   |

1 warning emitted

Pinned by W0012_signed_literal_out_of_range.

W0013 Format String Mismatch

A printf-style conversion whose argument has the wrong type — %d given a string, say. The formatting functions in fmt check their format strings at compile time.

import std::fmt;

function main() -> int {
    fmt::printf("%d\n", "not a number");
    return 0;
}
warning[W0013]: `%d` expects an integer, but this argument is a C string (`string`)
 --> main.cryo:4:25
   |
 2 | 
 3 | function main() -> int {
 4 |     fmt::printf("%d\n", "not a number");
   |                         ^~~~~~~~~~~~~~ `%d` expects an integer, but this argument is a C string (`string`)
 5 |     return 0;
 6 | }
   |

1 warning emitted

Pinned by W0013_format_string_mismatch.

W0014 Redundant Numeric Suffix

A type suffix on a numeric literal whose context already types it.

function main() -> int {
    const x: u8 = 5u8;
    return x as int;
}
warning[W0014]: redundant type suffix `u8` on numeric literal `5u8`; the surrounding context already types it
 --> main.cryo:2:19
   |
 1 | function main() -> int {
 2 |     const x: u8 = 5u8;
   |                   ^~~ redundant type suffix `u8` on numeric literal `5u8`; the surrounding context already types it
 3 |     return x as int;
 4 | }
   |

1 warning emitted

W0015 Explicit Destructor Call

An explicit .drop() call. The value is released at scope exit anyway, and an aggregate's fields are released after its own drop runs, so an explicit call is either redundant or a double release waiting to happen. To release early, write mem::drop(x), which also stops the value being used afterwards. This will become an error.

type struct Res {
    id: i64;
    drop(mut &this) -> void { }
}

function main() -> int {
    mut r: Res = Res { id: 1 };
    // Released at scope exit anyway; to release early, `mem::drop(r)`.
    r.drop();
    return 0;
}
warning[W0015]: explicit destructor call; the value is released at scope exit, and an aggregate's fields are released after its own `drop` runs. To release EARLY, write `mem::drop(x)`, which also stops the value being used afterwards. This is a warning while the tree is being cleaned up and will become an error
 --> main.cryo:9:5
    |
  7 |     mut r: Res = Res { id: 1 };
  8 |     // Released at scope exit anyway; to release early, `mem::drop(r)`.
  9 |     r.drop();
    |     ^~~~~~~~ explicit destructor call; the value is released at scope exit, and an aggregate's fields are released after its own `drop` runs. To release EARLY, write `mem::drop(x)`, which also stops the value being used afterwards. This is a warning while the tree is being cleaned up and will become an error
 10 |     return 0;
 11 | }
    |

1 warning emitted