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.
| Range | Category | Codes |
|---|---|---|
E0001–E0099 | Lexical analysis | 12 (3 reserved) |
E0050–E0069 | AST validation | 15 |
E0100–E0199 | Syntax | 17 (6 reserved) |
E0150–E0169 | Directives | 4 |
E0154–E0169 | Symbol resolution | 2 |
E0200–E0399 | Type checking | 42 (14 reserved) |
E0300–E0349 | Generics and traits | 11 (5 reserved) |
E0350–E0399 | Structs and classes | 16 (4 reserved) |
E0400–E0449 | Control flow | 7 (2 reserved) |
E0450–E0499 | Memory and ownership | 8 (2 reserved) |
E0500–E0549 | Modules and imports | 6 (1 reserved) |
E0600–E0699 | Code generation | 46 (27 reserved) |
E0700–E0799 | Linking | 5 (5 reserved) |
E0800–E0899 | System and I/O | 7 (3 reserved) |
E0900–E0999 | Internal compiler errors | 10 (8 reserved) |
W0001–W9999 | Warnings | 15 (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 E0450–E0451 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.
| Code | Title | |
|---|---|---|
E0001 | Unexpected Character | |
E0002 | Unterminated String | |
E0003 | Unterminated Character | |
E0004 | Invalid Number | reserved |
E0005 | Invalid Escape Sequence | reserved |
E0006 | Invalid Unicode | reserved |
E0007 | Invalid Hexadecimal | |
E0008 | Invalid Binary | |
E0009 | Invalid Octal | |
E0010 | Number Too Large | |
E0011 | Lexing Exception | |
E0012 | Unterminated 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.
| Code | Title | |
|---|---|---|
E0050 | Null AST Node | |
E0051 | Missing Function Name | |
E0052 | Missing Type Name | |
E0053 | Null Parameter | |
E0054 | Null Field | |
E0055 | Null Method | |
E0056 | Method Missing Function | |
E0057 | Enum Has No Variants | |
E0058 | Null Enum Variant | |
E0059 | Null Block Statement | |
E0060 | Empty Program | |
E0061 | Type Alias Missing Target | |
E0062 | Impl Block Missing Target | |
E0063 | Import Missing Path | |
E0064 | Duplicate 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.
| Code | Title | |
|---|---|---|
E0100 | Expected Token | |
E0101 | Unexpected Token | |
E0102 | Expected Expression | |
E0103 | Expected Statement | reserved |
E0104 | Expected Type | |
E0105 | Expected Identifier | |
E0106 | Expected Semicolon | reserved |
E0107 | Expected Parenthesis | reserved |
E0108 | Expected Brace | |
E0109 | Expected Bracket | reserved |
E0110 | Mismatched Delimiters | reserved |
E0111 | Invalid Syntax | |
E0112 | Unexpected End of File | |
E0113 | Invalid Pattern | |
E0114 | Duplicate Default | |
E0115 | Parse Recovery Failed | reserved |
E0116 | Parse 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.
| Code | Title | |
|---|---|---|
E0150 | Directive Bad Arity | |
E0151 | Directive Misplaced | |
E0152 | Directive Bad Argument | |
E0153 | Unknown 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.
| Code | Title | |
|---|---|---|
E0154 | Ambiguous Call | |
E0155 | Ambiguous 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.
| Code | Title | |
|---|---|---|
E0200 | Type Mismatch | |
E0201 | Undefined Variable | |
E0202 | Undefined Function | |
E0203 | Undefined Type | |
E0204 | Undefined Field | |
E0205 | Redefined Symbol | |
E0206 | Redefined Function | |
E0207 | Redefined Type | |
E0208 | Invalid Cast | |
E0209 | Invalid Operation | |
E0210 | Invalid Assignment | reserved |
E0211 | Incompatible Types | reserved |
E0212 | Void Value Used | reserved |
E0213 | Non-Callable | reserved |
E0214 | Argument Mismatch | |
E0215 | Too Many Arguments | |
E0216 | Too Few Arguments | |
E0217 | Const Violation | reserved |
E0218 | Immutable Assignment | |
E0219 | Uninitialized Variable | reserved |
E0220 | Unreachable Code | reserved |
E0221 | Circular Dependency | reserved |
E0222 | Invalid Dereference | reserved |
E0223 | Invalid Address-Of | |
E0224 | Invalid Index | reserved |
E0225 | Index Out of Bounds | reserved |
E0226 | Division by Zero | |
E0227 | Overflow | reserved |
E0228 | Underflow | reserved |
E0229 | Invalid Binary Operation | |
E0230 | Invalid Unary Operation | |
E0231 | Non-Callable Type | reserved |
E0232 | Invalid Assignment Target | |
E0233 | Undefined Symbol | |
E0234 | Invalid ? Operand | |
E0235 | ? Outside Result/Option Function | |
E0236 | Recursive Type | |
E0237 | Static Assertion Failed | |
E0238 | Branch Type Mismatch | |
E0239 | Non-Constant Array Size | |
E0240 | Namespace Not Reachable | |
E0241 | Export 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
| Code | Title | |
|---|---|---|
E0300 | Generic Instantiation Failed | reserved |
E0301 | Generic Type Resolution Failed | reserved |
E0302 | Generic Parameter Mismatch | |
E0303 | Invalid Generic Constraint | reserved |
E0304 | Ambiguous Generic | reserved |
E0305 | Recursive Generic | reserved |
E0306 | Trait Bound Not Satisfied | |
E0307 | Cannot Infer Type Argument | |
E0308 | Conflicting Trait Implementation | |
E0309 | Associated Type Not Bound | |
E0310 | Positional 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
| Code | Title | |
|---|---|---|
E0350 | Struct Field Not Found | |
E0351 | Class Member Not Found | reserved |
E0352 | Constructor Not Found | reserved |
E0353 | Private Access Violation | |
E0354 | Abstract Method Call | reserved |
E0355 | Missing Field Initialization | |
E0356 | Duplicate Field | |
E0357 | Invalid Instantiation | reserved |
E0358 | Undefined Method Implementation | |
E0359 | Protected Not Allowed in Struct | |
E0360 | Missing Visibility Block | |
E0361 | Destructure on Non-Struct Type | |
E0362 | Destructure Pattern Incomplete | |
E0363 | Union Literal Must Initialize Exactly One Field | |
E0364 | async Not Allowed on This Member | |
E0365 | async 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
| Code | Title | |
|---|---|---|
E0400 | Invalid Break | |
E0401 | Invalid Continue | |
E0402 | Invalid Return | reserved |
E0403 | Missing Return | |
E0404 | Unreachable Pattern | reserved |
E0405 | Non-Exhaustive Match | |
E0406 | Unknown 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.
| Code | Title | |
|---|---|---|
E0452 | Use After Move | |
E0453 | Double Free | |
E0454 | Memory Leak | reserved |
E0455 | Dangling Pointer | |
E0456 | Conditional Move | |
E0457 | Non-Copy Capture | reserved |
E0458 | Closure Argument Outside Free-Function Call | |
E0459 | Future 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 ¶m 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
| Code | Title | |
|---|---|---|
E0500 | Module Not Found | |
E0501 | Circular Import | |
E0502 | Invalid Import | |
E0503 | Private Symbol Access | |
E0504 | Namespace Conflict | reserved |
E0505 | Experimental 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.
| Code | Title | |
|---|---|---|
E0600 | Code Generation Failed | |
E0601 | LLVM Error | reserved |
E0602 | Invalid LLVM Type | |
E0603 | Invalid LLVM Value | reserved |
E0604 | Unimplemented Intrinsic | reserved |
E0605 | Optimization Failed | |
E0606 | Function Generation Error | |
E0607 | Variable Generation Error | |
E0608 | Intrinsic Generation Error | reserved |
E0609 | Type Mapping Error | |
E0610 | Class Generation Error | reserved |
E0611 | Enum Generation Error | reserved |
E0612 | Struct Generation Error | reserved |
E0613 | Control Flow Error | reserved |
E0614 | Assignment Error | |
E0615 | Binary Operation Error | reserved |
E0616 | Unary Operation Error | reserved |
E0617 | Memory Operation Error | reserved |
E0618 | Constructor Generation Error | reserved |
E0619 | Method Generation Error | |
E0620 | Module Context Error | reserved |
E0621 | Array Operation Error | |
E0622 | Member Access Error | |
E0623 | Scope Resolution Error | reserved |
E0624 | Exception Handler Error | reserved |
E0625 | Literal Generation Error | reserved |
E0626 | Cast Operation Error | reserved |
E0627 | Loop Generation Error | reserved |
E0628 | Pattern Matching Error | |
E0629 | Template Instantiation Error | reserved |
E0630 | Optimization Error | reserved |
E0631 | Enum Operation Error | reserved |
E0632 | Type Resolution Error | reserved |
E0633 | Function Body Error | |
E0634 | Variable Initialization Error | reserved |
E0635 | Type Constructor Undefined | |
E0636 | Undefined Function Call | |
E0637 | Invalid Generic Instantiation | reserved |
E0638 | Invalid Struct Initialization | |
E0639 | Invalid Class Instantiation | reserved |
E0640 | Invalid Enum Variant Access | |
E0641 | Null Cast Expression | reserved |
E0642 | Parameter Type Error | |
E0643 | Return Type Error | |
E0644 | Field Type Error | reserved |
E0645 | No 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.
| Code | Title | |
|---|---|---|
E0700 | Link Error | reserved |
E0701 | Undefined Symbol (Linker) | reserved |
E0702 | Duplicate Symbol (Linker) | reserved |
E0703 | Library Not Found | reserved |
E0704 | Invalid Target | reserved |
System and I/O
| Code | Title | |
|---|---|---|
E0800 | File Not Found | |
E0801 | File Read Error | |
E0802 | File Write Error | reserved |
E0803 | Permission Denied | reserved |
E0804 | Out of Memory | reserved |
E0805 | Internal Error | |
E0806 | Header 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
| Code | Title | |
|---|---|---|
E0900 | Internal Compiler Error | |
E0901 | Unexpected Compiler State | reserved |
E0902 | Compiler Assertion Failed | reserved |
E0903 | Unhandled Exception | reserved |
E0904 | Compiler Configuration Error | |
E0905 | SRM Manager Unavailable | reserved |
E0906 | SRM Type Resolution Failed | reserved |
E0907 | Method Not Found | reserved |
E0908 | SRM Method Resolution Failed | reserved |
E0909 | SRM Constructor Generation Failed | reserved |
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.
| Code | Title | |
|---|---|---|
W0001 | Unused Variable | |
W0002 | Unused Function | |
W0003 | Unused Import | reserved |
W0004 | Shadowed Variable | reserved |
W0005 | Implicit Conversion | |
W0006 | Lossy Conversion | reserved |
W0007 | Deprecated | reserved |
W0008 | Unnecessary Cast | reserved |
W0009 | Dead Code | |
W0010 | Missing Documentation | reserved |
W0011 | Duplicate Extern Symbol | |
W0012 | Signed Literal Out Of Range | |
W0013 | Format String Mismatch | |
W0014 | Redundant Numeric Suffix | |
W0015 | Explicit 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
function name<T>(param: Type) -> Ret { ... }Declares a function. Parameters are written name: Type and are never inferred; the return type follows -> and defaults to void. Declaration order does not matter: every signature is collected before any body is checked, so functions may call each other freely and recurse.
function main() -> intconst name: Type = value;An immutable binding: it cannot be reassigned after initialisation. The annotation may be omitted when an initialiser is present - the binding takes the initialiser's concrete type, inferred locally. Mutability is opt-in through mut.
const greeting: stringprimitive stringNUL-terminated raw string (u8*), matching the C ABI. Length-typed text is Str / String.
const c: charreturn value;Leaves the current function with the given value. A function whose return type is not void must return one; a bare return; is for void. In an async function it completes the future with the value.
const mask: i32const bits: i32const mode: i32const x: u8const x: i32function set_alpha(a: u8) -> void(parameter) a: u8function set_alpha(a: u8) -> voidconst ratio: f32const big: f64type struct Name { ... }
type enum Name { ... }
type trait Name { ... }
type class Name { ... }Begins a type declaration; the keyword that follows says which kind. On its own, type Name = Existing; declares an alias.
type union Name {
i: i64;
f: f64;
}An untagged, C-style union: every field shares the same storage at offset 0, so the size is that of the largest member. Writing one field and reading another reinterprets the bytes; there is no runtime tag.
language referencetype union Nothingtype enum Name {
Unit,
Payload(T),
Named { x: i32 },
}An algebraic data type. Variants may be bare or carry a tuple or struct payload, and a match on an enum must cover every variant. Name::Variant names a variant; methods are added with implement enum Name { ... }.
type enum Emptytype enum LevelLevel::LowLevel::Hightype enum ShapeShape::Circle(i32)Shape::Squaretype struct Name {
field: Type;
method(&this) -> Ret { ... }
}A value type with named fields and optional methods. Structs live on the stack and are passed by value. Fields are public by default and may carry = default values; methods take their receiver as &this, mut &this, or this.
type struct PointPoint.x: i32Point.y: i32const p: Pointtype struct PointPoint.x: i32Point.y: i32const pp: Point*const p: Pointconst pp: Point*function scale(factor: ) -> void(parameter) factor: ?function apply(f: (i32) -> i32, v: i32) -> i32(parameter) f: (i32) -> i32(parameter) v: i32(parameter) f: (i32) -> i32(parameter) v: i32function apply(f: (i32) -> i32, v: i32) -> i32function pair() ->const twice(parameter) n: i32(parameter) n: i32const twicemut name: Type = value;A mutable binding, reassignable after declaration - mutability is opt-in, const is the default. At module level it declares mutable global state. Before & it marks an exclusive reference or receiver instead.
mut a: i64![arch(x86_64, intel)]
asm {
mov ${=out}, ${in}
}Embeds raw target assembly as an LLVM inline-assembly call. A preceding ![arch(<arch>, <dialect>)] directive is mandatory and gates the block by target; Cryo values are bound into the text through ${...} operand holes. At module scope, with no operands, it emits module-level assembly.
mut a: i64match (subject) {
Pattern(x) => { ... }
_ => { ... }
}The primary discriminator: branches on enum variants, integers, ranges, and destructuring patterns, and the compiler rejects a match that misses a case. There is no fallthrough. As an expression it evaluates to the chosen arm's value, so every arm must produce the same type.
const x: i32start..endA half-open range - start up to but excluding end - and sugar for Range::new(start, end): an iterator usable in for, and an ordinary value anywhere else. It binds looser than arithmetic, so a..b + 1 is a..(b + 1).
pattern => { body }Separates a match arm's pattern from its body. Arms do not fall through.
_const x: i32function hot() -> voidfunction entry() -> voidtype struct PacketPacket.a: u8import a::b;
import a::b::{ X, Y };
import a::b as c;
import a::b::*;Brings a module, or selected items from it, into scope. One path per declaration: use the brace form for several items, as to alias, and * for everything public (prefer the brace form; wildcards invite name collisions).
namespace std::alloc::heap;heap: the mmap-backed process heap (Low-Level Plan Stage 3).
public function alloc(size: u64, align: u64) -> void*Allocate size bytes aligned to align (a non-zero power of two). Returns null on OOM or when size is 0.
public function alloc(size: u64, align: u64) -> void*Allocate size bytes aligned to align via GlobalAlloc. Null/zero guarded; returns null on OOM so the emitter's existing null-check on the old malloc result is unchanged.
const p: void*type trait Display {
fmt<W>(&this, f: mut &Formatter<W>) -> Result<(), FmtError>;
}Display / Debug / Formatter.
std::fmt::displaytype trait Debug {
fmt<W>(&this, f: mut &Formatter<W>) -> Result<(), FmtError>;
}implement struct Name { ... }
implement trait Trait for Type { ... }
implement u64 { ... }Adds methods to a type outside its declaration. implement struct T (or enum, or a bare primitive) opens an inherent block; implement trait Tr for T supplies a trait's methods for one type. Resolution is at compile time - no dynamic dispatch.
implement trait Trait for Type { ... }Introduces a trait implementation: the block supplies, for one concrete type, the method bodies the trait requires.
language referenceimplement trait Trait for Type { ... }Names the type a trait implementation is for. The block that follows supplies the bodies the trait requires.
language referencetype struct PointPoint::fmt<W>(&this, w: &W) -> i32type parameter W&this
mut &this
thisThe receiver of a method. Its first parameter declares how the method takes it: &this borrows read-only, mut &this borrows exclusively, and this / mut this move the value in and consume it. In the body, this.field reaches the members.
(parameter) w: &Wtype parameter Wtype parameter WPoint.x: i32mut sink: i32const p: PointPoint::fmt<W>(&this, w: &W) -> i32mut sink: i32mut count: i32mut count: i32mut small: u8const big: i64mut small: u8const big: i64const xs: i32[]const xs: i32[]type struct GridGrid.cells: i32const g: Gridtype struct GridGrid.cells: i32const g: Gridconst x: i32type struct CounterCounter.n: i32const c: Countertype struct CounterCounter.n: i32mut it: implement Iterator<i32>-> implement Trait<T>An opaque type: some one concrete type that implements the trait, without naming it. The compiler infers the real type and uses it everywhere; callers see only the trait's methods. How the stdlib returns iterators without exposing their engines.
language referencetype trait Iterator {
type Item;
next(mut &this) -> Option<This::Item>;
count(mut &this) -> u64;
fold<Acc>(mut &this, initial: Acc, f: (Acc, This::Item) -> Acc) -> Acc;
for_each(mut &this, f: (This::Item) -> void) -> void;
take(this, n: u64) -> TakeIter<This>;
skip(this, n: u64) -> SkipIter<This>;
map<B>(this, f: (This::Item) -> B) -> MapIter<This, B>;
filter(this, pred: (This::Item) -> boolean) -> FilterIter<This>;
chain<J>(this, other: J) -> ChainIter<This, J>;
// … 7 more
}const c: Counterfunction render(w: Widget) -> void(parameter) w: Widgettype struct PointPoint.x: i32const p: Pointconst x: i32function add(x: i32, x: i32) -> i32(parameter) x: i32(parameter) x: i32function add(x: i32, x: i32) -> i32function reset() -> voidtype struct PointPoint.x: i32Point.y: i32const n: i64const p: Pointvalue as TypeExplicit conversion: between numeric types, or reinterpreting one pointer type as another. Cryo never converts implicitly, and as inserts no range checks - a narrowing cast is the programmer's responsibility.
const t: stringtypeof(expr)The static type of an expression, usable anywhere a type is expected: bindings, pointer and optional wrappers, generic arguments, as targets. The expression is type-checked but never evaluated.
const x: i32function takes_int(x: i32) -> voidfunction takes_int(x: i32) -> voidCounter.v: i32Counter::bump(mut &this, by: i32) -> voidmut &this
mut &TypeAn exclusive, mutating borrow. As a receiver, mut &this lets the method modify the fields - and callers can see that from the signature alone. As a parameter type, mut &T lets the callee mutate through the reference while the caller keeps ownership.
(parameter) by: i32Counter.v: i32(parameter) by: i32mut c: Countertype struct Countermut c: CounterCounter::bump(mut &this, by: i32) -> voidtype struct MetresMetres.v: i32type struct SecondsSeconds.v: i32function walk(d: Metres) -> void(parameter) d: Metrestype struct Metresconst t: Secondstype struct SecondsSeconds.v: i32function walk(d: Metres) -> voidconst t: Secondsfunction set_alpha(a: u8) -> voidconst big: i64function set_count(n: u32) -> void(parameter) n: u32function set_count(n: u32) -> voidfunction write(p: i32*) -> void(parameter) p: i32*(parameter) p: i32*function relay(p: const i32*) -> void(parameter) p: ?(parameter) i32function write(p: i32*) -> void(parameter) p: ?function second<T>(a: T, b: T) -> Ttype parameter T(parameter) a: Ttype parameter T(parameter) b: T(parameter) b: Tconst r: i32function second<T>(a: T, b: T) -> Ttype trait Mul<Rhs, Output> {
mul(&this, rhs: &Rhs) -> Output;
}type struct Vec3Vec3.x: f32Vec3.y: f32Vec3.z: f32type struct Vec3Vec3::mul(&this, rhs: &f32) -> Vec3(parameter) rhs: &f32Vec3.x: f32(parameter) rhs: &f32Vec3.y: f32Vec3.z: f32Vec3::mul(&this, rhs: &Vec3) -> Vec3(parameter) rhs: &Vec3(parameter) rhs: &Vec3const a: Vec3const k: f64const s: Vec3const a: Vec3const k: f64Point::sum(&this) -> i32Point.x: i32Point.y: i32type struct Pointconst p: PointPoint::sum(&this) -> i32function add(a: i32) -> i32(parameter) a: i32(parameter) a: i32function add(a: i32, b: i32) -> i32(parameter) b: i32(parameter) a: i32(parameter) b: i32function add(a: i32, b: i32) -> i32(parameter) a: i32(parameter) b: i32function add(a: i32, b: i32) -> i32const limit: i32const limit: i32const p: i32*const x: i32type struct MoneyMoney.cents: i32const a: Moneytype struct MoneyMoney.cents: i32const b: Moneyconst c: Moneyconst a: Moneyconst b: Moneytype struct TagTag.v: i32const a: Tagtype struct TagTag.v: i32const b: Tagconst lt: booleanconst a: Tagconst b: Tagtype struct HandleHandle.v: i32const h: Handletype struct HandleHandle.v: i32const h: Handleconst x: i32mut s: stringmut s: stringconst y: i32const x: i32const y: i32type struct ConfigConfig.v: i32const c: Configtype struct Configconst x: i32value?Error propagation. On a Result<T, E> it yields the T of an Ok and otherwise returns the Err(e) from the enclosing function unchanged; on an Option<T> it yields the T of a Some or returns None. The enclosing function must return a matching Result / Option.
const y: i32function parse() -> Result<i32, string>Result::Ok(T)const v: i32function parse() -> Result<i32, string>const v: i32type struct NodeNode.value: i32Node.next: Nodetype struct Nodestatic_assert(sizeof(T) == 4, "message");A module-scope, compile-time assertion. The condition is folded after layouts are computed - literals, sizeof, alignof, and the arithmetic, comparison, logical, and bitwise operators - and a false or non-constant condition fails the build (E0237). Its main use is checking that a struct matches a C layout.
sizeof(T)The size of a type in bytes, as a compile-time constant - usable in static_assert. alignof(T) gives the alignment.
const c: booleantrueThe boolean literal. Booleans are a distinct 1-byte type, not interchangeable with integers; if and while conditions must be boolean.
const xif (condition) { ... } else if (condition) { ... } else { ... }Branches on a parenthesised boolean condition. Bodies are always braced; there is no single-statement form. Used as an expression, both arms are required and must produce the same type: const sign = if (n < 0) { -1 } else { 1 };.
const c: booleanif (condition) { ... } else { ... }The branch taken when every preceding if condition is false; else if chains another condition. In expression form the else arm is mandatory, so the expression always has a value.
mut runtime_len: i64type struct BufferBuffer.bytes: u8[runtime_len]mut runtime_len: i64Tag.n: i32type struct Buf<A = Tag>type parameter Atype struct TagBuf.a: Atype parameter Atype trait Name : Base {
method(&this, other: &This) -> Ret;
}Names a set of methods a type may implement. Inside the body This is the implementing type; methods may carry default bodies, and a trait may require a base trait (type trait Ord : Eq). Generic code asks for a trait with where T: Name.
type trait ShowShow::show(&this) -> inttype trait Showtype struct Buf<A = Tag>Buf::show(&this) -> intShow::show(&this) -> i32function display<T>(x: T) -> i32type parameter Twhere T: Show
(parameter) x: Twhere T: Ord + Clone, U: HashConstrains generic parameters to types that implement the listed traits, which is what makes those traits' methods callable on T. Bounds on one parameter join with +; parameters separate with ,. A where on a single method narrows the type's parameter for that method only.
type trait Show(parameter) x: Tfunction display<T>(x: T) -> i32type trait Seqtype Item;
type Output = i64;An associated type on a trait: named in the trait body, filled in by each implementation, and referred to as This::Item or I::Item.
type ItemSeq::next_one(&this) -> i32type struct NotCopyNotCopy.p: i32*type struct NotCopyNotCopy::destroy(&this) -> voidtype struct HolderHolder.v: i32type trait Seqtype struct HolderHolder::next_one(&this) -> i32Holder.v: i32type trait Default {
static default() -> This;
}Default: a canonical "zero value" for a type.
std::core::defaultfunction make<T>() -> Ttype parameter Twhere T: Default
static Default::default() -> Thisfunction make<T>() -> Ttype struct Wrap<T>Wrap.v: Ttype parameter Tstatic name(param: Type) -> Ret { ... }A method that belongs to the type rather than an instance, called as Type::name(..). For structs, static new(..) returning a struct literal is the idiomatic constructor.
static Wrap::new(x: T) -> Wrap<T>type struct Wrap<T>(parameter) x: Tstatic Wrap::new(x: T) -> Wrap<T>type trait GreetGreet::greet(&this) -> inttype struct PersonPerson.age: inttype trait Greettype struct PersonPerson::greet(&this) -> inttype trait Showint::show(&this) -> inti32::show(&this) -> inttype struct NumsNums.cur: i32type trait Seqtype struct NumsNums::next_one(&this) -> i32Nums.cur: i32type trait Conv<G>type parameter Gtype OutConv::run(&this) -> i32type struct WidgetWidget.v: i32type trait Conv<G>type struct WidgetWidget::run(&this) -> i32Widget.v: i32type struct Accountprivate type struct Name { ... }
private:On a field: only the declaring type's own methods may touch it (E0353). On a top-level item: confined to its own module, and naming a private type elsewhere is E0503. private: opens a block of such members.
Account.balance: i32public function name() { ... }
public:Visible to any module that imports this one - the default for top-level items and for struct fields, so it is mostly written to be explicit. public: opens a visibility block that groups the members after it.
static Account::open() -> Accounttype struct AccountAccount.balance: i32const a: Accountstatic Account::open() -> Accountconst a: AccountAccount.id: i32type struct AccountAccount.balance: i32Account.id: i32const a: Accounttype class Name : Base {
public:
field: Type;
Name(args) : Base(args) { ... }
virtual method(&this) -> Ret { ... }
}A heap-allocated reference type with single inheritance, constructors and destructors, and virtual dispatch. Instances are created with new and are pointers. Every member sits in an explicit public: / private: / protected: block. Default to struct; reach for a class when you need polymorphism.
type class Accountprotected:Class-only visibility: the member is accessible to the class and its subclasses. Structs accept only public: and private: blocks.
Account::Account(b: i32)Account.balance: i32(parameter) b: i32const a: Account*type class Accountnew Class(args)
new T[n]Heap allocation. new Class(args) runs the constructor and yields a Class*; new T[n] reserves n uninitialised T and yields a T* - the typed malloc. Structs have no new keyword: their static new(..) is an ordinary method.
const a: Account*type struct PointPoint.x: i32const p: PointConfig.retries: i32Config.name: stringtype struct ConfigConfig.retries: i32const c: Configtype struct PairPair.a: i32const n: i32for (mut i: i32 = 0; i < n; i++) { ... }
for (item in iterable) { ... }Two forms. The C-style for (init; condition; step) scopes its loop variable to the body. for (x in expr) iterates anything with next() -> Option<T>: an iterator, a range such as 0..n, an Array or Slice (their iter() is inserted for you), or a fixed-size array.
ifor (item in iterable) { ... }Separates the loop variable from the sequence in a for loop. The sequence is evaluated once; the loop lowers to loop { match (it.next()) { Some(x) => .., None => break } }.
const n: i32function printf(fmt: string, args...) -> i32Write a printf-style formatted string to stdout. Returns the number of bytes written, or a negative value on error (libc printf convention). Use this for genuinely variadic %s/%d output; for value formatting prefer an f-string through println (Display-based) below.
type struct String<A = GlobalAlloc> {
buffer: RawBuffer<u8, A>;
length: u64;
}String: owned, heap-allocated, length-typed UTF-8.
std::collections::stringmut dst: Array<String>type struct Array<T, A = GlobalAlloc> {
ptr: T*;
length: u64;
capacity: u64;
alloc: A;
}Array<T, A>: growable, heap-backed, contiguous sequence.
std::collections::arraystatic Array::new() -> Array<T, GlobalAlloc>Empty array backed by GlobalAlloc.
mut src: Array<String>mut dst: Array<String>Array::append(mut &this, source: Slice<T>) -> voidwhere T: Copy
Append every element of source to the end of this array. Panics on allocation failure; use try_append to recover.
mut src: Array<String>Array::as_slice(&this) -> Slice<T>Borrow the initialized prefix. Slice is invalidated by any reallocating method.
std::collections::arraytype class AnimalAnimal.name: i32type struct PointPoint.x: i32Point.y: i32const p: Pointtype union NumberNumber.i: i32Number.f: f32const n: Numbertype union NumberNumber.i: i32Number.f: f32namespace std::future;std::future: stackless async foundations — Future, Poll, Context, Waker, and the drivers that poll them.
type class WorkerWorker.v: i64virtual method(&this) -> Ret;
virtual method(&this) -> Ret { ... }Marks a class method dispatched through the vtable at runtime. Without a body it declares an interface point derived classes must implement; with one it provides a default they may override.
async function name(param: Type) -> T { ... }Compiles the function into a stackless state machine. Calling it builds a future whose output is the declared return type; nothing runs until that future is polled. Allowed on free functions, methods, and trait methods.
language referenceWorker::run(&this) -> i64Worker.v: i64type struct TaskTask.n: i64async function main() -> i64break;Exits the innermost enclosing loop immediately - for, while, loop, or do. Inside for (x in ..) it leaves the loop the compiler synthesised around the iterator.
continue;Skips the rest of the current iteration: the innermost loop's condition (or the iterator's next()) runs again.
function answer() -> i32type enum ColourColour::RedColour::GreenColour::Blueconst c: Colourtype enum ColourColour::Redconst c: ColourColour::Greentype enum ColourColour::Greenconst c: ColourColour::RedColour::BlueShape::Circle(int)Shape::Square(int)const s: Shapetype enum ShapeShape::Circle(int)const s: Shaper: intwtype struct HeapHeap.p: void*type struct HeapHeap::drop(mut &this) -> voidextern "C" function free(ptr: u8*) -> voidHeap.p: void*function consume(h: Heap) -> void(parameter) h: Heapmut a: Heapextern "C" function malloc(size: u64) -> u8*function consume(h: Heap) -> voidmut a: Heapmut b: Heaptype struct HeapHeap.p: void*mut h: Heapfunction consume(h: Heap) -> voidmut h: Heaptype struct HeapHeap.p: void*mut i: intmut i: intmut a: Heaptype class HolderHolder.a: i32Holder::drop(mut &this) -> voidmut h: Holdertype class HolderHolder.a: i32mut h: HolderHolder::drop(mut &this) -> voidtype struct HeapHeap.p: void*type struct OwnerOwner.h: HeapOwner.tag: i32type struct OwnerOwner::drop(mut &this) -> voidfunction take(x: Heap) -> void(parameter) x: Heapmut o: OwnerOwner.h: HeapOwner.tag: i32function take(x: Heap) -> voidmut o: Ownertype struct ResRes.id: i64Res::drop(mut &this) -> voidfunction consume(r: Res) -> i64(parameter) r: Restype struct Res(parameter) r: ResRes.id: i64mut a: Resmut p: Res*mut a: Resfunction consume(r: Res) -> i64mut p: Res*const n: i64function dangling() -> i32*mut x: i32mut x: i32type struct InnerInner.x: i32Inner::drop(mut &this) -> voidtype enum WrapWrap::Has(Inner)type struct InnerWrap::Nothingimplement enum Name { ... }Names the enum an inherent block extends - the only way to give an enum methods.
language referencetype enum WrapWrap::inner_ptr(&this) -> Inner*Wrap::Has(Inner)p: Innerp: InnerWrap::NothingnullThe null pointer literal, valid in any pointer context - including the primitive string, which is a NUL-terminated u8*. Compare with == null; it is not an Option.
type struct PendingThenReady<T> {
remaining: u32;
value: Option<T>;
}A future that reports Pending for its first pending_count polls and then completes with value. Exercises the suspend/resume path end-to-end under a driver without any I/O or threads — the canonical Phase-1 validation future.
async function fill() -> i64mut buf: u8[8]const p: u8*mut buf: u8[8]const _z: i64await futureSuspends the enclosing async body until the operand - which must implement Future - completes, and evaluates to its output. A prefix operator allowed anywhere an expression is (conditions, arms, guards), but only inside async code.
static PendingThenReady::new(pending_count: u32, value: T) -> PendingThenReady<T>const p: u8*Counter.base: i64Counter::bump(&this, n: i64) -> i64(parameter) n: i64const a: i64Counter.base: i64(parameter) n: i64const a: i64function make() -> Countertype struct Counterasync function drive() -> i64function make() -> CounterCounter::bump(&this, n: i64) -> i64const bias: i32const some: Option<i32>Option::Some(T)const shifted: Option<i32>const some: Option<i32>Option::map<U>(&this, f: (T) -> U) -> Option<U>(parameter) n: i32const bias: i32type struct Context {
waker: Waker;
}The context threaded through a poll call. Holds the Waker a future registers when it returns Pending. Passed by pointer down the poll tree, matching the stdlib convention of moving aggregates by pointer.
type struct Ready<T> {
value: Option<T>;
}A future that is already complete: the first poll returns Ready(value). The value is held in an Option and moved out on completion, so polling a Ready a second time is a contract violation (it panics) rather than a double-move.
type struct Waker {
data: u8*;
}A handle a stalled future stores so it can be re-scheduled: wake_fn(data) tells the executor "poll my task again". This is the manual-vtable shape used throughout the runtime — non-capturing function pointers plus an opaque data pointer identifying the task.
async function work() -> i64static Ready::new(value: T) -> Ready<T>mut f: i64async function work() -> i64mut cx: Contextstatic Context::new(waker: Waker) -> Contextstatic Waker::noop() -> WakerA waker that does nothing when woken. Used by drivers (like block_on) that re-poll unconditionally and never park a task.
const _firstmut f: i64mut cx: Contextmut g: i64async function countdown(n: i64) -> i64(parameter) n: i64const rest: i64async function countdown(n: i64) -> i64const rest: i64async function ready(v: i64) -> i64(parameter) v: i64(parameter) v: i64async function inside_unsafe() -> i64unsafe { ... }A documentation marker for code that does raw pointer arithmetic or crosses into extern calls. It lowers to a plain block: no check is relaxed inside it and none is imposed outside it - and 1.0 commits to keeping it that way.
const v: i64async function ready(v: i64) -> i64const v: i64const x: i32type enum Colourtype struct Only<T>Only.value: Ttype parameter TOnly::code(&this) -> i32static match (T) { ... }Opens a compile-time match on a type parameter: the one arm whose type matches is kept during monomorphisation and the rest are discarded.
static match (T) {
u8 | u16 => { ... }
_ => { ... }
}Selects a body by inspecting a type parameter at compile time. The subject is a type and each arm is a type (| groups several; _ takes the rest). During monomorphisation the one matching arm is kept and the others discarded, so there is no runtime branch.
u8u32mut a: Only<f32>type struct Only<T>mut a: Only<f32>Only::code(&this) -> i32const unused: intprivate function helper() -> intconst a: i32const b: i64const c: i64const a: i32const b: i64const c: i64extern function exit(code: i32) -> void;
extern "C" {
function puts(s: string) -> i32;
}Declares a function whose body the linker supplies - typically a C library symbol. The compiler trusts the Cryo signature; keeping it in step with the real C signature is on you.
language referencefunction compute(x: int) -> int(parameter) x: intfunction compute(x: int, y: int) -> int(parameter) y: intconst wrapped: i8const wrapped: i8const x: u8mut r: Restype struct ResRes.id: i64mut r: ResRes::drop(mut &this) -> voidfunction name(count: i32, args...)A variadic parameter: the trailing bucket receives any number of further arguments with C's calling convention, so the function stays ABI-compatible with the printf family. Wrap the bucket in VaArgs to read typed values from it.
function main() -> int// reservedListed among the declaration keywords, but the reference defines no export form today: top-level items are public by default, and private is what narrows them.
type trait Index<Idx, Output> {
index(&this, idx: Idx) -> Output*;
}Index<Idx, Output> is the contract for container[idx]-style access, returning a pointer to the element. The compiler rewrites a[i] into *(a.index(i)); because the deref of the returned Output* is a place, the one impl serves reads (v = a[i]), writes (a[i] = v), and compound assignment (a[i] += v).
type struct Widgetconst NAME: Type = value;At module level, a true compile-time constant, conventionally SCREAMING_SNAKE_CASE. (mut at module level declares mutable global state instead.)
const yconst Ttype trait Add<Rhs, Output> {
add(&this, rhs: &Rhs) -> Output;
}Arithmetic operator traits. Implementing one lets the compiler rewrite the corresponding operator on a user type (or a generic param bounded by the trait) into the trait method: a + b => a.add(&b), and likewise - => sub, * => mul, / => div, % => rem.
type trait Ord : Eq {
compare(&this, other: &This) -> Ordering;
}Types that have a total order: every pair is Less, Equal, or Greater. compare must be consistent with equals: if a.equals(b) then a.compare(b) == Equal.
type trait Deref<Target> {
deref(&this) -> Target*;
}Deref<Target> exposes the value a smart pointer points at. The returned Target* is mutable (Cryo pointers are not const-qualified), so this one trait covers both shared and mutable access - there is no separate DerefMut.
Result::Err(E)Option::Nonetype struct Nodetype struct Buf<A = Tag>type struct Tagtype trait Showtype trait Copy {}Types whose values can be duplicated by a bitwise copy. Integers, floats, pointers, and pure-POD structs qualify; anything that owns resources (heap storage, file handles, refcount) does not.
std::core::markertype Name = Existing;A type alias: a new, transparent name for an existing type - the two are interchangeable everywhere. Aliases may be generic: type StringResult<T> = Result<T, string>;.
type Item: Copytype enum Wraptype trait Conv<G>type trait Seqtype Item = i32;async function mainxoverride method(&this) -> Ret { ... }Required on a derived-class method that replaces a base class's virtual method. There is no implicit overriding.
type struct Innerfunction consume(r: Res) -> i64