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>
diff --git a/src/ast/module_clone_test.cc b/src/ast/module_clone_test.cc index 4e3fea6..87710e7 100644 --- a/src/ast/module_clone_test.cc +++ b/src/ast/module_clone_test.cc
@@ -65,7 +65,7 @@ var l0 : i32 = 3; var l1 : f32 = 8.0; var l2 : u32 = bitcast<u32>(4); - var l3 : vec2<u32> = vec2<u32>(l0, l1); + var l3 : vec2<u32> = vec2<u32>(u32(l0), u32(l1)); var l4 : S; var l5 : u32 = l4.m1[5]; var l6 : ptr<private, u32>;
diff --git a/src/reader/wgsl/parser_test.cc b/src/reader/wgsl/parser_test.cc index d75a742..b63c557 100644 --- a/src/reader/wgsl/parser_test.cc +++ b/src/reader/wgsl/parser_test.cc
@@ -38,7 +38,7 @@ [[stage(vertex)]] fn main() -> void { - gl_FragColor = vec4<f32>(.4, .2, .3, 1); + gl_FragColor = vec4<f32>(.4, .2, .3, 1.); } )"); auto program = Parse(&file);
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc index e1f1301..8b27d47 100644 --- a/src/resolver/resolver.cc +++ b/src/resolver/resolver.cc
@@ -597,16 +597,97 @@ } 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; }
diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h index 485bff8..ddf7c11 100644 --- a/src/resolver/resolver.h +++ b/src/resolver/resolver.h
@@ -177,6 +177,8 @@ 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*);
diff --git a/src/resolver/resolver_test.cc b/src/resolver/resolver_test.cc index 66bf2af..caf608e 100644 --- a/src/resolver/resolver_test.cc +++ b/src/resolver/resolver_test.cc
@@ -578,8 +578,20 @@ 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 @@ 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);
diff --git a/src/resolver/validation_test.cc b/src/resolver/validation_test.cc index 02965fe..9dc2e0d 100644 --- a/src/resolver/validation_test.cc +++ b/src/resolver/validation_test.cc
@@ -373,7 +373,7 @@ } TEST_F( - ResolverTest, + ResolverValidationTest, Stmt_Loop_ContinueInLoopBodySubscopeBeforeDecl_UsageInContinuingSubscope) { // loop { // if (true) { @@ -624,6 +624,1046 @@ "decorations"); } +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec2F32_Error_ScalarArgumentTypeMismatch) { + auto* tc = vec2<f32>( + create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1)), + 1.0f); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: type in vector constructor does not match vector " + "type: expected 'f32', found 'i32'"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec2U32_Error_ScalarArgumentTypeMismatch) { + auto* tc = vec2<u32>(1u, create<ast::ScalarConstructorExpression>( + Source{{12, 34}}, Literal(1))); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: type in vector constructor does not match vector " + "type: expected 'u32', found 'i32'"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec2I32_Error_ScalarArgumentTypeMismatch) { + auto* tc = vec2<i32>( + create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1u)), + 1); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: type in vector constructor does not match vector " + "type: expected 'i32', found 'u32'"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec2Bool_Error_ScalarArgumentTypeMismatch) { + auto* tc = vec2<bool>(true, create<ast::ScalarConstructorExpression>( + Source{{12, 34}}, Literal(1))); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: type in vector constructor does not match vector " + "type: expected 'bool', found 'i32'"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec2_Error_Vec3ArgumentCardinalityTooLarge) { + auto* tc = vec2<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.vec3<f32>(), ExprList())); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec2<f32>' with 3 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec2_Error_Vec4ArgumentCardinalityTooLarge) { + auto* tc = vec2<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.vec4<f32>(), ExprList())); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec2<f32>' with 4 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec2_Error_TooFewArgumentsScalar) { + auto* tc = vec2<f32>(create<ast::ScalarConstructorExpression>( + Source{{12, 34}}, Literal(1.0f))); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec2<f32>' with 1 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec2_Error_TooManyArgumentsScalar) { + auto* tc = vec2<f32>( + create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1.0f)), + create<ast::ScalarConstructorExpression>(Source{{12, 40}}, Literal(1.0f)), + create<ast::ScalarConstructorExpression>(Source{{12, 46}}, + Literal(1.0f))); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec2<f32>' with 3 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec2_Error_TooManyArgumentsVector) { + auto* tc = vec2<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.vec2<f32>(), ExprList()), + create<ast::TypeConstructorExpression>( + Source{{12, 40}}, ty.vec2<f32>(), ExprList())); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec2<f32>' with 4 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec2_Error_TooManyArgumentsVectorAndScalar) { + auto* tc = vec2<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.vec2<f32>(), ExprList()), + create<ast::ScalarConstructorExpression>( + Source{{12, 40}}, Literal(1.0f))); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec2<f32>' with 3 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec2_Error_InvalidConversionFromVec2Bool) { + SetSource(Source::Location({12, 34})); + + auto* tc = vec2<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.vec2<bool>(), ExprList())); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: type in vector constructor does not match vector " + "type: expected 'f32', found 'bool'"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec2_Error_InvalidArgumentType) { + auto* tc = vec2<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.mat2x2<f32>(), ExprList())); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: expected vector or scalar type in vector " + "constructor; found: mat2x2<f32>"); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vec2_Success_ZeroValue) { + auto* tc = vec2<f32>(); + 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(ResolverValidationTest, Expr_Constructor_Vec2F32_Success_Scalar) { + 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(ResolverValidationTest, Expr_Constructor_Vec2U32_Success_Scalar) { + auto* tc = vec2<u32>(1u, 1u); + 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::U32>()); + EXPECT_EQ(TypeOf(tc)->As<type::Vector>()->size(), 2u); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vec2I32_Success_Scalar) { + auto* tc = vec2<i32>(1, 1); + 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::I32>()); + EXPECT_EQ(TypeOf(tc)->As<type::Vector>()->size(), 2u); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vec2Bool_Success_Scalar) { + auto* tc = vec2<bool>(true, false); + 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::Bool>()); + EXPECT_EQ(TypeOf(tc)->As<type::Vector>()->size(), 2u); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vec2_Success_Identity) { + auto* tc = vec2<f32>(vec2<f32>()); + 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(ResolverValidationTest, + Expr_Constructor_Vec2_Success_Vec2TypeConversion) { + auto* tc = vec2<f32>(vec2<i32>()); + 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(ResolverValidationTest, + Expr_Constructor_Vec3F32_Error_ScalarArgumentTypeMismatch) { + auto* tc = vec3<f32>( + 1.0f, 1.0f, + create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1))); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: type in vector constructor does not match vector " + "type: expected 'f32', found 'i32'"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec3U32_Error_ScalarArgumentTypeMismatch) { + auto* tc = vec3<u32>( + 1u, + create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1)), + 1u); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: type in vector constructor does not match vector " + "type: expected 'u32', found 'i32'"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec3I32_Error_ScalarArgumentTypeMismatch) { + auto* tc = vec3<i32>( + 1, + create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1u)), + 1); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: type in vector constructor does not match vector " + "type: expected 'i32', found 'u32'"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec3Bool_Error_ScalarArgumentTypeMismatch) { + auto* tc = vec3<bool>( + true, + create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1)), + false); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: type in vector constructor does not match vector " + "type: expected 'bool', found 'i32'"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec3_Error_Vec4ArgumentCardinalityTooLarge) { + auto* tc = vec3<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.vec4<f32>(), ExprList())); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec3_Error_TooFewArgumentsScalar) { + auto* tc = vec3<f32>( + create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1.0f)), + create<ast::ScalarConstructorExpression>(Source{{12, 40}}, + Literal(1.0f))); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec3<f32>' with 2 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec3_Error_TooManyArgumentsScalar) { + auto* tc = vec3<f32>( + create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1.0f)), + create<ast::ScalarConstructorExpression>(Source{{12, 40}}, Literal(1.0f)), + create<ast::ScalarConstructorExpression>(Source{{12, 46}}, Literal(1.0f)), + create<ast::ScalarConstructorExpression>(Source{{12, 52}}, + Literal(1.0f))); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec3_Error_TooFewArgumentsVec2) { + auto* tc = vec3<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.vec2<f32>(), ExprList())); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec3<f32>' with 2 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec3_Error_TooManyArgumentsVec2) { + auto* tc = vec3<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.vec2<f32>(), ExprList()), + create<ast::TypeConstructorExpression>( + Source{{12, 40}}, ty.vec2<f32>(), ExprList())); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec3_Error_TooManyArgumentsVec2AndScalar) { + auto* tc = vec3<f32>( + create<ast::TypeConstructorExpression>(Source{{12, 34}}, ty.vec2<f32>(), + ExprList()), + create<ast::ScalarConstructorExpression>(Source{{12, 40}}, Literal(1.0f)), + create<ast::ScalarConstructorExpression>(Source{{12, 46}}, + Literal(1.0f))); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec3_Error_TooManyArgumentsVec3) { + auto* tc = vec3<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.vec3<f32>(), ExprList()), + create<ast::ScalarConstructorExpression>( + Source{{12, 40}}, Literal(1.0f))); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec3_Error_InvalidConversionFromVec3Bool) { + auto* tc = vec3<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.vec3<bool>(), ExprList())); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: type in vector constructor does not match vector " + "type: expected 'f32', found 'bool'"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec3_Error_InvalidArgumentType) { + auto* tc = vec3<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.mat2x2<f32>(), ExprList())); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: expected vector or scalar type in vector " + "constructor; found: mat2x2<f32>"); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vec3_Success_ZeroValue) { + auto* tc = vec3<f32>(); + 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(), 3u); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vec3F32_Success_Scalar) { + auto* tc = vec3<f32>(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(), 3u); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vec3U32_Success_Scalar) { + auto* tc = vec3<u32>(1u, 1u, 1u); + 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::U32>()); + EXPECT_EQ(TypeOf(tc)->As<type::Vector>()->size(), 3u); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vec3I32_Success_Scalar) { + auto* tc = vec3<i32>(1, 1, 1); + 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::I32>()); + EXPECT_EQ(TypeOf(tc)->As<type::Vector>()->size(), 3u); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vec3Bool_Success_Scalar) { + auto* tc = vec3<bool>(true, false, true); + 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::Bool>()); + EXPECT_EQ(TypeOf(tc)->As<type::Vector>()->size(), 3u); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vec3_Success_Vec2AndScalar) { + auto* tc = vec3<f32>(vec2<f32>(), 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(), 3u); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vec3_Success_ScalarAndVec2) { + auto* tc = vec3<f32>(1.0f, vec2<f32>()); + 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(), 3u); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vec3_Success_Identity) { + auto* tc = vec3<f32>(vec3<f32>()); + 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(), 3u); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec3_Success_Vec3TypeConversion) { + auto* tc = vec3<f32>(vec3<i32>()); + 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(), 3u); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec4F32_Error_ScalarArgumentTypeMismatch) { + auto* tc = vec4<f32>( + 1.0f, 1.0f, + create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1)), + 1.0f); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: type in vector constructor does not match vector " + "type: expected 'f32', found 'i32'"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec4U32_Error_ScalarArgumentTypeMismatch) { + auto* tc = vec4<u32>( + 1u, 1u, + create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1)), + 1u); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: type in vector constructor does not match vector " + "type: expected 'u32', found 'i32'"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec4I32_Error_ScalarArgumentTypeMismatch) { + auto* tc = vec4<i32>( + 1, 1, + create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1u)), + 1); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: type in vector constructor does not match vector " + "type: expected 'i32', found 'u32'"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec4Bool_Error_ScalarArgumentTypeMismatch) { + auto* tc = vec4<bool>( + true, false, + create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1)), + true); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: type in vector constructor does not match vector " + "type: expected 'bool', found 'i32'"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec4_Error_TooFewArgumentsScalar) { + auto* tc = vec4<f32>( + create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1.0f)), + create<ast::ScalarConstructorExpression>(Source{{12, 40}}, Literal(1.0f)), + create<ast::ScalarConstructorExpression>(Source{{12, 46}}, + Literal(1.0f))); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec4<f32>' with 3 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec4_Error_TooManyArgumentsScalar) { + auto* tc = vec4<f32>( + create<ast::ScalarConstructorExpression>(Source{{12, 34}}, Literal(1.0f)), + create<ast::ScalarConstructorExpression>(Source{{12, 40}}, Literal(1.0f)), + create<ast::ScalarConstructorExpression>(Source{{12, 46}}, Literal(1.0f)), + create<ast::ScalarConstructorExpression>(Source{{12, 52}}, Literal(1.0f)), + create<ast::ScalarConstructorExpression>(Source{{12, 58}}, + Literal(1.0f))); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec4_Error_TooFewArgumentsVec2AndScalar) { + auto* tc = vec4<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.vec2<f32>(), ExprList()), + create<ast::ScalarConstructorExpression>( + Source{{12, 40}}, Literal(1.0f))); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec4<f32>' with 3 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec4_Error_TooManyArgumentsVec2AndScalars) { + auto* tc = vec4<f32>( + create<ast::TypeConstructorExpression>(Source{{12, 34}}, ty.vec2<f32>(), + ExprList()), + create<ast::ScalarConstructorExpression>(Source{{12, 40}}, Literal(1.0f)), + create<ast::ScalarConstructorExpression>(Source{{12, 46}}, Literal(1.0f)), + create<ast::ScalarConstructorExpression>(Source{{12, 52}}, + Literal(1.0f))); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec4_Error_TooManyArgumentsVec2Vec2Scalar) { + auto* tc = vec4<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.vec2<f32>(), ExprList()), + create<ast::TypeConstructorExpression>( + Source{{12, 40}}, ty.vec2<f32>(), ExprList()), + create<ast::ScalarConstructorExpression>( + Source{{12, 46}}, Literal(1.0f))); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec4_Error_TooManyArgumentsVec2Vec2Vec2) { + auto* tc = vec4<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.vec2<f32>(), ExprList()), + create<ast::TypeConstructorExpression>( + Source{{12, 40}}, ty.vec2<f32>(), ExprList()), + create<ast::TypeConstructorExpression>( + Source{{12, 40}}, ty.vec2<f32>(), ExprList())); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec4<f32>' with 6 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec4_Error_TooFewArgumentsVec3) { + auto* tc = vec4<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.vec3<f32>(), ExprList())); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec4<f32>' with 3 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec4_Error_TooManyArgumentsVec3AndScalars) { + auto* tc = vec4<f32>( + create<ast::TypeConstructorExpression>(Source{{12, 34}}, ty.vec3<f32>(), + ExprList()), + create<ast::ScalarConstructorExpression>(Source{{12, 40}}, Literal(1.0f)), + create<ast::ScalarConstructorExpression>(Source{{12, 46}}, + Literal(1.0f))); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec4_Error_TooManyArgumentsVec3AndVec2) { + auto* tc = vec4<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.vec3<f32>(), ExprList()), + create<ast::TypeConstructorExpression>( + Source{{12, 40}}, ty.vec2<f32>(), ExprList())); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec4_Error_TooManyArgumentsVec2AndVec3) { + auto* tc = vec4<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.vec2<f32>(), ExprList()), + create<ast::TypeConstructorExpression>( + Source{{12, 40}}, ty.vec3<f32>(), ExprList())); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec4_Error_TooManyArgumentsVec3AndVec3) { + auto* tc = vec4<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.vec3<f32>(), ExprList()), + create<ast::TypeConstructorExpression>( + Source{{12, 40}}, ty.vec3<f32>(), ExprList())); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec4<f32>' with 6 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec4_Error_InvalidConversionFromVec4Bool) { + auto* tc = vec4<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.vec4<bool>(), ExprList())); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: type in vector constructor does not match vector " + "type: expected 'f32', found 'bool'"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vec4_Error_InvalidArgumentType) { + auto* tc = vec4<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, ty.mat2x2<f32>(), ExprList())); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: expected vector or scalar type in vector " + "constructor; found: mat2x2<f32>"); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vec4_Success_ZeroValue) { + auto* tc = vec4<f32>(); + 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(ResolverValidationTest, Expr_Constructor_Vec4F32_Success_Scalar) { + 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(ResolverValidationTest, Expr_Constructor_Vec4U32_Success_Scalar) { + auto* tc = vec4<u32>(1u, 1u, 1u, 1u); + 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::U32>()); + EXPECT_EQ(TypeOf(tc)->As<type::Vector>()->size(), 4u); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vec4I32_Success_Scalar) { + auto* tc = vec4<i32>(1, 1, 1, 1); + 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::I32>()); + EXPECT_EQ(TypeOf(tc)->As<type::Vector>()->size(), 4u); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vec4Bool_Success_Scalar) { + auto* tc = vec4<bool>(true, false, true, false); + 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::Bool>()); + EXPECT_EQ(TypeOf(tc)->As<type::Vector>()->size(), 4u); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vec4_Success_Vec2ScalarScalar) { + auto* tc = vec4<f32>(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(), 4u); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vec4_Success_ScalarVec2Scalar) { + auto* tc = vec4<f32>(1.0f, vec2<f32>(), 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(ResolverValidationTest, Expr_Constructor_Vec4_Success_ScalarScalarVec2) { + auto* tc = vec4<f32>(1.0f, 1.0f, vec2<f32>()); + 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(ResolverValidationTest, Expr_Constructor_Vec4_Success_Vec2AndVec2) { + auto* tc = vec4<f32>(vec2<f32>(), vec2<f32>()); + 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(ResolverValidationTest, Expr_Constructor_Vec4_Success_Vec3AndScalar) { + auto* tc = vec4<f32>(vec3<f32>(), 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(ResolverValidationTest, Expr_Constructor_Vec4_Success_ScalarAndVec3) { + auto* tc = vec4<f32>(1.0f, vec3<f32>()); + 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(ResolverValidationTest, Expr_Constructor_Vec4_Success_Identity) { + auto* tc = vec4<f32>(vec4<f32>()); + 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(ResolverValidationTest, + Expr_Constructor_Vec4_Success_Vec4TypeConversion) { + auto* tc = vec4<f32>(vec4<i32>()); + 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(ResolverValidationTest, + Expr_Constructor_NestedVectorConstructors_InnerError) { + auto* tc = vec4<f32>( + vec3<f32>(1.0f, vec2<f32>(create<ast::ScalarConstructorExpression>( + Source{{12, 34}}, Literal(1.0f)))), + 1.0f); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ( + r()->error(), + "12:34 error: attempted to construct 'vec2<f32>' with 1 component(s)"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_NestedVectorConstructors_Success) { + auto* tc = vec4<f32>(vec3<f32>(vec2<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(ResolverValidationTest, Expr_Constructor_Vector_Alias_Argument_Error) { + auto* alias = ty.alias("UnsignedInt", ty.u32()); + Global("uint_var", alias, ast::StorageClass::kNone); + + auto* tc = vec2<f32>(Expr(Source{{12, 34}}, "uint_var")); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: type in vector constructor does not match vector " + "type: expected 'f32', found 'u32'"); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vector_Alias_Argument_Success) { + auto* f32_alias = ty.alias("Float32", ty.f32()); + auto* vec2_alias = ty.alias("VectorFloat2", ty.vec2<f32>()); + Global("my_f32", f32_alias, ast::StorageClass::kNone); + Global("my_vec2", vec2_alias, ast::StorageClass::kNone); + + auto* tc = vec3<f32>("my_vec2", "my_f32"); + WrapInFunction(tc); + EXPECT_TRUE(r()->Resolve()) << r()->error(); +} + +TEST_F(ResolverValidationTest, Expr_Constructor_Vector_ElementTypeAlias_Error) { + auto* f32_alias = ty.alias("Float32", ty.f32()); + auto* vec_type = create<type::Vector>(f32_alias, 2); + + // vec2<Float32>(1.0f, 1u) + auto* tc = create<ast::TypeConstructorExpression>( + Source{{12, 34}}, vec_type, + ExprList(1.0f, create<ast::ScalarConstructorExpression>(Source{{12, 40}}, + Literal(1u)))); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:40 error: type in vector constructor does not match vector " + "type: expected 'f32', found 'u32'"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vector_ElementTypeAlias_Success) { + auto* f32_alias = ty.alias("Float32", ty.f32()); + auto* vec_type = create<type::Vector>(f32_alias, 2); + + // vec2<Float32>(1.0f, 1.0f) + auto* tc = create<ast::TypeConstructorExpression>(Source{{12, 34}}, vec_type, + ExprList(1.0f, 1.0f)); + WrapInFunction(tc); + + EXPECT_TRUE(r()->Resolve()) << r()->error(); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vector_ArgumentElementTypeAlias_Error) { + auto* f32_alias = ty.alias("Float32", ty.f32()); + auto* vec_type = create<type::Vector>(f32_alias, 2); + + // vec3<u32>(vec<Float32>(), 1.0f) + auto* tc = vec3<u32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, vec_type, ExprList()), + 1.0f); + WrapInFunction(tc); + + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: type in vector constructor does not match vector " + "type: expected 'u32', found 'f32'"); +} + +TEST_F(ResolverValidationTest, + Expr_Constructor_Vector_ArgumentElementTypeAlias_Success) { + auto* f32_alias = ty.alias("Float32", ty.f32()); + auto* vec_type = create<type::Vector>(f32_alias, 2); + + // vec3<f32>(vec<Float32>(), 1.0f) + auto* tc = vec3<f32>(create<ast::TypeConstructorExpression>( + Source{{12, 34}}, vec_type, ExprList()), + 1.0f); + WrapInFunction(tc); + + EXPECT_TRUE(r()->Resolve()) << r()->error(); +} + } // namespace } // namespace resolver } // namespace tint
diff --git a/src/transform/hlsl_test.cc b/src/transform/hlsl_test.cc index 70fdfb3..82c732d 100644 --- a/src/transform/hlsl_test.cc +++ b/src/transform/hlsl_test.cc
@@ -108,7 +108,7 @@ [[stage(vertex)]] fn main() -> void { const transform : mat2x2<f32> = ubo.transform; - var coord : array<vec2<f32>, 3> = array<vec2<f32>, 3>( + var coord : vec2<f32> = array<vec2<f32>, 3>( vec2<f32>(-1.0, 1.0), vec2<f32>( 1.0, 1.0), vec2<f32>(-1.0, -1.0) @@ -133,7 +133,7 @@ fn main() -> void { const transform : mat2x2<f32> = ubo.transform; const tint_symbol_1 : array<vec2<f32>, 3> = array<vec2<f32>, 3>(vec2<f32>(-1.0, 1.0), vec2<f32>(1.0, 1.0), vec2<f32>(-1.0, -1.0)); - var coord : array<vec2<f32>, 3> = tint_symbol_1[vertex_index]; + var coord : vec2<f32> = tint_symbol_1[vertex_index]; position = vec4<f32>((transform * coord), 0.0, 1.0); } )";
diff --git a/src/validator/validator_builtins_test.cc b/src/validator/validator_builtins_test.cc index c65393b..fa9aede 100644 --- a/src/validator/validator_builtins_test.cc +++ b/src/validator/validator_builtins_test.cc
@@ -509,7 +509,7 @@ ast::ExpressionList params; for (uint32_t i = 0; i < num_params; ++i) { - params.push_back(vec2<uint32_t>(1, 1)); + params.push_back(vec2<uint32_t>(1u, 1u)); } auto* builtin = Call(name, params); WrapInFunction(builtin); @@ -526,7 +526,7 @@ ast::ExpressionList params; for (uint32_t i = 0; i < num_params; ++i) { - params.push_back(vec3<uint32_t>(1, 1, 1)); + params.push_back(vec3<uint32_t>(1u, 1u, 1u)); } auto* builtin = Call(name, params); WrapInFunction(builtin); @@ -543,7 +543,7 @@ ast::ExpressionList params; for (uint32_t i = 0; i < num_params; ++i) { - params.push_back(vec4<uint32_t>(1, 1, 1, 1)); + params.push_back(vec4<uint32_t>(1u, 1u, 1u, 1u)); } auto* builtin = Call(name, params); WrapInFunction(builtin);
diff --git a/src/writer/spirv/builder_constructor_expression_test.cc b/src/writer/spirv/builder_constructor_expression_test.cc index 6a11cb0..e11f4ea 100644 --- a/src/writer/spirv/builder_constructor_expression_test.cc +++ b/src/writer/spirv/builder_constructor_expression_test.cc
@@ -141,7 +141,7 @@ } TEST_F(SpvBuilderConstructorTest, Vector_Bitcast_Params) { - auto* t = vec2<u32>(1, 1); + auto* t = vec2<u32>(Construct<u32>(1), Construct<u32>(1)); WrapInFunction(t); spirv::Builder& b = Build(); @@ -153,14 +153,14 @@ EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 0 %1 = OpTypeVector %2 2 -%3 = OpTypeInt 32 1 -%4 = OpConstant %3 1 +%4 = OpTypeInt 32 1 +%5 = OpConstant %4 1 )"); EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), - R"(%5 = OpBitcast %2 %4 -%6 = OpBitcast %2 %4 -%7 = OpCompositeConstruct %1 %5 %6 + R"(%3 = OpBitcast %2 %5 +%6 = OpBitcast %2 %5 +%7 = OpCompositeConstruct %1 %3 %6 )"); } @@ -1450,7 +1450,7 @@ TEST_F(SpvBuilderConstructorTest, IsConstructorConst_GlobalVectorWithMatchingTypeConstructors) { - // vec3<f32>(f32(1.0), f32(2.0)) -> false + // vec2<f32>(f32(1.0), f32(2.0)) -> false auto* t = vec2<f32>(Construct<f32>(1.f), Construct<f32>(2.f)); WrapInFunction(t); @@ -1463,7 +1463,7 @@ TEST_F(SpvBuilderConstructorTest, IsConstructorConst_GlobalWithTypeCastConstructor) { - // vec3<f32>(f32(1), f32(2)) -> false + // vec2<f32>(f32(1), f32(2)) -> false auto* t = vec2<f32>(Construct<f32>(1), Construct<f32>(2)); WrapInFunction(t); @@ -1521,22 +1521,10 @@ } TEST_F(SpvBuilderConstructorTest, - IsConstructorConst_VectorWith_TypeCastConstConstructors) { + IsConstructorConst_VectorWithTypeCastConstConstructors) { // vec2<f32>(f32(1), f32(2)) -> false - auto* t = vec2<f32>(1, 2); - WrapInFunction(t); - - spirv::Builder& b = Build(); - - EXPECT_FALSE(b.is_constructor_const(t, false)); - EXPECT_FALSE(b.has_error()); -} - -TEST_F(SpvBuilderConstructorTest, IsConstructorConst_WithTypeCastConstructor) { - // vec3<f32>(f32(1), f32(2)) -> false - - auto* t = vec3<f32>(1, 2); + auto* t = vec2<f32>(Construct<f32>(1), Construct<f32>(2)); WrapInFunction(t); spirv::Builder& b = Build(); @@ -1546,7 +1534,7 @@ } TEST_F(SpvBuilderConstructorTest, IsConstructorConst_BitCastScalars) { - auto* t = vec2<u32>(1, 1); + auto* t = vec2<u32>(Construct<u32>(1), Construct<u32>(1)); WrapInFunction(t); spirv::Builder& b = Build();