C+
Reference · View as Markdown

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 — 146 codes. Each entry gives the meaning, a minimal example that triggers it, and the typical fix. 114 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.

Names, types, and items

E0300 · Undefined name

A referenced name (variable, function, or self outside a method) is not in scope.

fn main() -> i32 { return x; }

Fix. Fix the typo, add the missing import, or remember a forgotten pub.

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 let mut.

fn main() -> i32 { let x = 1; x = 2; return 0; }

Fix. Declare the binding as let mut.

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.

fn main() -> i32 { let x = 1; let y = &x; return 0; }

Fix. Assign it to a fn(...)-typed binding to take the address.

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.

E0315 · Invalid cast

An as cast is between a pair of types that the language forbids.

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.

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 struct does not have.

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.

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(self) {} }
fn main() -> i32 { return 0; }

Fix. The target must be a declared struct or enum in scope.

E0326 · Duplicate method in impl

Two methods in the same impl block share a name.

struct P {}
impl P { fn f(self) {} fn f(self) {} }
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 mut self is called on an immutable receiver.

struct P { x: i32 }
impl P { fn bump(mut self) { self.x = self.x + 1; } }
fn main() -> i32 { let p: P = P { x: 0 }; p.bump(); return 0; }

Fix. Bind the receiver as let mut (or via a mut place).

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(mut self) {} }
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, ...].

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 mut + move, or borrow with move/mut.

fn f(mut move x: i32) -> i32 { return x; }
fn main() -> i32 { return f(1); }

Fix. Keep only one marker: mut, move, or borrow — they are mutually exclusive.

E0335 · Use of a moved value

