mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-17 08:57:26 +00:00
Resolver: Enforce vector constructor type rules
Added enforcement for vector constructor type rules according to the table in https://gpuweb.github.io/gpuweb/wgsl.html#type-constructor-expr. This surfaced a number of existing tests that violated some of these rules or had a type-declaration related bug, so this CL fixes those as well (these tests either passed the incorrect number of arguments to a vector constructor or relied on implicit conversions between numeric types). Fixed: tint:632 Fixed: tint:476 Change-Id: I8279be3eeae50b64db486ee7a91a43bd94fdff62 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/44480 Commit-Queue: Arman Uguray <armansito@chromium.org> Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
committed by
Commit Bot service account
parent
2f9ced0341
commit
3549e2ea8c
@@ -597,16 +597,97 @@ bool Resolver::IntrinsicCall(ast::CallExpression* call,
|
||||
}
|
||||
|
||||
bool Resolver::Constructor(ast::ConstructorExpression* expr) {
|
||||
if (auto* ty = expr->As<ast::TypeConstructorExpression>()) {
|
||||
for (auto* value : ty->values()) {
|
||||
if (auto* type_ctor = expr->As<ast::TypeConstructorExpression>()) {
|
||||
for (auto* value : type_ctor->values()) {
|
||||
if (!Expression(value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
SetType(expr, ty->type());
|
||||
SetType(expr, type_ctor->type());
|
||||
|
||||
// Now that the argument types have been determined, make sure that they
|
||||
// obey the constructor type rules laid out in
|
||||
// https://gpuweb.github.io/gpuweb/wgsl.html#type-constructor-expr.
|
||||
if (auto* vec_type = type_ctor->type()->As<type::Vector>()) {
|
||||
return VectorConstructor(*vec_type, type_ctor->values());
|
||||
}
|
||||
// TODO(crbug.com/tint/633): Validate matrix constructor
|
||||
// TODO(crbug.com/tint/634): Validate array constructor
|
||||
} else if (auto* scalar_ctor = expr->As<ast::ScalarConstructorExpression>()) {
|
||||
SetType(expr, scalar_ctor->literal()->type());
|
||||
} else {
|
||||
SetType(expr,
|
||||
expr->As<ast::ScalarConstructorExpression>()->literal()->type());
|
||||
TINT_ICE(diagnostics_) << "unexpected constructor expression type";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Resolver::VectorConstructor(const type::Vector& vec_type,
|
||||
const ast::ExpressionList& values) {
|
||||
type::Type* elem_type = vec_type.type()->UnwrapAll();
|
||||
size_t value_cardinality_sum = 0;
|
||||
for (auto* value : values) {
|
||||
type::Type* value_type = TypeOf(value)->UnwrapAll();
|
||||
if (value_type->is_scalar()) {
|
||||
if (elem_type != value_type) {
|
||||
diagnostics_.add_error(
|
||||
"type in vector constructor does not match vector type: "
|
||||
"expected '" +
|
||||
elem_type->FriendlyName(builder_->Symbols()) + "', found '" +
|
||||
value_type->FriendlyName(builder_->Symbols()) + "'",
|
||||
value->source());
|
||||
return false;
|
||||
}
|
||||
|
||||
value_cardinality_sum++;
|
||||
} else if (auto* value_vec = value_type->As<type::Vector>()) {
|
||||
type::Type* value_elem_type = value_vec->type()->UnwrapAll();
|
||||
// A mismatch of vector type parameter T is only an error if multiple
|
||||
// arguments are present. A single argument constructor constitutes a
|
||||
// type conversion expression.
|
||||
// NOTE: A conversion expression from a vec<bool> to any other vecN<T>
|
||||
// is disallowed (see
|
||||
// https://gpuweb.github.io/gpuweb/wgsl.html#conversion-expr).
|
||||
if (elem_type != value_elem_type &&
|
||||
(values.size() > 1u || value_vec->is_bool_vector())) {
|
||||
diagnostics_.add_error(
|
||||
"type in vector constructor does not match vector type: "
|
||||
"expected '" +
|
||||
elem_type->FriendlyName(builder_->Symbols()) + "', found '" +
|
||||
value_elem_type->FriendlyName(builder_->Symbols()) + "'",
|
||||
value->source());
|
||||
return false;
|
||||
}
|
||||
|
||||
value_cardinality_sum += value_vec->size();
|
||||
} else {
|
||||
// A vector constructor can only accept vectors and scalars.
|
||||
diagnostics_.add_error(
|
||||
"expected vector or scalar type in vector constructor; found: " +
|
||||
value_type->FriendlyName(builder_->Symbols()),
|
||||
value->source());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// A correct vector constructor must either be a zero-value expression
|
||||
// or the number of components of all constructor arguments must add up
|
||||
// to the vector cardinality.
|
||||
if (value_cardinality_sum > 0 && value_cardinality_sum != vec_type.size()) {
|
||||
if (values.empty()) {
|
||||
TINT_ICE(diagnostics_)
|
||||
<< "constructor arguments expected to be non-empty!";
|
||||
}
|
||||
const Source& values_start = values[0]->source();
|
||||
const Source& values_end = values[values.size() - 1]->source();
|
||||
const Source src(
|
||||
Source::Range(values_start.range.begin, values_end.range.end),
|
||||
values_start.file_path, values_start.file_content);
|
||||
diagnostics_.add_error(
|
||||
"attempted to construct '" +
|
||||
vec_type.FriendlyName(builder_->Symbols()) + "' with " +
|
||||
std::to_string(value_cardinality_sum) + " component(s)",
|
||||
src);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -177,6 +177,8 @@ class Resolver {
|
||||
bool Call(ast::CallExpression*);
|
||||
bool CaseStatement(ast::CaseStatement*);
|
||||
bool Constructor(ast::ConstructorExpression*);
|
||||
bool VectorConstructor(const type::Vector& vec_type,
|
||||
const ast::ExpressionList& values);
|
||||
bool Expression(ast::Expression*);
|
||||
bool Expressions(const ast::ExpressionList&);
|
||||
bool Function(ast::Function*);
|
||||
|
||||
@@ -578,8 +578,20 @@ TEST_F(ResolverTest, Expr_Constructor_Scalar) {
|
||||
EXPECT_TRUE(TypeOf(s)->Is<type::F32>());
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, Expr_Constructor_Type) {
|
||||
auto* tc = vec3<f32>(1.0f, 1.0f, 3.0f);
|
||||
TEST_F(ResolverTest, Expr_Constructor_Type_Vec2) {
|
||||
auto* tc = vec2<f32>(1.0f, 1.0f);
|
||||
WrapInFunction(tc);
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
ASSERT_NE(TypeOf(tc), nullptr);
|
||||
ASSERT_TRUE(TypeOf(tc)->Is<type::Vector>());
|
||||
EXPECT_TRUE(TypeOf(tc)->As<type::Vector>()->type()->Is<type::F32>());
|
||||
EXPECT_EQ(TypeOf(tc)->As<type::Vector>()->size(), 2u);
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, Expr_Constructor_Type_Vec3) {
|
||||
auto* tc = vec3<f32>(1.0f, 1.0f, 1.0f);
|
||||
WrapInFunction(tc);
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
@@ -590,6 +602,18 @@ TEST_F(ResolverTest, Expr_Constructor_Type) {
|
||||
EXPECT_EQ(TypeOf(tc)->As<type::Vector>()->size(), 3u);
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, Expr_Constructor_Type_Vec4) {
|
||||
auto* tc = vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
WrapInFunction(tc);
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
ASSERT_NE(TypeOf(tc), nullptr);
|
||||
ASSERT_TRUE(TypeOf(tc)->Is<type::Vector>());
|
||||
EXPECT_TRUE(TypeOf(tc)->As<type::Vector>()->type()->Is<type::F32>());
|
||||
EXPECT_EQ(TypeOf(tc)->As<type::Vector>()->size(), 4u);
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, Expr_Identifier_GlobalVariable) {
|
||||
auto* my_var = Global("my_var", ty.f32(), ast::StorageClass::kNone);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user