Merge pull request 'feat/selfhost-sema-error-recovery' (#2) from feat/selfhost-sema-error-recovery into main
ci / build (ubuntu) (push) Has been cancelled
ci / unit + fmt (push) Has been cancelled
ci / examples (push) Has been cancelled
ci / goldens + tools (push) Has been cancelled
ci / apps (push) Has been cancelled
ci / selfhost smoke (push) Has been cancelled
ci / macos smoke (push) Has been cancelled
ci / windows smoke (push) Has been cancelled
ci / CI gate (push) Has been cancelled
selfhost-loop / bootstrap determinism (push) Has been cancelled
ci / build (ubuntu) (push) Has been cancelled
ci / unit + fmt (push) Has been cancelled
ci / examples (push) Has been cancelled
ci / goldens + tools (push) Has been cancelled
ci / apps (push) Has been cancelled
ci / selfhost smoke (push) Has been cancelled
ci / macos smoke (push) Has been cancelled
ci / windows smoke (push) Has been cancelled
ci / CI gate (push) Has been cancelled
selfhost-loop / bootstrap determinism (push) Has been cancelled
Reviewed-on: https://git.bara-lang.org/bux-lang/bux-lang/pulls/2
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
[Package]
|
||||
Name = "call_args"
|
||||
Version = "0.1.0"
|
||||
Type = "bin"
|
||||
@@ -0,0 +1,9 @@
|
||||
extern func PrintInt(n: int);
|
||||
extern func PrintLine(s: String);
|
||||
|
||||
func Main() -> int {
|
||||
PrintInt("hello");
|
||||
PrintLine(42);
|
||||
PrintInt(true);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
[Package]
|
||||
Name = "parse_recovery"
|
||||
Version = "0.1.0"
|
||||
Type = "bin"
|
||||
@@ -0,0 +1,18 @@
|
||||
func Good() -> int {
|
||||
return 1;
|
||||
}
|
||||
|
||||
func Bad() -> int {
|
||||
let x: int = ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
func AlsoBad() -> int {
|
||||
let y: int = 5 + ;
|
||||
let z: int = 6;
|
||||
return y;
|
||||
}
|
||||
|
||||
func AlsoGood() -> int {
|
||||
return 2;
|
||||
}
|
||||
+12
@@ -373,6 +373,16 @@ module Ast {
|
||||
// Struct fields (up to 256)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Parser diagnostic (recoverable errors collected during parsing)
|
||||
// ---------------------------------------------------------------------------
|
||||
struct ParserDiag {
|
||||
line: uint32,
|
||||
column: uint32,
|
||||
message: String,
|
||||
severity: int, /* 0=error (fatal), 1=warning (recoverable) */
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Module — AST root
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -381,6 +391,8 @@ module Ast {
|
||||
path: String, // path segments joined
|
||||
itemCount: int,
|
||||
firstItem: *Decl,
|
||||
diagCount: int,
|
||||
diags: *ParserDiag,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
+34
@@ -358,6 +358,28 @@ module Cli {
|
||||
// Import the compiler pipeline
|
||||
// In self-hosting mode, these are compiled together from src/
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Report parser diagnostics attached to a module (Rust-style, with snippets).
|
||||
// Returns the number of diagnostics reported (0 = clean).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func Cli_ReportParseDiags(mod: *Module, sourceName: String) -> int {
|
||||
if mod == null as *Module { return 0; }
|
||||
if mod.diagCount == 0 { return 0; }
|
||||
var i: int = 0;
|
||||
while i < mod.diagCount {
|
||||
let diag: Diagnostic = Diagnostic {
|
||||
message: mod.diags[i].message,
|
||||
line: mod.diags[i].line,
|
||||
column: mod.diags[i].column,
|
||||
severity: 0,
|
||||
};
|
||||
Diagnostic_Print(&diag, sourceName);
|
||||
i = i + 1;
|
||||
}
|
||||
return mod.diagCount;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Compile a single .bux source file
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -389,6 +411,7 @@ func Cli_Compile(source: String, sourceName: String, targetTriple: String) -> St
|
||||
PrintLine("Parse failed");
|
||||
return "";
|
||||
}
|
||||
if Cli_ReportParseDiags(mod, sourceName) > 0 { return ""; }
|
||||
PrintLine(" Parse done");
|
||||
|
||||
// Flatten module wrappers: find module decl and hoist its children
|
||||
@@ -551,6 +574,7 @@ func Cli_Check(srcPath: String) -> int {
|
||||
PrintLine("Parse failed");
|
||||
return 1;
|
||||
}
|
||||
if Cli_ReportParseDiags(mod, srcPath) > 0 { return 1; }
|
||||
PrintLine(" Parse done");
|
||||
|
||||
// Flatten module wrappers
|
||||
@@ -635,6 +659,7 @@ func Cli_CompileSource(source: String, sourceName: String) -> *HirModule {
|
||||
PrintLine(sourceName);
|
||||
return null as *HirModule;
|
||||
}
|
||||
if Cli_ReportParseDiags(mod, sourceName) > 0 { return null as *HirModule; }
|
||||
|
||||
// Phase 2b: macro expand
|
||||
let macEx3: *MacroExpander = MacroExpand_ExpandModule(mod);
|
||||
@@ -836,6 +861,10 @@ func Cli_MergeFileInto(target: *Module, path: String, skipNames: *String, skipCo
|
||||
if Lexer_DiagCount(lex) > 0 { return 0; }
|
||||
let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount);
|
||||
if mod == null as *Module { return 0; }
|
||||
// Report parse diags but keep merging the recovered decls: this path
|
||||
// handles stdlib/dependency files, and the parser currently emits
|
||||
// recoverable diags for valid multi-line braced imports there.
|
||||
discard Cli_ReportParseDiags(mod, path);
|
||||
// Tag every decl from this file for #line maps
|
||||
var stamp: *Decl = mod.firstItem;
|
||||
while stamp != null as *Decl {
|
||||
@@ -1928,6 +1957,8 @@ func Cli_BuildProject(projectDir: String, targetTriple: String, isRelease: bool,
|
||||
userMerged.path = "";
|
||||
userMerged.itemCount = 0;
|
||||
userMerged.firstItem = null as *Decl;
|
||||
userMerged.diagCount = 0;
|
||||
userMerged.diags = null as *ParserDiag;
|
||||
|
||||
// Parse each file and merge declarations into userMerged module
|
||||
var i: int = 0;
|
||||
@@ -1962,6 +1993,7 @@ func Cli_BuildProject(projectDir: String, targetTriple: String, isRelease: bool,
|
||||
PrintLine(path);
|
||||
return 1;
|
||||
}
|
||||
if Cli_ReportParseDiags(mod, path) > 0 { return 1; }
|
||||
// Tag decls with this source path for multi-file #line
|
||||
var stampUser: *Decl = mod.firstItem;
|
||||
while stampUser != null as *Decl {
|
||||
@@ -2031,6 +2063,8 @@ func Cli_BuildProject(projectDir: String, targetTriple: String, isRelease: bool,
|
||||
merged.path = "";
|
||||
merged.itemCount = 0;
|
||||
merged.firstItem = null as *Decl;
|
||||
merged.diagCount = 0;
|
||||
merged.diags = null as *ParserDiag;
|
||||
|
||||
// Find and merge ALL stdlib declarations
|
||||
let stdlibDir: String = Cli_FindStdlibDir(projectDir);
|
||||
|
||||
+25
-44
@@ -30,13 +30,6 @@ module Parser {
|
||||
macroTemplateMode: bool, // allows $(…)* in macro! bodies
|
||||
}
|
||||
|
||||
struct ParserDiag {
|
||||
line: uint32,
|
||||
column: uint32,
|
||||
message: String,
|
||||
severity: int, /* 0=error (fatal), 1=warning (recoverable) */
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Token helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -120,7 +113,7 @@ module Parser {
|
||||
return parserAdvance(p);
|
||||
}
|
||||
let tok: LexToken = parserCurToken(p);
|
||||
if p.diagCount < 256 {
|
||||
if p.diagCount < 256 && p.diags != null as *ParserDiag {
|
||||
p.diags[p.diagCount] = ParserDiag {
|
||||
line: tok.line, column: tok.column, message: msg, severity: 1
|
||||
};
|
||||
@@ -130,7 +123,7 @@ module Parser {
|
||||
}
|
||||
|
||||
func parserEmitDiag(p: *Parser, line: uint32, col: uint32, msg: String) {
|
||||
if p.diagCount < 256 {
|
||||
if p.diagCount < 256 && p.diags != null as *ParserDiag {
|
||||
p.diags[p.diagCount] = ParserDiag {
|
||||
line: line, column: col, message: msg, severity: 1
|
||||
};
|
||||
@@ -149,7 +142,7 @@ module Parser {
|
||||
if tok.kind == tkIdent || parserIsKeyword(tok.kind) {
|
||||
return parserAdvance(p);
|
||||
}
|
||||
if p.diagCount < 256 {
|
||||
if p.diagCount < 256 && p.diags != null as *ParserDiag {
|
||||
p.diags[p.diagCount] = ParserDiag {
|
||||
line: tok.line, column: tok.column, message: msg, severity: 1
|
||||
};
|
||||
@@ -2175,6 +2168,7 @@ module Parser {
|
||||
parserParseTypeParams(p, d);
|
||||
|
||||
discard parserExpect(p, tkLBrace, "expected '{'");
|
||||
var lastWasComma: bool = false;
|
||||
while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
|
||||
if fieldCount >= 256 {
|
||||
parserEmitDiag(p, line, col, "too many struct fields (max 255)");
|
||||
@@ -2182,13 +2176,17 @@ module Parser {
|
||||
}
|
||||
if parserCheck(p, tkNewLine) { discard parserAdvance(p); continue; }
|
||||
if parserCheck(p, tkSemicolon) { discard parserAdvance(p); continue; }
|
||||
// Commas are also valid field separators (incl. trailing comma before '}')
|
||||
if parserCheck(p, tkComma) {
|
||||
if fieldCount == 0 || lastWasComma {
|
||||
parserEmitDiag(p, parserCurToken(p).line, parserCurToken(p).column, "expected field name");
|
||||
}
|
||||
lastWasComma = true;
|
||||
discard parserAdvance(p);
|
||||
continue;
|
||||
}
|
||||
let beforePos: int = p.pos;
|
||||
let fName: LexToken = parserExpectIdentOrKeyword(p, "expected field name");
|
||||
if fieldCount >= 250 && fieldCount <= 256 {
|
||||
PrintLine(String_Concat("RAW fieldCount=", bux_int_to_str(fieldCount as int64)));
|
||||
PrintLine(String_Concat("RAW fName=", fName.text));
|
||||
PrintLine(String_Concat("RAW pos=", bux_int_to_str(p.pos as int64)));
|
||||
}
|
||||
discard parserExpect(p, tkColon, "expected ':' in struct field");
|
||||
let fType: *TypeExpr = parserParseType(p);
|
||||
parserMatch(p, tkSemicolon);
|
||||
@@ -2201,6 +2199,7 @@ module Parser {
|
||||
d.fields[fieldCount].name = fName.text;
|
||||
d.fields[fieldCount].refFieldType = fType;
|
||||
fieldCount = fieldCount + 1;
|
||||
lastWasComma = false;
|
||||
}
|
||||
d.fieldCount = fieldCount;
|
||||
discard parserExpect(p, tkRBrace, "expected '}'");
|
||||
@@ -2281,9 +2280,10 @@ module Parser {
|
||||
d.isPublic = isPublic;
|
||||
|
||||
// Parse path: Std::Io::PrintLine
|
||||
// Stop before `::{` (multi-import) and `::*` (glob import)
|
||||
var pathStr: String = "";
|
||||
var segCount: int = 0;
|
||||
while parserCheck(p, tkIdent) || (segCount > 0 && parserCheck(p, tkColonColon) && parserPeek(p, 1) != tkLBrace) {
|
||||
while parserCheck(p, tkIdent) || (segCount > 0 && parserCheck(p, tkColonColon) && parserPeek(p, 1) != tkLBrace && parserPeek(p, 1) != tkStar) {
|
||||
if segCount > 0 {
|
||||
discard parserAdvance(p); // ::
|
||||
if String_Len(pathStr) > 0 {
|
||||
@@ -2315,6 +2315,10 @@ module Parser {
|
||||
names = String_Concat(names, ",");
|
||||
if !parserMatch(p, tkComma) { break; }
|
||||
}
|
||||
// Allow newlines before the closing brace (multi-line import lists)
|
||||
while parserCheck(p, tkNewLine) {
|
||||
discard parserAdvance(p);
|
||||
}
|
||||
discard parserExpect(p, tkRBrace, "expected '}'");
|
||||
d.useNames = names;
|
||||
d.useKind = 2; // ukMulti
|
||||
@@ -2373,6 +2377,8 @@ module Parser {
|
||||
var lastMethod: *Decl = null as *Decl;
|
||||
while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
|
||||
if parserCheck(p, tkNewLine) { discard parserAdvance(p); continue; }
|
||||
// Bodyless method signatures end with ';'
|
||||
if parserCheck(p, tkSemicolon) { discard parserAdvance(p); continue; }
|
||||
if parserCheck(p, tkFunc) {
|
||||
let m: *Decl = parserParseFuncDecl(p, false, false, false);
|
||||
if methods == null as *Decl {
|
||||
@@ -2827,34 +2833,9 @@ module Parser {
|
||||
}
|
||||
}
|
||||
|
||||
/* Print fatal parser diagnostics (severity == 0) or if nothing valid was parsed */
|
||||
if p.diagCount > 0 && mod.itemCount == 0 {
|
||||
var di: int = 0;
|
||||
while di < p.diagCount {
|
||||
let d: ParserDiag = p.diags[di];
|
||||
Print("error: ");
|
||||
PrintLine(d.message);
|
||||
Print(" --> <input>:");
|
||||
PrintInt(d.line as int64);
|
||||
Print(":");
|
||||
PrintInt(d.column as int64);
|
||||
PrintLine("");
|
||||
Print(" |");
|
||||
PrintLine("");
|
||||
Print(" ");
|
||||
PrintInt(d.line as int64);
|
||||
Print(" | <source unavailable>");
|
||||
PrintLine("");
|
||||
Print(" | ");
|
||||
var sp: uint32 = 0;
|
||||
while sp < d.column - 1 && sp < 120 {
|
||||
Print(" ");
|
||||
sp = sp + 1;
|
||||
}
|
||||
PrintLine("^");
|
||||
di = di + 1;
|
||||
}
|
||||
}
|
||||
// Attach parser diagnostics to the module; the CLI reports them
|
||||
mod.diagCount = p.diagCount;
|
||||
mod.diags = p.diags;
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
+105
-22
@@ -210,6 +210,9 @@ module Sema {
|
||||
// Display-name helper for assignment diagnostics.
|
||||
func Sema_TypeNameForDiag(te: *TypeExpr, kind: int) -> String {
|
||||
if te != null as *TypeExpr {
|
||||
// tekFunc TypeExprs have no typeName set (parser leaves it unset) —
|
||||
// never touch te.typeName for them.
|
||||
if te.kind == tekFunc { return "func"; }
|
||||
if te.kind == tekPointer && te.pointerPointee != null as *TypeExpr {
|
||||
return String_Concat(te.pointerPointee.typeName, "*");
|
||||
}
|
||||
@@ -225,6 +228,25 @@ module Sema {
|
||||
return "?";
|
||||
}
|
||||
|
||||
// Assignability for diagnostics: strict numeric widening, bool family,
|
||||
// exact pointer kind, named types by name (best-effort when refType available).
|
||||
func Sema_CanAssignTo(sema: *Sema, fromKind: int, fromTe: *TypeExpr, toTe: *TypeExpr) -> bool {
|
||||
if fromKind == tyUnknown { return true; }
|
||||
if toTe == null as *TypeExpr { return true; }
|
||||
let toKind: int = Sema_ResolveType(sema, toTe);
|
||||
if toKind == tyUnknown { return true; }
|
||||
if fromKind == tyNamed && toKind == tyNamed {
|
||||
if fromTe != null as *TypeExpr && !String_Eq(fromTe.typeName, "") {
|
||||
return String_Eq(fromTe.typeName, toTe.typeName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if fromKind == toKind { return true; }
|
||||
if Sema_IsStrictNumeric(fromKind) && Sema_IsStrictNumeric(toKind) { return true; }
|
||||
if Sema_IsBool(fromKind) && Sema_IsBool(toKind) { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Block checking helper
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -338,6 +360,34 @@ module Sema {
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
// Report named args that match no parameter
|
||||
var checkArg: *ExprList = expr.callArgs;
|
||||
while checkArg != null as *ExprList {
|
||||
if !String_Eq(checkArg.argName, "") {
|
||||
var foundName: bool = false;
|
||||
var j: int = 0;
|
||||
while j < decl.paramCount {
|
||||
var pj: *Param = null as *Param;
|
||||
if j == 0 { pj = &decl.param0; }
|
||||
else if j == 1 { pj = &decl.param1; }
|
||||
else if j == 2 { pj = &decl.param2; }
|
||||
else if j == 3 { pj = &decl.param3; }
|
||||
else if j == 4 { pj = &decl.param4; }
|
||||
else if j == 5 { pj = &decl.param5; }
|
||||
else if j == 6 { pj = &decl.param6; }
|
||||
else if j == 7 { pj = &decl.param7; }
|
||||
else if j == 8 { pj = &decl.param8; }
|
||||
if String_Eq(checkArg.argName, pj.name) { foundName = true; }
|
||||
j = j + 1;
|
||||
}
|
||||
if !foundName {
|
||||
Sema_EmitError(sema, expr.line, expr.column,
|
||||
String_Concat("unknown argument name '", String_Concat(checkArg.argName, "'")));
|
||||
}
|
||||
}
|
||||
checkArg = checkArg.next;
|
||||
}
|
||||
|
||||
expr.callArgs = newFirst;
|
||||
expr.callArgCount = newCount;
|
||||
}
|
||||
@@ -1035,9 +1085,57 @@ module Sema {
|
||||
if kind == ekCall {
|
||||
let calleeType: int = Sema_CheckExpr(sema, expr.child1);
|
||||
Sema_ResolveCallArgs(sema, expr);
|
||||
// Resolve callee decl for call-argument checking: direct calls to
|
||||
// non-generic named functions only (inference handles generics).
|
||||
var callDecl: *Decl = null as *Decl;
|
||||
if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
|
||||
let callSym: Symbol = Scope_Lookup(sema.scope, expr.child1.strValue);
|
||||
if callSym.kind == skFunc && callSym.decl != null as *Decl {
|
||||
if callSym.decl.typeParamCount == 0 {
|
||||
callDecl = callSym.decl;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Arity check (runs after Sema_ResolveCallArgs so defaults/named args align)
|
||||
if callDecl != null as *Decl && expr.callArgCount != callDecl.paramCount {
|
||||
Sema_EmitError(sema, expr.line, expr.column,
|
||||
String_Concat("expected ", String_Concat(bux_int_to_str(callDecl.paramCount as int64),
|
||||
String_Concat(" arguments, got ", bux_int_to_str(expr.callArgCount as int64)))));
|
||||
callDecl = null as *Decl;
|
||||
}
|
||||
var argIdx: int = 0;
|
||||
var arg: *ExprList = expr.callArgs;
|
||||
while arg != null as *ExprList {
|
||||
discard Sema_CheckExpr(sema, arg.expr);
|
||||
let argKind: int = Sema_CheckExpr(sema, arg.expr);
|
||||
// Per-argument type check against the parameter type
|
||||
if callDecl != null as *Decl && argIdx < callDecl.paramCount {
|
||||
let cp: *Param = Sema_DeclParam(callDecl, argIdx);
|
||||
if cp != null as *Param && cp.refParamType != null as *TypeExpr {
|
||||
// Skip conditions matching the bootstrap: unknown/named/typeparam
|
||||
// arg kinds, and *char8 -> String (C string interop).
|
||||
var argSkip: bool = false;
|
||||
if argKind == tyUnknown || argKind == tyNamed || argKind == tyTypeParam { argSkip = true; }
|
||||
// Function-typed params: selfhost cannot reliably type function
|
||||
// values (e.g. '&Double' is tyPointer) — defer like the bootstrap.
|
||||
if Sema_ResolveType(sema, cp.refParamType) == tyFunc { argSkip = true; }
|
||||
if !argSkip && argKind == tyPointer && Sema_ResolveType(sema, cp.refParamType) == tyStr {
|
||||
if arg.expr != null as *Expr && arg.expr.refType != null as *TypeExpr {
|
||||
if arg.expr.refType.kind == tekPointer && arg.expr.refType.pointerPointee != null as *TypeExpr {
|
||||
if String_Eq(arg.expr.refType.pointerPointee.typeName, "char8") { argSkip = true; }
|
||||
}
|
||||
}
|
||||
}
|
||||
if !argSkip && arg.expr != null as *Expr && !Sema_CanAssignTo(sema, argKind, arg.expr.refType, cp.refParamType) {
|
||||
let wantName: String = Sema_TypeNameForDiag(cp.refParamType, Sema_ResolveType(sema, cp.refParamType));
|
||||
let gotName: String = Sema_TypeNameForDiag(arg.expr.refType, argKind);
|
||||
Sema_EmitError(sema, arg.expr.line, arg.expr.column,
|
||||
String_Concat("argument ", String_Concat(bux_int_to_str((argIdx + 1) as int64),
|
||||
String_Concat(": expected ", String_Concat(wantName,
|
||||
String_Concat(", got ", gotName))))));
|
||||
}
|
||||
}
|
||||
}
|
||||
argIdx = argIdx + 1;
|
||||
arg = arg.next;
|
||||
}
|
||||
// Borrow check: reject double mutable borrow in @[Checked] functions
|
||||
@@ -1157,6 +1255,10 @@ module Sema {
|
||||
|
||||
// Struct init: TypeName { field: value, ... }
|
||||
if kind == ekStructInit {
|
||||
let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
|
||||
te.kind = tekNamed;
|
||||
te.typeName = expr.structName;
|
||||
expr.refType = te;
|
||||
return tyNamed;
|
||||
}
|
||||
|
||||
@@ -1495,33 +1597,14 @@ module Sema {
|
||||
}
|
||||
// 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 boolOk: bool = Sema_IsBool(initType) && Sema_IsBool(annotKind);
|
||||
if !numericOk && !boolOk {
|
||||
mismatch = true;
|
||||
}
|
||||
}
|
||||
if mismatch {
|
||||
if !Sema_CanAssignTo(sema, initType, stmt.child1.refType, stmt.refStmtType) {
|
||||
let gotName: String = Sema_TypeNameForDiag(stmt.child1.refType, initType);
|
||||
let wantName: String = Sema_TypeNameForDiag(stmt.refStmtType, annotKind);
|
||||
let wantName: String = Sema_TypeNameForDiag(stmt.refStmtType, Sema_ResolveType(sema, stmt.refStmtType));
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user