Compiled callees register a per-parameter nullable mask (self included for methods, specializations included); cross-module callees fall back to their vpi param annotations. A nullable binding/field argument now fails instead of silently narrowing.
A field annotated `own T?` / `T?` (and a local declared nullable) now fails fast on `base.field`: it may be None. Use `base?.field` or check against None first. FieldEntry carries a Nullable flag (appended), populated from the annotation during struct registration and inherited by flattened parent fields; both read and write attribute paths check it.
- Generic renum specialization: `Option[int].Some(42)` / `Option[int].None_()`
are intercepted before method dispatch (which would translate the
subscript type argument as a value), specialize the renum template and
construct the variant. `_ctor_call_agg_ty` recognizes the generic
qualifier so construction materializes the renum value.
- Builtin Option: the generator registers `renum Option[T]` (None_ |
Some(T)) lazily on first `Option[...]` use, with a reserved synthetic
module id. The copy in includes/builtins.vp is skipped during Phase1
template/variant registration so a user-defined `renum Option` (EnumTest)
keeps precedence; constructor/annotation lookup prefers the current
module's Option.
- Match: the renum class is derived from the concrete subject struct
(authoritative for `Option[int]`) instead of only the annotation name.
Both `case Option[int].Some(v)` and `case Some(v)` bind the payload;
`None_()` works qualified or bare.
LSP: pattern captures (`case Some(v)`) are declared as variables.
Probe: builtin Option without importing builtins prints 42 for Some and
hits the None_ arm; EnumTest (user renum Option) passes; self-host
110/110; full regression set passes.
Drop unreachable struct/type helpers, the legacy wrapper-main and build pipeline paths, debug-only scaffolding, unused logging helpers/constants, and the no-op LSP signature map. Add the audit report for the remaining duplication and logging work.
Three gaps kept `mod.f[T](x)` from compiling:
- get_func_name now unwraps a Subscript callee to its base name, so an
expression statement like `f[int](x)` / `mod.f[int](x)` reaches the
normal call path instead of failing name resolution. Generic class
constructors and type subscripts still return None, keeping their
dedicated paths.
- the generic-call dispatcher resolves `Attribute` bases inside the
subscript (`temp.array2list[int]`) to the defining module's SHA1, and
treats it as a module-qualified template lookup.
- inference understands pointer/array annotations around the type
parameter: `_annotation_refers_type_param` walks PtrT/ArrayT wrappers
and tuple slices, and inference peels as many pointer/array layers off
the argument as the parameter annotation wraps around T, so
`array2list(l, ...)` with `array: T*` and `l: []int` infers int.
Self-host: 110/110 with the new compiler building itself; GenericTest,
GenericTest3, GenericTest4, VectorGenericTest and ListTest pass.
Module-level functions and class methods each carried their own copy of "resolve the
return type", "create the parameter allocas" and "emit the implicit epilogue". The copies
drifted: an unannotated method was still typed i32 by the class-method body path while its
declaration said void, so the epilogue emitted `llvm.return 0 : i32` inside a void function
and the MLIR verifier rejected it ("'llvm.return' op expected no operands", Scheduler._yield).
- FuncGenBase.resolve_func_ret_ty: the single return-type policy (annotation -> storage
marker -> __init__/__new__ hooks -> unannotated = procedure void -> fail-fast). Used by
the forward-declare pass, the body pass, class methods and generic specializations
- FuncGenBase.param_llvm_ty: the single parameter mapping (CVoid skipped, bare class ->
Ptr(struct), ArrayT -> element pointer), used by every signature and alloca site
- FuncGenBase.emit_func_epilogue: the single implicit-return implementation (procedure ->
ret void, unreachable merge, fail-fast for an annotated fall-through), shared by module
functions, methods and nested functions
- FuncGenBase.translate_func_body: the shared body pipeline (scope entry, self/parameter
storage, *args va_list, try/except out-params, pre-scan, capture preparation, slife,
statements, epilogue, state restore). ClassGenVTable._translate_method now only handles
method naming, vtable/overload registration and add_param
- the same policy reaches cross-module and generic declares: unannotated vpi functions and
methods declare void, generic specialization declares use the shared resolver, and the
list.append call sites emit a void call
- method parameters now carry the const / volatile / pointee-volatile / pure markers that
the module-level path always had
- FuncGenMixin: the two dry-run helpers that return ints are annotated, as the language
now requires (an unannotated def is a procedure)
Verified: 41 Test projects pass (MethodProcTest, CrossModuleSigTest, RawPtrParamTest,
AstTest, ListTest, OopInheritTest, ...), ViperC self-hosts with the new compiler, and the
6 tests that still fail reproduce identically with the previous compiler.
A uint16 value that is widened to int/uint32/uint64 must be zero-extended
(movzwl), and an int16 value must be sign-extended (movswl). coerce_to_type
picks the direction from the source AST node's declared signedness, which
requires the translator; several call sites omitted it and therefore fell
back to sext, so a big-endian uint16 such as 0xDCBC became 0xFFFF...DCBC
(numGlyphs 56508 read as -9028 and every glyph range collapsed to zero).
- pass trans at the assignment, annotated-assignment, augmented-assignment,
global-array initializer, index, element-store, constructor-argument,
conditional-expression and return coercion sites
- printf argument promotion now receives the argument expression and trans,
so uint16/uint8 promote with zext like any other widening
- print selects the format token and extension from the source signedness
(%u/%llu + zext for unsigned, %d/%lld + sext for signed) instead of a
hardcoded %d + sext for 8/16-bit values