# 0097 — value-failable returning an ENUM corrupts the error slot on the success path **RESOLVED.** Root cause was **not** the field-offset/width miscalculation originally hypothesized — `tuple_init` / `tuple_get` and the backend struct layout were correct. The real cause was upstream in `lowerReturn` (`src/ir/lower.zig`): when lowering the returned expression of a value-carrying failable `-> (T..., !)`, `target_type` was set to the **full failable tuple** `(Color, !E)` instead of the success **value** type `Color`. A bare enum literal `.red` resolves its variant tag against `target_type` (`lowerEnumLiteral` → `resolveVariantValue`); against a tuple type there is no matching variant, so it returned the silent `0` default AND stamped the result with the tuple type. `lowerFailableSuccessReturn` then saw `val_ty == ret_ty` and took the **forwarding** branch, returning the half-built aggregate `{ value, undef }` as-is — the appended `constInt(0, err_ty)` was never inserted, leaving the error slot `undef` (read back as garbage nonzero) on the success path. **Fix:** in `lowerReturn`, when the function's return type is a value-carrying failable tuple, narrow `target_type` to `failableSuccessType(ret_ty)` (the value type / value-tuple) before lowering the returned expression. The enum literal then resolves to its real ordinal and is typed as the value type, so the success-return path correctly appends the `0` error slot. The s32 case was already correct because integer literals don't resolve variants against `target_type`. **Regression:** `examples/1055-errors-enum-value-failable-error-slot.sx` — reads the error slot at runtime on the success path (`cast(s64) e`, bare `if e`, `e == error.X`), exercises a non-zero ordinal (`.blue` = 2, which the bug also corrupted to 0), and asserts the error path still carries the right tag + `error_tag_name`. Fails on pre-fix code, passes after. Verified `zig build`, `zig build test`, and `bash tests/run_examples.sh` (452 ok) all green. Below preserved as a record of the original problem. ## Symptom A value-failable function `-> (EnumType, !ErrSet)` writes a **garbage nonzero tag into the error slot on the SUCCESS path**. Per specs.md the error channel must be `0` on success ("0 in the error slot means no error"). Every **runtime read** of the slot on success (`cast(s64) err`, bare `if err`, `err == error.X`, and therefore `error_tag_name(err)`) reports a false error. Only the path-sensitive compile-time proof `if !err` reads correctly (it is tied to the SSA value, not a runtime load of the slot), which is why it masks the bug. - **Observed (enum value):** success path → error slot reads nonzero (garbage `undef`), not `0`. - **Expected:** success path → error slot reads `0`; `if err` is false; `err == error.X` is false. ## Reproduction (only imports `modules/std.sx`) ```sx #import "modules/std.sx"; Color :: enum { red; green; blue; } E :: error { Nope } pick :: (s: string) -> (Color, !E) { if s == "red" { return .red; } // SUCCESS path raise error.Nope; } main :: () -> s32 { c, e := pick("red"); // SUCCESS -> error slot MUST be 0 print("error e (int) = {}\n", cast(s64) e); // EXPECT 0 ; BUG prints 1 if e { print("bare-if e: ERROR (WRONG)\n"); } else { print("bare-if e: ok\n"); } if e == error.Nope { print("e == Nope (WRONG)\n"); } else { print("e != Nope (ok)\n"); } if !e { print("guard !e: value c (int) = {}\n", cast(s64) c); } // c = 0 = .red (CORRECT) return 0; } ``` **Actual (buggy):** ```text error e (int) = 1 bare-if e: ERROR (WRONG) e == Nope (WRONG) guard !e: value c (int) = 0 ``` **Expected (now produced):** ```text error e (int) = 0 bare-if e: ok e != Nope (ok) guard !e: value c (int) = 0 ``` ## Contrast — the IDENTICAL shape with an s32 value is CORRECT ```sx pick :: (n: s32) -> (s32, !E) { if n > 0 { return n; } raise error.Nope; } // v, e := pick(5); → error slot = 0 (correct); bare-if e: ok ``` The split is **enum-value-specific** because only an enum literal (`return .variant`) resolves its tag against `target_type`. An integer literal does not, so the s32 path never got mis-stamped with the failable-tuple type and never took the false forwarding branch. ## Root cause (confirmed at ground truth) `return .red` in `pick` lowered the enum literal with `target_type = (Color, !E)` (the whole failable tuple). The LLVM IR on the success path was: ```llvm ret { i64, i32 } { i64 0, i32 undef } ; error slot UNDEF, not 0 (.blue gave i64 0 too — value lost) ``` vs. the s32 case which already produced `ret { i32, i32 } { i32 7, i32 0 }`. After narrowing the return target to the value type, the enum success path produces `ret { i64, i32 } zeroinitializer` (value 0 = `.red`, error slot 0), and `.blue` correctly carries ordinal 2.