tint/resolver: Allow shadowing of builtins

Fixed: tint:1610
Change-Id: Ie7a391dae2f47db84d084c89a18ec35115bdd667
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/106882
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
This commit is contained in:
Ben Clayton 2022-10-25 08:44:20 +00:00 committed by Dawn LUCI CQ
parent c0377056f4
commit 434edc2a12
2 changed files with 146 additions and 66 deletions

View File

@ -14,6 +14,7 @@
#include "src/tint/ast/builtin_texture_helper_test.h"
#include "src/tint/resolver/resolver_test_helper.h"
#include "src/tint/sem/type_initializer.h"
using namespace tint::number_suffixes; // NOLINT
@ -96,48 +97,169 @@ TEST_F(ResolverBuiltinValidationTest, InvalidPipelineStageIndirect) {
7:8 note: called by entry point 'main')");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsFunction) {
Func(Source{{12, 34}}, "mix", utils::Empty, ty.i32(), {});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a function)");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalConst) {
GlobalConst(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a 'const')");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalVar) {
GlobalVar(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i), ast::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a module-scope 'var')");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsAlias) {
Alias(Source{{12, 34}}, "mix", ty.i32());
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(12:34 error: 'mix' is a builtin and cannot be redeclared as an alias)");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsStruct) {
Structure(Source{{12, 34}}, "mix",
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsFunctionUsedAsFunction) {
auto* mix = Func(Source{{12, 34}}, "mix", utils::Empty, ty.i32(),
utils::Vector{
Return(1_i),
});
auto* use = Call("mix");
WrapInFunction(use);
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get<sem::Call>(use);
ASSERT_NE(sem, nullptr);
EXPECT_EQ(sem->Target(), Sem().Get(mix));
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsFunctionUsedAsVariable) {
Func(Source{{12, 34}}, "mix", utils::Empty, ty.i32(),
utils::Vector{
Return(1_i),
});
WrapInFunction(Decl(Var("v", Expr(Source{{56, 78}}, "mix"))));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(56:78 error: missing '(' for function call)");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsFunctionUsedAsType) {
Func(Source{{12, 34}}, "mix", utils::Empty, ty.i32(),
utils::Vector{
Return(1_i),
});
WrapInFunction(Construct(ty.type_name(Source{{56, 78}}, "mix")));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(56:78 error: cannot use function 'mix' as type
12:34 note: 'mix' declared here)");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalConstUsedAsFunction) {
GlobalConst(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i));
WrapInFunction(Call(Expr(Source{{56, 78}}, "mix"), 1_f, 2_f, 3_f));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(56:78 error: cannot call variable 'mix'
12:34 note: 'mix' declared here)");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalConstUsedAsVariable) {
auto* mix = GlobalConst(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i));
auto* use = Expr("mix");
WrapInFunction(Decl(Var("v", use)));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get<sem::VariableUser>(use);
ASSERT_NE(sem, nullptr);
EXPECT_EQ(sem->Variable(), Sem().Get(mix));
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalConstUsedAsType) {
GlobalConst(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i));
WrapInFunction(Construct(ty.type_name(Source{{56, 78}}, "mix")));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(56:78 error: cannot use variable 'mix' as type
12:34 note: 'mix' declared here)");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalVarUsedAsFunction) {
GlobalVar(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i), ast::AddressSpace::kPrivate);
WrapInFunction(Call(Expr(Source{{56, 78}}, "mix"), 1_f, 2_f, 3_f));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(56:78 error: cannot call variable 'mix'
12:34 note: 'mix' declared here)");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalVarUsedAsVariable) {
auto* mix =
GlobalVar(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i), ast::AddressSpace::kPrivate);
auto* use = Expr("mix");
WrapInFunction(Decl(Var("v", use)));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get<sem::VariableUser>(use);
ASSERT_NE(sem, nullptr);
EXPECT_EQ(sem->Variable(), Sem().Get(mix));
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalVarUsedAsType) {
GlobalVar(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i), ast::AddressSpace::kPrivate);
WrapInFunction(Construct(ty.type_name(Source{{56, 78}}, "mix")));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(56:78 error: cannot use variable 'mix' as type
12:34 note: 'mix' declared here)");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsAliasUsedAsFunction) {
Alias(Source{{12, 34}}, "mix", ty.i32());
WrapInFunction(Call(Source{{56, 78}}, "mix", 1_f, 2_f, 3_f));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(56:78 error: no matching initializer for i32(f32, f32, f32)
2 candidate initializers:
i32(i32) -> i32
i32() -> i32
1 candidate conversion:
i32<T>(T) -> i32 where: T is abstract-int, abstract-float, f32, f16, u32 or bool
)");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsAliasUsedAsVariable) {
Alias(Source{{12, 34}}, "mix", ty.i32());
WrapInFunction(Decl(Var("v", Expr(Source{{56, 78}}, "mix"))));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(56:78 error: missing '(' for builtin call)");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsAliasUsedAsType) {
auto* mix = Alias(Source{{12, 34}}, "mix", ty.i32());
auto* use = Construct(ty.type_name("mix"));
WrapInFunction(use);
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get<sem::Call>(use);
ASSERT_NE(sem, nullptr);
EXPECT_EQ(sem->Type(), Sem().Get(mix));
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsStructUsedAsFunction) {
Structure("mix", utils::Vector{
Member("m", ty.i32()),
});
WrapInFunction(Call(Source{{12, 34}}, "mix", 1_f, 2_f, 3_f));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a struct)");
R"(12:34 error: struct initializer has too many inputs: expected 1, found 3)");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsStructUsedAsVariable) {
Structure("mix", utils::Vector{
Member("m", ty.i32()),
});
WrapInFunction(Decl(Var("v", Expr(Source{{12, 34}}, "mix"))));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(12:34 error: missing '(' for builtin call)");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsStructUsedAsType) {
auto* mix = Structure("mix", utils::Vector{
Member("m", ty.i32()),
});
auto* use = Construct(ty.type_name("mix"));
WrapInFunction(use);
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get<sem::Call>(use);
ASSERT_NE(sem, nullptr);
EXPECT_EQ(sem->Type(), Sem().Get(mix));
}
namespace texture_constexpr_args {

View File

@ -642,14 +642,6 @@ bool Validator::GlobalVariable(
return false;
}
auto name = symbols_.NameFor(var->symbol);
if (sem::ParseBuiltinType(name) != sem::BuiltinType::kNone) {
AddError(
"'" + name + "' is a builtin and cannot be redeclared as a module-scope 'var'",
var->source);
return false;
}
return Var(global);
},
[&](const ast::Override*) { return Override(global, override_ids); },
@ -818,13 +810,6 @@ bool Validator::Override(
}
}
auto name = symbols_.NameFor(decl->symbol);
if (sem::ParseBuiltinType(name) != sem::BuiltinType::kNone) {
AddError("'" + name + "' is a builtin and cannot be redeclared as a 'override'",
decl->source);
return false;
}
if (!storage_ty->is_scalar()) {
AddError(sem_.TypeNameOf(storage_ty) + " cannot be used as the type of a 'override'",
decl->source);
@ -839,15 +824,7 @@ bool Validator::Override(
return true;
}
bool Validator::Const(const sem::Variable* v) const {
auto* decl = v->Declaration();
auto name = symbols_.NameFor(decl->symbol);
if (sem::ParseBuiltinType(name) != sem::BuiltinType::kNone) {
AddError("'" + name + "' is a builtin and cannot be redeclared as a 'const'", decl->source);
return false;
}
bool Validator::Const(const sem::Variable*) const {
return true;
}
@ -1033,13 +1010,6 @@ bool Validator::InterpolateAttribute(const ast::InterpolateAttribute* attr,
bool Validator::Function(const sem::Function* func, ast::PipelineStage stage) const {
auto* decl = func->Declaration();
auto name = symbols_.NameFor(decl->symbol);
if (sem::ParseBuiltinType(name) != sem::BuiltinType::kNone) {
AddError("'" + name + "' is a builtin and cannot be redeclared as a function",
decl->source);
return false;
}
for (auto* attr : decl->attributes) {
if (attr->Is<ast::WorkgroupAttribute>()) {
if (decl->PipelineStage() != ast::PipelineStage::kCompute) {
@ -1112,6 +1082,7 @@ bool Validator::Function(const sem::Function* func, ast::PipelineStage stage) co
func->Behaviors() != sem::Behavior::kNext && func->Behaviors() != sem::Behavior::kDiscard &&
func->Behaviors() != sem::Behaviors{sem::Behavior::kNext, //
sem::Behavior::kDiscard}) {
auto name = symbols_.NameFor(decl->symbol);
TINT_ICE(Resolver, diagnostics_)
<< "function '" << name << "' behaviors are: " << func->Behaviors();
}
@ -2178,24 +2149,11 @@ bool Validator::ArrayStrideAttribute(const ast::StrideAttribute* attr,
return true;
}
bool Validator::Alias(const ast::Alias* alias) const {
auto name = symbols_.NameFor(alias->name);
if (sem::ParseBuiltinType(name) != sem::BuiltinType::kNone) {
AddError("'" + name + "' is a builtin and cannot be redeclared as an alias", alias->source);
return false;
}
bool Validator::Alias(const ast::Alias*) const {
return true;
}
bool Validator::Structure(const sem::Struct* str, ast::PipelineStage stage) const {
auto name = symbols_.NameFor(str->Declaration()->name);
if (sem::ParseBuiltinType(name) != sem::BuiltinType::kNone) {
AddError("'" + name + "' is a builtin and cannot be redeclared as a struct",
str->Declaration()->source);
return false;
}
if (str->Members().empty()) {
AddError("structures must have at least one member", str->Declaration()->source);
return false;