From f0890e13f80c8dfc05eeb50dd600a58854a4ec30 Mon Sep 17 00:00:00 2001 From: dimgigov Date: Tue, 28 Jul 2026 20:43:19 +0300 Subject: [PATCH] feat(selfhost-sema): check annotation vs initializer type in let/var --- _test_error_recovery/src/Main.bux | 3 +- src/sema.bux | 58 +++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/_test_error_recovery/src/Main.bux b/_test_error_recovery/src/Main.bux index 71bc41a..0cd1173 100644 --- a/_test_error_recovery/src/Main.bux +++ b/_test_error_recovery/src/Main.bux @@ -1,5 +1,6 @@ func Main() -> int { - let bad: int = "a" + 1; + let x: int = "hello"; + let y: bool = 42; let z: int = undefined_variable; return 0; } diff --git a/src/sema.bux b/src/sema.bux index fd6f04e..6a62f21 100644 --- a/src/sema.bux +++ b/src/sema.bux @@ -198,6 +198,33 @@ module Sema { return kind == tyBool || kind == tyBool8 || kind == tyBool16 || kind == tyBool32; } + // Strict numeric predicate for assignment checking: unlike Sema_IsNumeric, + // this does NOT treat tyNamed/tyTypeParam as numeric. + func Sema_IsStrictNumeric(kind: int) -> bool { + if kind == tyInt8 || kind == tyInt16 || kind == tyInt32 || kind == tyInt64 || kind == tyInt { return true; } + if kind == tyUInt8 || kind == tyUInt16 || kind == tyUInt32 || kind == tyUInt64 || kind == tyUInt { return true; } + if kind == tyFloat32 || kind == tyFloat64 { return true; } + return false; + } + + // Display-name helper for assignment diagnostics. + func Sema_TypeNameForDiag(te: *TypeExpr, kind: int) -> String { + if te != null as *TypeExpr { + if te.kind == tekPointer && te.pointerPointee != null as *TypeExpr { + return String_Concat(te.pointerPointee.typeName, "*"); + } + if !String_Eq(te.typeName, "") { return te.typeName; } + } + if kind == tyBool { return "bool"; } + if kind == tyStr { return "String"; } + if kind == tyInt { return "int"; } + if kind == tyInt64 { return "int64"; } + if kind == tyUInt { return "uint"; } + if kind == tyFloat64 { return "float64"; } + if kind == tyPointer { return "*void"; } + return "?"; + } + // --------------------------------------------------------------------------- // Block checking helper // --------------------------------------------------------------------------- @@ -1466,6 +1493,37 @@ module Sema { sym.typeName = stmt.child1.refType.typeName; } } + // Assignment check: annotation vs initializer (skip when either side is unknown) + if stmt.refStmtType != null as *TypeExpr && initType != tyUnknown { + let annotKind: int = Sema_ResolveType(sema, stmt.refStmtType); + if annotKind != tyUnknown { + var mismatch: bool = false; + if initType == tyNamed && annotKind == tyNamed { + // Both named: kinds are equal, compare type names (when available). + if stmt.child1 != null as *Expr && stmt.child1.refType != null as *TypeExpr { + if !String_Eq(stmt.child1.refType.typeName, "") && + !String_Eq(stmt.child1.refType.typeName, stmt.refStmtType.typeName) { + mismatch = true; + } + } + } else if initType != annotKind { + let numericOk: bool = Sema_IsStrictNumeric(initType) && Sema_IsStrictNumeric(annotKind); + let initIsPtr: bool = initType == tyPointer || initType == tyStr; + let annotIsPtr: bool = annotKind == tyPointer || annotKind == tyStr; + let ptrOk: bool = initIsPtr && annotIsPtr; + if !numericOk && !ptrOk { + mismatch = true; + } + } + if mismatch { + let gotName: String = Sema_TypeNameForDiag(stmt.child1.refType, initType); + let wantName: String = Sema_TypeNameForDiag(stmt.refStmtType, annotKind); + let msg: String = String_Concat("cannot assign ", + String_Concat(gotName, String_Concat(" to ", wantName))); + Sema_EmitError(sema, stmt.line, stmt.column, msg); + } + } + } sym.isMutable = stmt.boolValue; sym.isPublic = false; sym.decl = null as *Decl;