Const declarations and compound types
The first milestone implemented const value declarations and the recursive type foundation for pointers and references. Raw pointers are now executable as described in unsafe contexts and pointers. Checked scalar and pointer-slot references are described in checked references.
Const values
Section titled “Const values”const i32 add(const i32 value) { const i32 increment = 2; return value + increment;}
i32 main() { const i32 answer = add(40); copy := answer; copy = copy + 1; return copy - 1;}- A const local requires an initializer, which may execute at runtime.
- Const parameters receive their initial value from the caller.
- Assignment to either is
FLECHA-S0005, pointing at the assignment target. - Missing const local initialization is
FLECHA-S0023, pointing at its name. - Reads produce values without the outer binding qualifier. Copies and inferred
declarations remain mutable unless their own declaration says
const. - Top-level const on return and cast types does not make the receiving binding const. It does not change the native ABI.
- Explicit
noneliterals use the destination type’s zero value in declarations, assignments, function arguments, and returns. This applies to mutable and const destinations alike. - Scope resolution and definite assignment still apply, including shadowing and jumps that skip a declaration’s initializer.
stringretains its owning copy behavior;stringvstill requires an initializer and must not outlive its owner. Const does not extend lifetimes.- Only typed const declarations are implemented;
const name := valueis not part of this milestone. Const does not imply compile-time evaluation.
Recursive types and aliases
Section titled “Recursive types and aliases”| Spelling | Normalized type | Fixed binding | Read-only target |
|---|---|---|---|
*u8 |
*u8 |
No | No |
*const u8 |
*const u8 |
No | Yes |
constptr u8 |
const *u8 |
Yes | No |
constptr const u8 |
const *const u8 |
Yes | Yes |
&u8 |
&u8 |
No | No |
&const u8 |
&const u8 |
No | Yes |
constref u8 |
const &u8 |
Yes | No |
constref const u8 |
const &const u8 |
Yes | Yes |
Each type layer has its own const qualifier. &constptr u8 normalizes to
&const *u8; a reference to a raw pointer is representable. Adjacent reference
markers use whitespace (& &u8) to distinguish them from the logical && token.
const constptr u8 and const const u8 duplicate a qualifier on one layer and
are rejected. constval is not reserved.
Syntax nodes retain each layer’s full source range and the range of its const keyword (or alias). Semantic types compare recursively by kind, qualifier, and target, rather than by pointer identity. Diagnostics print expanded type names. Symbols and function signatures retain declared qualifiers; bound value expressions omit only the outer qualifier. Nested qualifiers are never erased by this value-copy normalization.
Current boundary
Section titled “Current boundary”Scalar and raw-pointer storage can be borrowed with &value; see
checked references for qualifier, move, and lifetime
rules. Unsupported reference storage shapes emit FLECHA-S0024.
Raw pointers use the explicit &raw value operation inside unsafe contexts;
see unsafe pointers for executable operations and limits.
Allocation, free, defer, general owner moves, slices, and volatile access remain later work in the implementation plan.
Validation
Section titled “Validation”make test covers lexing, recursive parsing and ranges, aliases, type equality,
const diagnostics, lowering, LLVM verification, and the native
NativeSemanticTests.ConstDeclarations fixture. The fixture exercises runtime
initializers, parameters, scalar casts, shadowing, independent owning string
copies, and borrowed views; it prints initialized and exits with 42.
make test-sanitizers runs the same native fixture with the instrumented runtime
and leak detection. make test-neovim checks the added keywords and & marker.