resolver: Replace ICEs with proper errors

If you attempted to:
 * Use a function as a type
 * Use a variable as a type
 * Use a variable as a call target

You would get an ICE. Replace these with proper error diagnostics.

Change-Id: Ibbe4cd1b59b1aadd451aa0445ad137859aa071eb
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/79765
Reviewed-by: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
Ben Clayton 2022-02-09 22:23:17 +00:00 committed by Tint LUCI CQ
parent 88d1a433aa
commit 597fc4cdbe
3 changed files with 83 additions and 25 deletions

View File

@ -252,6 +252,19 @@ TEST_F(ResolverCallValidationTest, LetPointerPrivate) {
"identifier expression or a function parameter");
}
TEST_F(ResolverCallValidationTest, CallVariable) {
// var v : i32;
// fn f() {
// v();
// }
Global("v", ty.i32(), ast::StorageClass::kPrivate);
Func("f", {}, ty.void_(), {CallStmt(Call(Source{{12, 34}}, "v"))});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(error: cannot call variable 'v'
note: 'v' declared here)");
}
} // namespace
} // namespace resolver
} // namespace tint

View File

@ -266,12 +266,26 @@ sem::Type* Resolver::Type(const ast::Type* ty) {
if (ty->As<ast::ExternalTexture>()) {
return builder_->create<sem::ExternalTexture>();
}
if (auto* type = ResolvedSymbol<sem::Type>(ty)) {
return type;
}
TINT_UNREACHABLE(Resolver, diagnostics_)
<< "Unhandled ast::Type: " << ty->TypeInfo().name;
return nullptr;
return Switch(
ResolvedSymbol(ty), //
[&](sem::Type* type) { return type; },
[&](sem::Variable* var) {
auto name = builder_->Symbols().NameFor(var->Declaration()->symbol);
AddError("cannot use variable '" + name + "' as type", ty->source);
AddNote("'" + name + "' declared here", var->Declaration()->source);
return nullptr;
},
[&](sem::Function* func) {
auto name = builder_->Symbols().NameFor(func->Declaration()->symbol);
AddError("cannot use function '" + name + "' as type", ty->source);
AddNote("'" + name + "' declared here", func->Declaration()->source);
return nullptr;
},
[&](Default) {
TINT_UNREACHABLE(Resolver, diagnostics_)
<< "Unhandled ast::Type: " << ty->TypeInfo().name;
return nullptr;
});
}();
if (s) {
@ -1347,26 +1361,33 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) {
Mark(ident);
auto* resolved = ResolvedSymbol(ident);
if (auto* ty = As<sem::Type>(resolved)) {
return type_ctor_or_conv(ty);
}
return Switch(
resolved, //
[&](sem::Type* type) { return type_ctor_or_conv(type); },
[&](sem::Function* func) {
return FunctionCall(expr, func, std::move(args), arg_behaviors);
},
[&](sem::Variable* var) {
auto name = builder_->Symbols().NameFor(var->Declaration()->symbol);
AddError("cannot call variable '" + name + "'", ident->source);
AddNote("'" + name + "' declared here", var->Declaration()->source);
return nullptr;
},
[&](Default) -> sem::Call* {
auto name = builder_->Symbols().NameFor(ident->symbol);
auto builtin_type = sem::ParseBuiltinType(name);
if (builtin_type != sem::BuiltinType::kNone) {
return BuiltinCall(expr, builtin_type, std::move(args),
std::move(arg_tys));
}
if (auto* fn = As<sem::Function>(resolved)) {
return FunctionCall(expr, fn, std::move(args), arg_behaviors);
}
auto name = builder_->Symbols().NameFor(ident->symbol);
auto builtin_type = sem::ParseBuiltinType(name);
if (builtin_type != sem::BuiltinType::kNone) {
return BuiltinCall(expr, builtin_type, std::move(args), std::move(arg_tys));
}
TINT_ICE(Resolver, diagnostics_)
<< expr->source << " unresolved CallExpression target:\n"
<< "resolved: " << (resolved ? resolved->TypeInfo().name : "<null>")
<< "\n"
<< "name: " << builder_->Symbols().NameFor(ident->symbol);
return nullptr;
TINT_ICE(Resolver, diagnostics_)
<< expr->source << " unresolved CallExpression target:\n"
<< "resolved: " << (resolved ? resolved->TypeInfo().name : "<null>")
<< "\n"
<< "name: " << builder_->Symbols().NameFor(ident->symbol);
return nullptr;
});
}
sem::Call* Resolver::BuiltinCall(const ast::CallExpression* expr,

View File

@ -695,6 +695,30 @@ TEST_F(ResolverTypeValidationTest, ArrayOfNonStorableType) {
"an array");
}
TEST_F(ResolverTypeValidationTest, VariableAsType) {
// var<private> a : i32;
// var<private> b : a;
Global("a", ty.i32(), ast::StorageClass::kPrivate);
Global("b", ty.type_name("a"), ast::StorageClass::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(error: cannot use variable 'a' as type
note: 'a' declared here)");
}
TEST_F(ResolverTypeValidationTest, FunctionAsType) {
// fn f() {}
// var<private> v : f;
Func("f", {}, ty.void_(), {});
Global("v", ty.type_name("f"), ast::StorageClass::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(error: cannot use function 'f' as type
note: 'f' declared here)");
}
namespace GetCanonicalTests {
struct Params {
builder::ast_type_func_ptr create_ast_type;