diff --git a/src/tint/resolver/builtin_validation_test.cc b/src/tint/resolver/builtin_validation_test.cc index a0581d98bd..b67a9ee1d8 100644 --- a/src/tint/resolver/builtin_validation_test.cc +++ b/src/tint/resolver/builtin_validation_test.cc @@ -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(), {}); +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); - EXPECT_FALSE(r()->Resolve()); - EXPECT_EQ(r()->error(), - R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a function)"); + ASSERT_TRUE(r()->Resolve()) << r()->error(); + auto* sem = Sem().Get(use); + 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)); + WrapInFunction(Call(Expr(Source{{56, 78}}, "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 'const')"); + EXPECT_EQ(r()->error(), R"(56:78 error: cannot call variable 'mix' +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(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"(12:34 error: 'mix' is a builtin and cannot be redeclared as a module-scope 'var')"); + EXPECT_EQ(r()->error(), R"(56:78 error: cannot call variable 'mix' +12:34 note: 'mix' declared here)"); } -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(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"(12:34 error: 'mix' is a builtin and cannot be redeclared as an alias)"); + 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) -> i32 where: T is abstract-int, abstract-float, f32, f16, u32 or bool +)"); } -TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsStruct) { - Structure(Source{{12, 34}}, "mix", - utils::Vector{ - Member("m", ty.i32()), - }); +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(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(use); + ASSERT_NE(sem, nullptr); + EXPECT_EQ(sem->Type(), Sem().Get(mix)); } namespace texture_constexpr_args { diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc index 9c693dcd0d..6dab977d98 100644 --- a/src/tint/resolver/validator.cc +++ b/src/tint/resolver/validator.cc @@ -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()) { 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;