tint: Validate no template args with functions / builtin calls
This was already handled when resolving regular identifiers, but calls special case this resolving, and failed to check for template arguments. Bug: chromium:1424273 Change-Id: Id756c7fbca93afcd9fd3792466471aa43d3dff04 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/123980 Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Ben Clayton <bclayton@google.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
parent
ebf8b7fd91
commit
97382d96f5
|
@ -478,5 +478,34 @@ TEST_F(ResolverCallValidationTest, MustUseBuiltin) {
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: ignoring return value of builtin 'max'");
|
EXPECT_EQ(r()->error(), "12:34 error: ignoring return value of builtin 'max'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverCallValidationTest, UnexpectedFunctionTemplateArgs) {
|
||||||
|
// fn a() {}
|
||||||
|
// fn b() {
|
||||||
|
// a<i32>();
|
||||||
|
// }
|
||||||
|
Func(Source{{56, 78}}, "a", utils::Empty, ty.void_(), utils::Empty);
|
||||||
|
Func("b", utils::Empty, ty.void_(),
|
||||||
|
utils::Vector{
|
||||||
|
CallStmt(Call(Ident(Source{{12, 34}}, "a", "i32"))),
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(), R"(12:34 error: function 'a' does not take template arguments
|
||||||
|
56:78 note: function 'a' declared here)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverCallValidationTest, UnexpectedBuiltinTemplateArgs) {
|
||||||
|
// fn f() {
|
||||||
|
// min<i32>(1, 2);
|
||||||
|
// }
|
||||||
|
Func("f", utils::Empty, ty.void_(),
|
||||||
|
utils::Vector{
|
||||||
|
Decl(Var("v", Call(Ident(Source{{12, 34}}, "min", "i32"), 1_a, 2_a))),
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(), R"(12:34 error: builtin 'min' does not take template arguments)");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace tint::resolver
|
} // namespace tint::resolver
|
||||||
|
|
|
@ -2156,7 +2156,12 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) {
|
||||||
return Switch(
|
return Switch(
|
||||||
sem_.Get(ast_node), //
|
sem_.Get(ast_node), //
|
||||||
[&](type::Type* t) { return ty_init_or_conv(t); },
|
[&](type::Type* t) { return ty_init_or_conv(t); },
|
||||||
[&](sem::Function* f) { return FunctionCall(expr, f, args, arg_behaviors); },
|
[&](sem::Function* f) -> sem::Call* {
|
||||||
|
if (!TINT_LIKELY(CheckNotTemplated("function", ident))) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return FunctionCall(expr, f, args, arg_behaviors);
|
||||||
|
},
|
||||||
[&](sem::Expression* e) {
|
[&](sem::Expression* e) {
|
||||||
sem_.ErrorUnexpectedExprKind(e, "call target");
|
sem_.ErrorUnexpectedExprKind(e, "call target");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -2168,6 +2173,9 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto f = resolved->BuiltinFunction(); f != builtin::Function::kNone) {
|
if (auto f = resolved->BuiltinFunction(); f != builtin::Function::kNone) {
|
||||||
|
if (!TINT_LIKELY(CheckNotTemplated("builtin", ident))) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
return BuiltinCall(expr, f, args);
|
return BuiltinCall(expr, f, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2331,13 +2339,7 @@ type::Type* Resolver::BuiltinType(builtin::Builtin builtin_ty, const ast::Identi
|
||||||
auto& b = *builder_;
|
auto& b = *builder_;
|
||||||
|
|
||||||
auto check_no_tmpl_args = [&](type::Type* ty) -> type::Type* {
|
auto check_no_tmpl_args = [&](type::Type* ty) -> type::Type* {
|
||||||
if (TINT_UNLIKELY(ident->Is<ast::TemplatedIdentifier>())) {
|
return TINT_LIKELY(CheckNotTemplated("type", ident)) ? ty : nullptr;
|
||||||
AddError("type '" + b.Symbols().NameFor(ident->symbol) +
|
|
||||||
"' does not take template arguments",
|
|
||||||
ident->source);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return ty;
|
|
||||||
};
|
};
|
||||||
auto f32 = [&] { return b.create<type::F32>(); };
|
auto f32 = [&] { return b.create<type::F32>(); };
|
||||||
auto i32 = [&] { return b.create<type::I32>(); };
|
auto i32 = [&] { return b.create<type::I32>(); };
|
||||||
|
@ -3019,25 +3021,15 @@ sem::Expression* Resolver::Identifier(const ast::IdentifierExpression* expr) {
|
||||||
return user;
|
return user;
|
||||||
},
|
},
|
||||||
[&](const type::Type* ty) -> sem::TypeExpression* {
|
[&](const type::Type* ty) -> sem::TypeExpression* {
|
||||||
if (TINT_UNLIKELY(ident->Is<ast::TemplatedIdentifier>())) {
|
if (!TINT_LIKELY(CheckNotTemplated("type", ident))) {
|
||||||
AddError("type '" + builder_->Symbols().NameFor(ident->symbol) +
|
|
||||||
"' does not take template arguments",
|
|
||||||
ident->source);
|
|
||||||
sem_.NoteDeclarationSource(ast_node);
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder_->create<sem::TypeExpression>(expr, current_statement_, ty);
|
return builder_->create<sem::TypeExpression>(expr, current_statement_, ty);
|
||||||
},
|
},
|
||||||
[&](const sem::Function* fn) -> sem::FunctionExpression* {
|
[&](const sem::Function* fn) -> sem::FunctionExpression* {
|
||||||
if (TINT_UNLIKELY(ident->Is<ast::TemplatedIdentifier>())) {
|
if (!TINT_LIKELY(CheckNotTemplated("function", ident))) {
|
||||||
AddError("function '" + builder_->Symbols().NameFor(ident->symbol) +
|
|
||||||
"' does not take template arguments",
|
|
||||||
ident->source);
|
|
||||||
sem_.NoteDeclarationSource(ast_node);
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder_->create<sem::FunctionExpression>(expr, current_statement_, fn);
|
return builder_->create<sem::FunctionExpression>(expr, current_statement_, fn);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -4329,6 +4321,21 @@ void Resolver::ApplyDiagnosticSeverities(NODE* node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Resolver::CheckNotTemplated(const char* use, const ast::Identifier* ident) {
|
||||||
|
if (TINT_UNLIKELY(ident->Is<ast::TemplatedIdentifier>())) {
|
||||||
|
AddError(std::string(use) + " '" + builder_->Symbols().NameFor(ident->symbol) +
|
||||||
|
"' does not take template arguments",
|
||||||
|
ident->source);
|
||||||
|
if (auto resolved = dependencies_.resolved_identifiers.Get(ident)) {
|
||||||
|
if (auto* ast_node = resolved->Node()) {
|
||||||
|
sem_.NoteDeclarationSource(ast_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Resolver::ErrorMismatchedResolvedIdentifier(const Source& source,
|
void Resolver::ErrorMismatchedResolvedIdentifier(const Source& source,
|
||||||
const ResolvedIdentifier& resolved,
|
const ResolvedIdentifier& resolved,
|
||||||
std::string_view wanted) {
|
std::string_view wanted) {
|
||||||
|
|
|
@ -480,6 +480,11 @@ class Resolver {
|
||||||
template <typename NODE>
|
template <typename NODE>
|
||||||
void ApplyDiagnosticSeverities(NODE* node);
|
void ApplyDiagnosticSeverities(NODE* node);
|
||||||
|
|
||||||
|
/// Checks @p ident is not an ast::TemplatedIdentifier.
|
||||||
|
/// If @p ident is a ast::TemplatedIdentifier, then an error diagnostic is raised.
|
||||||
|
/// @returns true if @p ident is not a ast::TemplatedIdentifier.
|
||||||
|
bool CheckNotTemplated(const char* use, const ast::Identifier* ident);
|
||||||
|
|
||||||
/// Raises an error diagnostic that the resolved identifier @p resolved was not of the expected
|
/// Raises an error diagnostic that the resolved identifier @p resolved was not of the expected
|
||||||
/// kind.
|
/// kind.
|
||||||
/// @param source the source of the error diagnostic
|
/// @param source the source of the error diagnostic
|
||||||
|
|
Loading…
Reference in New Issue