tint: Extract intrinsic-table common type to helper
• Add sem::Type::Common() which returns the 'common' type for the list of
types.
• Migrate intrisnic table to use this.
• Add a whole-lotta-tests.
• Deduplicate and improve the EXPECT_TEST() macro. Move it to a common home.
Bug: tint:1504
Change-Id: I1564f67ecf87fc594f3f54274da906ff0d822795
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/91020
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/tint/resolver/intrinsic_table.cc b/src/tint/resolver/intrinsic_table.cc
index 0f20bcd..6c19f7c 100644
--- a/src/tint/resolver/intrinsic_table.cc
+++ b/src/tint/resolver/intrinsic_table.cc
@@ -124,16 +124,11 @@
if (existing == ty) {
return ty;
}
- if (sem::Type::ConversionRank(ty, existing) != sem::Type::kNoConversion) {
- // ty can be converted to the existing type. Keep the existing type.
- return existing;
+ ty = sem::Type::Common({existing, ty});
+ if (ty) {
+ res.first->second = ty;
}
- if (sem::Type::ConversionRank(existing, ty) != sem::Type::kNoConversion) {
- // template type can be converted to ty. Constrain the existing type.
- types_[idx] = ty;
- return ty;
- }
- return nullptr;
+ return ty;
}
/// If the number with index `idx` is undefined, then it is defined with the number `number` and
diff --git a/src/tint/resolver/intrinsic_table_test.cc b/src/tint/resolver/intrinsic_table_test.cc
index a72eb00..854be5f 100644
--- a/src/tint/resolver/intrinsic_table_test.cc
+++ b/src/tint/resolver/intrinsic_table_test.cc
@@ -27,21 +27,13 @@
#include "src/tint/sem/reference.h"
#include "src/tint/sem/sampled_texture.h"
#include "src/tint/sem/storage_texture.h"
+#include "src/tint/sem/test_helper.h"
#include "src/tint/sem/type_constructor.h"
#include "src/tint/sem/type_conversion.h"
namespace tint::resolver {
namespace {
-#define EXPECT_TYPE(GOT, EXPECT) \
- if ((GOT) != (EXPECT)) { \
- FAIL() << #GOT " != " #EXPECT "\n" \
- << " " #GOT ": " << NameOf(GOT) << "\n" \
- << " " #EXPECT ": " << NameOf(EXPECT); \
- } \
- do { \
- } while (false)
-
using ::testing::HasSubstr;
using BuiltinType = sem::BuiltinType;
@@ -830,13 +822,8 @@
builder::sem_type_func_ptr arg_rhs;
};
-class IntrinsicTableAbstractBinaryTest : public testing::TestWithParam<Case>,
- public ProgramBuilder {
- public:
- std::string NameOf(const sem::Type* type) {
- return type ? type->FriendlyName(Symbols()) : "<null>";
- }
-
+struct IntrinsicTableAbstractBinaryTest : public testing::TestWithParam<Case>,
+ public ProgramBuilder {
std::unique_ptr<IntrinsicTable> table = IntrinsicTable::Create(*this);
};
@@ -1017,13 +1004,8 @@
builder::sem_type_func_ptr arg_c;
};
-class IntrinsicTableAbstractTernaryTest : public testing::TestWithParam<Case>,
- public ProgramBuilder {
- public:
- std::string NameOf(const sem::Type* type) {
- return type ? type->FriendlyName(Symbols()) : "<null>";
- }
-
+struct IntrinsicTableAbstractTernaryTest : public testing::TestWithParam<Case>,
+ public ProgramBuilder {
std::unique_ptr<IntrinsicTable> table = IntrinsicTable::Create(*this);
};
diff --git a/src/tint/sem/test_helper.h b/src/tint/sem/test_helper.h
index 94f66e4..e1b4eb3 100644
--- a/src/tint/sem/test_helper.h
+++ b/src/tint/sem/test_helper.h
@@ -44,4 +44,16 @@
} // namespace tint::sem
+/// Helper macro for testing that a semantic type was as expected
+#define EXPECT_TYPE(GOT, EXPECT) \
+ do { \
+ const sem::Type* got = GOT; \
+ const sem::Type* expect = EXPECT; \
+ if (got != expect) { \
+ ADD_FAILURE() << #GOT " != " #EXPECT "\n" \
+ << " " #GOT ": " << FriendlyName(got) << "\n" \
+ << " " #EXPECT ": " << FriendlyName(expect); \
+ } \
+ } while (false)
+
#endif // SRC_TINT_SEM_TEST_HELPER_H_
diff --git a/src/tint/sem/type.cc b/src/tint/sem/type.cc
index df38a2b..40666e3 100644
--- a/src/tint/sem/type.cc
+++ b/src/tint/sem/type.cc
@@ -232,4 +232,26 @@
});
}
+const sem::Type* Type::Common(Type const* const* types, size_t count) {
+ if (count == 0) {
+ return nullptr;
+ }
+ const auto* common = types[0];
+ for (size_t i = 1; i < count; i++) {
+ auto* ty = types[i];
+ if (ty == common) {
+ continue; // ty == common
+ }
+ if (sem::Type::ConversionRank(ty, common) != sem::Type::kNoConversion) {
+ continue; // ty can be converted to common.
+ }
+ if (sem::Type::ConversionRank(common, ty) != sem::Type::kNoConversion) {
+ common = ty; // common can be converted to ty.
+ continue;
+ }
+ return nullptr; // Conversion is not valid.
+ }
+ return common;
+}
+
} // namespace tint::sem
diff --git a/src/tint/sem/type.h b/src/tint/sem/type.h
index 2a7ad96..9987637 100644
--- a/src/tint/sem/type.h
+++ b/src/tint/sem/type.h
@@ -135,6 +135,21 @@
/// or array, otherwise nullptr.
static const Type* ElementOf(const Type* ty, uint32_t* count = nullptr);
+ /// @param types a pointer to a list of `const Type*`.
+ /// @param count the number of types in `types`.
+ /// @returns the lowest-ranking type that all types in `types` can be implicitly converted to,
+ /// or nullptr if there is no consistent common type across all types in `types`.
+ /// @see https://www.w3.org/TR/WGSL/#conversion-rank
+ static const sem::Type* Common(Type const* const* types, size_t count);
+
+ /// @param types an initializer_list of `const Type*`.
+ /// @returns the lowest-ranking type that all types in `types` can be implicitly converted to,
+ /// or nullptr if there is no consistent common type across all types in `types`.
+ /// @see https://www.w3.org/TR/WGSL/#conversion-rank
+ static const sem::Type* Common(std::initializer_list<const Type*> types) {
+ return Common(types.begin(), types.size());
+ }
+
protected:
Type();
};
diff --git a/src/tint/sem/type_test.cc b/src/tint/sem/type_test.cc
index 62fc82f..c11efea 100644
--- a/src/tint/sem/type_test.cc
+++ b/src/tint/sem/type_test.cc
@@ -91,16 +91,6 @@
EXPECT_EQ(Type::ConversionRank(f16, ai), Type::kNoConversion);
}
-/// Helper macro for testing that a semantic type was as expected
-#define EXPECT_TYPE(GOT, EXPECT) \
- if ((GOT) != (EXPECT)) { \
- FAIL() << #GOT " != " #EXPECT "\n" \
- << " " #GOT ": " << FriendlyName(GOT) << "\n" \
- << " " #EXPECT ": " << FriendlyName(EXPECT); \
- } \
- do { \
- } while (false)
-
TEST_F(TypeTest, ElementOf) {
auto* f32 = create<F32>();
auto* f16 = create<F16>();
@@ -179,5 +169,225 @@
EXPECT_EQ(count, 5u);
}
+TEST_F(TypeTest, Common2) {
+ auto* ai = create<AbstractInt>();
+ auto* af = create<AbstractFloat>();
+ auto* f32 = create<F32>();
+ auto* f16 = create<F16>();
+ auto* i32 = create<I32>();
+ auto* u32 = create<U32>();
+
+ EXPECT_TYPE(Type::Common({ai, ai}), ai);
+ EXPECT_TYPE(Type::Common({af, af}), af);
+ EXPECT_TYPE(Type::Common({f32, f32}), f32);
+ EXPECT_TYPE(Type::Common({f16, f16}), f16);
+ EXPECT_TYPE(Type::Common({i32, i32}), i32);
+ EXPECT_TYPE(Type::Common({u32, u32}), u32);
+
+ EXPECT_TYPE(Type::Common({i32, u32}), nullptr);
+ EXPECT_TYPE(Type::Common({u32, f32}), nullptr);
+ EXPECT_TYPE(Type::Common({f32, f16}), nullptr);
+ EXPECT_TYPE(Type::Common({f16, i32}), nullptr);
+
+ EXPECT_TYPE(Type::Common({ai, af}), af);
+ EXPECT_TYPE(Type::Common({ai, f32}), f32);
+ EXPECT_TYPE(Type::Common({ai, f16}), f16);
+ EXPECT_TYPE(Type::Common({ai, i32}), i32);
+ EXPECT_TYPE(Type::Common({ai, u32}), u32);
+
+ EXPECT_TYPE(Type::Common({af, ai}), af);
+ EXPECT_TYPE(Type::Common({f32, ai}), f32);
+ EXPECT_TYPE(Type::Common({f16, ai}), f16);
+ EXPECT_TYPE(Type::Common({i32, ai}), i32);
+ EXPECT_TYPE(Type::Common({u32, ai}), u32);
+
+ EXPECT_TYPE(Type::Common({ai, af}), af);
+ EXPECT_TYPE(Type::Common({f32, af}), f32);
+ EXPECT_TYPE(Type::Common({f16, af}), f16);
+ EXPECT_TYPE(Type::Common({i32, af}), nullptr);
+ EXPECT_TYPE(Type::Common({u32, af}), nullptr);
+
+ EXPECT_TYPE(Type::Common({af, ai}), af);
+ EXPECT_TYPE(Type::Common({af, f32}), f32);
+ EXPECT_TYPE(Type::Common({af, f16}), f16);
+ EXPECT_TYPE(Type::Common({af, i32}), nullptr);
+ EXPECT_TYPE(Type::Common({af, u32}), nullptr);
+
+ auto* vec3_ai = create<Vector>(ai, 3u);
+ auto* vec3_af = create<Vector>(af, 3u);
+ auto* vec3_f32 = create<Vector>(f32, 3u);
+ auto* vec3_f16 = create<Vector>(f16, 3u);
+ auto* vec4_f32 = create<Vector>(f32, 4u);
+ auto* vec3_u32 = create<Vector>(u32, 3u);
+ auto* vec3_i32 = create<Vector>(i32, 3u);
+
+ EXPECT_TYPE(Type::Common({vec3_ai, vec3_ai}), vec3_ai);
+ EXPECT_TYPE(Type::Common({vec3_af, vec3_af}), vec3_af);
+ EXPECT_TYPE(Type::Common({vec3_f32, vec3_f32}), vec3_f32);
+ EXPECT_TYPE(Type::Common({vec3_f16, vec3_f16}), vec3_f16);
+ EXPECT_TYPE(Type::Common({vec4_f32, vec4_f32}), vec4_f32);
+ EXPECT_TYPE(Type::Common({vec3_u32, vec3_u32}), vec3_u32);
+ EXPECT_TYPE(Type::Common({vec3_i32, vec3_i32}), vec3_i32);
+
+ EXPECT_TYPE(Type::Common({vec3_ai, vec3_f32}), vec3_f32);
+ EXPECT_TYPE(Type::Common({vec3_ai, vec3_f16}), vec3_f16);
+ EXPECT_TYPE(Type::Common({vec3_ai, vec4_f32}), nullptr);
+ EXPECT_TYPE(Type::Common({vec3_ai, vec3_u32}), vec3_u32);
+ EXPECT_TYPE(Type::Common({vec3_ai, vec3_i32}), vec3_i32);
+
+ EXPECT_TYPE(Type::Common({vec3_f32, vec3_ai}), vec3_f32);
+ EXPECT_TYPE(Type::Common({vec3_f16, vec3_ai}), vec3_f16);
+ EXPECT_TYPE(Type::Common({vec4_f32, vec3_ai}), nullptr);
+ EXPECT_TYPE(Type::Common({vec3_u32, vec3_ai}), vec3_u32);
+ EXPECT_TYPE(Type::Common({vec3_i32, vec3_ai}), vec3_i32);
+
+ EXPECT_TYPE(Type::Common({vec3_af, vec3_f32}), vec3_f32);
+ EXPECT_TYPE(Type::Common({vec3_af, vec3_f16}), vec3_f16);
+ EXPECT_TYPE(Type::Common({vec3_af, vec4_f32}), nullptr);
+ EXPECT_TYPE(Type::Common({vec3_af, vec3_u32}), nullptr);
+ EXPECT_TYPE(Type::Common({vec3_af, vec3_i32}), nullptr);
+
+ EXPECT_TYPE(Type::Common({vec3_f32, vec3_af}), vec3_f32);
+ EXPECT_TYPE(Type::Common({vec3_f16, vec3_af}), vec3_f16);
+ EXPECT_TYPE(Type::Common({vec4_f32, vec3_af}), nullptr);
+ EXPECT_TYPE(Type::Common({vec3_u32, vec3_af}), nullptr);
+ EXPECT_TYPE(Type::Common({vec3_i32, vec3_af}), nullptr);
+
+ auto* mat4x3_af = create<Matrix>(vec3_af, 4u);
+ auto* mat3x4_f32 = create<Matrix>(vec4_f32, 3u);
+ auto* mat4x3_f32 = create<Matrix>(vec3_f32, 4u);
+ auto* mat4x3_f16 = create<Matrix>(vec3_f16, 4u);
+
+ EXPECT_TYPE(Type::Common({mat4x3_af, mat4x3_af}), mat4x3_af);
+ EXPECT_TYPE(Type::Common({mat3x4_f32, mat3x4_f32}), mat3x4_f32);
+ EXPECT_TYPE(Type::Common({mat4x3_f32, mat4x3_f32}), mat4x3_f32);
+ EXPECT_TYPE(Type::Common({mat4x3_f16, mat4x3_f16}), mat4x3_f16);
+
+ EXPECT_TYPE(Type::Common({mat4x3_af, mat3x4_f32}), nullptr);
+ EXPECT_TYPE(Type::Common({mat4x3_af, mat4x3_f32}), mat4x3_f32);
+ EXPECT_TYPE(Type::Common({mat4x3_af, mat4x3_f16}), mat4x3_f16);
+
+ EXPECT_TYPE(Type::Common({mat3x4_f32, mat4x3_af}), nullptr);
+ EXPECT_TYPE(Type::Common({mat4x3_f32, mat4x3_af}), mat4x3_f32);
+ EXPECT_TYPE(Type::Common({mat4x3_f16, mat4x3_af}), mat4x3_f16);
+}
+
+TEST_F(TypeTest, Common3) {
+ auto* ai = create<AbstractInt>();
+ auto* af = create<AbstractFloat>();
+ auto* f32 = create<F32>();
+ auto* f16 = create<F16>();
+ auto* i32 = create<I32>();
+ auto* u32 = create<U32>();
+
+ EXPECT_TYPE(Type::Common({ai, ai, ai}), ai);
+ EXPECT_TYPE(Type::Common({af, af, af}), af);
+ EXPECT_TYPE(Type::Common({f32, f32, f32}), f32);
+ EXPECT_TYPE(Type::Common({f16, f16, f16}), f16);
+ EXPECT_TYPE(Type::Common({i32, i32, i32}), i32);
+ EXPECT_TYPE(Type::Common({u32, u32, u32}), u32);
+
+ EXPECT_TYPE(Type::Common({ai, af, ai}), af);
+ EXPECT_TYPE(Type::Common({ai, f32, ai}), f32);
+ EXPECT_TYPE(Type::Common({ai, f16, ai}), f16);
+ EXPECT_TYPE(Type::Common({ai, i32, ai}), i32);
+ EXPECT_TYPE(Type::Common({ai, u32, ai}), u32);
+
+ EXPECT_TYPE(Type::Common({af, ai, af}), af);
+ EXPECT_TYPE(Type::Common({f32, ai, f32}), f32);
+ EXPECT_TYPE(Type::Common({f16, ai, f16}), f16);
+ EXPECT_TYPE(Type::Common({i32, ai, i32}), i32);
+ EXPECT_TYPE(Type::Common({u32, ai, u32}), u32);
+
+ EXPECT_TYPE(Type::Common({ai, f32, ai}), f32);
+ EXPECT_TYPE(Type::Common({ai, f16, ai}), f16);
+ EXPECT_TYPE(Type::Common({ai, i32, ai}), i32);
+ EXPECT_TYPE(Type::Common({ai, u32, ai}), u32);
+
+ EXPECT_TYPE(Type::Common({f32, ai, f32}), f32);
+ EXPECT_TYPE(Type::Common({f16, ai, f16}), f16);
+ EXPECT_TYPE(Type::Common({i32, ai, i32}), i32);
+ EXPECT_TYPE(Type::Common({u32, ai, u32}), u32);
+
+ EXPECT_TYPE(Type::Common({af, f32, af}), f32);
+ EXPECT_TYPE(Type::Common({af, f16, af}), f16);
+ EXPECT_TYPE(Type::Common({af, i32, af}), nullptr);
+ EXPECT_TYPE(Type::Common({af, u32, af}), nullptr);
+
+ EXPECT_TYPE(Type::Common({f32, af, f32}), f32);
+ EXPECT_TYPE(Type::Common({f16, af, f16}), f16);
+ EXPECT_TYPE(Type::Common({i32, af, i32}), nullptr);
+ EXPECT_TYPE(Type::Common({u32, af, u32}), nullptr);
+
+ EXPECT_TYPE(Type::Common({ai, af, f32}), f32);
+ EXPECT_TYPE(Type::Common({ai, af, f16}), f16);
+ EXPECT_TYPE(Type::Common({ai, af, i32}), nullptr);
+ EXPECT_TYPE(Type::Common({ai, af, u32}), nullptr);
+
+ auto* vec3_ai = create<Vector>(ai, 3u);
+ auto* vec3_af = create<Vector>(af, 3u);
+ auto* vec3_f32 = create<Vector>(f32, 3u);
+ auto* vec3_f16 = create<Vector>(f16, 3u);
+ auto* vec4_f32 = create<Vector>(f32, 4u);
+ auto* vec3_u32 = create<Vector>(u32, 3u);
+ auto* vec3_i32 = create<Vector>(i32, 3u);
+
+ EXPECT_TYPE(Type::Common({vec3_ai, vec3_ai, vec3_ai}), vec3_ai);
+ EXPECT_TYPE(Type::Common({vec3_af, vec3_af, vec3_af}), vec3_af);
+ EXPECT_TYPE(Type::Common({vec3_f32, vec3_f32, vec3_f32}), vec3_f32);
+ EXPECT_TYPE(Type::Common({vec3_f16, vec3_f16, vec3_f16}), vec3_f16);
+ EXPECT_TYPE(Type::Common({vec4_f32, vec4_f32, vec4_f32}), vec4_f32);
+ EXPECT_TYPE(Type::Common({vec3_u32, vec3_u32, vec3_u32}), vec3_u32);
+ EXPECT_TYPE(Type::Common({vec3_i32, vec3_i32, vec3_i32}), vec3_i32);
+
+ EXPECT_TYPE(Type::Common({vec3_f32, vec3_ai, vec3_f32}), vec3_f32);
+ EXPECT_TYPE(Type::Common({vec3_f16, vec3_ai, vec3_f16}), vec3_f16);
+ EXPECT_TYPE(Type::Common({vec4_f32, vec3_ai, vec4_f32}), nullptr);
+ EXPECT_TYPE(Type::Common({vec3_u32, vec3_ai, vec3_u32}), vec3_u32);
+ EXPECT_TYPE(Type::Common({vec3_i32, vec3_ai, vec3_i32}), vec3_i32);
+
+ EXPECT_TYPE(Type::Common({vec3_ai, vec3_f32, vec3_ai}), vec3_f32);
+ EXPECT_TYPE(Type::Common({vec3_ai, vec3_f16, vec3_ai}), vec3_f16);
+ EXPECT_TYPE(Type::Common({vec3_ai, vec4_f32, vec3_ai}), nullptr);
+ EXPECT_TYPE(Type::Common({vec3_ai, vec3_u32, vec3_ai}), vec3_u32);
+ EXPECT_TYPE(Type::Common({vec3_ai, vec3_i32, vec3_ai}), vec3_i32);
+
+ EXPECT_TYPE(Type::Common({vec3_f32, vec3_af, vec3_f32}), vec3_f32);
+ EXPECT_TYPE(Type::Common({vec3_f16, vec3_af, vec3_f16}), vec3_f16);
+ EXPECT_TYPE(Type::Common({vec4_f32, vec3_af, vec4_f32}), nullptr);
+ EXPECT_TYPE(Type::Common({vec3_u32, vec3_af, vec3_u32}), nullptr);
+ EXPECT_TYPE(Type::Common({vec3_i32, vec3_af, vec3_i32}), nullptr);
+
+ EXPECT_TYPE(Type::Common({vec3_af, vec3_f32, vec3_af}), vec3_f32);
+ EXPECT_TYPE(Type::Common({vec3_af, vec3_f16, vec3_af}), vec3_f16);
+ EXPECT_TYPE(Type::Common({vec3_af, vec4_f32, vec3_af}), nullptr);
+ EXPECT_TYPE(Type::Common({vec3_af, vec3_u32, vec3_af}), nullptr);
+ EXPECT_TYPE(Type::Common({vec3_af, vec3_i32, vec3_af}), nullptr);
+
+ EXPECT_TYPE(Type::Common({vec3_ai, vec3_af, vec3_f32}), vec3_f32);
+ EXPECT_TYPE(Type::Common({vec3_ai, vec3_af, vec3_f16}), vec3_f16);
+ EXPECT_TYPE(Type::Common({vec3_ai, vec3_af, vec4_f32}), nullptr);
+ EXPECT_TYPE(Type::Common({vec3_ai, vec3_af, vec3_u32}), nullptr);
+ EXPECT_TYPE(Type::Common({vec3_ai, vec3_af, vec3_i32}), nullptr);
+
+ auto* mat4x3_af = create<Matrix>(vec3_af, 4u);
+ auto* mat3x4_f32 = create<Matrix>(vec4_f32, 3u);
+ auto* mat4x3_f32 = create<Matrix>(vec3_f32, 4u);
+ auto* mat4x3_f16 = create<Matrix>(vec3_f16, 4u);
+
+ EXPECT_TYPE(Type::Common({mat4x3_af, mat4x3_af, mat4x3_af}), mat4x3_af);
+ EXPECT_TYPE(Type::Common({mat3x4_f32, mat3x4_f32, mat3x4_f32}), mat3x4_f32);
+ EXPECT_TYPE(Type::Common({mat4x3_f32, mat4x3_f32, mat4x3_f32}), mat4x3_f32);
+ EXPECT_TYPE(Type::Common({mat4x3_f16, mat4x3_f16, mat4x3_f16}), mat4x3_f16);
+
+ EXPECT_TYPE(Type::Common({mat3x4_f32, mat4x3_af, mat3x4_f32}), nullptr);
+ EXPECT_TYPE(Type::Common({mat4x3_f32, mat4x3_af, mat4x3_f32}), mat4x3_f32);
+ EXPECT_TYPE(Type::Common({mat4x3_f16, mat4x3_af, mat4x3_f16}), mat4x3_f16);
+
+ EXPECT_TYPE(Type::Common({mat4x3_af, mat3x4_f32, mat4x3_af}), nullptr);
+ EXPECT_TYPE(Type::Common({mat4x3_af, mat4x3_f32, mat4x3_af}), mat4x3_f32);
+ EXPECT_TYPE(Type::Common({mat4x3_af, mat4x3_f16, mat4x3_af}), mat4x3_f16);
+}
+
} // namespace
} // namespace tint::sem