diff --git a/NOW.md b/NOW.md index b0750d5499..bf59a8bd57 100644 --- a/NOW.md +++ b/NOW.md @@ -2,6 +2,13 @@ Last updated: 2026-08-08 +## typecheck: W456 immutable-array-element error scoped to const ROM (Closes #1925) + +- W456 keyed on is_mutable alone, so fn-local let-arrays (inferred-mutability convention, like scalars) raised a hard error on element assignment +- SymbolEntry now carries is_const (module-level const only): const arrays keep the ROM error (unit test preserved), local let-arrays get the scalar-style warning +- tri-net full-spec typecheck sweep: 2 failing -> 0 -- the whole corpus is typecheck-clean for the first time +- Unit suite 1537/1537; FROZEN_HASH resealed + ## 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 diff --git a/bootstrap/src/compiler.rs b/bootstrap/src/compiler.rs index 6a7cb9f017..22f0fb6ec5 100644 --- a/bootstrap/src/compiler.rs +++ b/bootstrap/src/compiler.rs @@ -12320,6 +12320,9 @@ struct SymbolEntry { name: String, type_info: TypeInfo, is_mutable: bool, + /// True only for module-level `const` declarations (ROM); fn-local lets + /// follow t27's inferred-mutability convention. + is_const: bool, } struct FnEntry { @@ -12346,6 +12349,7 @@ pub fn typecheck_ast(ast: &Node) -> TypeCheckResult { name: child.name.clone(), type_info: t, is_mutable: false, + is_const: true, }); } NodeKind::StructDecl | NodeKind::EnumDecl => { @@ -12353,6 +12357,7 @@ pub fn typecheck_ast(ast: &Node) -> TypeCheckResult { name: child.name.clone(), type_info: TypeInfo::Custom(child.name.clone()), is_mutable: false, + is_const: false, }); } NodeKind::FnDecl => { @@ -12442,6 +12447,7 @@ pub fn typecheck_ast(ast: &Node) -> TypeCheckResult { name: pname.clone(), type_info: resolve_type_str(ptype), is_mutable: true, + is_const: false, }); } // #920 bug 2: thread an accumulating scope so a StmtLocal declared in @@ -12597,7 +12603,8 @@ fn check_stmt(node: &Node, symbols: &mut Vec, fns: &[FnEntry], resu name: node.name.clone(), type_info: t, is_mutable: node.extra_mutable, - }); + is_const: false, + }); } NodeKind::StmtAssign => { if !node.children.is_empty() { @@ -12634,11 +12641,24 @@ fn check_stmt(node: &Node, symbols: &mut Vec, fns: &[FnEntry], resu } else { String::new() }; - result.error_count += 1; - result.errors.push(format!( - "error: cannot assign to immutable array element '{}[...]'{}", - base_name, line - )); + if sym.is_const { + // W456: a module-level const array is ROM. + result.error_count += 1; + result.errors.push(format!( + "error: cannot assign to immutable array element '{}[...]'{}", + base_name, line + )); + } else { + // A fn-local let-array follows the same + // inferred-mutability convention as scalars: + // the backends promote it to var/mut, so this + // is a style warning, not an error. + result.warnings += 1; + result.errors.push(format!( + "warning: element assignment promotes '{}' to mutable{}", + base_name, line + )); + } } } } diff --git a/bootstrap/stage0/FROZEN_HASH b/bootstrap/stage0/FROZEN_HASH index 2345349dca..6b9c6d49dc 100644 --- a/bootstrap/stage0/FROZEN_HASH +++ b/bootstrap/stage0/FROZEN_HASH @@ -1 +1 @@ -37b349425f4d6551c495b3357d229b5a6fb2acf6c55712af4d4ecf5271ec5f53 +e489784ba9eb56980a9cc0c39c7ff9fcac7029f88aac44133dffed1e30efea2e diff --git a/docs/NOW.md b/docs/NOW.md index 6cc3b37a5c..d90af958c1 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -1,7 +1,14 @@ -# NOW — typecheck: context-polymorphic integer literals (2026-08-08) +# NOW — typecheck: W456 scoped to const ROM arrays (2026-08-08) Last updated: 2026-08-08 +## typecheck: W456 immutable-array-element error scoped to const ROM (Closes #1925) + +- W456 keyed on is_mutable alone, so fn-local let-arrays (inferred-mutability convention, like scalars) raised a hard error on element assignment +- SymbolEntry now carries is_const (module-level const only): const arrays keep the ROM error (unit test preserved), local let-arrays get the scalar-style warning +- tri-net full-spec typecheck sweep: 2 failing -> 0 -- the whole corpus is typecheck-clean for the first time +- Unit suite 1537/1537; FROZEN_HASH resealed + ## 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