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 | 13 (3 reserved) |
E0050–E0069 | AST validation | 15 |
E0100–E0199 | Syntax | 18 (7 reserved) |
E0150–E0169 | Directives | 4 |
E0154–E0169 | Symbol resolution | 3 |
E0200–E0399 | Type checking | 45 (12 reserved) |
E0300–E0349 | Generics and traits | 17 (5 reserved) |
E0350–E0399 | Structs | 10 |
E0400–E0449 | Control flow | 8 (2 reserved) |
E0450–E0499 | Memory and ownership | 8 (2 reserved) |
E0500–E0549 | Modules and imports | 6 (1 reserved) |
E0600–E0699 | Code generation | 44 (24 reserved) |
E0700–E0799 | Linking | 5 (5 reserved) |
E0800–E0899 | System and I/O | 7 (4 reserved) |
E0900–E0999 | Internal compiler errors | 9 (7 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 | |
E0013 | Bad Octal Escape |
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_constant_expression_narrows, 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.
E0013 Bad Octal Escape
An octal escape that is not one. An octal escape is one to three digits 0–7, read as C reads them: it ends after three digits or at the first character that is not one, so "\033[1m" is ESC and then [1m, and "\0123" is a newline and then 3. \8 and \9 are not octal digits, and a value above \377 does not fit in a byte; both are refused rather than read as something the author did not write.
function main() -> int {
// Octal digits stop at 7, and three of them stop at \377.
const a: string = "\8";
const b: string = "\400";
return 0;
}
error[E0013]: `\8` is not an escape: an octal escape's digits are `0` to `7`
--> main.cryo:3:24
|
1 | function main() -> int {
2 | // Octal digits stop at 7, and three of them stop at \377.
3 | const a: string = "\8";
| ^~ `\8` is not an escape: an octal escape's digits are `0` to `7`
4 | const b: string = "\400";
5 | return 0;
|
help: write the digit itself without a backslash, or a byte by its octal or hex value (`\033` and `\x1b` are both ESC)
error[E0013]: `\400` is out of range: an octal escape is one byte, `\0` to `\377`
--> main.cryo:4:24
|
2 | // Octal digits stop at 7, and three of them stop at \377.
3 | const a: string = "\8";
4 | const b: string = "\400";
| ^~~~ `\400` is out of range: an octal escape is one byte, `\0` to `\377`
5 | return 0;
6 | }
|
help: write fewer digits; an escape ends after three, so `\0123` is `\012` and then `3`
aborting due to 2 errors
Pinned by E0013_bad_octal_escape.
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 | reserved |
E0113 | Invalid Pattern | |
E0114 | Duplicate Default | |
E0115 | Parse Recovery Failed | reserved |
E0116 | Parse Exception | |
E0117 | Removed Language Form |
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
The 19 keywords that open a declaration (function, const, type, module, static, async and the rest) cannot name a variable, a parameter or a pattern binding. A statement that begins with one is parsed as that declaration, so module = q; could never be an assignment. Keywords that open nothing, like default and string, are still legal names.
function main() -> int {
// `module` opens a declaration, so it cannot name a variable.
const module: i32 = 42;
return module;
}
error[E0100]: expected a variable name, found `module`, which is a reserved word
--> main.cryo:3:11
|
1 | function main() -> int {
2 | // `module` opens a declaration, so it cannot name a variable.
3 | const module: i32 = 42;
| ^~~~~~ expected a variable name, found `module`, which is a reserved word
4 | return module;
5 | }
|
aborting due to 1 error
Pinned by E0100_array_type_unclosed, E0100_declaration_keyword_as_binding_name, E0100_enum_comma_separator, E0100_fixed_array_type_unclosed, E0100_intrinsic_const_removed, 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_array_type_missing_length, E0102_expected_expression, E0102_generic_args_without_turbofish.
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; }
|
aborting due to 1 error
Pinned by E0104_array_type_missing_element, E0104_bracket_tuple_type, E0104_expected_type, E0104_non_identifier_after_colons, E0104_postfix_array_type, 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.
E0117 Removed Language Form
A form the language does not have, written where it would be. Cryo had classes before 1.0 and no longer does: type class, a : Base list on a struct, a constructor or destructor, virtual and override on a method, protected, implement class and for class are each refused where they are written, with a help line saying what to write instead, and the parse goes on past it so every one in a file is reported. A class is a struct that implements a trait, and code that takes any type implementing the trait takes a trait object, virtual &Trait (section 10.7).
type class Shape {
sides: i32;
}
function main() -> int { return 0; }
error[E0117]: `type class` is not part of Cryo
--> main.cryo:1:1
|
1 | type class Shape {
| ^~~~~~~~~~ a class
2 | sides: i32;
3 | }
|
help: declare a struct and implement a trait
aborting due to 1 error
A struct is built with a literal or a static factory, never a constructor, and new Point(1, 2) is refused the same way:
type struct Point {
x: i32;
y: i32;
Point(x: i32, y: i32) { this.x = x; this.y = y; }
}
function main() -> int { return 0; }
error[E0117]: `Point(..)` is not part of Cryo
--> main.cryo:5:5
|
3 | y: i32;
4 |
5 | Point(x: i32, y: i32) { this.x = x; this.y = y; }
| ^~~~~~~~~~~~~~~~~~~~~ a constructor
6 | }
|
help: write a `static new` factory
aborting due to 1 error
A method that varies by type is declared in a trait and implemented for each type:
type struct Square {
side: i32;
virtual area(&this) -> i32 { return this.side * this.side; }
}
function main() -> int { return 0; }
error[E0117]: `virtual` is not part of Cryo
--> main.cryo:4:5
|
2 | side: i32;
3 |
4 | virtual area(&this) -> i32 { return this.side * this.side; }
| ^~~~~~~ a method modifier
5 | }
|
help: declare the method in a trait and implement it
aborting due to 1 error
type struct Account {
protected:
balance: i32;
}
function main() -> int { return 0; }
error[E0117]: `protected:` is not part of Cryo
--> main.cryo:2:1
|
1 | type struct Account {
2 | protected:
| ^~~~~~~~~~ a visibility no declaration has
3 | balance: i32;
4 | }
|
help: write `private:` or `public:`
aborting due to 1 error
Pinned by E0117_new_with_constructor_arguments, E0117_removed_class_forms.
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, 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/enum, not function
--> main.cryo:1:1
|
1 | ![repr(C)]
| ^~~~~~~~~~ directive `![repr]` only applies to struct/union/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
Pinned by E0151_caller_location_value, E0151_caller_location_variadic.
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
Pinned by E0152_allow_arguments.
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 codes are the refusals. None of them is ever settled by declaration or import order.
| Code | Title | |
|---|---|---|
E0154 | Ambiguous Call | |
E0155 | Ambiguous Bare Name | |
E0156 | Ambiguous Trait Method |
E0154 Ambiguous Call
A name that is in scope twice. Two wildcard imports can each offer a function called alloc; 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. Two brace entries naming the same thing are refused earlier, at the second import (E0243).
import std::alloc::heap::*;
import std::alloc::allocator::*;
function main() -> int {
// Both wildcards offer an `alloc`; 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:6:22
|
4 | function main() -> int {
5 | // Both wildcards offer an `alloc`; the bare name picks neither.
6 | const p: void* = alloc(64, 8);
| ^~~~~ ambiguous name
7 | return 0;
8 | }
|
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
Pinned by E0154_ambiguous_bare_call, E0154_ambiguous_bare_type_annotation, E0154_ambiguous_module_prefix_annotation, E0154_free_function_generic_tie, E0154_overloaded_function_value_untyped, E0154_overloaded_generic_function_value, E0154_overloaded_generic_method_value, E0154_overloaded_generic_static_call, generic_and_plain_method_both_apply, import_type_tie_beside_function_refused, overloaded_value_help_types_the_binding, 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.
E0156 Ambiguous Trait Method
A method that more than one applicable implementation provides for the receiver's type. That can be two traits that both provide m, or an inherent m beside a trait's m: no kind of method outranks another, so where Rust would let the inherent method win, Cryo refuses the call. The same tie is reported through every spelling of the call: p.m(), P::m(&p), a static P::m(), and P::m taken as a value. The note names each candidate where it is declared, and the help writes the impl-qualified path that chooses one, (Tr for P)::m(&p). A call through a bound (x.m() with where T: Tr) names Tr::m and is never ambiguous.
type trait Shown { fmt<W>(&this, w: &W) -> i32; }
type trait Dumped { fmt<W>(&this, w: &W) -> i32; }
type struct Point { x: i32; }
implement trait Shown for Point {
fmt<W>(&this, w: &W) -> i32 { return 1; }
}
implement trait Dumped 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[E0156]: `fmt` is provided by more than one trait implemented for `Point`
--> main.cryo:17:12
|
15 | mut sink: i32 = 0;
16 | // `fmt` comes from two traits, and nothing picks one.
17 | return p.fmt(&sink);
| ^~~~~ which `fmt` is meant here is ambiguous
18 | }
|
note: candidate #1: `Shown::fmt`, from `implement trait main::Shown for Point`
--> main.cryo:7:5
|
5 |
6 | implement trait Shown for Point {
7 | fmt<W>(&this, w: &W) -> i32 { return 1; }
| ^~~ candidate #1: `Shown::fmt`, from `implement trait main::Shown for Point`
8 | }
9 | implement trait Dumped for Point {
|
note: candidate #2: `Dumped::fmt`, from `implement trait main::Dumped for Point`
--> main.cryo:10:5
|
8 | }
9 | implement trait Dumped for Point {
10 | fmt<W>(&this, w: &W) -> i32 { return 2; }
| ^~~ candidate #2: `Dumped::fmt`, from `implement trait main::Dumped for Point`
11 | }
|
help: name the implementation the call is meant through: `(Shown for Point)::fmt(&p, &sink)` or `(Dumped for Point)::fmt(&p, &sink)`
aborting due to 1 error
Pinned by E0156_ambiguous_trait_method, E0156_static_path_two_traits, impl_qualified_call_tie_help, inherent_trait_method_ambiguous, method_value_inherent_trait_ambiguous, static_path_generic_owner_ambiguous, static_path_inherent_trait_ambiguous, static_path_tie_has_no_type, trait_impl_same_name_call_ambiguous.
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 | |
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 | |
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 | |
E0242 | Undefined Trait | |
E0243 | Name Imported Twice | |
E0244 | Import Names an Item and a Module |
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_assignment_instance_element_type, E0200_char_range_on_int, E0200_coalesce_on_other_option, E0200_compare_unrelated_enums, E0200_compare_unrelated_pointers, E0200_condition_not_boolean, E0200_contradictory_numeric_suffix, E0200_delete_non_pointer, E0200_delete_trait_object, E0200_fn_pointer_field_assign, E0200_for_init_narrowing, E0200_generic_body_assign_mismatch, E0200_generic_body_option_as_param, E0200_generic_body_return_mismatch, E0200_impl_trait_unmet_bound, E0200_index_no_impl, E0200_null_branch_takes_other_branch_type, E0200_opaque_assoc_item_binding, E0200_opaque_assoc_item_return, E0200_pattern_names_another_enum, E0200_payload_or_generic_enum_to_boolean, E0200_pointer_to_converting_pointee, E0200_static_match_arm_return_mismatch, E0200_unit_value_as_pointer, compile_fail_typeerror, match_char_range_on_reference, match_string_pattern_on_reference, match_string_pattern_on_struct, static_match_arms_checked_before_mono, this_member_in_trait_default_refused, trait_default_this_calls_typed, type_declaration_bound_types_calls.
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_primitive_has_no_constant, 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_bare_allocator_leaf, 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_cast_to_undeclared_type, E0203_path_drops_trailing_segment, E0203_plain_import_binds_no_names, E0203_projection_names_no_associated_type, E0203_projection_through_supertrait, E0203_refused_signature_no_cascade, E0203_refused_struct_literal_name_no_cascade, E0203_type_argument_undeclared, E0203_undefined_type, E0203_unknown_local_var_type, E0203_unknown_module_prefix, sizeof_undeclared_type, static_call_undeclared_type, unimported_module_path_refused.
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_field_on_function_value, E0204_field_through_type_parameter, E0204_method_read_as_field, E0204_trait_object_field, 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, E0205_type_named_as_its_module, import_module_alias_named_like_module, import_name_redeclared_locally, import_type_beside_function_meets_module, module_type_import_collision, module_type_name_collision.
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_overload_differs_only_by_alias, 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.
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/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/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, E0208_cast_into_other_aggregate, E0208_cast_struct_pointer_to_value, E0208_cast_to_boolean.
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_constant_expression_out_of_range, E0214_arg_narrowing, E0214_c_import_narrowing, E0214_const_pointer_drop, E0214_fn_pointer_argument, E0214_free_function, E0214_generic_body_call_arg_mismatch, E0214_generic_body_method_arg_mismatch, E0214_generic_body_method_argument_unbound, E0214_generic_inference_conflict, E0214_generic_numeric_conflict, E0214_generic_owner_generic_method_call_argument, E0214_instance_method, E0214_literal_array_type_parameter_refusals, E0214_negative_literal_unsigned_arg, E0214_overload_set_no_match, E0214_pointer_argument_converting_pointee, E0214_pointer_argument_other_pointee, E0214_projection_argument, E0214_runtime_sign_reinterpret, E0214_static_call_no_overload_fits, E0214_static_method, E0214_trait_method, E0214_trait_object_call_argument_type, E0214_trait_path_shared_for_exclusive, E0214_turbofish_call_arg_mismatch, E0214_variadic_fixed_param, E0214_variant_payload_against_written_arguments, E0214_wrong_struct, array_method_argument_mismatch, instantiation_method_argument_mismatch, module_suffix_tie_rejects_mismatch, overload_integer_literal_no_match, overload_qualified_call_no_match, type_parameter_for_concrete_param.
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_impl_qualified_arity, 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, E0216_trait_object_call_arguments, E0216_variadic_missing_fixed.
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
A reference is never compared with a plain number, boolean, char, string or enum. The comparison used to compare the reference's address and was silently false; now the value has to be read explicitly with *x == 7. Two references compare with each other, and a reference compares with null.
function is_seven(x: &i32) -> boolean {
// Write `*x == 7`: a reference is never compared with a plain value.
if (x == 7) { return true; }
return false;
}
function main() -> int {
const n: i32 = 7;
if (is_seven(&n)) { return 0; }
return 1;
}
error[E0229]: Cannot compare Reference and Int; dereference the reference to compare the value it refers to (`*x`)
--> main.cryo:3:9
|
1 | function is_seven(x: &i32) -> boolean {
2 | // Write `*x == 7`: a reference is never compared with a plain value.
3 | if (x == 7) { return true; }
| ^~~~~~ Cannot compare Reference and Int; dereference the reference to compare the value it refers to (`*x`)
4 | return false;
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_reference_compared_with_value, E0229_relational_no_ord, E0229_trait_object_comparison, E0229_user_trait_spelled_add, E0229_user_trait_spelled_add_bound, compare_with_null_refused, generic_operator_needs_bound.
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_generic_alias_path, E0233_generic_alias_struct_literal, E0233_impl_qualified_no_member, E0233_impl_qualified_param_no_member, E0233_template_param_spelling_is_not_the_param, E0233_trait_qualified_unselected, E0233_undefined_static, export_braces_offer_listed_only, shadow_type_module_already_imported.
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, E0234_user_enum_into_result, E0234_user_enum_spelled_ok_err.
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_generic_body_branch_mismatch, 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:29
|
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.
E0242 Undefined Trait
A name written where a trait must stand — a where bound, a supertrait, an impl head's trait — that names no trait in scope. It is reported where it is written, whether or not anything ever calls through the bound.
function show<T>(x: &T) -> i32 where T: Printable {
return 1;
}
function main() -> int {
return 0;
}
error[E0242]: cannot find trait `Printable` in this scope
--> main.cryo:1:41
|
1 | function show<T>(x: &T) -> i32 where T: Printable {
| ^~~~~~~~~ not found in this scope
2 | return 1;
3 | }
|
aborting due to 1 error
Pinned by unknown_trait_bound_no_cascade, unknown_trait_refused_where_written.
E0243 Name Imported Twice
A brace import bringing in a name an earlier explicit import already brought into the file, in the same namespace. It is refused at the second import, where the collision is written, and the first one keeps the name. Remove one of them or bind one under another name: import std::alloc::allocator::{ alloc as raw_alloc };. Types and values are separate namespaces, so a type Tag and a function Tag from two modules do not collide.
import std::alloc::heap::{ alloc };
import std::alloc::allocator::{ alloc };
function main() -> int {
const p: void* = alloc(64, 8);
return 0;
}
error[E0243]: `alloc` is imported twice
--> main.cryo:2:1
|
1 | import std::alloc::heap::{ alloc };
2 | import std::alloc::allocator::{ alloc };
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ `alloc` is imported again here
3 |
4 | function main() -> int {
|
note: first imported here
--> main.cryo:1:1
|
1 | import std::alloc::heap::{ alloc };
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ first imported here
2 | import std::alloc::allocator::{ alloc };
|
help: remove one of the imports, or import one under another name with `as`
aborting due to 1 error
Pinned by export_alias_exported_twice, import_alias_imported_twice, import_collision_bare_use_refused, import_collision_qualified_use, import_twice_refused_at_import.
E0244 Import Names an Item and a Module
import A::B as C; where A declares an item B and A::B is also a module. The path names both, and neither is chosen for the writer. import A::{ B as C }; names the item; import A::B; names the module.
Pinned by import_braced_item_or_module_refused, import_path_item_or_module_refused.
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 | |
E0311 | Generic Parameter Shadowed | |
E0312 | Trait Implementation Mismatch | |
E0313 | Trait Used as a Type | |
E0314 | Method Unavailable Through a Trait Object | |
E0315 | Value Cannot Become a Trait Object | |
E0316 | virtual on a Non-Trait or in an extern Signature |
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, E0302_trait_head_undeclared_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 {
drop(mut &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
A for (x in expr) loop iterates through std::core::iter::Iterator, so a scrutinee that neither implements it nor exposes iter() is refused here, as the bound the loop needs. A method that is merely spelled next does not make a type iterable:
import std::fmt;
function main() -> int {
const n: i32 = 42;
// `for (x in expr)` needs `expr` to implement `Iterator` or expose `iter()`.
for (i in n) {
fmt::printf("%d\n", i);
}
return 0;
}
error[E0306]: `i32` does not implement `Iterator`
--> main.cryo:6:15
|
4 | const n: i32 = 42;
5 | // `for (x in expr)` needs `expr` to implement `Iterator` or expose `iter()`.
6 | for (i in n) {
| ^ no `implement trait Iterator for i32` selects an implementation here
7 | fmt::printf("%d\n", i);
8 | }
|
aborting due to 1 error
import std::fmt;
type struct Countdown { n: i32; }
implement struct Countdown {
next(mut &this) -> Option<i32> {
if (this.n <= 0) { return Option::None; }
this.n = this.n - 1;
return Option::Some(this.n + 1);
}
}
function main() -> int {
const c: Countdown = Countdown { n: 3 };
// A method spelled `next` is not an `implement trait Iterator`.
for (i in c) {
fmt::printf("%d\n", i);
}
return 0;
}
error[E0306]: `Countdown` does not implement `Iterator`
--> main.cryo:16:15
|
14 | const c: Countdown = Countdown { n: 3 };
15 | // A method spelled `next` is not an `implement trait Iterator`.
16 | for (i in c) {
| ^ no `implement trait Iterator for Countdown` selects an implementation here
17 | fmt::printf("%d\n", i);
18 | }
|
aborting due to 1 error
Pinned by E0306_assoc_decl_bound, E0306_enum_where_bound, E0306_filter_iter_where_bound, E0306_for_in_duck_typed_next, E0306_for_in_non_iterator, E0306_impl_qualified_unimplemented, E0306_object_bound_unmet, E0306_send_denied_std_types, E0306_separate_block_drop_not_copy, E0306_trait_bound, E0306_type_where_bound, object_bound_unmet_help, object_bound_unmet_help_call, object_bound_unmet_note, object_bound_unmet_note_call, type_where_bound_inferred.
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_alias_type_arguments, E0307_cannot_infer_type_arg, E0307_generic_method_call_type_arguments, E0307_generic_method_value_uninferable, E0307_generic_owner_method_value_uninferable, E0307_method_call_type_arguments, E0307_object_argument_count, E0307_object_call_type_arguments, E0307_owner_literal_default, E0307_payload_free_variant_uninferable, E0307_type_argument_count, E0307_variant_type_arguments_uninferable, generic_function_value_refused.
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
Two heads that unify — some type both would accept — conflict just the same, whether one is generic and one concrete, or the two differ only in their where clauses. There is no specialisation and no selection by declaration order. Heads no type satisfies together, like Show for Pair<T, T> beside Show for Pair<i32, u8>, are distinct.
type trait Show { show(&this) -> i32; }
type struct Wrap<T> { v: T; }
implement<T> trait Show for struct Wrap<T> { show(&this) -> i32 { return 1; } }
// A `Wrap<i32>` would select both implementations.
implement trait Show for struct Wrap<i32> { show(&this) -> i32 { return 2; } }
function main() -> int {
return 0;
}
error[E0308]: conflicting implementations of trait `Show`: `Show for Wrap<i32>` overlaps `Show for Wrap<T>`, so a type both heads accept would select both
--> main.cryo:6:1
|
2 | type struct Wrap<T> { v: T; }
3 |
4 | implement<T> trait Show for struct Wrap<T> { show(&this) -> i32 { return 1; } }
| ------------------------------------------------------------------------------- overlaps this one
5 | // A `Wrap<i32>` would select both implementations.
6 | implement trait Show for struct Wrap<i32> { show(&this) -> i32 { return 2; } }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this implementation
7 |
8 | function main() -> int {
|
note: two implementations of one trait must not both accept any type; a where-clause narrows which types a head is FOR but does not separate two heads, so make the targets disjoint or keep one implementation
aborting due to 1 error
Pinned by E0308_conflicting_trait_impl, E0308_generic_trait_duplicate, E0308_overlapping_impls, E0308_primitive_alias_duplicate, E0308_projection_head_duplicate, where_bound_leaf_collision.
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.
E0311 Generic Parameter Shadowed
A generic parameter named like one its enclosing declaration already declares: a method's <T> inside struct Holder<T>, a trait method's <T> inside trait Conv<T>, an impl method's <T> inside implement<T>, or a second T in one list. The owner's T is in scope in every nested declaration, so an inner one of the same name could only be that parameter again. Shadowing a type of the same spelling, or naming a value parameter like a generic one, is allowed.
type struct Holder<T> {
v: T;
// `T` is already `Holder`'s parameter; this one could only be it again.
size_of<T>(&this, x: T) -> u64 { return sizeof(T); }
}
function main() -> int {
return 0;
}
error[E0311]: the generic parameter `T` shadows a generic parameter of the same name
--> main.cryo:4:13
|
1 | type struct Holder<T> {
| - first declared here
2 | v: T;
3 | // `T` is already `Holder`'s parameter; this one could only be it again.
4 | size_of<T>(&this, x: T) -> u64 { return sizeof(T); }
| ^ declared again here
5 | }
6 |
|
note: a generic parameter is in scope for every declaration nested in its owner; give the inner one a distinct name
aborting due to 1 error
Pinned by E0311_generic_param_shadowed.
E0312 Trait Implementation Mismatch
An implementation whose methods are not the trait's. Each method must have the signature the trait declares — the same receiver (this, &this, mut &this), the same number of type parameters and parameters, the same parameter and return types — read with This as the implementing type and the trait's parameters as the head's arguments. A method the trait does not declare is refused, and so is leaving out one the trait gives no body; a method with a body in the trait may be left out, and the trait's body is used. Each disagreement is reported at the method, or at the head for a missing one, and names what the trait declares.
type trait Shape {
area(&this) -> i32;
scale(mut &this, k: i32) -> void;
}
type struct Square { side: i32; }
implement trait Shape for struct Square {
area(&this) -> i64 { return (this.side * this.side) as i64; }
scale(&this, k: i32) -> void { }
}
function main() -> int { return 0; }
error[E0312]: `area` returns `i32` in trait `Shape`, but `i64` here
--> main.cryo:9:5
|
7 |
8 | implement trait Shape for struct Square {
9 | area(&this) -> i64 { return (this.side * this.side) as i64; }
| ^~~~ `area` returns `i32` in trait `Shape`, but `i64` here
10 | scale(&this, k: i32) -> void { }
11 | }
|
error[E0312]: `scale` takes `mut &this` in trait `Shape`, but `&this` here
--> main.cryo:10:11
|
8 | implement trait Shape for struct Square {
9 | area(&this) -> i64 { return (this.side * this.side) as i64; }
10 | scale(&this, k: i32) -> void { }
| ^~~~~ `scale` takes `mut &this` in trait `Shape`, but `&this` here
11 | }
|
aborting due to 2 errors
Pinned by E0312_allocate_for_enum, E0312_subtrait_without_supertrait, E0312_trait_impl_mismatch.
E0313 Trait Used as a Type
A trait written where a type goes: a parameter, a return type, a field, a local, a type argument, or an alias's target. A trait is not a type, and &Shape and Shape* are refused the same way. Code that accepts any type implementing a trait takes a bounded type parameter, area_of<T>(p: &T) where T: Shape, which compiles one copy per implementing type, or a trait object, virtual &Shape, which compiles one copy for every type. &This inside a trait names the implementing type and is not a trait object.
type trait Shape {
area(&this) -> i32;
}
type struct Square { side: i32; }
implement trait Shape for struct Square {
area(&this) -> i32 { return this.side * this.side; }
}
// A trait is not a type: take a bounded type parameter instead.
function area_of(p: &Shape) -> i32 { return p.area(); }
function main() -> int {
const s: Square = Square { side: 3 };
return area_of(&s);
}
error[E0313]: `Shape` is a trait, so `&Shape` is not a type
--> main.cryo:12:21
|
10 |
11 | // A trait is not a type: take a bounded type parameter instead.
12 | function area_of(p: &Shape) -> i32 { return p.area(); }
| ^~~~~~ a reference to a trait
13 |
14 | function main() -> int {
|
note: a trait object is written `virtual &Shape` or `virtual Shape*`
help: to accept any type that implements `Shape`, use a bounded type parameter: `function f<T>(p: &T) where T: Shape`
aborting due to 1 error
Pinned by E0313_trait_as_pointee, E0313_trait_as_value_type.
E0314 Method Unavailable Through a Trait Object
A call through a trait object of a method the object's table holds no function for. A method is callable through an object only when its signature names the implementing type as its receiver alone: a &this or mut &this receiver, no type parameters of its own, and This in no other parameter and not in its return. A static method, one taking this by value, one with type parameters, and one naming This elsewhere are each refused at the call, with the reason in a note; the object's other methods stay callable.
type trait Shape {
area(&this) -> i32;
duplicate(&this) -> This;
}
function copy_of(s: virtual &Shape) -> i32 {
s.duplicate();
return s.area();
}
function main() -> int { return 0; }
error[E0314]: `duplicate` cannot be called through `virtual &main::Shape`
--> main.cryo:7:7
|
5 |
6 | function copy_of(s: virtual &Shape) -> i32 {
7 | s.duplicate();
| ^~~~~~~~~ `duplicate` cannot be called through `virtual &main::Shape`
8 | return s.area();
9 | }
|
note: its return type names `This`, the implementing type, which is not known where the object is used
aborting due to 1 error
Pinned by E0314_object_method_unavailable, E0314_object_unbound_associated_type.
E0315 Value Cannot Become a Trait Object
A value that cannot become the trait object it is checked against. The object carries the table of the value's type's implementation of the trait, so the type must implement it, at the arguments the object writes, and implement every supertrait the trait requires. A type whose implementation is generic, or a value of a type parameter, does not convert yet: no table is known for it where the conversion is written.
type trait Shape {
area(&this) -> i32;
}
type struct Square { side: i32; }
function area_of(s: virtual &Shape) -> i32 { return s.area(); }
function main() -> int {
const q: Square = Square { side: 3 };
return area_of(&q);
}
error[E0315]: `main::Square` does not implement `main::Shape`, so `&main::Square` cannot become `virtual &main::Shape`
--> main.cryo:11:20
|
9 | function main() -> int {
10 | const q: Square = Square { side: 3 };
11 | return area_of(&q);
| ^~ `main::Square` does not implement `main::Shape`, so `&main::Square` cannot become `virtual &main::Shape`
12 | }
|
aborting due to 1 error
Pinned by E0315_object_conversion_generic, E0315_object_conversion_not_implemented, E0315_object_conversion_supertrait, E0315_object_conversion_trait_arguments.
E0316 `virtual` on a Non-Trait or in an `extern` Signature
virtual written on something that is not a trait. A trait object is a value with a table of its type's methods for one trait; virtual &Circle with Circle a struct, virtual i32*, or virtual &T with T a type parameter names no table.
type struct Circle { r: i32; }
function radius(c: virtual &Circle) -> i32 { return 1; }
function main() -> int { return 0; }
error[E0316]: `Circle` is not a trait, so `virtual &Circle` is not a trait object
--> main.cryo:3:20
|
1 | type struct Circle { r: i32; }
2 |
3 | function radius(c: virtual &Circle) -> i32 { return 1; }
| ^~~~~~~~~~~~~~~ not a trait
4 |
5 | function main() -> int { return 0; }
|
aborting due to 1 error
A trait object anywhere in an extern function's signature is refused under the same code: the C side has no table to call through.
type trait Shape {
area(&this) -> i32;
}
extern "C" {
function draw(s: virtual &Shape) -> void;
}
function main() -> int { return 0; }
error[E0316]: `virtual &Shape` is a trait object, which an `extern` function `draw` cannot take or return
--> main.cryo:6:22
|
4 |
5 | extern "C" {
6 | function draw(s: virtual &Shape) -> void;
| ^~~~~~~~~~~~~~ a trait object in an `extern` signature
7 | }
|
aborting due to 1 error
Pinned by E0316_trait_object_in_extern, E0316_virtual_on_non_trait.
Structs
| Code | Title | |
|---|---|---|
E0350 | Struct Field Not Found | |
E0353 | Private Access Violation | |
E0355 | Missing Field Initialization | |
E0356 | Duplicate Field | |
E0358 | Undefined Method Implementation | |
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.)
Pinned by generic_destructure_shape_refused.
E0353 Private Access Violation
Access to a private member from where it is not visible. Visibility is scoped to modules, not types: a private field or method is reachable from anywhere in the module that declares it, including free functions beside the type, and from nowhere else. That includes an implement block written in another module. A method is declared in the module its implement block is written in, so a private method in an impl is private to that block's module.
Two uses count as access that might not look like it. Calling a function-typed field, h.run(1), reads the field. A struct literal sets every field, so it can only be written where every field is visible; outside the module, a struct with a private field is built through a constructor the module provides:
// module app::money
type struct Cents {
private:
n: i64;
public:
static of(n: i64) -> Cents { return Cents { n: n }; }
}
// module app::report
const c: Cents = Cents { n: 5 }; // error[E0353]: field `n` of `Cents` is private
const d: Cents = Cents::of(5); // fine
The same code covers a private function or static called from another module. Every form of this error needs two modules, so it is pinned by multi-module tests rather than a single file.
Pinned by extern_private_function_is_private, private_function_field_call_refused, visibility_function_beside_type, visibility_gate, visibility_import_gate, visibility_module_private, 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 method call on a type parameter that no bound in scope names is refused the same way, where the call is written, whether or not anything instantiates the body.
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]: the trait bound `std::collections::string::String<std::alloc::allocator::GlobalAlloc>: Copy` is not satisfied
--> 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());
| ^~~~~~ the trait bound `std::collections::string::String<std::alloc::allocator::GlobalAlloc>: Copy` is not satisfied
10 | return 0;
11 | }
|
note: method `append` requires `T: Copy`
help: `std::collections::string::String<std::alloc::allocator::GlobalAlloc>` owns resources, so it isn't `Copy`, and `append` is only available when `T` is
aborting due to 1 error
Pinned by E0358_generic_body_method_bound_unmet, E0358_hashmap_get_noncopy, E0358_iter_min_non_ord, E0358_method_on_function_value, E0358_method_on_tuple, E0358_param_path_in_uninstantiated_body, E0358_param_path_member_no_bound_declares, E0358_primitive_item_default, E0358_push_slice_non_copy, E0358_trait_object_unknown_method, bounded_param_method_refused, drop_call_help_names_the_pointer, drop_call_on_type_parameter_help, drop_on_unbounded_param_refused, generic_receiver_alias_refused, generic_receiver_other_instantiation_refused, impl_concrete_arg_filters_impl, projection_bound_leaf_collision, trait_default_leaf_collision, unbounded_param_method_refused.
E0361 Destructure on Non-Struct Type
A destructuring binding whose type is not a struct.
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; a field holds a value, not a computation to suspend.
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_field.
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 | |
E0407 | Inconsistent Or-Pattern Alternatives |
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, trait_default_copy_pattern_spans.
E0407 Inconsistent Or-Pattern Alternatives
An or-pattern whose alternatives bind different variables. The arm has one variable per name, whichever alternative matched, so every alternative must bind the same names, each at the same type and each written mut alike. A name bound in only some alternatives would be unset whenever another one matched.
type enum Shape {
Circle(int);
Rect(int, int);
}
function area(s: Shape) -> int {
return match (s) {
// `h` is bound by the first alternative but not the second.
Shape::Rect(w, h) | Shape::Circle(w) => { w * h }
};
}
function main() -> int {
return area(Shape::Circle(2));
}
error[E0407]: `h` is not bound in this alternative
--> main.cryo:9:29
|
7 | return match (s) {
8 | // `h` is bound by the first alternative but not the second.
9 | Shape::Rect(w, h) | Shape::Circle(w) => { w * h }
| - ^^^^^^^^^^^^^^^^ binds no `h`
| |
| bound here, in the arm's first alternative
10 | };
11 | }
|
note: the arm's body runs whichever alternative matched, so every alternative binds `h` alike: the same name, the same type, and `mut` written alike
aborting due to 1 error
Pinned by E0407_alternatives_bind_different_mutability, E0407_alternatives_bind_different_names, E0407_alternatives_bind_different_types.
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 value placed into a struct, array, or other aggregate literal, 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 struct Holder {
a: i32;
}
implement trait Drop for struct Holder {
drop(mut &this) -> void {}
}
function main() -> int {
mut h: Holder = Holder { a: 1 };
h.drop();
h.drop();
return 0;
}
warning[W0015]: explicit destructor call; write `mem::drop(x)` instead. On a local, `x.drop()` releases the value there and ends the local as `mem::drop(x)` does: it is not released again at scope exit, and a later use is refused. On a field, the owner releases the field again when it is released itself. This is a warning while the tree is being cleaned up and will become an error
--> main.cryo:11:5
|
9 | function main() -> int {
10 | mut h: Holder = Holder { a: 1 };
11 | h.drop();
| ^~~~~~~~ explicit destructor call; write `mem::drop(x)` instead. On a local, `x.drop()` releases the value there and ends the local as `mem::drop(x)` does: it is not released again at scope exit, and a later use is refused. On a field, the owner releases the field again when it is released itself. This is a warning while the tree is being cleaned up and will become an error
12 | h.drop();
13 | return 0;
|
warning[W0015]: explicit destructor call; write `mem::drop(x)` instead. On a local, `x.drop()` releases the value there and ends the local as `mem::drop(x)` does: it is not released again at scope exit, and a later use is refused. On a field, the owner releases the field again when it is released itself. This is a warning while the tree is being cleaned up and will become an error
--> main.cryo:12:5
|
10 | mut h: Holder = Holder { a: 1 };
11 | h.drop();
12 | h.drop();
| ^~~~~~~~ explicit destructor call; write `mem::drop(x)` instead. On a local, `x.drop()` releases the value there and ends the local as `mem::drop(x)` does: it is not released again at scope exit, and a later use is refused. On a field, the owner releases the field again when it is released itself. This is a warning while the tree is being cleaned up and will become an error
13 | return 0;
14 | }
|
error[E0452]: use of moved value 'h'
--> main.cryo:12:5
|
9 | function main() -> int {
10 | mut h: Holder = Holder { a: 1 };
11 | h.drop();
| - value moved here
12 | h.drop();
| ^ value used here after move
13 | return 0;
14 | }
|
aborting due to 1 error; 2 warnings emitted
Pinned by E0452_assign_rhs_use_after_move, E0452_async_giveaway_then_borrow_same_state, E0452_async_parameter_used_after_move, E0452_branch_revive_one_path, E0452_closure_body_use_after_move, E0452_closure_capture_then_use, E0452_destructure_source_used_after, E0452_destructured_binding_moved_twice, E0452_explicit_drop_twice, E0452_if_branch_move_then_use, E0452_loop_carried_byval_arg, E0452_loop_carried_move, E0452_match_arm_double_move, E0452_match_catchall_binding_moved_twice, E0452_match_payload_moved_twice, E0452_match_payload_taken_in_loop, E0452_match_payload_used_after_move, E0452_match_subject_used_after_payload_taken, E0452_move_in_loop_condition, E0452_shadow_declaration_hid_outer_move, E0452_straight_line_use_after_move, E0452_use_after_move_in_unsafe.
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;
}
implement trait Drop for struct Res {
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:15:28
|
13 | mut p: Res* = &a;
14 | // `a` still owns the value; `consume` would drop a bitwise copy of it.
15 | 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)
16 | return n as int;
17 | }
|
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_destructor_field_move_out, E0453_fill_literal_owning_element, 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_destructured_local_address, 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_held_by_function_type, 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
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_no_module, 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 | |
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 |
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 | |
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 | |
E0646 | Thread-Local Initializer Is Not a Constant |
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, E0600_await_outside_async, E0600_break_in_unsafe_block_in_awaiting_loop, E0600_continue_in_static_match_arm_in_awaiting_loop.
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_new_variant_the_enum_lacks, 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.
E0646 Thread-Local Initializer Is Not a Constant
A ![thread_local] global whose initializer is not a constant. Every thread's copy starts from the initializer as static data, so it must fold — a number, null, a string literal, or array and struct literals of those writing every field; a value computed at program start would reach the main thread's copy only. Initialize it with a constant and assign the computed value on each thread that reads it. The check runs when the program is built, so cryo check does not report it.
Pinned by thread_local_computed_initializer_refused.
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 | reserved |
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 |
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 link_failure_carries_linker_report, stamp_check_refuses_unanswered_enum_pattern, stamp_check_refuses_unanswered_name.
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_lambda_capture_is_a_use, W0001_noncapturing_lambda_body, 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 with different signatures; a call binds the declaration it names, and at most one of them matches the C definition
--> main.cryo:6:5
|
4 |
5 | extern "C" {
6 | function compute(x: int, y: int) -> int;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ duplicate extern declaration here
7 | }
|
help: also declared as `main::compute`; make the two signatures agree, or remove one
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_checked_by_definition, 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; write `mem::drop(x)` instead. On a local, `x.drop()` releases the value there and ends the local as `mem::drop(x)` does: it is not released again at scope exit, and a later use is refused. On a field, the owner releases the field again when it is released itself. 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; write `mem::drop(x)` instead. On a local, `x.drop()` releases the value there and ends the local as `mem::drop(x)` does: it is not released again at scope exit, and a later use is refused. On a field, the owner releases the field again when it is released itself. 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: f64const a: stringconst b: stringtype struct Name { ... }
type enum Name { ... }
type trait Name { ... }
type union 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::Squarepublic module collections::array;In a directory's _module.cryo aggregator, lists a submodule and whether it is public. Importing the directory's namespace sees only the modules declared public here.
type 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() -> [i32, i32]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: i32// reservedOpens no form of the language: a declaration or member written with it is refused where it is written (E0117). Declare a type struct and implement a trait for it; code that takes any type implementing the trait takes a trait object, virtual &Trait.
type class ShapeShape.sides: i32Point::Point(x: i32, y: i32)(parameter) x: i32(parameter) y: i32&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.
Point.x: i32(parameter) x: i32Point.y: i32(parameter) y: i32type struct SquareSquare.side: i32virtual &Trait
virtual mut &Trait
virtual Trait*Makes a trait object: a value of any type implementing the trait, held with a table of that type's methods for it, so code taking one is compiled once for every implementing type. The three forms borrow the value, borrow it mutably, or point at it. Written on anything but a trait it is refused (E0316); written on a method, it is E0117.
language referenceSquare::area(&this) -> i32Square.side: i32type struct Account// reservedOpens no form of the language: a declaration or member written with it is refused where it is written (E0117). A struct's fields and methods are public or private, and both are scoped to the declaring module.
Account.balance: 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).
const p: void*public function alloc(size: u64, align: u64) -> void*alloc is also declared in std::alloc::heap
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.
type 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 ShownShown::fmt<W>(&this, w: &W) -> i32type parameter W(parameter) w: &Wtype parameter Wtype trait DumpedDumped::fmt<W>(&this, w: &W) -> i32type parameter Wimplement 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 referencetype trait Shownimplement 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 Wtype trait Dumpedtype 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: 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: stringfunction is_seven(x: &i32) -> boolean(parameter) x: &i32if (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 };.
(parameter) x: &i32trueThe boolean literal. Booleans are a distinct 1-byte type, not interchangeable with integers; if and while conditions must be boolean.
falseThe boolean literal. Booleans are a distinct 1-byte type, not interchangeable with integers; if and while conditions must be boolean.
const n: i32function is_seven(x: &i32) -> booleanconst n: i32const 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: booleanconst xconst 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: i64function show<T>(x: &T) -> i32
where T: Printabletype parameter Twhere T: Printable
(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.
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.
Tag.n: i32type struct Buf<A = Tag>type parameter Atype struct TagBuf.a: Atype parameter Atype trait ShowShow::show(&this) -> inttype trait Showtype struct Buf<A = Tag>Buf::show(&this) -> intShow::show(&this) -> i32function display<T>(x: T) -> i32
where T: Showtype parameter Twhere T: Show
(parameter) x: Ttype trait Show(parameter) x: Tfunction display<T>(x: T) -> i32
where T: Showtype 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::drop(mut &this) -> voidtype struct HolderHolder.v: i32type trait Seqtype struct HolderHolder::next_one(&this) -> i32Holder.v: 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 CountdownCountdown.n: i32implement struct Name { ... }Names the struct an inherent block extends. The methods inside are interchangeable with ones declared in the struct body.
language referencetype struct CountdownCountdown::next(mut &this) -> Option<i32>Countdown.n: i32Option::NoneOption::Some(T)const c: Countdowni: i32const c: Countdowni: i32type trait Default {
static default() -> This;
}Default: a canonical "zero value" for a type.
std::core::defaultfunction make<T>() -> T
where T: Defaulttype parameter Twhere T: Default
static Default::default() -> Thisfunction make<T>() -> T
where T: Defaulttype 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>Wrap.v: 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 parameter Ttype trait Showtype struct Wrap<T>type parameter TWrap::show(&this) -> i32type 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 Holder<T>Holder.v: Ttype parameter THolder::size_of<T>(&this, x: T) -> u64type parameter Ttype trait ShapeShape::area(&this) -> i32Shape::scale(mut &this, k: i32) -> void(parameter) k: i32type trait Shapetype struct SquareSquare::area(&this) -> i64Square.side: i32Square::scale(&this, k: i32) -> voidtype trait Shapetype struct SquareSquare.side: i32function area_of(p: &Shape) -> i32(parameter) p: &Shape(parameter) p: &ShapeShape::area(&this) -> i32const s: Squarefunction area_of(p: &Shape) -> i32const s: SquareShape::duplicate(&this) -> ThisThisThe implementing type, inside a trait or implement body. other: &This in a trait method is a reference to whichever type implements it; This::Item names an associated type.
function copy_of(s: virtual &Shape) -> i32(parameter) s: virtual &Shapetype trait Shape(parameter) s: virtual &ShapeShape::duplicate(&this) -> ThisShape::area(&this) -> i32function area_of(s: virtual &Shape) -> i32type trait Shape(parameter) s: virtual &ShapeShape::area(&this) -> i32const q: Squaretype struct SquareSquare.side: i32function area_of(s: virtual &Shape) -> i32const q: Squaretype struct CircleCircle.r: i32function radius(c: virtual &Circle) -> i32(parameter) c: virtual &Circletype struct Circleextern 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 draw(s: virtual &Shape) -> voidtype trait Shapetype struct Centsprivate 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.
Cents.n: i64public 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 Cents::of(n: i64) -> Cents(parameter) n: i64type struct CentsCents.n: i64(parameter) n: i64const 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 c: Centsconst d: Centsstatic Cents::of(n: i64) -> Centstype struct PointPoint.x: i32const p: PointConfig.retries: i32Config.name: stringtype struct ConfigConfig.retries: i32const c: Configtype struct PairPair.a: i32type 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> where A: Allocator {
ptr: T*;
length: i64;
capacity: i64;
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>) -> void
where T: CopyAppend 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 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 struct Taskasync 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 referenceTask.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: intwShape::Rect(int, int)function area(s: Shape) -> int(parameter) s: Shapetype enum Shape(parameter) s: ShapeShape::Rect(int, int)w: inth: intShape::Circle(int)w: inth: intfunction area(s: Shape) -> inttype 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: HeapHolder.a: i32type struct HolderHolder::drop(mut &this) -> voidmut h: 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: i64type struct ResRes::drop(mut &this) -> voidfunction consume(r: Res) -> i64(parameter) r: 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) -> 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>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>Only.value: Tmut a: Only<f32>Only::code(&this) -> i32const unused: intprivate function helper() -> intconst a: i32const b: i64const c: i64const a: i32const b: i64const c: i64function 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() -> inttype 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>;.
// reservedOpens no form of the language: a declaration or member written with it is refused where it is written (E0117). It still names a method: Default::default() is the trait's constructor.
// 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.
// reservedOpens no form of the language: a declaration or member written with it is refused where it is written (E0117). An implementation replaces a trait's default method by writing the method.
language referencenew T { field: value }
new E::Variant(args)
new T[n]Heap allocation. new T { .. } places a struct literal on the heap and yields a T*, and new E::V(args) does the same for an enum variant; new T[n] reserves n uninitialised T - the typed malloc. The storage comes from T's Allocate implementation, or else the global allocator. Parentheses after a struct's name are refused (E0117): there are no constructors, and a struct's static new(..) is an ordinary method.
type struct Pointtype 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 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)type struct Nodetype struct Tagtype struct Buf<A = Tag>type 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 Item: Copyxtype enum Wraptype trait Conv<G>type struct Pairtype trait Seqtype Item = i32;type struct Holdertype enum Shapefunction area_of(s: virtual &Shape) -> i32(parameter) p: &Ttype struct Circleasync function maintype struct Innerfunction consume(r: Res) -> i64const d: Cents