Diagnostics & Compiler Errors
All diagnostic messages in cppbolt are represented by the unified CompileError struct. Error formatting follows GCC and Clang conventions:
file.cpp:line:column: error: message
Diagnostics never expose Rust internal debug formatting (Debug) or panics.
1. Diagnostic Architecture
Source Coordinate Fidelity
- Preprocessor Origins: When files are included via
#include, the preprocessor records line origin metadata (src/preprocessor/lines.rs). Errors originating from header files report the path and line number of the header itself rather than the combined translation unit. - Token Spans: The lexer attaches a precise
Span(FileId, line, column, length) to every emitted token. - Expression Precision: Every AST expression node stores an
ExprPosholding the exact line and column of its initial token. Diagnostics for invalid member accesses, dereferences, or conversions point directly at the offending expression. - Statement Lowering Coordinates: Code generation errors that cannot name a sub-expression report the source position of the parent statement.
The corpus test tests/diagnostic_positions.rs compiles every corpus file and enforces that no diagnostic reports a fabricated line or column.
2. Diagnostic Categories (ErrorKind)
The compiler categorizes diagnostics into five distinct kinds:
#![allow(unused)] fn main() { pub enum ErrorKind { Lexical, // Tokenization and preprocessor syntax errors Syntax, // Parser grammar failures Semantic, // Type checking, symbol resolution, and layout failures Unsupported, // Features outside the v1 C++ subset Internal, // Internal compiler invariants or resource limits } }
3. Catalog of Common Diagnostics & Trigger Examples
The following catalog lists diagnostics emitted by the compiler, paired with real triggers from tests/negative/:
Unsupported C++ Features (ErrorKind::Unsupported)
multiple inheritance is not supported
- Cause: A class declaration attempts to inherit from more than one base class.
- Trigger (
tests/negative/multiple_inheritance.cpp):struct A {}; struct B {}; struct C : public A, public B {}; // error: multiple inheritance is not supported
virtual inheritance is not supported
- Cause: A base class specifier includes the
virtualkeyword. - Trigger (
tests/negative/virtual_inheritance.cpp):struct Base {}; struct Derived : virtual public Base {}; // error: virtual inheritance is not supported
range expression must be an array or class with begin() and end()
- Cause: Attempting a range-based
forloop on a scalar type or a class lacking.begin()and.end()methods. - Trigger (
tests/negative/range_for_non_iterable.cpp):int main() { int total = 0; for (int x : 42) { // error: range expression must be an array or class with begin() and end(), found 'int' total += x; } return total; }
Arrow member access on non-pointer
- Cause: Using
->on a struct or primitive variable that does not define an overloadedoperator->(). - Trigger (
tests/negative/operator_arrow_invalid.cpp):struct Dummy { int val; }; int main() { Dummy d(1); return d->val; // error: Arrow member access on non-pointer }
circular operator-> delegation on '<class>'
- Cause: Recursive
operator->()chaining encounters a cyclical delegation loop (e.g.A -> B -> AorA -> A). - Trigger (
tests/negative/operator_arrow_cycle.cpp):struct B; struct A { B operator->(); }; struct B { A operator->(); }; int main() { A a; return a->val; // error: circular operator-> delegation on 'A' }
operator-> must return a pointer to a struct or class
- Cause: Overloaded
operator->()returns a primitive or non-pointer type instead of a class pointer or wrapper object. - Trigger (
tests/negative/operator_arrow_non_pointer.cpp):struct Bad { int operator->() { return 42; } }; int main() { Bad b; return b->val; // error: operator-> must return a pointer to a struct or class }
reference to '<name>' is ambiguous
- Cause: An unqualified identifier is provided by two active
using namespacedirectives or using declarations with equal precedence. - Trigger (
tests/negative/ambiguous_using_directives.cpp):namespace A { int shared = 1; } namespace B { int shared = 2; } using namespace A; using namespace B; int main() { return shared; } // error: reference to 'shared' is ambiguous
call of overloaded '<name>(...)' is ambiguous
- Cause: Overload resolution finds multiple candidate functions with identical ranking scores.
- Trigger (
tests/negative/ambiguous_overload_call.cpp):void pick(float); void pick(long long); int main() { pick(1.0); // error: call of overloaded 'pick(double)' is ambiguous }
lvalue required as left operand of assignment
- Cause: Attempting to assign a value to an rvalue expression or temporary.
- Trigger (
tests/negative/assign_to_rvalue.cpp):int main() { 1 = 2; // error: lvalue required as left operand of assignment }
Cannot assign to variable '<name>' with const-qualified type
- Cause: Mutating a variable declared with
const. - Trigger (
tests/negative/const_assign.cpp):int main() { const int c = 5; c = 6; // error: Cannot assign to variable 'c' with const-qualified type }
no matching conversion for argument N of '<func>': cannot convert '<type1>' to '<type2>'
- Cause: Passing an argument whose type cannot be converted to the parameter type through standard promotions or converting constructors.
- Trigger (
tests/negative/function_int_to_struct.cpp):struct Other { Other(const Other&); }; void takes_struct(Other); int main() { takes_struct(1); // error: no matching conversion for argument 1 of 'takes_struct': cannot convert 'int' to 'Other' }
no matching constructor for initialization of '<class>'
- Cause: Creating an object where provided arguments do not match any constructor overload.
- Trigger (
tests/negative/constructor_too_many_arguments.cpp):struct T { T(int); }; int main() { T x(1, 2); // error: no matching constructor for initialization of 'T' }
invalid operands to binary expression: two pointers cannot be added
- Cause: Applying the binary
+operator between two pointer operands. - Trigger (
tests/negative/pointer_plus_pointer.cpp):int main() { int a = 1, b = 2; return (&a + &b) != 0; // error: invalid operands to binary expression: two pointers cannot be added }
'<member>' is a private member of '<class>'
- Cause: Accessing a private struct or class member from outside its member methods.
- Trigger (
tests/negative/private_access.cpp):class Secret { int hidden; }; int main() { Secret s; return s.hidden; // error: 'hidden' is a private member of 'Secret' }
Variable type '<class>' is an abstract class
- Cause: Instantiating an object of a class containing un-overridden pure virtual functions (
= 0). - Trigger (
tests/negative/pure_virtual_instantiation.cpp):struct Abstract { virtual void run() = 0; }; int main() { Abstract a; // error: Variable type 'Abstract' is an abstract class }
narrowing conversion from '<type1>' to '<type2>' inside braced-init-list
- Cause: Converting between incompatible types inside a braced initializer list (e.g. floating-point to integer).
- Trigger (
tests/negative/narrowing_float_to_int.cpp):int main() { int x{3.14}; // error: narrowing conversion from 'double' to 'int' inside braced-init-list return x; }
narrowing conversion of constant <val> from '<type1>' to '<type2>' inside braced-init-list
- Cause: Converting a constant expression or literal to an integer type whose representable range cannot accommodate the value.
- Trigger (
tests/negative/narrowing_constant_overflow.cpp):int main() { char c{300}; // error: narrowing conversion of constant 300 from 'int' to 'char' inside braced-init-list return c; }
chosen constructor for '<class>' is explicit in copy-list-initialization
- Cause: An
explicitconstructor is matched during copy-list-initialization (Type x = {...},return {...},fn({...})). - Trigger (
tests/negative/explicit_ctor_copy_list.cpp):struct Foo { explicit Foo(int) {} }; int main() { Foo f = {42}; // error: chosen constructor for 'Foo' is explicit in copy-list-initialization return 0; }
scalar initializer has multiple expressions
- Cause: Initializing a scalar or pointer variable with a braced list containing more than one element.
- Trigger (
tests/negative/scalar_excess_elements.cpp):int main() { int x{1, 2}; // error: scalar initializer has multiple expressions return x; }
too many initializers for aggregate '<class>'
- Cause: Providing more initializer elements in a braced list than the target aggregate class has fields or subobjects.
- Trigger (
tests/negative/aggregate_excess_elements.cpp):struct Point { int x; int y; }; int main() { Point p{1, 2, 3}; // error: too many initializers for aggregate 'Point' return 0; }
excess elements in array initializer: expected at most <N>, have <M>
- Cause: A dynamic array allocation
new T[N]{...}specifies more initializer expressions than the declared array boundN. - Trigger (
tests/negative/new_array_excess_elements.cpp):int main() { int* p = new int[2]{1, 2, 3}; // error: excess elements in array initializer: expected at most 2, have 3 return 0; }
cannot initialize variable '<name>' of type <target> with <source>
- Cause: Initializing a typed variable with an incompatible expression, such as assigning a non-zero integer or
boolto a pointer, discardingconstqualifiers, violating multi-level pointer qualification rules, or assigning a string literal to a non-constchar*. - Trigger (
tests/negative/pointer_conversion_int_nonzero.cpp):int main() { int* p = 42; // error: cannot initialize variable 'p' of type int * with int return 0; }
Preprocessor & Lexical Errors (ErrorKind::Lexical)
Invalid digit '<digit>' in binary literal
- Cause: Non-binary digit in a binary integer literal (
0b...). - Trigger (
tests/negative/invalid_binary_digit.cpp):int main() { int x = 0b102; // error: Invalid digit '2' in binary literal return x; }
Invalid digit separator
- Cause: Consecutive digit separators or quotes at literal boundaries.
- Trigger (
tests/negative/invalid_digit_separator.cpp):int main() { int x = 1''000; // error: Invalid digit separator return x; }
Invalid digit '<digit>' in octal literal
- Cause: Non-octal digit (
8or9) in a zero-prefixed octal literal. - Trigger (
tests/negative/invalid_octal_digit.cpp):int main() { int x = 088; // error: Invalid digit '8' in octal literal return x; }
include cycle detected: '<path>'
- Cause: Cyclic
#includeinclusion chain detected by the preprocessor. - Trigger (
tests/negative/include_cycle.cpp):#include "include_cycle_a.h" // a includes b, which includes a // error: include cycle detected: 'tests/negative/include_cycle_a.h'
#endif without #if
- Cause: A stray
#endifencountered without an active conditional block. - Trigger (
tests/negative/stray_endif.cpp):#endif // error: #endif without #if
Unterminated #if/#ifdef/#ifndef block at EOF
- Cause: Reaching end of file while a conditional compilation block remains unclosed.
- Trigger (
tests/negative/unterminated_if.cpp):#ifdef MISSING // error: Unterminated #if/#ifdef/#ifndef block at EOF
Resource & Safety Diagnostics (ErrorKind::Internal)
Nesting depth exceeds the supported limit
- Cause: An input program exceeds calibrated recursion depth limits (e.g. >1,600 nested parentheses or >1,599 nested
ifblocks) to protect the WebAssembly shadow stack. - Trigger (
tests/negative/nesting_depth_limit.cpp):// 1601 nested parentheses: (((...(((1)))...))) // error: Nesting depth exceeds the supported limit