selfhost: is/as validity + let/return assignability checks
Three more structural checks from C check.c ported to selfhost, at the AST level (no resolved tinfo). is/as validity: e is T / e as T require e's declared type to be a tagged union and T to name a variant. Mirrors the case-variant check that just landed. let init-type and return-type assignability: a new exprtype helper infers an AST type-node for literal/ident/call/cast/?/as/is expressions; isassignable approximates C type_assignable on the shapes we can resolve — exact match, untyped numeric → typed numeric, untyped nil → ptr/slice/chan/fn, variant inclusion, and two-primitive-mismatch. isassignable returns (ok, confident). When confident=false the check emits no error — better to miss a real bug than fire a false positive on a binary-op expression we can't infer. This keeps existing selfhost code clean while still catching the common typo cases (let x: bool = 42; return "hi" from i32 fn). Naming: all new helpers follow Plan 9 run-together convention per CLAUDE.md (`typeeqast`, `isassignable`, `exprtype`, ...). Earlier work that used snake_case helpers (`case_variant_in`, `check_match_exhaustive`, ...) got the same treatment — bulk renamed in this commit. Five new rows in 950_selfcheck exercise the new checks (is-not-a-variant, two let mismatches, return mismatch, plus the case-variant row already there).
This commit is contained in:
@@ -103,6 +103,23 @@ static const struct row rows[] = {
|
||||
" };\n"
|
||||
"};\n",
|
||||
"case: not a variant" },
|
||||
/* is T where T isn't a variant of the operand */
|
||||
{ "fn pick() (i32 | str) = { return 1; };\n"
|
||||
"fn caller() void = {\n"
|
||||
" let v: (i32 | str) = pick();\n"
|
||||
" if (v is f64) { };\n"
|
||||
"};\n",
|
||||
"is/as: not a variant" },
|
||||
/* let init-type mismatch on primitives */
|
||||
{ "fn caller() void = {\n"
|
||||
" let x: bool = 42;\n"
|
||||
"};\n",
|
||||
"let: not assignable" },
|
||||
/* return type mismatch */
|
||||
{ "fn caller() i32 = {\n"
|
||||
" return \"hi\";\n"
|
||||
"};\n",
|
||||
"return: not assignable" },
|
||||
};
|
||||
|
||||
int
|
||||
@@ -136,6 +153,14 @@ main(void)
|
||||
"?: enclosing fn has no tagged-union return") != NULL);
|
||||
err_present = err_present || (err && strstr(err,
|
||||
"case: not a variant") != NULL);
|
||||
err_present = err_present || (err && strstr(err,
|
||||
"is/as: not a variant") != NULL);
|
||||
err_present = err_present || (err && strstr(err,
|
||||
"is/as: operand is not a tagged union") != NULL);
|
||||
err_present = err_present || (err && strstr(err,
|
||||
"let: not assignable") != NULL);
|
||||
err_present = err_present || (err && strstr(err,
|
||||
"return: not assignable") != NULL);
|
||||
int ok;
|
||||
if (expected_no_err) ok = !err_present;
|
||||
else ok = got_match;
|
||||
|
||||
Reference in New Issue
Block a user