A non-Copy binding is read after it was moved (into a call, a move 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.

struct P { x: i32 }
impl P { fn drop(mut self) {} }
fn echo(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 move; clone the value first, or restructure so the move and the use are on disjoint paths.

E0337 · Cannot move out of a non-binding place

A move was attempted from something other than a whole binding (a struct field, an array slot, or a temporary); partial moves are deferred.

struct Inner { x: i32 }
impl Inner { fn drop(mut self) {} }
struct Outer { i: Inner }
fn take(move i: Inner) -> i32 { return i.x; }
fn main() -> i32 { let o: Outer = Outer { i: Inner { x: 1 } }; return take(o.i); }

Fix. Move a whole binding, or clone/copy the field into a local first.

E0338 · Destructor drop has the wrong signature

A drop method has a signature other than fn drop(mut self) (extra parameters, a return type, or a non-mut self receiver), or a drop was written on an enum.

struct B { x: i32 }
impl B { fn drop(self) {} }
fn main() -> i32 { return 0; }

Fix. Declare it exactly fn drop(mut self) — 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.

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.

Ownership and borrowing

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(mut self) { return; } }
fn drain(move b: B, n: i32) { return; }
fn peek(borrow b: B) -> i32 { return b.x; }
fn caller() {
  let 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(move 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(mut self) { return; } }
fn sink(move b: B) { return; }
fn use_it(borrow b: B) -> i32 { return b.x; }
fn caller(c: bool) {
  let 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(mut self) { return; } }
fn longest(a: B, b: B) -> B {
  if a.x > b.x { return a; }
  return b;
}
fn drain(move 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(mut self) { return; } }
struct Pair { left: Inner, right: Inner }
impl Pair { fn drop(mut self) { return; } }
fn write_pair(mut 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 (mut) 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(mut self) { return; } }
fn modify_both(mut a: B, mut b: B) { return; }
fn caller() {
  let y: B = B { x: 1 };
  modify_both(y, y);
  return;
}

Fix. Split into two calls, or borrow distinct sub-places (e.g. f(mut y.left, mut y.right)).

E0381 · Exclusive borrow with a concurrent shared read

A place is exclusively borrowed (mut) 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(mut self) { return; } }
fn write_thing(mut a: B, n: i32) { return; }
fn peek(borrow b: B) -> i32 { return b.x; }
fn caller() {
  let y: B = B { x: 1 };
  write_thing(y, peek(y));
  return;
}

Fix. Split into two statements: let tmp = peek(y); write_thing(mut y, tmp);

E0382 · Move and exclusive borrow of the same binding in one call

The same non-Copy binding is exclusively borrowed (mut) 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(mut self) { return; } }
fn write_and_take(mut a: B, move b: B) { return; }
fn caller() {
  let 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 · Read of an exclusively-borrowed place

A place (or a sub-place of it) is read while it is held in an exclusive borrow by a live borrower, including method calls on an exclusively-borrowed receiver.

struct B { x: i32 }
impl B { fn drop(mut self) { return; } }
fn cursor(mut b: B) -> B { return b; }
fn peek(borrow b: B) -> i32 { return b.x; }
fn caller() {
  let v: B = B { x: 1 };
  let cur: B = cursor(v);
  let n: i32 = peek(v);
  return;
}

Fix. Drop the exclusive borrower before reading the place, or restructure so the read happens before the borrow is established.

E0384 · Cannot infer which parameter the return borrows from

A function or method has at least one return rooted at a parameter, but the borrow checker cannot determine which parameter every return path derives from.

struct B { x: i32 }
impl B { fn drop(mut self) { return; } }
fn merge(a: B, b: B) -> B {
  if a.x > 0 { return a; }
  return B { x: 0 };
}

Fix. Annotate the signature explicitly with a borrow region, e.g. fn merge(a: borrow A B, ...) -> borrow A B.

E0503 · Interface impl missing a required method

An impl Type for Interface block omits a method that the interface declares.

interface Two { fn first(self) -> i32; fn second(self) -> i32; }
struct P { x: i32 }
impl P for Two { fn first(self) -> 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 for Interface block contains a method that the interface does not declare.

interface One { fn a(self) -> i32; }
struct P { x: i32 }
impl P for One { fn a(self) -> i32 { return 0; } fn extra(self) -> 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 Self with the target type.

interface One { fn a(self) -> i32; }
struct P { x: i32 }
impl P for One { fn a(self) -> 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 for 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(self) -> i32; }
struct P { x: i32 }
impl P for One { fn a(self) -> i32 { return 0; } }
impl P for One { fn a(self) -> 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 for 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 for Iface { fn a(self) -> i32 { return 0; } }

Fix. Declare the impl in the same file as either the interface or the type.

E0508 · Self used outside an interface or impl body

The type Self is named where there is no surrounding interface or impl body to give it meaning.

fn loose(x: Self) -> 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: unsafe { malloc(16 as usize) } }; }
    fn drop(mut self) { unsafe { free(self.ptr); } return; }
}
struct Pair { a: Owned, b: Owned }
impl Pair {
    fn drop(mut self) { unsafe { free(self.a.ptr); } unsafe { free(self.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(self.f)), or mark the field opaque if another owner frees it.

E0511 · Return type names a borrow region no parameter declares

A return type names a borrow region that no parameter declares, so the borrow it names has no provenance and the annotation is inert.

fn f(a: borrow A str) -> borrow Z str { return a; }
fn main() -> i32 { return 0; }

Fix. Add a same-region parameter, or drop the region.

E0512 · Returned borrow's region differs from the declared return region

A returned borrow is rooted at a parameter whose region differs from the region the signature declares for the return.

fn weird(a: borrow A str, b: borrow B str) -> borrow A str { return b; }
fn main() -> i32 { return 0; }

Fix. Return a borrow from a same-region parameter.

E0513 · Returning a str / T[] view of a local that drops

A returned str / T[] view is rooted at a function-local non-Copy owned value (directly or via as_str / as_slice, including inside a returned aggregate), so the view would dangle when that local is freed at return.

extern fn malloc(n: usize) -> *u8;
extern fn free(p: *u8);
struct Buf { ptr: *u8 }
impl Buf {
    fn drop(mut self) { unsafe { free(self.ptr); } return; }
    fn as_str(self) -> str { return unsafe { #str_from_raw_parts(self.ptr, 4 as usize) }; }
}
fn mk_buf() -> Buf { return Buf { ptr: unsafe { malloc(4 as usize) } }; }
fn bad() -> str {
    let s: Buf = mk_buf();
    return s.as_str();
}

Fix. Return an owned value (string / Vec[T]), or borrow from a parameter.

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.

Modules, paths, and visibility

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
pub 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 that is not marked pub in its declaring file.

import "./math" as math;
fn main() -> i32 { return math::square(7); }

Fix. Mark the item pub in its declaration to export it. (Requires an imported module; math.cplus declares fn square without pub.)

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.

pub 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 pub extern fn is not C-ABI compatible

A parameter or return type in a pub 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).

pub 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 for Ord; provide the impl, or for thread-crossing use unsafe impl T for 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.

E0801 · Operation (or unsafe fn call) requires unsafe

A raw-pointer dereference, extern/unsafe fn call, atomic op, inline #asm, static mut access, or similar invariant-breaking operation appeared outside an enclosing unsafe { ... } block.

unsafe fn danger() -> i32 { return 1; }
fn main() -> i32 { let d: i32 = danger(); return d; }

Fix. Wrap it in unsafe { ... }.

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);
    let mut 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 {
    let mut 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.

Real-time contracts

E0900 · Borrow-shaped parameter in an async fn

An async fn parameter is borrow-shaped (str / T[]) or a mut-bound non-Copy value (pointer-passed), which may dangle once a borrow lives across an await.

pub struct Future[T] { pub opaque handle: *u8 } async fn fetch(url: str) -> i32 { return 0 as i32; }

Fix. Use Text / Vec[T] instead of str / T[], or move ownership in / bind locally instead of mut.

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].

pub struct Future[T] { pub 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() { unsafe { 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 pub

A #[test] function is marked pub; tests are project-internal helpers discovered by the runner, never part of the exported API.

#[test] pub fn t() { return; }
fn main() -> i32 { return 0; }

Fix. Remove pub from the test function.

E0890 · Duplicate #asm operand name

Two operands of an inline #asm(...) share the same operand name.

fn f(a: i64) { unsafe { #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(mut self) { return; } } fn f(a: Owned) { unsafe { #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) { unsafe { #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(mut p: P, a: i64) {
    unsafe { #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 mut variable, then copy it into the field/index afterward.

const / static / char

E0X30 · const/static initializer is not a literal

A const or static initializer used a non-literal shape (arithmetic, an identifier, a call, or a generic struct literal); const is literal-only and static allows only literals, #zero::[T](), array literals/fills, or non-generic struct literals of such.

const FOO: i32 = 1 + 2;

Fix. Use a literal initializer (or an accepted static shape such as #zero::[T]() or an array/struct literal of literals).

E0X33 · Read of static mut requires unsafe

A static mut was read outside an enclosing unsafe { ... } block, where the borrow checker cannot prove absence of data races on module-scope mutable state.

static mut COUNTER: i32 = 0;
fn main() -> i32 { return COUNTER; }

Fix. Wrap the read in unsafe { ... }.

E0X34 · Write to static mut requires unsafe

A static mut was written outside an enclosing unsafe { ... } block, where the borrow checker cannot prove absence of data races on module-scope mutable state.

static mut COUNTER: i32 = 0;
fn main() -> i32 { COUNTER = 5; return 0; }

Fix. Wrap the write in unsafe { ... }.

E0X36 · 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.

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 · A Send / Sync impl that omits unsafe

A Send or Sync marker is implemented with a bare impl (no unsafe), or with a non-empty body — both are rejected because the marker is an unverifiable thread-safety promise that must be a method-less unsafe impl.

struct Handle { opaque p: *u8 }
impl Handle for Send {}
fn main() -> i32 { return 0; }
// -> [E0860] `Send` is an unsafe assertion — write `unsafe impl Handle for Send {}`

Fix. Write unsafe impl T for Send {} — the marker is a promise you make.

E0861 · unsafe on an impl that is not a Send / Sync marker

An impl block carries unsafe but the interface being implemented is something other than the Send / Sync markers, where unsafe is meaningless.

interface Greet { fn hi(self) -> i32; }
struct S { x: i32 }
unsafe impl S for Greet { fn hi(self) -> i32 { return self.x; } }
fn main() -> i32 { return 0; }
// -> [E0861] `unsafe impl` applies only to the `Send` / `Sync` markers

Fix. Remove unsafe; it applies only to those two markers.

E0862 · Host vs target triple mismatch

A dependency declares bundled binaries but its [link].triples does not include the triple actually being linked (the host triple for a native build, or the selected --target's artifact triple for a cross build), so no matching prebuilt artifact exists.

# vendor/foo/Cplus.toml
[package]
name = "foo"
[link]
bundled = ["libfoo.a"]
triples = ["aarch64-apple-darwin"]
# linking on/ for a triple not in that list:
# -> [E0862] package `foo` does not ship a build for host/target triple `<triple>`

Fix. Add the host/target triple to [link].triples and ship the matching binaries, or build the package from source for that triple.

E0863 · [link].bundled set without [link].triples

A manifest's [link].bundled lists prebuilt binaries but [link].triples is empty, so the compiler cannot tell which host triples those binaries are built for.

# vendor/foo/Cplus.toml
[package]
name = "foo"
[link]
bundled = ["libfoo.a"]
# no triples = [...]
# -> [E0863] `[link].bundled` is non-empty but `[link].triples` is empty

Fix. Declare the host triples your bundled binaries target, e.g. triples = ["aarch64-apple-darwin"].

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;
pub 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.

pub 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.

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 for Drop {
    fn drop(self) {
        if some_condition() { free(self.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).