fix: struct-literal → optional coercion + #get through optional chain (issue 0160)
Two fixes for optional interactions surfaced by the #set/#get review. The
original issue 0160 mis-diagnosed (A) as an optional-chain bug; the chain works
fine for real fields. The actual bugs:
(A) A bare struct literal `.{ ... }` against an optional target `?T` was built
into the optional's {payload, has_value} layout instead of the inner T, then
re-wrapped — corrupting the value (a multi-field payload's first field clobbered
by the has_value flag, or a `?T` arg silently null) or failing LLVM
verification. lowerStructLiteral now builds the inner T, materializes it, and
wraps via coerceToType; lowerVarDecl's previously-UNCONDITIONAL optional wrap is
guarded so an already-`?T` value isn't double-wrapped. Fixed across var-decl,
arg, return, nested field, reassignment, and array-element contexts.
(B) `#get` accessors are now reachable through an optional chain (`obj?.getter`):
lowerOptionalChain dispatches the getter via a synthetic receiver, and
expr_typer types `obj?.getter` through a shared getterReturnTypeOnDeref helper
(handles `?T` and `?*T`, value and pointer optionals, and generic-instance
getters like List.len). The `#set` write side through `?.` is intentionally left
matching real-field behavior (optional-chain assignment unsupported).
Regression tests: examples/optionals/0906 (struct-literal → optional) and 0907
(accessor through chain). issues/0160 marked RESOLVED with the corrected root
cause.
This commit is contained in:
@@ -308,9 +308,13 @@ pub fn lowerVarDecl(self: *Lowering, vd: *const ast.VarDecl) void {
|
||||
self.target_type = saved_target;
|
||||
self.force_block_value = saved_fbv;
|
||||
// If target is optional and value isn't null, wrap with optional_wrap
|
||||
// — UNLESS the value is already that optional (e.g. a `?T`-returning
|
||||
// call, or a struct literal that lowered straight to `?T`); wrapping
|
||||
// again would build a `??`-shaped value typed `?T` and corrupt it /
|
||||
// fail LLVM verification (issue 0160).
|
||||
if (!ty.isBuiltin()) {
|
||||
const ty_info = self.module.types.get(ty);
|
||||
if (ty_info == .optional and val.data != .null_literal) {
|
||||
if (ty_info == .optional and val.data != .null_literal and self.builder.getRefType(ref) != ty) {
|
||||
ref = self.builder.optionalWrap(ref, ty);
|
||||
} else if (ty_info == .slice) {
|
||||
// Array → slice promotion: if value is an array, convert to slice
|
||||
|
||||
Reference in New Issue
Block a user