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:
parent
c0377056f4
commit
434edc2a12
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include "src/tint/ast/builtin_texture_helper_test.h"
|
#include "src/tint/ast/builtin_texture_helper_test.h"
|
||||||
#include "src/tint/resolver/resolver_test_helper.h"
|
#include "src/tint/resolver/resolver_test_helper.h"
|
||||||
|
#include "src/tint/sem/type_initializer.h"
|
||||||
|
|
||||||
using namespace tint::number_suffixes; // NOLINT
|
using namespace tint::number_suffixes; // NOLINT
|
||||||
|
|
||||||
|
@ -96,48 +97,169 @@ TEST_F(ResolverBuiltinValidationTest, InvalidPipelineStageIndirect) {
|
||||||
7:8 note: called by entry point 'main')");
|
7:8 note: called by entry point 'main')");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsFunction) {
|
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsFunctionUsedAsFunction) {
|
||||||
Func(Source{{12, 34}}, "mix", utils::Empty, ty.i32(), {});
|
auto* mix = Func(Source{{12, 34}}, "mix", utils::Empty, ty.i32(),
|
||||||
|
utils::Vector{
|
||||||
|
Return(1_i),
|
||||||
|
});
|
||||||
|
auto* use = Call("mix");
|
||||||
|
WrapInFunction(use);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
EXPECT_EQ(r()->error(),
|
auto* sem = Sem().Get<sem::Call>(use);
|
||||||
R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a function)");
|
ASSERT_NE(sem, nullptr);
|
||||||
|
EXPECT_EQ(sem->Target(), Sem().Get(mix));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalConst) {
|
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));
|
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_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(), R"(56:78 error: cannot call variable 'mix'
|
||||||
R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a 'const')");
|
12:34 note: 'mix' declared here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalVar) {
|
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);
|
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_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(r()->error(), R"(56:78 error: cannot call variable 'mix'
|
||||||
r()->error(),
|
12:34 note: 'mix' declared here)");
|
||||||
R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a module-scope 'var')");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsAlias) {
|
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());
|
Alias(Source{{12, 34}}, "mix", ty.i32());
|
||||||
|
WrapInFunction(Call(Source{{56, 78}}, "mix", 1_f, 2_f, 3_f));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(), R"(56:78 error: no matching initializer for i32(f32, f32, f32)
|
||||||
R"(12:34 error: 'mix' is a builtin and cannot be redeclared as an alias)");
|
|
||||||
|
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, BuiltinRedeclaredAsStruct) {
|
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsAliasUsedAsVariable) {
|
||||||
Structure(Source{{12, 34}}, "mix",
|
Alias(Source{{12, 34}}, "mix", ty.i32());
|
||||||
utils::Vector{
|
WrapInFunction(Decl(Var("v", Expr(Source{{56, 78}}, "mix"))));
|
||||||
Member("m", ty.i32()),
|
|
||||||
});
|
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_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
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 {
|
namespace texture_constexpr_args {
|
||||||
|
|
|
@ -642,14 +642,6 @@ bool Validator::GlobalVariable(
|
||||||
return false;
|
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);
|
return Var(global);
|
||||||
},
|
},
|
||||||
[&](const ast::Override*) { return Override(global, override_ids); },
|
[&](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()) {
|
if (!storage_ty->is_scalar()) {
|
||||||
AddError(sem_.TypeNameOf(storage_ty) + " cannot be used as the type of a 'override'",
|
AddError(sem_.TypeNameOf(storage_ty) + " cannot be used as the type of a 'override'",
|
||||||
decl->source);
|
decl->source);
|
||||||
|
@ -839,15 +824,7 @@ bool Validator::Override(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Validator::Const(const sem::Variable* v) const {
|
bool Validator::Const(const sem::Variable*) 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1033,13 +1010,6 @@ bool Validator::InterpolateAttribute(const ast::InterpolateAttribute* attr,
|
||||||
bool Validator::Function(const sem::Function* func, ast::PipelineStage stage) const {
|
bool Validator::Function(const sem::Function* func, ast::PipelineStage stage) const {
|
||||||
auto* decl = func->Declaration();
|
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) {
|
for (auto* attr : decl->attributes) {
|
||||||
if (attr->Is<ast::WorkgroupAttribute>()) {
|
if (attr->Is<ast::WorkgroupAttribute>()) {
|
||||||
if (decl->PipelineStage() != ast::PipelineStage::kCompute) {
|
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::Behavior::kNext && func->Behaviors() != sem::Behavior::kDiscard &&
|
||||||
func->Behaviors() != sem::Behaviors{sem::Behavior::kNext, //
|
func->Behaviors() != sem::Behaviors{sem::Behavior::kNext, //
|
||||||
sem::Behavior::kDiscard}) {
|
sem::Behavior::kDiscard}) {
|
||||||
|
auto name = symbols_.NameFor(decl->symbol);
|
||||||
TINT_ICE(Resolver, diagnostics_)
|
TINT_ICE(Resolver, diagnostics_)
|
||||||
<< "function '" << name << "' behaviors are: " << func->Behaviors();
|
<< "function '" << name << "' behaviors are: " << func->Behaviors();
|
||||||
}
|
}
|
||||||
|
@ -2178,24 +2149,11 @@ bool Validator::ArrayStrideAttribute(const ast::StrideAttribute* attr,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Validator::Alias(const ast::Alias* alias) const {
|
bool Validator::Alias(const ast::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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Validator::Structure(const sem::Struct* str, ast::PipelineStage stage) const {
|
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()) {
|
if (str->Members().empty()) {
|
||||||
AddError("structures must have at least one member", str->Declaration()->source);
|
AddError("structures must have at least one member", str->Declaration()->source);
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Reference in New Issue