wcc: case T => variant validity check (C + selfhost)
`match (u) { case T => ... }` where T isn't a variant of u was
silently accepted by both checkers. The cgen would emit a tag
comparison against an index that never appears, leaving the arm
unreachable — wasted code that's almost always a bug or typo.
C check.c now mirrors the existing is/as rule for match arms:
each `case T` and each alt of multi-pattern `case T1 | T2` is
checked against the scrutinee's variant list via variant_present.
selfhost check.ww gets the same shape with AST-level type_eq_ast
comparison. Both checks land in the same scope-aware pass that
already runs exhaustiveness and ? subset.
New test rows in 300_check (C side) and 950_selfcheck (selfhost
side) exercise both single-pattern and multi-pattern alt typos.
The 950 driver's err_present detector picks up the new
"case: not a variant" prefix.
This commit is contained in:
@@ -92,6 +92,17 @@ static const struct row rows[] = {
|
||||
" return v + 1;\n"
|
||||
"};\n",
|
||||
"?: error variant not in enclosing return" },
|
||||
/* case T => names a non-variant */
|
||||
{ "fn pick() (i32 | str) = { return 1; };\n"
|
||||
"fn caller() void = {\n"
|
||||
" let v: (i32 | str) = pick();\n"
|
||||
" match (v) {\n"
|
||||
" case let n: i32 => { };\n"
|
||||
" case let s: str => { };\n"
|
||||
" case let f: f64 => { };\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
"case: not a variant" },
|
||||
};
|
||||
|
||||
int
|
||||
@@ -123,6 +134,8 @@ main(void)
|
||||
"?: enclosing fn return is not tagged") != NULL);
|
||||
err_present = err_present || (err && strstr(err,
|
||||
"?: enclosing fn has no tagged-union return") != NULL);
|
||||
err_present = err_present || (err && strstr(err,
|
||||
"case: not a variant") != NULL);
|
||||
int ok;
|
||||
if (expected_no_err) ok = !err_present;
|
||||
else ok = got_match;
|
||||
|
||||
Reference in New Issue
Block a user