tint/resolver: Fix validation of type constructor without assignment

Tint used to validate that type initializers and conversions were not
used as statements. The WGSL specification has been recently updated to
allow this, but Tint requires more work to correctly handle these as
statements.

For now, restore the validation.

Bug: tint:1836
Bug: chromium:1414489
Change-Id: I9f6aabece26c30b0a0d789ae0dfa10d6f43ee4dc
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/119360
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
This commit is contained in:
Ben Clayton 2023-02-09 23:19:42 +00:00 committed by Dawn LUCI CQ
parent 0755fdbc8f
commit eb30a0ddee
3 changed files with 175 additions and 150 deletions

View File

@ -233,7 +233,6 @@ INSTANTIATE_TEST_SUITE_P(
{Def::kBuiltinType, Use::kAccess, kPass}, {Def::kBuiltinType, Use::kAccess, kPass},
{Def::kBuiltinType, Use::kAddressSpace, kPass}, {Def::kBuiltinType, Use::kAddressSpace, kPass},
{Def::kBuiltinType, Use::kCallExpr, kPass}, {Def::kBuiltinType, Use::kCallExpr, kPass},
{Def::kBuiltinType, Use::kCallStmt, kPass},
{Def::kBuiltinType, Use::kFunctionReturnType, kPass}, {Def::kBuiltinType, Use::kFunctionReturnType, kPass},
{Def::kBuiltinType, Use::kMemberType, kPass}, {Def::kBuiltinType, Use::kMemberType, kPass},
{Def::kBuiltinType, Use::kTexelFormat, R"(TODO(crbug.com/tint/1810))"}, {Def::kBuiltinType, Use::kTexelFormat, R"(TODO(crbug.com/tint/1810))"},
@ -260,7 +259,6 @@ INSTANTIATE_TEST_SUITE_P(
{Def::kStruct, Use::kAccess, R"(TODO(crbug.com/tint/1810))"}, {Def::kStruct, Use::kAccess, R"(TODO(crbug.com/tint/1810))"},
{Def::kStruct, Use::kAddressSpace, R"(TODO(crbug.com/tint/1810))"}, {Def::kStruct, Use::kAddressSpace, R"(TODO(crbug.com/tint/1810))"},
{Def::kStruct, Use::kCallStmt, kPass},
{Def::kStruct, Use::kFunctionReturnType, kPass}, {Def::kStruct, Use::kFunctionReturnType, kPass},
{Def::kStruct, Use::kMemberType, kPass}, {Def::kStruct, Use::kMemberType, kPass},
{Def::kStruct, Use::kTexelFormat, R"(TODO(crbug.com/tint/1810))"}, {Def::kStruct, Use::kTexelFormat, R"(TODO(crbug.com/tint/1810))"},
@ -289,7 +287,6 @@ INSTANTIATE_TEST_SUITE_P(
{Def::kTypeAlias, Use::kAccess, R"(TODO(crbug.com/tint/1810))"}, {Def::kTypeAlias, Use::kAccess, R"(TODO(crbug.com/tint/1810))"},
{Def::kTypeAlias, Use::kAddressSpace, R"(TODO(crbug.com/tint/1810))"}, {Def::kTypeAlias, Use::kAddressSpace, R"(TODO(crbug.com/tint/1810))"},
{Def::kTypeAlias, Use::kCallExpr, kPass}, {Def::kTypeAlias, Use::kCallExpr, kPass},
{Def::kTypeAlias, Use::kCallStmt, kPass},
{Def::kTypeAlias, Use::kFunctionReturnType, kPass}, {Def::kTypeAlias, Use::kFunctionReturnType, kPass},
{Def::kTypeAlias, Use::kMemberType, kPass}, {Def::kTypeAlias, Use::kMemberType, kPass},
{Def::kTypeAlias, Use::kTexelFormat, R"(TODO(crbug.com/tint/1810))"}, {Def::kTypeAlias, Use::kTexelFormat, R"(TODO(crbug.com/tint/1810))"},

View File

@ -2190,11 +2190,11 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) {
// ast::CallExpression has a target which is either an ast::Type or an // ast::CallExpression has a target which is either an ast::Type or an
// ast::IdentifierExpression // ast::IdentifierExpression
sem::Call* call = nullptr; auto call = [&]() -> sem::Call* {
if (expr->target.type) { if (expr->target.type) {
// ast::CallExpression has an ast::Type as the target. // ast::CallExpression has an ast::Type as the target.
// This call is either a type initializer or type conversion. // This call is either a type initializer or type conversion.
call = Switch( return Switch(
expr->target.type, expr->target.type,
[&](const ast::Vector* v) -> sem::Call* { [&](const ast::Vector* v) -> sem::Call* {
Mark(v); Mark(v);
@ -2206,7 +2206,8 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) {
return nullptr; return nullptr;
} }
} }
if (auto* c = ct_init_or_conv(VectorInitConvIntrinsic(v->width), template_arg)) { if (auto* c =
ct_init_or_conv(VectorInitConvIntrinsic(v->width), template_arg)) {
builder_->Sem().Add(expr->target.type, c->Target()->ReturnType()); builder_->Sem().Add(expr->target.type, c->Target()->ReturnType());
return c; return c;
} }
@ -2252,8 +2253,8 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) {
} else { } else {
el_count = builder_->create<type::ConstantArrayCount>( el_count = builder_->create<type::ConstantArrayCount>(
static_cast<uint32_t>(args.Length())); static_cast<uint32_t>(args.Length()));
auto arg_tys = auto arg_tys = utils::Transform(
utils::Transform(args, [](auto* arg) { return arg->Type()->UnwrapRef(); }); args, [](auto* arg) { return arg->Type()->UnwrapRef(); });
el_ty = type::Type::Common(arg_tys); el_ty = type::Type::Common(arg_tys);
if (!el_ty) { if (!el_ty) {
AddError( AddError(
@ -2324,7 +2325,9 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) {
// specified, so there's no need to infer element types. // specified, so there's no need to infer element types.
return ty_init_or_conv(ty); return ty_init_or_conv(ty);
}, },
[&](sem::Function* func) { return FunctionCall(expr, func, args, arg_behaviors); }, [&](sem::Function* func) {
return FunctionCall(expr, func, args, arg_behaviors);
},
[&](Default) { [&](Default) {
ErrorMismatchedResolvedIdentifier(ident->source, *resolved, "call target"); ErrorMismatchedResolvedIdentifier(ident->source, *resolved, "call target");
return nullptr; return nullptr;
@ -2343,6 +2346,7 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) {
ErrorMismatchedResolvedIdentifier(ident->source, *resolved, "call target"); ErrorMismatchedResolvedIdentifier(ident->source, *resolved, "call target");
return nullptr; return nullptr;
} }
}();
if (!call) { if (!call) {
return nullptr; return nullptr;

View File

@ -3206,19 +3206,43 @@ TEST_F(ResolverTypeInitializerValidationTest, NonConstructibleType_Sampler) {
EXPECT_EQ(r()->error(), "12:34 error: type is not constructible"); EXPECT_EQ(r()->error(), "12:34 error: type is not constructible");
} }
TEST_F(ResolverTypeInitializerValidationTest, TypeInitializerAsStatement) { TEST_F(ResolverTypeInitializerValidationTest, BuilinTypeInitializerAsStatement) {
WrapInFunction(CallStmt(vec2<f32>(Source{{12, 34}}, 1_f, 2_f))); WrapInFunction(CallStmt(vec2<f32>(Source{{12, 34}}, 1_f, 2_f)));
EXPECT_FALSE(r()->Resolve()); EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: type initializer evaluated but not used"); EXPECT_EQ(r()->error(), "12:34 error: type initializer evaluated but not used");
} }
TEST_F(ResolverTypeInitializerValidationTest, TypeConversionAsStatement) { TEST_F(ResolverTypeInitializerValidationTest, StructInitializerAsStatement) {
Structure("S", utils::Vector{Member("m", ty.i32())});
WrapInFunction(CallStmt(Call(Source{{12, 34}}, "S", 1_a)));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: type initializer evaluated but not used");
}
TEST_F(ResolverTypeInitializerValidationTest, AliasInitializerAsStatement) {
Alias("A", ty.i32());
WrapInFunction(CallStmt(Call(Source{{12, 34}}, "A", 1_i)));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: type initializer evaluated but not used");
}
TEST_F(ResolverTypeInitializerValidationTest, BuilinTypeConversionAsStatement) {
WrapInFunction(CallStmt(Call(Source{{12, 34}}, ty.f32(), 1_i))); WrapInFunction(CallStmt(Call(Source{{12, 34}}, ty.f32(), 1_i)));
EXPECT_FALSE(r()->Resolve()); EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: type conversion evaluated but not used"); EXPECT_EQ(r()->error(), "12:34 error: type conversion evaluated but not used");
} }
TEST_F(ResolverTypeInitializerValidationTest, AliasConversionAsStatement) {
Alias("A", ty.i32());
WrapInFunction(CallStmt(Call(Source{{12, 34}}, "A", 1_f)));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: type conversion evaluated but not used");
}
} // namespace } // namespace
} // namespace tint::resolver } // namespace tint::resolver