Skip to content

An inferred return type keeps undefined and null - #429

Merged
ASDAlexander77 merged 1 commit into
mainfrom
fix-return-inference-undefined
Oct 1, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
fix-return-inference-undefined

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

An inferred return type is the union of what the function returns, as in TypeScript, so undefined and null are no longer dropped:

function before after
if (c) return "a"; return undefined; string (=== undefined false) string | undefined
if (c) return undefined; return "a"; string | undefined same
if (c) return "a"; return; string string | undefined
if (c) return 1; return undefined; s32 s32 | undefined
if (c) return { v: "x" }; return undefined; {v: string} {v: string} | undefined
if (c) return "a"; return null; compile error (strict null) string | null

Change

  • processReturnType filtered out a later return undefined / return null, and mergeType absorbs undefined into the other type (canCastFromTo(undefined -> T)), per field of a tuple too. The new MLIRTypeHelper::mergeReturnType makes one side being undefined or null a union, and merges tuples field by field. It's used only by processReturnType, so mergeType's other callers (array literals, conditionals, generic arguments) don't change.
  • A bare return; beside value returns adds undefined to the inferred type (PassResult::hasBareReturn; never over an explicit return type), and in a function that returns a value it returns undefined. A bare return in a void function or a constructor is unchanged.
  • Generators keep the old merge. The rewritten next() returns Iterator<T>'s {value: T, done}, and a finished generator's value is T's zero by design (00generator_done_value_zero.ts). Making it T | undefined broke every Iterator<T> match (56 tests). next() is marked InternalFlags::GeneratorNext and goes through PassResult::iteratorResult.

Visible in DefaultLib

reduce / reduceRight (inferred return type, return undefined on an empty array with no initial value) now return V | undefined. Their call sites still work: assigning to number, printing, arithmetic, and an empty array really gives undefined. But arithmetic on the result warns casting from optional (undefined) value. TypeScript declares reduce as returning V; declaring : V there in DefaultLib would keep the old signature.

Not changed

  • Falling off the end (function f(c) { if (c) return "a"; }) still infers string. It needs a reachability check, and getting it wrong would add | undefined to functions that always return.
  • A finished generator's value (see above).

Tests

  • New 00return_inference_undefined.ts (compile + jit): both orders, bare return, integer, object, null, a void function with a bare return, a declared T | undefined, a constructor.
  • Release suite: 3343/3343 passed.
  • DefaultLib (main b67a213) rebuilt release/gc with this compiler: 158/158 compile, 158/158 jit.
  • The new test passes under gc, rc and none.
  • Not run: Debug, x86, -mm=own.

🤖 Generated with Claude Code

…d T | null unions, and add tests for bare returns
@ASDAlexander77
ASDAlexander77 merged commit aee5d10 into main Oct 1, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the fix-return-inference-undefined branch October 1, 2026 20:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant