fix(0098): enum literal resolves against the unwrapped optional child; non-enum targets are diagnosed

lowerEnumLiteral resolved the variant against the raw destination type,
so any non-enum destination fell into resolveVariantValue's silent
return-0 tail with the enum_init stamped as the wrong type:

  - ?E destinations produced variant 0 mis-typed as the optional
    (observed as variant 0 OR null, layout-dependent);
  - builtin destinations (i64) silently became 0;
  - unknown variants of real enums silently became variant 0;
  - a destination-less literal panicked LLVM emission (unresolved
    type reached codegen).

Now: optional destinations unwrap to the child enum (the coercion
layer's .optional_wrap handles E -> ?E), and the remaining shapes are
diagnosed — unknown variant (with the variant list, via the new
emitBadEnumVariant twin of emitBadVariant), non-enum destination, and
no destination (cascade-guarded: silent when the destination's type
already failed to resolve and was reported).

Regression tests: examples/0183 (return/assign/reassign into ?Enum,
non-zero variants, null path) + examples/1169/1170 (each diagnostic);
all three FAIL on pre-fix master. zig build test 426/426;
tests/run_examples.sh 598/598.
This commit is contained in:
agra
2026-06-12 12:35:20 +03:00
parent d8076b9333
commit 1bc60d3a35
15 changed files with 253 additions and 1 deletions

View File

@@ -0,0 +1,34 @@
// Enum literals flowing into OPTIONAL destinations resolve against the
// optional's CHILD type and wrap (issue 0098). Pre-fix, the literal fell
// into resolveVariantValue's non-enum fallback: variant 0, mis-typed as
// the optional itself — `return .android_apk;` was observed by callers as
// `.ios` or even null, layout-dependent.
#import "modules/std.sx";
Platform :: enum u8 { ios; android_apk; macos; linux; windows; }
classify :: (n: i64) -> ?Platform {
if n == 1 { return .android_apk; } // return: literal into ?Platform
if n == 2 { return .windows; } // non-zero, non-adjacent variant
return null;
}
main :: () -> i32 {
p := classify(1);
if p == null { print("BUG: null\n"); return 1; }
if p! != .android_apk { print("BUG: wrong variant\n"); return 2; }
w := classify(2);
if w == null or w! != .windows { print("BUG: windows\n"); return 3; }
if classify(9) != null { print("BUG: not null\n"); return 4; }
// assignment + reassignment: literal into a ?Platform slot
q : ?Platform = .macos;
if q == null or q! != .macos { print("BUG: assign\n"); return 5; }
q = .linux;
if q! != .linux { print("BUG: reassign\n"); return 6; }
print("ok\n");
return 0;
}

View File

@@ -0,0 +1,14 @@
// Enum literals against unusable destinations are DIAGNOSED, never a
// silent variant 0 (issue 0098's sibling holes): an unknown variant of a
// real enum, a non-enum destination type, and a destination-less literal
// each get their own error.
#import "modules/std.sx";
Platform :: enum u8 { ios; android_apk; }
main :: () -> i32 {
a : Platform = .nonexistent; // unknown variant: lists the real ones
b : i64 = .foo; // non-enum destination
print("{}{}\n", a, b);
return 0;
}

View File

@@ -0,0 +1,11 @@
// A destination-less enum literal is diagnosed (issue 0098's third hole —
// it previously panicked the LLVM backend with an unresolved type). Kept
// as the ONLY error in this file: the diagnostic is cascade-guarded, so it
// stays silent when the destination type itself already failed to resolve.
#import "modules/std.sx";
main :: () -> i32 {
c := .ios;
print("{}\n", c);
return 0;
}

View File

@@ -0,0 +1 @@
0

View File

@@ -0,0 +1 @@
ok

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,11 @@
error: 'nonexistent' is not a variant of 'Platform' (variants are: ios, android_apk)
--> examples/1169-diagnostics-enum-literal-bad-target.sx:10:20
|
10 | a : Platform = .nonexistent; // unknown variant: lists the real ones
| ^^^^^^^^^^^^
error: enum literal '.foo' cannot type itself from non-enum destination 'i64'
--> examples/1169-diagnostics-enum-literal-bad-target.sx:11:15
|
11 | b : i64 = .foo; // non-enum destination
| ^^^^

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,5 @@
error: enum literal '.ios' has no destination type to resolve against
--> examples/1170-diagnostics-enum-literal-no-target.sx:8:10
|
8 | c := .ios;
| ^^^^