From 602f75e6ecafc119cf088b87406bdbae75090871 Mon Sep 17 00:00:00 2001 From: Vasilev Dmitrii Date: Sat, 8 Aug 2026 15:36:10 +0700 Subject: [PATCH] typecheck: bare non-negative integer literals are context-polymorphic infer_expr pinned every integer literal to I32, so in u32-dominant specs every 'let x = 0; ... x = u32_expr;' and 'metric = 100 - value' raised a false 'cannot assign U32 to I32' -- 27 of tri-net's specs failed typecheck on exactly this, invisible until #1922 made typecheck gate (the hook only checks staged files). A bare NON-NEGATIVE literal now infers Unknown (context-polymorphic); only an explicitly negative literal commits to I32; and promote_types resolves Unknown+Known to the KNOWN operand. tri-net sweep: 27 failing -> 2 (both 'assign to immutable array element' singletons, tracked with #1919). Unit suite 1537/1537; FROZEN_HASH resealed. Closes #1923 Co-Authored-By: Claude Fable 5 --- NOW.md | 7 +++++++ bootstrap/src/compiler.rs | 19 ++++++++++++++++--- bootstrap/stage0/FROZEN_HASH | 2 +- docs/NOW.md | 9 ++++++++- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/NOW.md b/NOW.md index bee96426c3..b0750d5499 100644 --- a/NOW.md +++ b/NOW.md @@ -2,6 +2,13 @@ Last updated: 2026-08-08 +## typecheck: bare non-negative literals are context-polymorphic (Closes #1923) + +- infer_expr pinned every integer literal to I32; in u32-dominant specs every 'let x = 0; x = u32_expr;' raised a false 'cannot assign U32 to I32' -- 27 tri-net specs failed typecheck on exactly this +- Now: bare non-negative literals infer Unknown (context-polymorphic); explicitly negative literals stay I32; promote_types resolves Unknown+Known to the KNOWN operand ('100 - value' with value: u32 is u32) +- tri-net sweep: 27 failing specs -> 2 (both 'assign to immutable array element' singletons, tracked with #1919) +- Unit suite 1537/1537; FROZEN_HASH resealed + ## typecheck: promote call-arity mismatch from warning to hard error (Closes #1921) - The arity check existed but only warned, and nothing reads warnings (tri-net's hook greps 'Typecheck OK'; gen paths skip typecheck) -- two tri-net specs shipped wrong-arity calls for months (tri-net#323) diff --git a/bootstrap/src/compiler.rs b/bootstrap/src/compiler.rs index b7da330eeb..6a7cb9f017 100644 --- a/bootstrap/src/compiler.rs +++ b/bootstrap/src/compiler.rs @@ -12794,7 +12794,15 @@ fn infer_expr(node: &Node, symbols: &[SymbolEntry], fns: &[FnEntry]) -> TypeInfo return TypeInfo::Str; } if node.value.parse::().is_ok() { - return TypeInfo::I32; + // A bare NON-NEGATIVE integer literal is context-polymorphic: + // t27 specs are u32-dominant, and pinning literals to I32 made + // every `let x = 0; ... x = u32_expr;` a false type error + // (27 tri-net specs failed typecheck on exactly this). Only an + // explicitly negative literal commits to a signed type. + if node.value.trim_start().starts_with('-') { + return TypeInfo::I32; + } + return TypeInfo::Unknown; } if node.value.parse::().is_ok() { return TypeInfo::F64; @@ -12851,8 +12859,13 @@ fn promote_types(a: &TypeInfo, b: &TypeInfo) -> TypeInfo { if a == b { return a.clone(); } - if *a == TypeInfo::Unknown || *b == TypeInfo::Unknown { - return TypeInfo::Unknown; + // The KNOWN operand determines the type: `100 - value` where value: u32 + // is a u32 expression, not an unknown one. + if *a == TypeInfo::Unknown { + return b.clone(); + } + if *b == TypeInfo::Unknown { + return a.clone(); } let a_rank = type_rank(a); let b_rank = type_rank(b); diff --git a/bootstrap/stage0/FROZEN_HASH b/bootstrap/stage0/FROZEN_HASH index 4cef74bcaf..2345349dca 100644 --- a/bootstrap/stage0/FROZEN_HASH +++ b/bootstrap/stage0/FROZEN_HASH @@ -1 +1 @@ -8770de8b22dfda24d5c4099b2610c4a5a6efd49e43a19e335f08eeb09f788743 +37b349425f4d6551c495b3357d229b5a6fb2acf6c55712af4d4ecf5271ec5f53 diff --git a/docs/NOW.md b/docs/NOW.md index d55e1f0b5c..6cc3b37a5c 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -1,7 +1,14 @@ -# NOW — typecheck: arity mismatch is a hard error (2026-08-08) +# NOW — typecheck: context-polymorphic integer literals (2026-08-08) Last updated: 2026-08-08 +## typecheck: bare non-negative literals are context-polymorphic (Closes #1923) + +- infer_expr pinned every integer literal to I32; in u32-dominant specs every 'let x = 0; x = u32_expr;' raised a false 'cannot assign U32 to I32' -- 27 tri-net specs failed typecheck on exactly this +- Now: bare non-negative literals infer Unknown (context-polymorphic); explicitly negative literals stay I32; promote_types resolves Unknown+Known to the KNOWN operand ('100 - value' with value: u32 is u32) +- tri-net sweep: 27 failing specs -> 2 (both 'assign to immutable array element' singletons, tracked with #1919) +- Unit suite 1537/1537; FROZEN_HASH resealed + ## typecheck: promote call-arity mismatch from warning to hard error (Closes #1921) - The arity check existed but only warned, and nothing reads warnings (tri-net's hook greps 'Typecheck OK'; gen paths skip typecheck) -- two tri-net specs shipped wrong-arity calls for months (tri-net#323)