feat(selfhost-sema): check annotation vs initializer type in let/var

This commit is contained in:
2026-07-28 20:43:19 +03:00
parent 76ea9cc083
commit f0890e13f8
2 changed files with 60 additions and 1 deletions
+58
View File
@@ -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;