Test that entry point IO attributes are of valid types
BUG=tint:773 Change-Id: I94e8624647c645efe7ed558caa3d3bd05dd72f63 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/50260 Commit-Queue: Ryan Harrison <rharrison@chromium.org> Auto-Submit: Ryan Harrison <rharrison@chromium.org> Reviewed-by: Ben Clayton <bclayton@google.com> Reviewed-by: James Price <jrprice@google.com>
This commit is contained in:
parent
ff7a5f8dc9
commit
74490e118e
|
@ -23,13 +23,14 @@
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
|
namespace resolver {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class ResolverEntryPointValidationTest : public resolver::TestHelper,
|
class ResolverEntryPointValidationTest : public TestHelper,
|
||||||
public testing::Test {};
|
public testing::Test {};
|
||||||
|
|
||||||
TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Location) {
|
TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Location) {
|
||||||
// [[stage(vertex)]]
|
// [[stage(fragment)]]
|
||||||
// fn main() -> [[location(0)]] f32 { return 1.0; }
|
// fn main() -> [[location(0)]] f32 { return 1.0; }
|
||||||
Func(Source{{12, 34}}, "main", {}, ty.f32(), {Return(1.0f)},
|
Func(Source{{12, 34}}, "main", {}, ty.f32(), {Return(1.0f)},
|
||||||
{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
|
{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
|
||||||
|
@ -514,5 +515,118 @@ TEST_F(ResolverEntryPointValidationTest,
|
||||||
"in its return type");
|
"in its return type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace TypeValidationTests {
|
||||||
|
struct Params {
|
||||||
|
create_ast_type_func_ptr create_ast_type;
|
||||||
|
bool is_valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
using TypeValidationTest = resolver::ResolverTestWithParam<Params>;
|
||||||
|
|
||||||
|
static constexpr Params cases[] = {
|
||||||
|
{ast_f32, true},
|
||||||
|
{ast_i32, true},
|
||||||
|
{ast_u32, true},
|
||||||
|
{ast_bool, false},
|
||||||
|
{ast_vec2<ast_f32>, true},
|
||||||
|
{ast_vec3<ast_f32>, true},
|
||||||
|
{ast_vec4<ast_f32>, true},
|
||||||
|
{ast_mat2x2<ast_f32>, false},
|
||||||
|
{ast_mat2x2<ast_i32>, false},
|
||||||
|
{ast_mat2x2<ast_u32>, false},
|
||||||
|
{ast_mat2x2<ast_bool>, false},
|
||||||
|
{ast_mat3x3<ast_f32>, false},
|
||||||
|
{ast_mat3x3<ast_i32>, false},
|
||||||
|
{ast_mat3x3<ast_u32>, false},
|
||||||
|
{ast_mat3x3<ast_bool>, false},
|
||||||
|
{ast_mat4x4<ast_f32>, false},
|
||||||
|
{ast_mat4x4<ast_i32>, false},
|
||||||
|
{ast_mat4x4<ast_u32>, false},
|
||||||
|
{ast_mat4x4<ast_bool>, false},
|
||||||
|
{ast_alias<ast_f32>, true},
|
||||||
|
{ast_alias<ast_i32>, true},
|
||||||
|
{ast_alias<ast_u32>, true},
|
||||||
|
{ast_alias<ast_bool>, false},
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_P(TypeValidationTest, BareInputs) {
|
||||||
|
// [[stage(fragment)]]
|
||||||
|
// fn main([[location(0)]] a : *) {}
|
||||||
|
auto params = GetParam();
|
||||||
|
auto* a = Param("a", params.create_ast_type(ty), {Location(0)});
|
||||||
|
Func(Source{{12, 34}}, "main", {a}, ty.void_(), {},
|
||||||
|
{Stage(ast::PipelineStage::kFragment)});
|
||||||
|
|
||||||
|
if (params.is_valid) {
|
||||||
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
} else {
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(TypeValidationTest, StructInputs) {
|
||||||
|
// struct Input {
|
||||||
|
// [[location(0)]] a : *;
|
||||||
|
// };
|
||||||
|
// [[stage(fragment)]]
|
||||||
|
// fn main(a : Input) {}
|
||||||
|
auto params = GetParam();
|
||||||
|
auto* input = Structure(
|
||||||
|
"Input", {Member("a", params.create_ast_type(ty), {Location(0)})});
|
||||||
|
auto* a = Param("a", input, {});
|
||||||
|
Func(Source{{12, 34}}, "main", {a}, ty.void_(), {},
|
||||||
|
{Stage(ast::PipelineStage::kFragment)});
|
||||||
|
|
||||||
|
if (params.is_valid) {
|
||||||
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
} else {
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(TypeValidationTest, BareOutputs) {
|
||||||
|
// [[stage(fragment)]]
|
||||||
|
// fn main() -> [[location(0)]] * {
|
||||||
|
// return *();
|
||||||
|
// }
|
||||||
|
auto params = GetParam();
|
||||||
|
Func(Source{{12, 34}}, "main", {}, params.create_ast_type(ty),
|
||||||
|
{Return(Construct(params.create_ast_type(ty)))},
|
||||||
|
{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
|
||||||
|
|
||||||
|
if (params.is_valid) {
|
||||||
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
} else {
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(TypeValidationTest, StructOutputs) {
|
||||||
|
// struct Output {
|
||||||
|
// [[location(0)]] a : *;
|
||||||
|
// };
|
||||||
|
// [[stage(fragment)]]
|
||||||
|
// fn main() -> Output {
|
||||||
|
// return Output();
|
||||||
|
// }
|
||||||
|
auto params = GetParam();
|
||||||
|
auto* output = Structure(
|
||||||
|
"Output", {Member("a", params.create_ast_type(ty), {Location(0)})});
|
||||||
|
Func(Source{{12, 34}}, "main", {}, output, {Return(Construct(output))},
|
||||||
|
{Stage(ast::PipelineStage::kFragment)});
|
||||||
|
|
||||||
|
if (params.is_valid) {
|
||||||
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
} else {
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
INSTANTIATE_TEST_SUITE_P(ResolverEntryPointValidationTest,
|
||||||
|
TypeValidationTest,
|
||||||
|
testing::ValuesIn(cases));
|
||||||
|
|
||||||
|
} // namespace TypeValidationTests
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
} // namespace resolver
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
|
|
|
@ -832,6 +832,19 @@ bool Resolver::ValidateEntryPoint(const ast::Function* func,
|
||||||
diagnostics_.add_error(err, source);
|
diagnostics_.add_error(err, source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that all user defined attributes are numeric scalars, vectors
|
||||||
|
// of numeric scalars.
|
||||||
|
// Testing for being a struct is handled by the if portion above.
|
||||||
|
if (!pipeline_io_attribute->Is<ast::BuiltinDecoration>()) {
|
||||||
|
if (!Canonical(ty)->is_numeric_scalar_or_vector()) {
|
||||||
|
diagnostics_.add_error(
|
||||||
|
"User defined entry point IO types must be a numeric scalar, "
|
||||||
|
"a numeric vector, or a structure",
|
||||||
|
source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -132,6 +132,15 @@ bool Type::is_bool_scalar_or_vector() const {
|
||||||
return Is<Bool>() || is_bool_vector();
|
return Is<Bool>() || is_bool_vector();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Type::is_numeric_vector() const {
|
||||||
|
return Is<Vector>(
|
||||||
|
[](const Vector* v) { return v->type()->is_numeric_scalar(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Type::is_numeric_scalar_or_vector() const {
|
||||||
|
return is_numeric_scalar() || is_numeric_vector();
|
||||||
|
}
|
||||||
|
|
||||||
bool Type::is_handle() const {
|
bool Type::is_handle() const {
|
||||||
return IsAnyOf<Sampler, Texture>();
|
return IsAnyOf<Sampler, Texture>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,6 +125,10 @@ class Type : public Castable<Type, Node> {
|
||||||
bool is_bool_vector() const;
|
bool is_bool_vector() const;
|
||||||
/// @returns true if this type is boolean scalar or vector
|
/// @returns true if this type is boolean scalar or vector
|
||||||
bool is_bool_scalar_or_vector() const;
|
bool is_bool_scalar_or_vector() const;
|
||||||
|
/// @returns true if this type is a numeric vector
|
||||||
|
bool is_numeric_vector() const;
|
||||||
|
/// @returns true if this type is a numeric scale or vector
|
||||||
|
bool is_numeric_scalar_or_vector() const;
|
||||||
/// @returns true if this type is a handle type
|
/// @returns true if this type is a handle type
|
||||||
bool is_handle() const;
|
bool is_handle() const;
|
||||||
|
|
||||||
|
|
|
@ -508,13 +508,13 @@ TEST_F(CanonicalizeEntryPointIOTest, SortedMembers) {
|
||||||
struct VertexOutput {
|
struct VertexOutput {
|
||||||
[[location(1)]] b : u32;
|
[[location(1)]] b : u32;
|
||||||
[[builtin(position)]] pos : vec4<f32>;
|
[[builtin(position)]] pos : vec4<f32>;
|
||||||
[[location(3)]] d : bool;
|
[[location(3)]] d : u32;
|
||||||
[[location(0)]] a : f32;
|
[[location(0)]] a : f32;
|
||||||
[[location(2)]] c : i32;
|
[[location(2)]] c : i32;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FragmentInputExtra {
|
struct FragmentInputExtra {
|
||||||
[[location(3)]] d : bool;
|
[[location(3)]] d : u32;
|
||||||
[[builtin(position)]] pos : vec4<f32>;
|
[[builtin(position)]] pos : vec4<f32>;
|
||||||
[[location(0)]] a : f32;
|
[[location(0)]] a : f32;
|
||||||
};
|
};
|
||||||
|
@ -536,13 +536,13 @@ fn frag_main([[builtin(front_facing)]] ff : bool,
|
||||||
struct VertexOutput {
|
struct VertexOutput {
|
||||||
b : u32;
|
b : u32;
|
||||||
pos : vec4<f32>;
|
pos : vec4<f32>;
|
||||||
d : bool;
|
d : u32;
|
||||||
a : f32;
|
a : f32;
|
||||||
c : i32;
|
c : i32;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FragmentInputExtra {
|
struct FragmentInputExtra {
|
||||||
d : bool;
|
d : u32;
|
||||||
pos : vec4<f32>;
|
pos : vec4<f32>;
|
||||||
a : f32;
|
a : f32;
|
||||||
};
|
};
|
||||||
|
@ -555,7 +555,7 @@ struct tint_symbol {
|
||||||
[[location(2)]]
|
[[location(2)]]
|
||||||
c : i32;
|
c : i32;
|
||||||
[[location(3)]]
|
[[location(3)]]
|
||||||
d : bool;
|
d : u32;
|
||||||
[[builtin(position)]]
|
[[builtin(position)]]
|
||||||
pos : vec4<f32>;
|
pos : vec4<f32>;
|
||||||
};
|
};
|
||||||
|
@ -574,7 +574,7 @@ struct tint_symbol_3 {
|
||||||
[[location(2)]]
|
[[location(2)]]
|
||||||
c : i32;
|
c : i32;
|
||||||
[[location(3)]]
|
[[location(3)]]
|
||||||
d : bool;
|
d : u32;
|
||||||
[[builtin(position)]]
|
[[builtin(position)]]
|
||||||
pos : vec4<f32>;
|
pos : vec4<f32>;
|
||||||
[[builtin(front_facing)]]
|
[[builtin(front_facing)]]
|
||||||
|
|
Loading…
Reference in New Issue