Error codes
Every C+ diagnostic carries a numbered code, a source span, and often a machine-applicable suggestion. cpc --diagnostics=json emits the same information in a machine-readable shape for editors and agents. Codes prefixed with W are non-fatal warnings; the build continues. The normative ranges and what each phase owns are fixed in §20 of the language specification.
This is the complete index — 191 codes. Each entry gives the meaning, a minimal example that triggers it, and the typical fix. 150 of the examples are reproduced directly by cpc check; the rest need a multi-file project, a --target, or a build-time file, and say so in the example.
Lexical
E0001 · Unexpected character
The lexer hit a byte it cannot start a token with (also fired for a bad char literal such as an empty '', a multi-byte 'ab', or a non-ASCII 'á').
fn main() -> i32 { let x = 'ab'; return 0; }
Fix. Remove or correct the stray character; for UTF-8 text use a str instead of a char literal.
E0002 · Unterminated block comment
A /* ... */ block comment was opened but never closed before end of input.
/* hello
Fix. Close the comment with */.
E0003 · Invalid number literal
A numeric literal has no valid digits or a malformed exponent (e.g. 0x with no hex digits, or 1e with no exponent).
fn main() -> i32 { let x = 0x; return 0; }
Fix. Write a well-formed literal with at least one digit.
E0004 · Invalid numeric type suffix
A number literal carries a type suffix that is not one of i8/i16/i32/i64/u8/u16/u32/u64/isize/usize/f32/f64.
fn main() -> i32 { let x = 42xyz; return 0; }
Fix. Use a valid suffix or drop it.
E0005 · Unterminated string literal
A string literal was opened with " but reached end of line or end of input before a closing quote.
fn main() -> i32 { let s = "oops; return 0; }
Fix. Add the closing " (or use a """...""" triple-quoted string for multi-line text).
Parser
E0100 · Unexpected token
The parser found a token where a different one was expected (the most common case is a missing ;).
fn main() -> i32 { let x = 1 0 }
Fix. Insert the expected token; the compiler often suggests ;.
E0101 · Unexpected end of input
Input ended while the parser was still expecting more tokens (e.g. an unmatched {).
fn main() -> i32 {
Fix. Close the open construct (e.g. add the missing }).
E0102 · Non-chainable comparison
Comparison operators were chained (e.g. a < b < c), which is not allowed.
fn main() -> i32 { let r = 1 < 2 < 3; 0 }
Fix. Split into separate comparisons joined with &&, e.g. a < b && b < c.
E0103 · Expression or statement nesting too deep
Source nests expressions, statement blocks, prefix operators, or types past the recursive-descent depth limit. Each level costs a native stack frame; without the bound the parser (and the later passes that recurse over the same tree) would overflow the stack and abort the process.
fn main() -> i32 { return ((((((((((… 300 levels …)))))))))); }
Fix. Flatten the nesting: introduce intermediate let bindings, split deep expressions across statements, or reduce redundant parentheses. A limit this high is only reached by hostile or machine-generated input.
Names, types, and items
E0300 · Undefined name
A referenced name (variable, function, or this outside a method) is not in scope.
fn main() -> i32 { return x; }
Fix. Fix the typo, add the missing import, or check the name isn't _-private (module-private) in its declaring file.
E0301 · Duplicate definition
Two items (functions, or types/interfaces) share the same name.
fn f() -> i32 { 0 }
fn f() -> i32 { 1 }
fn main() -> i32 { return f(); }
Fix. Rename one of the conflicting items.
E0302 · Type mismatch
An expression's type does not match the type required by its context (declared type, argument, condition, etc.).
fn main() -> i32 { let x: i32 = true; return 0; }
Fix. Insert an as cast or change the declared type.
E0303 · Unknown type
A named type cannot be resolved to any declared type, enum, or in-scope generic parameter.
fn main() -> Foo { return 0; }
Fix. Typo, missing import, or a generic param not in scope. The owned string type was removed: use Text and import "stdlib/text".
E0304 · Condition must be bool
The condition of an if or while is not of type bool.
fn main() -> i32 { return if 1 { 1 } else { 2 }; }
Fix. Use a boolean expression, e.g. compare with != 0.
E0305 · Assignment to immutable binding
An assignment targets a binding (or a place rooted at one) that was not declared var.
fn main() -> i32 { let x = 1; x = 2; return 0; }
Fix. Declare the binding as var.
E0306 · Block produces no value but one is required
A function whose return type is non-Unit reaches the end of its body without an explicit return ...; or a diverging tail.
fn f() -> i32 { 1; }
fn main() -> i32 { return f(); }
Fix. End the body with an explicit return EXPR;.
E0307 · return without a value
A bare return; appears in a function that declares a non-Unit return type.
fn f() -> i32 { return; }
fn main() -> i32 { return f(); }
Fix. Return a value: return EXPR;.
E0308 · Wrong number of arguments
A call passes a different number of arguments than the function (or intrinsic) declares.
fn main() -> i32 { #println(1, 2); return 0; }
Fix. Match the function's parameter count.
E0309 · Wrong main signature
main is declared with parameters or a return type other than fn main() -> i32.
fn main() { }
Fix. Declare it as fn main() -> i32.
E0312 · Function used as value
A function name is used as a bare value (or another unsupported form such as &x, a range outside for, or a malformed path) where a callable or value of the right shape was required. An ASSOCIATED fn — Type::f, no receiver — is a namespaced fn and takes its address the same way; a METHOD is not, because fn(this, …) is not the fn(…) written at the binding, and there is nothing to supply its receiver.
fn main() -> i32 { let x = 1; let y = &x; return 0; }
Fix. Assign it to a fn(...)-typed binding (or pass it where one is expected) to take the address. For a method, pass a bound method reference to a handler slot instead — the *u8 after it carries the receiver.
E0313 · Assignment target is not a place
The left-hand side of an assignment is not a place expression (e.g. a literal or temporary).
fn main() -> i32 { 1 = 2; return 0; }
Fix. Assign to a variable, field, or index that names a storage location.
E0314 · Integer literal out of range
An integer literal does not fit the type it resolves to (the annotated type, the suffix type, or the i32 default). The lexer accepts any magnitude up to u64::MAX, so the value is range-checked against the target type. A leading - is a separate unary op, so a negated literal is checked against the type minimum's magnitude (-128 fits i8, 9223372036854775808 does not fit i64 but -9223372036854775808 does).
fn main() -> i32 { let x: i8 = 300; return x as i32; }
Fix. Use a value within the type's range, or widen the type (e.g. i32/i64, or an unsigned type for large non-negative values).
E0315 · Invalid cast
An as cast is between a pair of types that the language forbids — or (v0.0.27) an as? checked narrowing was written with a non-integer side (as? supports plain integer source and target only).
fn main() -> i32 { let _b: bool = 1 as bool; return 0; }
Fix. Some pairs are forbidden (for example int to bool, *T to i32); restructure the conversion. For as?, cast to a plain integer first — a distinct value via its base ((x as i64) as? u8).
E0316 · Modulo on float types
The % operator was applied to a floating-point operand, which is not supported.
fn main() -> i32 { let x: f64 = 1.0 % 2.0; let _y: f64 = x; return 0; }
Fix. Use integer operands, or compute the remainder another way.
E0317 · Unknown enum variant
A path or expression names a variant that the enum does not declare.
enum Color { Red }
fn main() -> i32 { let _c: Color = Color::Purple; return 0; }
Fix. Use a variant the enum actually declares.
E0318 · Duplicate enum variant
Two variants in the same enum share a name.
enum E { A, A }
fn main() -> i32 { return 0; }
Fix. Rename one of the variants.
E0319 · Duplicate field in struct literal
A struct literal lists the same field name twice.
struct E { x: i32, x: i32 }
fn main() -> i32 { return 0; }
Fix. List each field once; match the declaration.
E0320 · Unknown struct field
A field access (s.f) names a field the struct does not declare.
struct A { x: i32 }
fn main() -> i32 { let a: A = A { x: 1 }; let _v: i32 = a.y; return 0; }
Fix. Access a field the struct actually declares.
E0321 · Missing field in struct literal
A struct literal omits a field the struct declares.
struct A { x: i32, y: i32 }
fn main() -> i32 { let _a: A = A { x: 1 }; return 0; }
Fix. Provide every declared field; match the declaration.
E0322 · Extra field in struct literal
A struct literal includes a field the struct does not declare.
struct A { x: i32 }
fn main() -> i32 { let _a: A = A { x: 1, y: 2 }; return 0; }
Fix. Remove the extra field; match the declaration.
E0323 · Field access on non-struct type
A .field access is performed on a value whose type is not a struct.
fn main() -> i32 { let x: i32 = 5; let _v: i32 = x.foo; return 0; }
Fix. Only access fields on struct values.
E0324 · Unknown method
A method call names a method (or free fn in the type's module) that the receiver's type does not have. On a str receiver this includes the len() habit (the stdlib spells it count()) and builds that never import stdlib/str, whose blessed impl str block declares the method set.
struct P {}
impl P {}
fn main() -> i32 { let p: P = P {}; return p.missing(); }
Fix. Call a method the type actually declares, or define it in an impl. For str: use count(), add import "stdlib/str" somewhere in the build, or convert with to_text() when the operation needs an owned string.
E0325 · impl on an unknown or non-struct type
An impl names a target that is not a declared struct or (non-generic) enum in scope.
impl Foo { fn f(this) {} }
fn main() -> i32 { return 0; }
Fix. The target must be a declared struct or enum in scope.
E0326 · Duplicate method in impl
Two methods on the same type share a name. Same block, two blocks, or an extension (E0388) walking into a name the type already has — an extension adds a method, it never replaces one. A method is declared once per program: two modules may not both add one to abc, whether or not any single file imports both. First declaration holds the name; the second reports.
struct P {}
impl P { fn f(this) {} fn f(this) {} }
fn main() -> i32 { return 0; }
Fix. Rename one of the methods.
E0327 · Wrong call form
An associated function was called as an instance method (or an instance method via the type, or an enum variant was called like a function).
struct P { x: i32 }
impl P { fn make() -> P { return P { x: 0 }; } }
fn main() -> i32 { let p: P = P { x: 0 }; let _q: P = p.make(); return 0; }
Fix. Type::method() for associated, value.method() for instance.
E0328 · Mutable receiver required
A method declared with ref this is called on an immutable receiver.
struct P { x: i32 }
impl P { fn bump(ref this) { this.x = this.x + 1; } }
fn main() -> i32 { let p: P = P { x: 0 }; p.bump(); return 0; }
Fix. Bind the receiver as var.
E0329 · Mixed element types in array literal
Elements of an array literal do not all share one type.
fn main() -> i32 { let _xs: [i32; 2] = [1, true]; return 0; }
Fix. Make every element the same type.
E0330 · Array literal length mismatch
An array literal has a different element count than its declared [T; N] length.
fn main() -> i32 { let _xs: [i32; 3] = [1, 2]; return 0; }
Fix. Match the literal's element count to the declared length.
E0331 · Indexing a non-array type
The [] index operator is applied to a value that is not an array.
fn main() -> i32 { let x: i32 = 5; return x[0 as usize]; }
Fix. Only index array (or array-like) values.
E0332 · Empty array literal
An empty array literal [] was written, which is not supported.
fn main() -> i32 { let _xs: [i32; 0] = []; return 0; }
Fix. Provide at least one element.
E0339 · Fill-array element type is not Copy
A fill-array literal [expr; N] has a non-Copy (owning / drop-carrying) element type. The fill expression is evaluated once and copied into every slot, which would make N elements share one owned resource and double-free when they are dropped.
struct Owner { id: i32 }
impl Owner { fn drop(ref this) {} }
fn mk() -> Owner { return Owner { id: 1 }; }
fn main() -> i32 { let _a: [Owner; 2] = [mk(); 2]; return 0; }
Fix. Use a Copy element type, or construct each element explicitly with [expr0, expr1, ...].
E0361 · Enum has no variants
An enum is declared with zero variants. Such a type is uninhabited (no value can ever be constructed), but match exhaustiveness treats it as vacuously covered and the tag ABI lowers it as a plain i32. C+ has no uninhabited / never type.
enum Void {}
fn main() -> i32 { return 0; }
Fix. Declare at least one variant, or remove the enum.
E0364 · Cannot infer struct type of { ... }
A type-inferred struct literal { field: ... } appears where the expected type is absent or is not a known struct, so the compiler has no struct to construct.
struct A { x: i32 }
fn main() -> i32 { let a = { x: 1 }; return 0; }
Fix. Name the struct (A { field: ... }), or give the binding a struct type annotation so the literal's type can be inferred.
E0385 · Duplicate impl str
The builtin str view takes its method set from exactly one impl str { ... } block program-wide — stdlib's src/str.cplus. A second block, in any file or package, is a conflict.
impl str { fn a(this) -> usize { return #str_len(this); } }
impl str { fn b(this) -> usize { return #str_len(this); } }
fn main() -> i32 { return 0; }
Fix. Remove the extra block. To add operations over str, write free functions taking a str parameter, or convert with to_text() and use Text.
E0386 · Unsupported member in impl str
A method in the blessed impl str block has a shape the builtin does not support: generic parameters, gen/async, an associated fn (no receiver), a ref this/take this receiver (str is a Copy view — the receiver is always plain this), an interface conformance block, or a redeclaration of the compiler-provided to_text/hash/eq.
impl str { fn m(ref this) -> usize { return #str_len(this); } }
fn main() -> i32 { return 0; }
Fix. Declare the method as a plain fn name(this, ...); keep generics, interface impls, and the compiler-provided names off the block.
E0387 · Generic impl away from its template
An impl on a generic type sits in a different file than the template it names. Concrete types may be extended from any module under the import gate (E0388); generic types may not — a generic impl stays in the template's own file.
# in acme/src/a.cplus: struct Holder[T] { v: T }
# in acme/src/b.cplus:
impl Holder[T] { fn get(this) -> T { return this.v; } }
fn main() -> i32 { return 0; }
Fix. Move the impl next to the struct Name[T] / enum Name[T] it extends.
E0389 · Extension declares a destructor
An extension declares drop. Every other extension method is opt-in — you see it where you imported it — but drop decides whether values of the type are torn down at all, everywhere they are owned. That cannot depend on which files imported what. A destructor also usually needs the private fields it releases, which an extension cannot see. It belongs to the module that declares the type.
# in ext/ext.cplus, where `Point` is declared in dep/dep.cplus:
import "dep/dep" as d;
impl d::Point { fn drop(ref this) { } }
Fix. Move the destructor beside the struct it tears down, or expose a named release method the caller invokes.
E0390 · Unknown lang item
#[lang("...")] designates a declaration as one of the handful of types the compiler itself reaches for — the owned string, the gen fn and async fn protocol types, Option, JoinHandle. A name outside that set designates nothing, so the feature that was meant to find this type keeps looking and reports a missing-stdlib error somewhere else entirely.
#[lang("iterater")] struct Iterator[T] { opaque _handle: *u8 }
Fix. Spell the lang item as one of the names the message lists, or drop the attribute.
E0822 · Method cannot be used as a bound reference
obj.method in value position builds a handler from a function pointer plus the receiver's address. Not every method can be one: a take this method would consume the receiver on its first fire, a receiverless method has nothing to bind, and a generic method or one with take / ref parameters has no single fn-pointer shape to lower to. It is also refused anywhere inside a GENERIC impl body, whatever the receiver — the bridge is synthesized for one concrete type, and such a body is compiled once per instantiation (bug-29).
struct S { n: i32 }
impl S { fn eat(take this) { return; } }
fn take_handler(f: fn(*u8), ctx: *u8 = 0 as *u8) -> i32 { return 1; }
fn main() -> i32 { var s: S = S { n: 0 }; return take_handler(s.eat); }
Fix. Give the method a this or ref this receiver and only by-value parameters, or pass a plain fn directly instead of a bound reference.
E0823 · Bound method reference does not fit the expected handler
Either the parameter's fn-pointer type does not match the method — a handler type must be the method's parameters plus a trailing *u8 context, with the same return type — or the method takes ref this while the receiver expression is not a writable place (a let binding is not; a var, a field of one, or a static is).
struct S { n: i32 }
impl S { fn tick(ref this) { this.n = this.n + 1; return; } }
fn take_handler(f: fn(*u8), ctx: *u8 = 0 as *u8) -> i32 { return 1; }
fn main() -> i32 { let s: S = S { n: 0 }; return take_handler(s.tick); }
Fix. Match the handler's declared fn-pointer shape, and bind a ref this method only to a writable receiver.
E0824 · Callee has no context slot for a bound method reference
A bound reference passes the receiver's address in the argument slot right after the handler, so the callee must declare a defaulted *u8 context parameter there — and the call site must leave it to the compiler. Either the parameter is missing (or not a defaulted *u8), or the call wrote an explicit argument the bound reference would silently clobber.
struct S { n: i32 }
impl S { fn tick(ref this) { this.n = this.n + 1; return; } }
fn take_handler(f: fn(*u8)) -> i32 { return 1; }
fn main() -> i32 { var s: S = S { n: 0 }; return take_handler(s.tick); }
Fix. Declare ctx: *u8 = 0 as *u8 immediately after the handler parameter, and omit it at the call site.
E0913 · Recursive type has infinite size
A struct or enum contains itself by value — directly (struct S { s: S }), mutually (A holds B, B holds A), or through an inline array ([S; N]). Such a type has no finite size.
struct S { s: S }
fn main() -> i32 { return 0; }
Fix. Break the cycle with an indirection: store the recursive field behind a pointer (*S). A raw-pointer field needs opaque or a fn drop(ref this) (see E0510).
E0917 · Item name contains reserved __
A struct, enum, or (non-extern) function name contains an interior __. The double underscore is the compiler's monomorphization separator (Box[i32] mangles to Box__i32), so a literal Box__i32 next to a Box[T] template would collide with the instantiation's symbol — two items under one name, one silently shadowing the other.
struct Box__i32 { v: i32 }
fn main() -> i32 { return 0; }
// -> [E0917] struct name `Box__i32` contains `__`, which is reserved for compiler name mangling
Fix. Use a single underscore (box_i32). Exempt: extern fn names (existing C symbols like __errno_location never monomorphize) and names whose only __ is leading (__x — an instantiation's template base is never empty).
E0919 · Declaration claims the reserved __cplus_ runtime-ABI prefix
A function is declared with a name starting __cplus_, the prefix the compiler reserves for symbols it generates itself — the reactor helpers #reactor_get_state lowers to, the coroutine hooks, the thread trampolines, the bound-method bridges. A declaration under that prefix is claiming to name one of those symbols, and an unmarked one could take a runtime symbol's place at link time by accident or on purpose.
extern fn __cplus_reactor_get_state() -> *u8;
fn main() -> i32 { return 0; }
// -> [E0919] `__cplus_reactor_get_state` starts with `__cplus_`, the compiler's reserved runtime-ABI prefix
Fix. If the declaration really does name a compiler-generated symbol (the stdlib reactor bindings do), mark it #[runtime_abi] — the same doctrine as opaque and #[lang]: a small trusted surface, written down. Otherwise pick a name outside the prefix.
E0920 · Field not derivable for this interface
An empty impl Type: Interface {} asked the compiler to derive a memberwise implementation, but one of the struct's fields has a shape the derived method cannot handle: an enum with payload variants (no generated match in v1), an array / slice / tuple field, a pointer field where the interface needs hash or cmp or a text form, or — for ToText — a build with no #[lang("string")] type.
enum E { A, B(i32) }
struct P { e: E }
impl P: Eq {}
fn main() -> i32 { return 0; }
// -> [E0920] cannot derive `Eq` for `P`: field `e` — its enum type has payload variants; write `eq` manually
Fix. Write the named method by hand for this type, or change the field to a derivable shape. For payload enums the usual fix is a hand-written method that matches on the variants.
E0922 · distinct requires a plain integer base type
A type X = distinct BASE; declaration named a base that is not a plain integer type (i8–i64, u8–u64, isize, usize). Distinct aliases exist to give integers nominal identity at zero ABI cost; floats, pointers, strings, and aggregates are not supported bases.
type Speed = distinct f64;
fn main() -> i32 { return 0; }
// -> [E0922] `distinct` requires a plain integer base type; `Speed` is `f64`
Fix. Use an integer base, or a wrapper struct for non-integer types.
E0923 · Invalid enum discriminant or representation
A payload-free-enum feature was used on the wrong shape: an explicit discriminant (Variant = N) or an integer #[repr(...)] on an enum with payload variants, a discriminant outside the pinned representation's range, or two variants with the same value.
#[repr(u8)]
enum Mode { Off = 0, On = 300 }
fn main() -> i32 { return 0; }
// -> [E0923] discriminant 300 of `On` does not fit the enum's representation
Fix. Explicit discriminants and integer reprs describe C enums: keep the enum payload-free, keep every value inside the #[repr] type's range, and give each variant a unique value.
E0924 · Impure #[requires] expression
A #[requires(...)] precondition used a construct with effects or evaluation-order weight — a call, an assignment, a block. A contract that can change state changes the program it guards, so the expression grammar is restricted to operators, literals, parameter and const reads, field reads, and casts.
fn probe(x: i32) -> bool { return x > 0; }
#[requires(probe(n))]
fn f(n: i32) -> i32 { return n; }
fn main() -> i32 { return 0; }
// -> [E0924] a `#[requires]` expression must be pure
Fix. Restate the condition with pure reads, or hoist the computed value into a parameter the contract can read.
E0925 · Invalid union
A union broke one of the rules that follow from it having no tag: a member type is not Copy (nothing can know which member is live, so no destructor can be run correctly), the union is generic (its Copy rule cannot be checked until the members are known, and C headers are not generic), it declares no members, or a union literal named other than exactly one member.
union U { a: i32, b: u32 }
fn main() -> i32 { let x = U { a: 1, b: 2 }; return 0; }
// -> [E0925] a `union` literal names exactly one member
Fix. Keep every member Copy, keep the union non-generic and non-empty, and name exactly one member when constructing it — the one being made live.
E0926 · Invalid #[repr(packed)]
A #[repr(..., packed)] / #[repr(..., packed = N)] declaration broke one of the rules that follow from packing moving fields off their natural alignment: N is not a power of two from 1 to 16, the attribute is on an enum (a single integer, with no fields to pack), a field's type is not Copy (a destructor is handed the address of what it tears down, and a packed field has none it can believe), or a ref / #addr_of tried to take the address of a field sitting at an offset its own type is not aligned to.
#[repr(C, packed)] struct P { x: u8, y: u32 }
fn bump(ref v: u32) -> () { v = v + (1 as u32); }
fn main() -> i32 { var p: P = P { x: 1 as u8, y: 7 as u32 }; bump(p.y); return 0; }
// -> [E0926] `y` sits at offset 1 of a packed struct
Fix. Keep packed = N a power of two in 1..=16, put it on a struct, keep every field Copy, and read or write an under-aligned field directly instead of pointing at it — copy it into a local when something needs an address.
E0927 · Invalid bitfield
A #[bits(N)] field broke one of the rules that follow from a bitfield being bits inside a storage unit it shares: its type is not an integer, N is 0 or wider than that type, the struct is not #[repr(C)] (a bit position is a claim about C storage units), the field is a union member (every union member starts at offset 0), the struct is generic (a C header is not), or a ref / #addr_of tried to take its address — it has none of its own, and a pointer to it would read and write its neighbours.
#[repr(C)] struct S { #[bits(3)] a: u32, #[bits(5)] b: u32 }
fn bump(ref v: u32) -> () { v = v + (1 as u32); }
fn main() -> i32 { var s: S = S { a: 1 as u32, b: 2 as u32 }; bump(s.a); return 0; }
// -> [E0927] `a` is a bitfield: it has no address of its own
Fix. Give the field an integer type and a width from 1 to that type's bit count, declare the struct #[repr(C)] and non-generic, and read or write the field directly instead of borrowing it. C's :0 (force the next field to a boundary) has no C+ spelling; declare padding as a named field with the width you want skipped.
E0928 · Invalid #[ensures]
An #[ensures(EXPR)] used result where there is nothing to name — the function returns () — or the function already has a binding called result, so the contract's result and the declared one cannot both be meant. (A postcondition's purity rule is E0924, the same one #[requires] follows.)
#[ensures(result > 0)]
fn nothing(n: i32) { return; }
// -> [E0928] `result` names the value being returned, and this function returns nothing
Fix. On a function that returns nothing, write the postcondition about parameters and this instead; a ref parameter or this field is exactly what such a function changes. Otherwise rename the parameter that collides with result.
Control flow and matching
E0333 · Implicit return (function body ends with a tail expression)
A function body ends with an implicit tail expression instead of an explicit return; C+ function bodies never use a trailing value expression.
fn f() -> i32 { 42 }
fn main() -> i32 { return f(); }
Fix. Add an explicit return EXPR; (or ; after the closing } when the tail is unit-typed).
E0334 · Mutually-exclusive parameter ownership markers
A parameter carries two ownership markers that cannot combine, such as ref + take.
fn f(ref take x: i32) -> i32 { return x; }
fn main() -> i32 { return f(1); }
Fix. Keep at most one marker: ref (exclusive borrow), take (consume), or bare (a read-only borrow).
E0335 · Use of a moved value
A non-Copy binding is read after it was moved (into a call, a take parameter, or a let y = x;). Flow-sensitive: a move only on a branch that returns / breaks does not poison the other path, and it also fires for non-Copy types whose Copy-ness depends on a generic payload. A match also moves: matching an owned binding of a Drop-carrying enum consumes it when any arm binds a name, so the binding cannot be read or matched again.
struct P { x: i32 }
impl P { fn drop(ref this) {} }
fn echo(take p: P) -> i32 { return p.x; }
fn main() -> i32 {
let p: P = P { x: 1 };
let r: i32 = echo(p);
return p.x;
}
Fix. Do not read after a take; clone the value first, or restructure so the move and the use are on disjoint paths. For a match: bind nothing (E::A(_)) if you only need to test the discriminant — that form does not consume, so the binding stays matchable. For a guard let, reach the complement payload with else |E::B(x)| rather than re-matching the scrutinee in the else block.
E0338 · Destructor drop has the wrong signature
A drop method has a signature other than fn drop(ref this) (extra parameters, a return type, or a non-ref this receiver), or a drop was written on an enum.
struct B { x: i32 }
impl B { fn drop(this) {} }
fn main() -> i32 { return 0; }
Fix. Declare it exactly fn drop(ref this) — no extra parameters, no return type; enums get a compiler-synthesized destructor instead.
E0340 · Non-exhaustive match
A match on an enum does not cover every variant and has no catch-all arm.
enum M { A, B, C }
fn main() -> i32 { let m: M = M::A; return match m { M::A => 0 }; }
Fix. Add the missing arm or a _ => catch-all.
E0341 · Pattern type does not match the scrutinee
A match scrutinee is not an enum, a pattern names a different enum than the scrutinee, or a nested variant pattern appears in a payload position.
fn main() -> i32 { let x: i32 = 5; return match x { _ => 0 }; }
Fix. Match on an enum value, and make each pattern name the scrutinee's enum (payload patterns must be _ or a binding).
E0342 · Wrong number of payload values for a variant
A variant pattern or construction supplies a different number of payload values than the variant declares.
enum M { A(i32, i32) }
fn main() -> i32 { let m: M = M::A(1, 2); return match m { M::A(v) => v }; }
Fix. Match the variant's declared payload arity in both the pattern and the constructor.
E0343 · A match mixes literal and variant patterns
One match used both literal patterns (1 => ...) and variant patterns (M::A => ...). The two ask different questions — a literal matches a VALUE, a variant matches a CASE — and they lower to different code, so a mixed match has no single meaning.
enum M { A, B }
fn main() -> i32 { let m: M = M::A; return match m { 1 => 0, M::B => 1 }; }
Fix. Split the match, or convert the literal arms into variants of the same enum.
E0344 · Literal match is non-exhaustive or has an unreachable arm
A match over literals either has no catch-all — each literal arm covers exactly one value, so the compiler cannot prove the rest are handled — or it has an arm after a catch-all, which can never run.
fn main() -> i32 { let n: i32 = 3; return match n { 1 => 10, 2 => 20 }; }
Fix. End a literal match with _ or a binding arm, and put nothing after it.
E0345 · Use of a possibly-unassigned binding
A binding is read on a control-flow path where it is not definitely assigned.
fn main() -> i32 { let x: i32; return x; }
Fix. Initialize the binding on every control-flow path before reading it.
E0346 · Uninitialized let requires a type annotation
A let with no initializer has no type annotation, so there is nothing to infer the type from.
fn main() -> i32 { let x; x = 5; return x; }
Fix. Add a type annotation (let x: T;) or give the let an initializer.
E0347 · Irrefutable if let / while let pattern
An if let or while let uses a pattern that always matches (a bare binding or _), so the conditional form is pointless.
fn main() -> i32 {
if let x = 7 { return x; }
return 0;
}
Fix. Use a plain let (or loop) instead, or write a refutable variant pattern.
E0348 · guard let else block must diverge
The else block of a guard let falls through instead of diverging on every path.
enum Maybe { Some(i32), None }
fn main() -> i32 {
let m: Maybe = Maybe::Some(7);
guard let Maybe::Some(v) = m else { let x: i32 = 1; };
return v;
}
Fix. Make the else block diverge on every path (return / break / continue).
E0350 · guard let complement overlaps the success pattern
The explicit complement pattern in else |Pat| references the same enum variant as the success pattern, so the two overlap.
enum Maybe { Some(i32), None }
fn main() -> i32 {
let m: Maybe = Maybe::Some(7);
guard let Maybe::Some(v) = m else |Maybe::Some(_)| { return 0; };
return v;
}
Fix. Make the complement pattern cover only the cases the success pattern does not.
E0351 · guard let must bind at least one value
A guard let pattern binds no names, so there is nothing for it to extract.
enum Maybe { Some(i32), None }
fn main() -> i32 {
let m: Maybe = Maybe::Some(7);
guard let Maybe::None = m else { return 0; };
return 0;
}
Fix. Use if let for inspection-only, or write a pattern that binds a value.
E0352 · Multi-binding guard let is not supported
A guard let pattern binds more than one value; only single-binding patterns are supported.
enum Pair { Both(i32, i32) }
fn main() -> i32 {
let p: Pair = Pair::Both(1, 2);
guard let Pair::Both(a, b) = p else { return 0; };
return a;
}
Fix. Use one guard let per binding.
E0353 · break / continue outside a loop
A break or continue appears outside any loop body.
fn main() -> i32 { break; return 0; }
Fix. Move it into a loop body.
E0363 · Name already declared in this scope (no same-scope shadowing)
Two bindings with the same name are declared in one block. C+ forbids redeclaring a name in a scope; same-scope shadowing would silently swap a binding's type, so it is rejected.
fn main() -> i32 { let x: i32 = 1; let x: bool = true; return 0; }
Fix. Pick a new name, or assign to the existing binding. Shadowing in a nested block (or shadowing a parameter) is still allowed — only same-block re-declaration is rejected.
Ownership and borrowing
E0337 · A bare borrow escapes its call
A bare (read-only borrow) parameter, a raw-pointer dereference, or a value matched out of a borrow is made to outlive the call — returned, stored in a field, or re-passed to a take parameter. The borrow has no owner to keep its storage alive past the call.
struct B { x: i32 }
impl B { fn drop(ref this) {} }
fn keep(b: B) -> B { return b; }
fn main() -> i32 { return 0; }
Fix. Take the value by value (take) so the callee owns it, or .clone() it; return an owned value rather than a borrow. For the raw-pointer case in a container — reading a value OUT of storage the container owns and then disarming the source so it is never dropped twice — use #take::[T](p), which states that ownership transfers here; the analysis cannot see the disarm, so it is declared, the same way opaque (E0510) and #[keeps] (E0516) declare at the raw seam.
E0365 · A value that captured a local's address escapes the frame
A bound method reference (obj.handler in value position) lowers to a function pointer plus the receiver's raw ADDRESS. When the receiver is storage this frame frees — a local, a take parameter, a take this, or a by-value parameter of a Copy type — and the value holding that address then leaves the frame (returned, stored into a static or a ref target, or handed to a call), the handler points at a stack slot that is gone before it fires. The analysis is transitive: a method that binds its own receiver taints every value it returns, and a binding that absorbs such a value carries the capture onward.
struct Child { clicks: i32 }
impl Child {
fn clicked(ref this) { this.clicks = this.clicks + 1; return; }
fn build(ref this) -> i32 { return take_handler(this.clicked); }
}
fn take_handler(f: fn(*u8), ctx: *u8 = 0 as *u8) -> i32 { return 1; }
fn make() -> i32 {
var c: Child = Child { clicks: 0 };
return c.build();
}
Fix. Give the receiver storage that outlives the escaping value — a field of this, a static, or a Box. Binding a handler to a local is legal as long as it does not escape; binding to this or to a field is always legal, which is why this costs nothing in ordinary component code.
E0370 · Move and shared-borrow of the same binding in one call
A non-Copy binding is moved at one argument position while a sibling argument in the same call reads (shared-borrows) the same place.
struct B { x: i32 }
impl B { fn drop(ref this) { return; } }
fn drain(take b: B, n: i32) { return; }
fn peek(b: B) -> i32 { return b.x; }
fn caller() {
var y: B = B { x: 1 };
drain(y, peek(y));
return;
}
In this minimal single-call form cpc reports the broader use-after-move error E0335; E0370 is the borrow checker's name for the move / shared-borrow conflict.
Fix. Split into two statements so the value is read before it is moved: let tmp = peek(y); drain(take y, tmp);
E0371 · Use of a possibly-moved binding
A non-Copy binding is moved on some control-flow branches but not others, then read at a point where it may already be moved (its merged state is MaybePartial).
struct B { x: i32 }
impl B { fn drop(ref this) { return; } }
fn sink(take b: B) { return; }
fn use_it(b: B) -> i32 { return b.x; }
fn caller(c: bool) {
var y: B = B { x: 1 };
if c { sink(y); }
let z: i32 = use_it(y);
return;
}
Reported as E0335 in simple cases; E0371 specifically covers a use of a binding moved on only some control-flow paths.
Fix. Ensure every branch either moves or preserves the binding, or clone it before the branch: let y_owned = y.clone();
E0372 · Move of a binding while it is borrowed
A binding is moved while a live borrower still holds a borrow of it (or one of its sub-places) at an overlapping place.
struct B { x: i32 }
impl B { fn drop(ref this) { return; } }
fn longest(a: B, b: B) -> B {
if a.x > b.x { return a; }
return b;
}
fn drain(take b: B) { return; }
fn caller() {
let a: B = B { x: 1 };
let b: B = B { x: 2 };
let r: B = longest(a, b);
drain(a);
return;
}
In this minimal form cpc reports E0335; E0372 is the borrow checker's classification of moving a value while it is borrowed.
Fix. Drop the borrower before moving the value, or clone it if both bindings must outlive the move.
E0374 · Partial-place borrow conflict
A borrow of a place overlaps a sibling access to one of its sub-places (or vice versa) — a borrow of a place includes all of its sub-places.
struct Inner { v: i32 }
impl Inner { fn drop(ref this) { return; } }
struct Pair { left: Inner, right: Inner }
impl Pair { fn drop(ref this) { return; } }
fn write_pair(ref a: Pair, b: Inner) { return; }
fn caller() {
let p: Pair = Pair { left: Inner { v: 1 }, right: Inner { v: 2 } };
write_pair(p, p.left);
return;
}
A whole-place / sub-field overlap in one call is reported as E0337; E0374 is the borrow checker's partial-place conflict.
Fix. Split into two calls if the operations are independent, or restructure to operate on a single uniform place.
E0380 · Two exclusive borrows of the same place in one call
The same non-Copy binding is exclusively borrowed (ref) at two argument positions in a single call, but at most one exclusive borrow of a place can be live at a time.
struct B { x: i32 }
impl B { fn drop(ref this) { return; } }
fn modify_both(ref a: B, ref b: B) { return; }
fn caller() {
var y: B = B { x: 1 };
modify_both(y, y);
return;
}
Fix. Split into two calls, or borrow distinct sub-places (e.g. f(ref y.left, ref y.right)).
E0381 · Exclusive borrow with a concurrent shared read
A place is exclusively borrowed (ref) while a sibling argument shared-reads it in the same call, or a method is called on a receiver that is currently shared-borrowed.
struct B { x: i32 }
impl B { fn drop(ref this) { return; } }
fn write_thing(ref a: B, n: i32) { return; }
fn peek(b: B) -> i32 { return b.x; }
fn caller() {
var y: B = B { x: 1 };
write_thing(y, peek(y));
return;
}
Fix. Split into two statements: let tmp = peek(y); write_thing(ref y, tmp);
E0382 · Move and exclusive borrow of the same binding in one call
The same non-Copy binding is exclusively borrowed (ref) at one argument position and moved at another in a single call; the exclusive borrow claims access for the whole call, which conflicts with the move's consumption.
struct B { x: i32 }
impl B { fn drop(ref this) { return; } }
fn write_and_take(ref a: B, take b: B) { return; }
fn caller() {
var y: B = B { x: 1 };
write_and_take(y, y);
return;
}
Fix. Split into two statements so the exclusive borrow and the move do not overlap.
E0383 · Access to a binding while it is exclusively borrowed
A place is read, or has a method called on it, while an exclusive borrow of that same place is still live. The exclusive borrow claims the place for its whole lifetime, so no overlapping access is admitted. E0374 is the same rule for a place that only partially overlaps; E0381 is the shared-borrow twin.
struct B { x: i32 }
impl B { fn drop(ref this) { return; } }
fn cursor(ref b: B) -> B { return b; }
fn peek(b: B) -> i32 { return b.x; }
fn caller() {
var v: B = B { x: 1 };
let cur: B = cursor(v);
let n: i32 = peek(v);
return;
}
Not reachable through the driver as of v0.0.26: a function that returns a borrow of a ref parameter is rejected by sema (E0337) first, and sema errors bail the pipeline before borrowck runs. The rule is live in borrowck and pinned by its unit tests.
Fix. End the borrow before the access — let the borrower go out of scope, or move it — or restructure so the two do not overlap.
E0384 · Cannot infer which parameter a returned borrow comes from
A function with two or more borrow-like parameters returns a value rooted at one of them, but the elision rules cannot pick which — so the caller has nothing to tie the returned borrow's lifetime to.
struct B { x: i32 }
impl B { fn drop(ref this) { return; } }
fn longest(ref a: B, ref b: B) -> B {
if a.x > b.x { return a; }
return b;
}
Not reachable through the driver as of v0.0.26, for the same reason as E0383: returning a parameter is a sema E0337, which bails before borrowck runs.
Fix. Return an owned value (Text / Vec[T]), or restructure so exactly one parameter can be the borrow's source. The borrow REGION T annotation this once suggested is retired and is not the remedy.
E0503 · Interface impl missing a required method
An impl Type: Interface block omits a method that the interface declares.
interface Two { fn first(this) -> i32; fn second(this) -> i32; }
struct P { x: i32 }
impl P: Two { fn first(this) -> i32 { return 0; } }
fn main() -> i32 { return 0; }
Fix. Implement every method the interface declares.
E0504 · Interface impl declares a method the interface does not
An impl Type: Interface block contains a method that the interface does not declare.
interface One { fn a(this) -> i32; }
struct P { x: i32 }
impl P: One { fn a(this) -> i32 { return 0; } fn extra(this) -> i32 { return 1; } }
fn main() -> i32 { return 0; }
Fix. Move the extra method to an inherent impl Type { ... } block.
E0505 · Interface method signature mismatch
An impl method's signature does not match the interface's declared signature after substituting This with the target type.
interface One { fn a(this) -> i32; }
struct P { x: i32 }
impl P: One { fn a(this) -> bool { return true; } }
fn main() -> i32 { return 0; }
Fix. Make the impl method's signature match the interface declaration exactly.
E0506 · Duplicate interface impl for the same type
Two impl Type: Interface blocks exist for the same (interface, type) pair; a type may have at most one impl of any given interface.
interface One { fn a(this) -> i32; }
struct P { x: i32 }
impl P: One { fn a(this) -> i32 { return 0; } }
impl P: One { fn a(this) -> i32 { return 1; } }
fn main() -> i32 { return 0; }
Fix. Remove the duplicate impl block.
E0507 · Orphan-rule violation for an interface impl
An impl Type: Interface block lives in a file that declares neither the interface nor the type; the orphan rule requires the impl to be co-located with one of them.
// in a third file that imports both Iface and Ty:
impl Ty: Iface { fn a(this) -> i32 { return 0; } }
Fix. Declare the impl in the same file as either the interface or the type.
E0508 · This used outside an interface or impl body
The type This is named where there is no surrounding interface or impl body to give it meaning.
fn loose(x: This) -> i32 { return 0; }
fn main() -> i32 { return 0; }
Fix. Use a concrete type name, or move the code into an interface / impl body.
E0509 · Move of a field out of a Drop type
A non-Copy value is moved out of a field or index of a place whose type implements drop, which would let the destructor free the moved field a second time.
extern fn malloc(n: usize) -> *u8;
extern fn free(p: *u8);
struct Owned { ptr: *u8 }
impl Owned {
fn make() -> Owned { return Owned { ptr: { malloc(16 as usize) } }; }
fn drop(ref this) { { free(this.ptr); } return; }
}
struct Pair { a: Owned, b: Owned }
impl Pair {
fn drop(ref this) { { free(this.a.ptr); } { free(this.b.ptr); } return; }
}
fn main() -> i32 {
let p: Pair = Pair { a: Owned::make(), b: Owned::make() };
let q: Owned = p.a;
return 0;
}
Fix. Clone the field, or restructure so it is not owned by a Drop type.
E0510 · Unaccounted raw-pointer field in a Drop type
A struct has a raw-pointer field that is neither released in a drop (no releasing drop, or only via a helper) nor marked opaque.
extern fn malloc(n: usize) -> *u8;
struct Buf { ptr: *u8 }
fn main() -> i32 { return 0; }
Fix. Release it in drop (free(this.f)), or mark the field opaque if another owner frees it.
E0513 · Returning a str / T[] view of a local that drops
A str / T[] view rooted at a function-local non-Copy owned value escapes the frame — returned directly, returned inside an aggregate (a struct with a str field CARRIES the view's borrow, including when built through a call like store(local.view(), ..) or returned via an alias), or stored into a place that outlives the frame (a static, or a ref target). The local is freed at return, so the escaped view would dangle.
extern fn malloc(n: usize) -> *u8;
extern fn free(p: *u8);
struct Buf { ptr: *u8 }
impl Buf {
fn drop(ref this) { { free(this.ptr); } return; }
fn as_str(this) -> str { return { #str_from_raw_parts(this.ptr, 4 as usize) }; }
}
fn mk_buf() -> Buf { return Buf { ptr: { malloc(4 as usize) } }; }
fn bad() -> str {
let s: Buf = mk_buf();
return s.as_str();
}
Fix. Own the bytes instead: store/return Text / Vec[T], or borrow the view from a non-take parameter. Literal-backed views ('static bytes) escape freely.
E0514 · Owner goes out of scope while a view of it is still live
A binding declared inside a block owns bytes that a view (or a view-carrying struct/enum) bound OUTSIDE the block still reads. The owner is dropped when the block ends, so the outer binding dangles from that point on. Assigning a view outward is the same escape as moving the owner while borrowed (E0372), caught at the scope boundary instead of at a move.
struct Buf { x: i32 }
impl Buf {
fn drop(ref this) { return; }
fn view(this) -> str { return ""; }
}
fn bad() {
var s: str = "";
{
let t: Buf = Buf { x: 1 };
s = t.view();
}
return;
}
Fix. Declare the borrower inside the block, extend the owner's scope past the borrower's last use, or store owned bytes instead: Text, or text::intern when a set-once process-lifetime view is wanted.
E0515 · Storing a borrowed view parameter into a target that outlives the call
A str / T[] parameter (or a view-carrying one) is stored into a static, a ref parameter target, or a field of ref this. The caller only guarantees the view's bytes for the duration of the call; the target outlives it, so the stored view dangles as soon as the caller's owner drops. str params previously slipped through the owned-root check because str is Copy — this was the laundering path behind the stored-key use-after-free family.
struct Holder { view: str }
impl Holder {
fn set(ref this, k: str) {
this.view = k;
return;
}
}
Fix. Own the bytes (a Text field), intern them (text::intern returns a process-lifetime view), or — for the receiver-store case only — declare the method #[keeps(this)]: the store becomes a declared flow and every caller ties the receiver to the argument's owner (E0372/E0514 then guard the owner).
E0516 · Storing a view through a raw pointer without a declared flow
A str / T[] / view-carrying value is stored through a raw-pointer deref — *slot = v, and equally any projection of the pointee ((*sink).key = v, (*sink)[i] = v). No flow analysis can see what the pointer points at, so a field of the pointee is exactly as opaque as the whole pointee, and the function's effect on view lifetimes is unknowable — silence at the raw seam is not neutral, the same doctrine as the raw-pointer field rule (drop-or-opaque, E0510).
fn stash(slot: *str, v: str) {
*slot = v;
return;
}
Fix. Declare the function's flow: #[keeps(this)] if the view survives inside the receiver (callers then tie the receiver to the argument's owner), or #[keeps(nothing)] if the bytes are copied and no borrowed view escapes. Better still, remove the seam: store an owned Text so the struct outlives its own bytes. Byte and pointer stores never trigger this — only a view value does, and a store to a field of a plain local is not a raw store at all.
E0612 · Interpolated type does not implement ToText
A ${...} interpolation segment embeds a value whose type does not implement ToText (and is not a blessed/numeric type or an owned Text).
struct Point { x: i32, y: i32 }
fn main() -> i32 {
let p: Point = Point { x: 1, y: 2 };
let s = "point: ${p}";
return 0;
}
Fix. Implement ToText for the type, or interpolate a field that is already ToText-able.
E0613 · Owned string (Text) named without its import
An expression produces an owned string (via .to_text() or string interpolation) but the Text type is not in scope because stdlib/text was not imported.
fn f() -> i32 { let n: i32 = 1; let s = n.to_text(); return 0; }
Fix. Add import "stdlib/text"; borrowed str views need no import.
E0915 · Send / Sync marker impl has a non-empty body
Send and Sync are marker interfaces with no methods; the assertion is the empty impl Type: Send {} itself. A non-empty body is rejected.
struct Handle { opaque p: *u8 }
impl Handle: Send { fn x(this) -> i32 { return 0; } }
fn main() -> i32 { return 0; }
// -> [E0915] `impl Handle: Send` must have an empty body
Fix. Make the body empty: impl Type: Send {}.
E0916 · Empty impl of an interface that is neither derivable nor a marker
An empty impl Type: Interface {} was written for an interface the compiler cannot fill in. An empty impl derives the memberwise implementation for the five blessed interfaces (Eq, Ord, Hash, Clone, ToText) on a struct target, or asserts the Send / Sync markers; a user interface's methods must be provided, and deriving needs a struct (not an enum) target.
interface Greet { fn hi(this) -> i32; }
struct S { x: i32 }
impl S: Greet {}
fn main() -> i32 { return 0; }
// -> [E0916] empty `impl` derives `Eq` / `Ord` / `Hash` / `Clone` / `ToText`
// or asserts the `Send` / `Sync` markers; `Greet` requires a body
Fix. Implement the interface's methods, or — if you meant to derive — make the target a struct and the interface one of Eq / Ord / Hash / Clone / ToText.
Modules, paths, and visibility
E0388 · Extension method not in scope
A module other than the one declaring the type added this method, and the file making the call never imported that module. Extensions travel with their import: a file's method set is its types' own methods plus whatever the modules it imports added. Packages play no part — a sibling file is gated exactly like a vendored dependency. The method is real and there is exactly one of it in the program; it is simply not in scope here.
# in ext/ext.cplus: impl d::Point { fn sum(this) -> i32 { ... } }
# in this file — ext/ext never imported:
import "dep/dep" as d;
fn probe(p: d::Point) -> i32 { return p.sum(); }
Fix. Import the extending module in this file, or call a method declared with the type itself.
E0401 · Imported file not found
An import "..." string did not resolve to an existing .cplus file on disk.
import "./missing" as m;
fn main() -> i32 { return 0; }
Fix. Correct the import path (the compiler offers a did-you-mean for the closest existing filename), or create the file.
E0402 · Unknown import prefix
A prefix::Item path uses an as prefix that was never bound by an import declaration in this file.
import "ghost/widget" as g; // `ghost` is not a declared dependency
fn use_it() -> i32 { return g::value(); }
Needs a project: the import path's first segment names no dependency in Cplus.toml. A bare unknown name in code is reported as E0300/E0303 instead.
Fix. Add the matching import "./module" as prefix;, or fix the prefix to one that is imported.
E0403 · Private item accessed across a file boundary
A cross-file reference touched a function, type, field, method, const, static, type alias, or interface whose name begins with _ (module-private) in its declaring file.
import "./math" as math;
fn main() -> i32 { return math::square(7); }
Fix. Remove the leading _ from the name to make it public (or export it for the C ABI). (Requires an imported module; math.cplus declares fn _square as private.)
E0404 · Cyclic import dependency
The import graph contains a cycle, so the files mutually depend on each other and cannot be ordered.
import "./a" as a;
fn main() -> i32 { return 0; }
Fix. Break the cycle: factor the shared declarations into a third module that both files import. (Requires multiple files; here a.cplus imports b.cplus which imports a.cplus.)
E0405 · No such item in module
A prefix::name path (or duplicate as prefix) names an item that does not exist in the imported module at all, or two imports share an as prefix.
import "./lib" as lib;
fn main() -> i32 { return lib::nope(); }
Fix. Fix the name to one the module actually exports, or give each import a distinct as prefix. (Requires an imported module; lib.cplus has no item named nope.)
E0406 · Malformed or incomplete manifest
Cplus.toml failed to parse, is missing a required field, or names an unsupported edition.
[[[ not valid toml
Fix. Repair the TOML, supply the missing field, or set edition = "2026".
E0407 · Cannot read the manifest
An I/O error occurred while reading Cplus.toml (for example the file is unreadable or vanished mid-build).
[package]
name = "x"
Fix. Ensure Cplus.toml exists and is readable from the build directory.
E0408 · Both [[bin]] and [lib] declared
A single manifest declares both a binary target and a library target, which are mutually exclusive.
[package]
name = "both"
[[bin]]
name = "exe"
[lib]
Fix. A manifest is either an executable or a library; split it into two crates if you need both.
E0409 · fn main defined in a library target
A manifest that declares [lib] also defines a fn main, but a library has no entry point.
fn add(a: i32, b: i32) -> i32 { return a + b; }
fn main() -> i32 { return 0; }
Fix. Remove fn main, or use [[bin]] instead of [lib] if you meant to build an executable. (Requires a [lib] manifest.)
E0410 · Type in export extern fn is not C-ABI compatible
A parameter or return type in an export extern fn cannot cross the C function-call ABI (for example a str/slice fat pointer, a tagged enum, a non-#[repr(C)] struct, or a Drop type).
export extern fn echo(s: str) -> i32 { return 0; }
fn main() -> i32 { return 0; }
Fix. Use C-representable types: pass a *u8 plus a usize length instead of a fat pointer, or mark structs #[repr(C)].
E0411 · restrict on a non-pointer parameter
The restrict marker was placed on a parameter whose type is not a raw pointer.
fn bad(restrict x: i32) -> i32 { return x; }
fn main() -> i32 { return bad(0); }
Fix. Only *T accepts restrict; remove it or change the parameter to a raw-pointer type.
E0412 · Unsupported crate-type value
A [lib] crate-type value is not one of the accepted kinds.
[package]
name = "mathlib"
[lib]
crate-type = "rlib"
Fix. Use one of staticlib, cdylib, or both.
Generics and bounds
E0500 · Cannot infer a type parameter
A declared generic parameter never appears in an argument position, so the compiler cannot infer it from the call's arguments.
fn make[T]() -> i32 { return 0; }
fn main() -> i32 { return make(); }
Fix. Supply the name::[T1, T2](...) turbofish, or use the parameter in an argument so inference can pin it.
E0501 · Wrong type-argument count
A turbofish or generic instantiation supplied a different number of type arguments than the generic parameter list declares (including supplying any on a non-generic item).
fn id[T](x: T) -> T { return x; }
fn main() -> i32 { let a: i32 = id::[i32, bool](7); return a; }
Fix. Match the generic parameter list: supply exactly as many type arguments as the declaration has.
E0502 · Bound not satisfied
A concrete type argument does not satisfy a declared bound on its type parameter (also fired for a !Send / !Sync type passed where Send / Sync is required across threads).
fn max[T: Ord](a: T, b: T) -> T { return a; }
struct Point { x: i32 }
fn main() -> i32 { let p: Point = Point { x: 0 }; let r: Point = max(p, p); return 0; }
Fix. T: Ord requires impl Point: Ord; provide the impl, or for thread-crossing use impl T: Send {} when the marker holds.
Unsafe, FFI, and intrinsics
E0700 · Tuple literal with fewer than two elements
A tuple literal was written with zero or one element, but () is the unit value and (x) is grouping, so a tuple must have at least two elements.
fn main() -> i32 {
let t = (1,);
return 0;
}
Fix. Add a second element, or use ()/(x) if you meant the unit value or a parenthesized expression.
E0821 · Cannot take the address of a generic function
A generic function name was used as a function-pointer value without specifying its type parameters, so there is no single monomorphized instance to point at.
fn identity[T](x: T) -> T { return x; }
fn main() -> i32 { let f: fn(i32) -> i32 = identity; return 0; }
Fix. Specify the type parameters at the take-address site (turbofish), so a concrete instance is selected.
E0905 · Unknown compiler intrinsic #name
A #name(...) intrinsic is not recognized, or a compiler builtin was called as a bare name instead of with the # sigil.
fn main() -> i32 { return #not_a_real_intrinsic(1); }
Fix. Fix the typo; check the intrinsics list, and spell builtins with the # sigil.
Compile-time builtins
E0870 · #include_bytes/#include_str file not found
The path passed to #include_bytes/#include_str could not be resolved or read relative to the including file at compile time.
fn main() -> i32 { let s: str = #include_str("missing.txt"); return 0; }
Fix. Correct the path (it is resolved relative to the file containing the call) or create the missing file.
E0871 · #include_bytes/#include_str argument must be a string literal
The path argument to #include_bytes/#include_str was not a string literal, so the file cannot be resolved at compile time.
fn main() -> i32 { let s: str = #include_str(some_var); return 0; }
Fix. Pass a string literal path, e.g. #include_str("data.txt").
E0872 · #include_bytes/#include_str file exceeds the 64 MiB cap
The file embedded via #include_bytes/#include_str is larger than the 64 MiB sanity limit the compiler will read at compile time.
fn main() -> i32 { let b: *const [u8; 0] = #include_bytes("huge.bin"); return 0; }
// where huge.bin is larger than 64 MiB
Fix. Embed a smaller file, or load the data at runtime instead of compile time.
E0873 · SIMD lane/shift index must be a literal
A SIMD .lane(...) or shift method was given a non-literal u32 index, but the lane/shift count must be a compile-time literal.
fn main() -> i32 {
let v: f32x4 = f32x4::splat(1.0f32);
var i: u32 = 0 as u32;
let x: f32 = v.lane(i);
return 0;
}
Fix. Pass a literal u32 index, e.g. v.lane(0 as u32).
E0874 · SIMD lane/shift index out of range
A SIMD .lane(...) index or shift count is at or beyond the vector's lane count (or the per-lane bit width for shifts).
fn main() -> i32 {
let v: f32x4 = f32x4::splat(1.0f32);
let x: f32 = v.lane(7 as u32);
return 0;
}
Fix. Use an index within range (0..lane_count), or a shift count below the lane bit width.
E0875 · #include_str file is not valid UTF-8
The file embedded via #include_str contains bytes that are not valid UTF-8; the message reports the byte offset of the first invalid byte.
fn main() -> i32 { let s: str = #include_str("bad.bin"); return 0; }
// where bad.bin contains a stray 0xFF byte
Fix. Use #include_bytes for binary data, or fix the file so it is valid UTF-8.
E0876 · #env("X"): env var not set at compile time
The environment variable named in #env("NAME") was not set in the compiler's own process environment when cpc was invoked.
fn main() -> i32 {
let _v: str = #env("CPC_TEST_DEFINITELY_MISSING_99");
return 0;
}
Fix. Set the variable when invoking cpc, or pick a different default.
E1000 · Missing stdlib type for gen fn / Iterator::next
A gen fn was used without Iterator[T] from stdlib/iterator in scope (or Iterator::next was reached without Option[T] from stdlib/option), so the compiler cannot synthesize the iterator/option type.
gen fn count_up(n: i32) -> i32 {
var i: i32 = 1;
while i <= n { yield i; i = i +% (1 as i32); }
return;
}
fn main() -> i32 { return 0; }
// fails when `import "stdlib/iterator"` is absent
Fix. Add import "stdlib/iterator" (and import "stdlib/option") so the required generic types are available.
E1001 · yield outside a gen fn body
A yield expression appeared outside the body of a gen fn, where there is no iterator to produce values into.
fn main() -> i32 {
yield 1;
return 0;
}
Fix. Move the yield into a gen fn body, or remove it.
E1002 · Named arguments not supported yet
A call used a named argument (f(name: value)). The parser accepts the syntax, but the argument-matching pass that reorders named arguments into positional order and splices defaults is not implemented yet (see docs/design/named-params-and-defaults.md). This is a temporary guard so a labeled call is rejected cleanly rather than silently bound by position.
fn add(n1: i32, n2: i32) -> i32 { return n1 +% n2; }
fn main() -> i32 {
return add(v: 1); // -> E1002 on a method/other call form
}
Fix. Pass the arguments positionally for now: f(value).
E1004 · Positional argument after a named argument
A positional argument followed a named one. Positional arguments must all come before any named argument so the call has a single readable shape.
fn add(n1: i32, n2: i32) -> i32 { return n1 +% n2; }
fn main() -> i32 {
return add(n1: 1, 2); // -> E1004 positional argument after a named argument
}
Fix. Move the positional argument before the first named argument, or give it a label.
E1005 · Unknown argument label
A named argument used a label that is not a parameter of the called function.
fn add(n1: i32, n2: i32) -> i32 { return n1 +% n2; }
fn main() -> i32 {
return add(bogus: 1, n2: 2); // -> E1005 unknown argument label `bogus`
}
Fix. Use a parameter name from the function's signature.
E1006 · Argument provided more than once
The same parameter was given a value more than once — by position and by label, or by two labels.
fn add(n1: i32, n2: i32) -> i32 { return n1 +% n2; }
fn main() -> i32 {
return add(n1: 1, n1: 2); // -> E1006 argument `n1` is provided more than once
}
Fix. Provide each argument exactly once.
E1007 · Required parameter after a defaulted one
A parameter without a default value follows one that has a default. Defaults must be trailing so a positional call can omit them unambiguously.
fn f(a: i32 = 0, b: i32) -> i32 { return a +% b; }
// ^ -> E1007 required parameter `b` cannot follow a defaulted one
Fix. Move the defaulted parameters to the end, or give the later ones defaults too.
E1008 · Default value on an extern fn parameter
An extern fn parameter declared a default value. The C ABI has no notion of default arguments, and extern fn declarations are call-shapes for a foreign symbol.
extern fn g(x: i32 = 0) -> i32; // -> E1008 extern parameter cannot have a default
Fix. Remove the default; pass the argument explicitly at every call.
Real-time contracts
E0900 · Borrow-shaped parameter in an async fn
An async fn parameter is borrow-shaped (str / T[]) or a ref-bound non-Copy value (pointer-passed), which may dangle once a borrow lives across an await.
struct Future[T] { opaque handle: *u8 } async fn fetch(url: str) -> i32 { return 0 as i32; }
Fix. Use Text / Vec[T] instead of str / T[], or take ownership in / bind locally instead of ref.
E0901 · #[no_alloc] violation (or await outside async fn)
A #[no_alloc] function or a callee heap-allocates, builds an interpolated Text, runs allocating drop-glue at scope exit, or calls something not proven non-allocating; the code reused for the contract also rejects await outside an async fn.
fn helper(x: i32) -> i32 { return x +% 1; }
#[no_alloc] fn caller(x: i32) -> i32 { return helper(x); }
fn main() -> i32 { return 0; }
Fix. Remove the allocation (or the offending call), drop the #[no_alloc] contract, or mark the callee #[no_alloc].
E0902 · await of a non-Future expression
An await is applied to an expression that does not evaluate to a Future[T].
struct Future[T] { opaque handle: *u8 } async fn bad() -> i32 { let x: i32 = await (7 as i32); return x; }
Fix. Await a Future[T] value (the result of calling an async fn).
E0903 · Invalid compiler-intrinsic call shape
A #name(...) intrinsic (such as #selector or #compile_shader) is called with the wrong number/kind of arguments, stray type arguments, or an unsupported -> T return ascription.
fn main() -> i32 {
let n: i32 = 42;
let p: *u8 = #selector(n);
return 0;
}
Fix. Call the intrinsic with the exact argument shape it documents (e.g. #selector takes one string literal).
E0904 · #compile_shader target or toolchain error
A #compile_shader(...) names an unsupported target, or the shader toolchain invocation (xcrun metal / metallib) failed or produced no output.
fn main() -> i32 {
let p: *u8 = #compile_shader("k.spv", "spirv") as *u8;
return 0;
}
Fix. Use a supported target ("msl") and make sure the shader source compiles with the toolchain.
E0906 · #[bounded_recursion] violation
The call graph of a #[bounded_recursion] function cycles back to itself, directly or transitively.
#[bounded_recursion] fn r(x: i32) -> i32 {
if x == 0 { return 0; }
return r(x -% 1);
}
fn main() -> i32 { return 0; }
Fix. Break the recursion so the call graph no longer cycles back to the function.
E0907 · #[no_block] violation
A #[no_block] function or a callee calls a blocking primitive directly or transitively, or an extern/user function not proven non-blocking.
extern fn sleep(secs: u32) -> u32;
#[no_block] fn f() { { sleep(1); } return; }
fn main() -> i32 { return 0; }
Fix. Use a non-blocking API, or mark the callee #[no_block] if it is known not to block.
E0908 · #[max_stack(N)] exceeded
A function's estimated stack frame (parameters plus locals with known types) is larger than the #[max_stack(N)] byte budget.
#[max_stack(64)] fn f() { let buf: [u8; 100] = [0u8; 100]; return; }
fn main() -> i32 { return 0; }
Fix. Shrink locals/parameters, or raise the N budget.
E0909 · Non-asm statement in a #[naked] function
A #[naked] function body contains a statement (or a value tail) other than inline #asm(...); no prologue/epilogue is emitted, so there is no stack frame to use.
#[naked]
fn bad() -> i64 { let x: i64 = 1; return x; }
fn main() -> i32 { return 0; }
Fix. Keep a #[naked] body inline assembly only; move other code into a normal function the asm calls.
Attributes
E0354 · Unknown attribute
An attribute name is not recognized.
#[tset] fn x() { return; }
Fix. Fix the typo (the compiler suggests a did-you-mean fix).
E0355 · Bad attribute argument shape
An attribute is given the wrong arguments — too many, too few, or the wrong literal kind for what the attribute expects.
#[repr] struct P { x: i32 }
Fix. Supply the exact argument shape the attribute expects (e.g. #[repr(C)]).
E0356 · Wrong attribute target
An attribute is placed on a kind of item it does not apply to; some attributes are function-only, others struct-only.
#[test] struct X { v: i32 }
Fix. Move the attribute to the item kind it is valid on.
E0357 · Duplicate attribute
An attribute that must be unique appears more than once on the same item.
#[test] #[test] fn x() { return; }
Fix. Remove the duplicate; the attribute may appear only once.
E0358 · Invalid #[test] function signature
A #[test] function does not have the signature fn() -> i32 or fn() — it takes parameters or returns some other type.
#[test] fn t(n: i32) { return; }
fn main() -> i32 { return 0; }
Fix. Give the test function the signature fn() -> i32 or fn() (no parameters).
E0359 · #[test] function cannot be export
A #[test] function is marked export; tests are project-internal helpers discovered by the runner, never part of the exported C-ABI surface.
#[test] export fn t() { return; }
fn main() -> i32 { return 0; }
Fix. Remove export from the test function.
E0362 · #[watch] hook has the wrong signature
A #[watch] struct's on_value is the write barrier the compiler calls on every field write, so its shape is fixed. It must be fn on_value(ref this, field: str) or the snapshot form fn on_value(ref this, field: str, old: S, new: S) for the struct's own type.
#[watch] struct S { x: i32 }
impl S { fn on_value(ref this, field: i32) { return; } }
Fix. Give the hook one of the two accepted signatures. The snapshot form additionally requires the struct to be Copy (see E0361).
E0890 · Duplicate #asm operand name
Two operands of an inline #asm(...) share the same operand name.
fn f(a: i64) { { #asm("mov {a}, {a}", a = in(reg) a, a = in(reg) a); } return; }
fn main() -> i32 { return 0; }
Fix. Give each #asm operand a distinct name.
E0892 · Non-register-sized #asm operand
An inline #asm(...) operand has a type that does not fit a register; only integer, pointer, and bool operands are allowed.
struct Owned { x: i32 } impl Owned { fn drop(ref this) { return; } } fn f(a: Owned) { { #asm("nop {a}", a = in(reg) a); } return; }
fn main() -> i32 { return 0; }
Fix. Pass a register-sized scalar (integer, pointer, or bool) instead of an aggregate.
E0893 · #asm reg operand has no template placeholder
A compiler-chosen (reg) inline-asm operand has no matching {name} placeholder in the template, so the template cannot name the register the compiler picked.
fn f(a: i64) { { #asm("nop", a = in(reg) a); } return; }
fn main() -> i32 { return 0; }
Fix. Reference the operand by its {name} placeholder in the template, or use an explicit-register operand.
E0895 · #asm out/inout operand must be a variable
An out or inout inline-asm operand binds to a general place (a field or index) rather than a plain variable; those are not yet supported.
struct P { x: i64 }
fn f(ref p: P, a: i64) {
{ #asm("mov {o}, {a}", o = out(reg) p.x, a = in(reg) a); }
return;
}
fn main() -> i32 { return 0; }
Fix. Write the output into a var variable, then copy it into the field/index afterward.
const / static / char
E0911 · const/static initializer shape not accepted
A const or static initializer used a shape outside the accepted set. A scalar-typed const or static takes a literal or any constant expression (folded by lower, see E0921); a non-scalar const is literal-only; a non-scalar static allows #zero::[T](), array literals/fills, and non-generic struct literals of such.
const C: [i32; 4] = [1, 2, 3, 4];
Fix. Use a literal or constant expression (or, for a non-scalar static, one of the aggregate shapes).
E0912 · Unknown const array length
An array length named a const that is not in scope, is not an integer, is negative, or exceeds the u32 maximum.
fn main() -> i32 { let a: [i32; NOPE] = [0; 1]; return a[0]; }
Fix. Use an integer literal, or a const in scope with a non-negative integer literal initializer.
E0918 · Compile-time include/shader path escapes the package
A #include_bytes / #include_str / #compile_shader path resolves outside the including file's package directory — an absolute path or a .. chain that leaves the package. In project mode (a Cplus.toml is present) these compile-time file reads are contained to the package tree, the same boundary imports (E0914) and [[bin]]/[lib] paths (E0868) enforce, so untrusted source can't bake an arbitrary readable host file into the artifact.
fn main() -> i32 {
let _ = #include_bytes("/etc/passwd");
return 0;
}
// -> [E0918] `#include_bytes` path `/etc/passwd` resolves outside the package directory
Fix. Move the asset inside the package and reference it with a package-relative path. A .. that stays within the package (e.g. ../adapter/asset.bin from src/) is allowed; only paths that leave the package are rejected.
E0921 · Invalid constant expression
A const/static initializer or an array-length expression failed compile-time evaluation: arithmetic overflowed the declared type's width, a shift amount was out of range, a division by zero occurred, two consts reference each other in a cycle, operand types mixed without a cast, or the expression used a non-constant construct (a call, a field, a runtime name).
const A: u8 = 255u8 + 1u8;
fn main() -> i32 { return 0; }
// -> [E0921] constant arithmetic overflows `u8`; use `+%` to wrap
Fix. Constant evaluation is typed: match operand types with suffixes or as casts, and keep results inside the declared type. Overflow is an error by design — the wrapping spellings +% / -% / *% wrap, exactly as at runtime.
Targets and packages
E0852 · Import names an undeclared dependency (or no manifest is reachable)
An import's first path segment looks like a package name but is not a declared [dependencies] entry in Cplus.toml (or there is no reachable manifest at all, so the bare package/... import has nothing to resolve against).
// bare.cplus, compiled with `cpc --emit-obj bare.cplus` and no Cplus.toml in reach:
import "stdlib/atomic" as atomic;
fn f() -> i32 { return 0; }
// -> [E0852] first segment `stdlib` is not a declared dependency
Fix. Add package = "*" to [dependencies] in Cplus.toml, or change the import to ./path for a file-relative one.
E0853 · Bare import that is neither file-relative nor a declared dependency
An import path is not prefixed with ./ or ../ (so it is not file-relative) and its first segment does not match any declared [dependencies] entry, so the resolver cannot classify it.
import "bare" as b;
fn main() -> i32 { return 0; }
// -> [E0853] bare import `bare` — paths must start with `./`/`../` or match a `[dependencies]` entry
Fix. Use ./bare for a file-relative import, or add bare to [dependencies] in Cplus.toml for a vendor import.
E0854 · Vendor package missing its Cplus.toml
A [dependencies] entry resolves to a vendor/<name>/ directory that has no Cplus.toml, so the vendor package's manifest cannot be loaded.
# consumer Cplus.toml
[package]
name = "app"
[dependencies]
foo = "*"
# but vendor/foo/Cplus.toml does not exist
# -> [E0854] vendor package `foo` is missing `Cplus.toml`
Fix. Create vendor/<name>/Cplus.toml for the dependency, or remove the [dependencies] entry.
E0855 · Vendor package name does not match its directory
A vendor package's Cplus.toml declares a [package].name that differs from the vendor/<name>/ directory it lives in.
# vendor/foo/Cplus.toml
[package]
name = "bar" # but the directory is vendor/foo/
# -> [E0855] declares name `bar` but lives in `vendor/foo/`
Fix. Make [package].name match the directory name (a vendor package's name must equal its directory).
E0857 · Invalid dependency name
A [dependencies] key does not match [a-z][a-z0-9_]* (it contains dots, slashes, or uppercase), so the first segment of an import path would be ambiguous.
[package]
name = "x"
[dependencies]
Stdlib = "*"
# -> [E0857] dependency name `Stdlib` must match `[a-z][a-z0-9_]*`
Fix. Rename the dependency key to a lowercase identifier (no dots, slashes, or uppercase).
E0858 · Import path carries a .cplus extension
An import path ends in .cplus, but Phase 2 imports are extension-less, so the trailing extension is rejected.
import "utils/math.cplus" as math;
fn main() -> i32 { return 0; }
// -> [E0858] import has a `.cplus` extension — drop it
Fix. Drop the .cplus extension from the import path (the compiler offers a machine-applicable suggestion).
E0859 · Vendor import escapes its src/ directory
A vendor import path contains a .. segment, which would let a package reach files outside its own src/ directory — disallowed for security.
import "utils/../escape" as e;
fn main() -> i32 { return 0; }
// -> [E0859] vendor import contains `..` — packages cannot reach outside their own `src/`
Fix. Remove the .. segment; a package may only import files within its own src/ tree.
E0860 · Declared [link].bundled file missing on host
A vendored package's manifest declares a file in [link].bundled, but lib/<host-triple>/<basename> does not exist. The manifest says the package ships that binary for this triple; the file is missing.
[link]
triples = ["arm64-apple-darwin"]
bundled = ["libfoo.a"]
# lib/arm64-apple-darwin/libfoo.a absent
# -> [E0860] package declares bundled `libfoo.a` but the file is not present
Fix. Add the missing file under lib/<host-triple>/, or remove its entry from [link].bundled.
E0861 · Orphan binary under lib/ not declared in [link].bundled
A binary artifact (.a, .o, .lib) sits under a package's lib/<triple>/, but the package manifest does not declare it in [link].bundled. The manifest is the single source of truth for shipped binaries.
# vendor/foo/lib/arm64-apple-darwin/liborphan.a exists
# vendor/foo/Cplus.toml has no [link] section
# -> [E0861] package ships `liborphan.a` but the manifest doesn't declare it
Fix. Add the file to [link].bundled, or delete it from the package.
E0864 · [link] extra-objects entry not found
A [link].extra-objects path (resolved relative to the manifest) does not exist on disk, caught before clang is invoked so the user gets a clean diagnostic instead of a linker error.
[package]
name = "missing-obj"
[[bin]]
name = "missing-obj"
path = "src/main.cplus"
[link]
extra-objects = ["does-not-exist.o"]
# -> [E0864] [link] extra-objects entry `does-not-exist.o` not found
Fix. Provide the object file at the declared path, or remove the entry from [link].extra-objects.
E0865 · [link] ${VAR} not set and has no fallback
A ${VAR} reference in [link].search-paths or [link].extra-objects names an environment variable that is unset at manifest-parse time and the reference carries no :-default fallback.
[package]
name = "x"
[link]
search-paths = ["${CPLUS_DEFINITELY_UNSET_VAR}/lib"]
# with the var unset:
# -> [E0865] cannot expand `${CPLUS_DEFINITELY_UNSET_VAR}/lib` in `[link]`
Fix. Set the variable, or give a default with ${VAR:-/path} (caught at manifest parse time).
E0866 · A stdlib module the target lacks was imported
An import names a stdlib module excluded from the selected target's package profile — on an embedded target (e.g. esp32-xtensa) the POSIX half (thread, net, fs, the async executor/reactor, etc.) is unavailable.
import "stdlib/thread" as m;
fn f() -> i32 { return 0; }
// compiled with `cpc check --target esp32-xtensa`
// -> [E0866] import `stdlib/thread` is not available on target `esp32-xtensa`
Fix. On an embedded target the POSIX modules are unavailable; use espidf for the embedded equivalents.
E0867 · async fn on a 32-bit target
An async fn is checked against a target whose pointer width is under 64 bits; the async runtime (reactor plus coroutine frames) is 64-bit-only today.
fn helper() -> i32 { return 1; }
async fn fetch() -> i32 { return helper(); }
fn main() -> i32 { return 0; }
// compiled with `cpc check --target esp32-xtensa`
// -> [E0867] async functions are not supported on 32-bit target `esp32-xtensa`
Fix. The coroutine runtime is 64-bit only; restructure without async on that target.
E0868 · [lib] / [[bin]] path escapes the package directory
A [lib].path or [[bin]].path key resolves outside the package directory — an absolute path or a .. chain. Source targets must live inside the package tree; a hostile vendored manifest must not point compilation at arbitrary host files. [link] search paths and ${VAR}-expanded extra objects are exempt (they legitimately name external SDK locations).
[package]
name = "esc"
[[bin]]
name = "esc"
path = "../../outside/main.cplus"
# -> [E0868] `[[bin]] `esc`` path resolves outside the package directory
Fix. Move the source file into the package and use a package-relative path (e.g. path = "src/main.cplus").
E0869 · Conflicting declarations of one dependency
One package name is declared in two places that could both apply — [dependencies] and a platform section, or two platform sections — with different specs. There is no conflict resolver by design: the compiler will not silently pick a winner.
[package]
name = "app"
[dependencies]
objc = "*"
[macos.dependencies]
objc = "*"
Fix. Declare the package once, or give every declaration the same spec. Platform sections are for packages that only apply to that platform.
E0914 · Relative import escapes the project directory
A file-relative import (./x / ../x) has a .. chain that resolves to a file outside the importing package's tree — the same escape the vendor import path (E0859) blocks, on the relative path that previously left it open.
import "../../../../etc/whatever" as e;
fn main() -> i32 { return 0; }
// -> [E0914] relative import resolves outside the project directory
Fix. Keep relative imports inside the package. To use another package, add it to [dependencies] and import it by name (import "dep/module").
E1900 · Construct is outside the wasm subset
The wasm backend accepts a deliberately small subset of C+ — scalar types, arithmetic, control flow and direct calls — and the program uses something outside it. It is the single code the backend emits for any unsupported construct.
fn main() -> i32 { let s: str = "hello"; return 0; }
Emitted only by the in-process wasm backend, which the browser playground drives. The native driver refuses the wasm32 target before reaching it.
Fix. Keep wasm-targeted code inside the subset, or build for a native target instead.
Warnings
W0001 · sum() / product() over narrow integer SIMD lanes silently wraps
A horizontal sum() or product() over integer SIMD lanes narrower than 32 bits returns that same narrow lane type, which cannot hold the reduction of more than a couple of near-max lanes, so the result silently wraps.
fn main() -> i32 {
let a: i8x16 = i8x16::splat(50i8);
let prod: i8x16 = a.mul(i8x16::splat(50i8));
return prod.sum() as i32;
}
// -> W0001 `sum` over narrow integer lanes (`i8x16`) silently wraps
Fix. .widen() the lanes first, or use simd/integer::dot_i32.
W0002 · Conditionally-freed raw-pointer field in a Drop type
A raw-pointer field in a Drop type is freed inside drop only under some condition, so the compiler cannot prove the release always runs on every owning path.
struct Cell { p: *u8 }
impl Cell: Drop {
fn drop(this) {
if some_condition() { free(this.p); } // freed only conditionally
}
}
// -> W0002 raw-pointer field `p` is freed only conditionally in `drop`
Fix. Confirm it frees on every owning path (expected for refcounted types).
W0003 · [[bin]] [link] libs / frameworks ignored
A [[bin]] package declares its own top-level [link] libs / frameworks, but those are read only when a package is a dependency of another — a [[bin]] is never a dependency, so they are ignored when building the binary.
[package]
name = "app"
[[bin]]
name = "app"
path = "src/main.cplus"
[link]
libs = ["boguslib"]
# -> W0003 `[link] libs` on a `[[bin]]` package is ignored when building the binary
Fix. Move them under [[bin]] libs / frameworks (top-level [link] libs apply only when the package is a dependency).
W0004 · on_value has the #[watch] hook signature but the struct is not #[watch]
on_value in the watch-hook shape is a compiler-invoked name: the only thing that calls it is the #[watch] write barrier. Without the attribute the method is unreachable, so every field write skips it silently. This is the fail-open half of E0361 — that error stops a #[watch] struct from having no hook; this warning stops a hook from having no #[watch].
struct Counter { n: i32 }
impl Counter {
fn on_value(ref this, field: str) { return; } // nothing ever calls this
}
// -> W0004 `Counter::on_value` has the `#[watch]` hook signature but
// `struct Counter` is not `#[watch]`, so nothing calls it
Fix. Add #[watch] to the struct, or rename the method if it is not meant to be a write hook. Only the two accepted hook shapes are flagged, so an on_value with any other signature stays an ordinary method.
W0005 · Source file is not reachable from the entry
A file under src/ compiles only when something reachable from the entry imports it. An unimported file is invisible: it compiles never, warns never, and reads as if it described the live API — a call to an undefined function in one still builds exit 0. For anyone reasoning from the source (a reviewer, an agent), unreachable code is false evidence. Platform-suffixed siblings (runtime_linux.cplus beside a loaded runtime.cplus) are the resolver's convention for reachable on another target and are exempt; the scan stays inside the package's own src/, since a vendored dependency legitimately ships more modules than one consumer imports.
// src/dead.cplus — no reachable module imports it
fn never_compiled() -> i32 { return undefined_function(); } // builds exit-0 today
// -> W0005 `src/dead.cplus` is not reachable from the entry — it never
// compiles, and nothing it says is checked
Fix. Import the file from a reachable module, or delete it. If it is a platform variant, name it <module>_<platform>.cplus beside its base module so the resolver owns the choice.
W0006 · Use of a #[deprecated] item
A call resolved to a function or method carrying #[deprecated]. The item still exists and still works; the attribute says it is on its way out, and the optional string is the author's migration note, printed verbatim. Reported at the USE, never at the declaration — a deprecated item is expected to still be defined and still be exercised by its own tests.
#[deprecated("use parse_v2 instead")]
fn parse() -> i32 { return 1; }
fn main() -> i32 { return parse(); }
// -> W0006 `parse` is deprecated: use parse_v2 instead
Fix. Move to the replacement the note names. Nothing breaks until the item is actually removed, so a warning list can be worked through at leisure; that is the point of the attribute over a hard rename.
W0824 · Handler parameter cannot receive a bound method
A caller may pass this.method where a fn-pointer is expected, but only if the callee declares a *u8 context parameter IMMEDIATELY after the handler — that is the slot the compiler fills with the receiver's address (E0824). Nothing in the declaration says so, so without this warning the author of a handler-taking function learns the rule from a CALLER hitting E0824 in another file, where it cannot be fixed. Only the wired-handler shape is flagged: a fn-pointer taking at least one real parameter plus a trailing *u8. A bare fn(*u8) is the release-hook shape and is left alone, as is a handler that already has an adjacent *u8 (defaulted or not — an undefaulted one is a deliberate the caller always supplies it API).
struct Row { n: i32 }
impl Row { fn clicked(ref this, sender: str) { this.n = this.n + 1; return; } }
// -> W0824: `on_click` cannot receive a bound method
fn bad(on_click: fn(str, *u8) = 0 as fn(str, *u8)) -> i32 { return 1; }
// the shape that accepts `bad(on_click: row.clicked)`
fn good(on_click: fn(str, *u8) = 0 as fn(str, *u8),
on_click_ctx: *u8 = 0 as *u8) -> i32 { return 2; }
Fix. Add <handler>_ctx: *u8 = 0 as *u8 immediately after the handler parameter. Leave it out only if callers are meant to pass free functions and thread the context themselves.
W0825 · Handler takes its context FIRST, so it cannot receive a bound method
The mirror of W0824. A defaulted *u8 sits right after the handler, so a wired handler is plainly what was meant — but the fn-pointer takes its *u8 FIRST. A bound reference fills the slot after the handler with the receiver's address and the bridge reads it from the fn's LAST parameter, so a ctx-first handler can never receive a method however correct the slot beside it looks. W0824 does not see this shape, because it looks for a TRAILING *u8. Quiet on fn(*u8, *u8) (the ordinary sender-plus-ctx handler), on a bare fn(*u8) (the release-hook shape), and on a ctx-first fn with no context slot beside it (not a wired handler at all).
struct Row { n: i32 }
impl Row { fn build(ref this, at: usize) -> i32 { return 1; } }
// -> W0825: `f` takes its context first
fn set_row(f: fn(*u8, usize) -> i32, ctx: *u8 = 0 as *u8) -> i32 { return 1; }
// the shape that accepts `set_row(row.build)`
fn good(f: fn(usize, *u8) -> i32, ctx: *u8 = 0 as *u8) -> i32 { return 2; }
Fix. Move the *u8 to the end of the handler's parameter list: fn(usize, *u8) rather than fn(*u8, usize). The context parameter beside it is already right; it is the fn type that is reversed.
Generics
E0910 · Generic instantiation exceeds the recursion limit
A generic function calls itself (directly or through a cycle) with a type argument that grows on every step — rec::[*T], rec::[[T; 2]], rec::[Box[T]]. Each step is a distinct concrete type, so monomorphization never converges and the compiler would hang. Two limits catch this: a ceiling on the number of instantiations, and a ceiling on the size of any one synthesized type name. A wrapper that names its parameter more than once (rec::[Pair[T, T]]) doubles that name at every step, so it hits the size ceiling while the instantiation count is still small.
fn rec[T]() -> i32 { let _z: i32 = rec::[*T](); return 0; }
fn main() -> i32 { return rec::[i32](); }
Fix. Reduce the type argument toward a non-generic base case, or drop the wrapper so the recursive call reuses the same type (rec::[T]). Runtime recursion on a value parameter is fine; only the type argument must not grow.