tint: Implement abstract-numeric overload resolution
Support overload resolution of abstract-numeric argument types,
allowing them to implicitly convert down to concrete parameter
types (and in the near future, abstract parameter types).
Major kudos to cwallez for the suggested algorithm which is a
minor adaption of what we had already.
Bug: tint:1504
Change-Id: I85fa8e70ab0b6aa643caec4c51433f15784af55f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/90522
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 6e379fd..0cacca4 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -384,6 +384,9 @@
"resolver/validator.cc",
"resolver/validator.h",
"scope_stack.h",
+ "sem/abstract_float.h",
+ "sem/abstract_int.h",
+ "sem/abstract_numeric.h",
"sem/array.h",
"sem/atomic.h",
"sem/behavior.h",
diff --git a/src/tint/intrinsics.def b/src/tint/intrinsics.def
index 400b937..b12244c 100644
--- a/src/tint/intrinsics.def
+++ b/src/tint/intrinsics.def
@@ -199,14 +199,30 @@
// the same argument type. //
// //
// //
-// Matching algorithm: //
-// ------------------- //
+// Matching algorithm for a single overload: //
+// ----------------------------------------- //
+// //
+// The goal of matching is to compare a function call's arguments in the //
+// program source against a possibly-templated overload declaration, and //
+// determine if the call satisfies the form and type constraints of the //
+// overload. Currently it is impossible for a call to match more than one //
+// overload definition. In the event that more than one overload matches, an //
+// ICE will be raised. Note that Tint may need to support multiple-overload //
+// resolution in the future, depending on future overload definitions. //
// //
// Prior to matching an overload, all template types are undefined. //
// //
-// Template types become defined on the first attempt to match an argument to //
-// that template type. Once template types are defined, they remain that type //
-// for the rest of the overload evaluation. //
+// Template types are first defined with the type of the leftmost argument //
+// that matches against that template type name. Subsequent arguments that //
+// attempt to match against the template type name will either reject the //
+// overload or refine the template, in one of 3 ways: //
+// (a) Fail to match, causing the overload to be immediately rejected. //
+// (b) Match the existing template type, either exactly or via implicit //
+// conversion, and overload resolution continues. //
+// (c) Match via implicit conversion of the currently defined template type //
+// to the argument type. In this situation, the template type is refined //
+// with the more constrained argument type, and overload resolution //
+// continues. //
// //
// To better understand, let's consider the following hypothetical overload //
// declaration: //
@@ -228,17 +244,24 @@
// type of the first argument. //
// There's no verification that the T type is a scalar at this stage. //
// (2) The second parameter is then compared against the second argument. //
-// As the template type T is now defined the argument type is compared //
-// against the value of the defined type of T. If the types match, then //
-// the overload is still a candidate for matching, otherwise the //
-// overload is no longer considered. //
+// As the template type T is now defined the argument type is compared //
+// against the value of the defined type of T. Depending on the //
+// comparison of the argument type to the template type, either the //
+// actions of (a), (b) or (c) from above will occur. //
// (3) If all the parameters matched, constraints on the template types //
// need to be checked next. If the defined type does not match the //
// 'match' constraint, then the overload is no longer considered. //
// //
-// The algorithm for matching template numbers is almost identical to //
-// matching template types, except of course, they match against integer //
-// numbers or enumerators instead of types. //
+// This algorithm is less general than the overload resolution described in //
+// the WGSL spec. But it makes the same decisions because the overloads //
+// defined by WGSL are monotonic in the sense that once a template parameter //
+// has been refined, there is never a need to backtrack and un-refine it to //
+// match a later argument. //
+// //
+// The algorithm for matching template numbers is similar to matching //
+// template types, except numbers need to exactly match across all uses - //
+// there is no implicit conversion. Template numbers may match integer //
+// numbers or enumerators. //
// //
// //
// * More examples: //
diff --git a/src/tint/resolver/intrinsic_table.cc b/src/tint/resolver/intrinsic_table.cc
index 67f14dd..af89023 100644
--- a/src/tint/resolver/intrinsic_table.cc
+++ b/src/tint/resolver/intrinsic_table.cc
@@ -20,6 +20,9 @@
#include <utility>
#include "src/tint/program_builder.h"
+#include "src/tint/sem/abstract_float.h"
+#include "src/tint/sem/abstract_int.h"
+#include "src/tint/sem/abstract_numeric.h"
#include "src/tint/sem/atomic.h"
#include "src/tint/sem/depth_multisampled_texture.h"
#include "src/tint/sem/depth_texture.h"
@@ -104,12 +107,33 @@
/// Used by the MatchState.
class TemplateState {
public:
- /// If the type with index `idx` is undefined, then it is defined with type `ty` and Type()
- /// returns true. If the template type is defined, then `Type()` returns true iff it is equal to
- /// `ty`.
- bool Type(size_t idx, const sem::Type* ty) {
+ /// If the template type with index `idx` is undefined, then it is defined with the `ty` and
+ /// Type() returns `ty`.
+ /// If the template type is defined, and `ty` can be converted to the template type then the
+ /// template type is returned.
+ /// If the template type is defined, and the template type can be converted to `ty`, then the
+ /// template type is replaced with `ty`, and `ty` is returned.
+ /// If none of the above applies, then `ty` is a type mismatch for the template type, and
+ /// nullptr is returned.
+ const sem::Type* Type(size_t idx, const sem::Type* ty) {
auto res = types_.emplace(idx, ty);
- return res.second || res.first->second == ty;
+ if (res.second) {
+ return ty;
+ }
+ auto* existing = res.first->second;
+ 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;
+ }
+ 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;
}
/// If the number with index `idx` is undefined, then it is defined with the number `number` and
@@ -126,6 +150,9 @@
return (it != types_.end()) ? it->second : nullptr;
}
+ /// SetType replaces the template type with index `idx` with type `ty`.
+ void SetType(size_t idx, const sem::Type* ty) { types_[idx] = ty; }
+
/// Type returns the number type with index `idx`.
Number Num(size_t idx) const {
auto it = numbers_.find(idx);
@@ -197,7 +224,7 @@
/// Checks whether the given type matches the matcher rules, and returns the
/// expected, canonicalized type on success.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
virtual const sem::Type* Match(MatchState& state, const sem::Type* type) const = 0;
@@ -226,8 +253,8 @@
};
/// TemplateTypeMatcher is a Matcher for a template type.
-/// The TemplateTypeMatcher will initially match against any type (so long as it is
-/// consistent for all uses in the overload)
+/// The TemplateTypeMatcher will initially match against any type, and then will only be further
+/// constrained based on the conversion rules defined at https://www.w3.org/TR/WGSL/#conversion-rank
class TemplateTypeMatcher : public TypeMatcher {
public:
/// Constructor
@@ -237,7 +264,10 @@
if (type->Is<Any>()) {
return state.templates.Type(index_);
}
- return state.templates.Type(index_, type) ? type : nullptr;
+ if (auto* templates = state.templates.Type(index_, type)) {
+ return templates;
+ }
+ return nullptr;
}
std::string String(MatchState& state) const override;
@@ -305,7 +335,7 @@
}
bool match_f32(const sem::Type* ty) {
- return ty->IsAnyOf<Any, sem::F32>();
+ return ty->IsAnyOf<Any, sem::F32, sem::AbstractNumeric>();
}
const sem::I32* build_i32(MatchState& state) {
@@ -313,7 +343,7 @@
}
bool match_i32(const sem::Type* ty) {
- return ty->IsAnyOf<Any, sem::I32>();
+ return ty->IsAnyOf<Any, sem::I32, sem::AbstractInt>();
}
const sem::U32* build_u32(MatchState& state) {
@@ -321,7 +351,7 @@
}
bool match_u32(const sem::Type* ty) {
- return ty->IsAnyOf<Any, sem::U32>();
+ return ty->IsAnyOf<Any, sem::U32, sem::AbstractInt>();
}
bool match_vec(const sem::Type* ty, Number& N, const sem::Type*& T) {
@@ -1247,6 +1277,11 @@
case 1:
break;
default:
+ // Note: Currently the intrinsic table does not contain any overloads which may result
+ // in ambiguities, so here we call ErrMultipleOverloadsMatched() which will produce and
+ // ICE. If we end up in the situation where this is unavoidable, we'll need to perform
+ // further overload resolution as described in
+ // https://www.w3.org/TR/WGSL/#overload-resolution-section.
ErrMultipleOverloadsMatched(num_matched, intrinsic_name, args, templates, candidates);
}
@@ -1290,36 +1325,51 @@
std::min(num_parameters, num_arguments));
}
- std::vector<IntrinsicPrototype::Parameter> parameters;
-
+ // Invoke the matchers for each parameter <-> argument pair.
+ // If any arguments cannot be matched, then `score` will be increased.
+ // If the overload has any template types or numbers then these will be set based on the
+ // argument types. Template types may be refined by constraining with later argument types. For
+ // example calling `F<T>(T, T)` with the argument types (abstract-int, i32) will first set T to
+ // abstract-int when matching the first argument, and then constrained down to i32 when matching
+ // the second argument.
+ // Note that inferred template types are not tested against their matchers at this point.
auto num_params = std::min(num_parameters, num_arguments);
for (size_t p = 0; p < num_params; p++) {
auto& parameter = overload->parameters[p];
auto* indices = parameter.matcher_indices;
- auto* type = Match(templates, overload, indices).Type(args[p]->UnwrapRef());
- if (type) {
- parameters.emplace_back(IntrinsicPrototype::Parameter{type, parameter.usage});
- } else {
+ if (!Match(templates, overload, indices).Type(args[p]->UnwrapRef())) {
score += kMismatchedParamTypePenalty;
}
}
if (score == 0) {
- // Check all constrained template types matched
+ // Check all constrained template types matched their constraint matchers.
+ // If the template type *does not* match any of the types in the constraint matcher, then
+ // `score` is incremented. If the template type *does* match a type, then the template type
+ // is replaced with the first matching type. The order of types in the template matcher is
+ // important here, which can be controlled with the [[precedence(N)]] decorations on the
+ // types in intrinsics.def.
for (size_t ot = 0; ot < overload->num_template_types; ot++) {
auto* matcher_index = &overload->template_types[ot].matcher_index;
if (*matcher_index != kNoMatcher) {
- auto* template_type = templates.Type(ot);
- if (!template_type ||
- !Match(templates, overload, matcher_index).Type(template_type)) {
- score += kMismatchedTemplateTypePenalty;
+ if (auto* template_type = templates.Type(ot)) {
+ if (auto* ty = Match(templates, overload, matcher_index).Type(template_type)) {
+ // Template type matched one of the types in the template type's matcher.
+ // Replace the template type with this type.
+ templates.SetType(ot, ty);
+ continue;
+ }
}
+ score += kMismatchedTemplateTypePenalty;
}
}
}
if (score == 0) {
- // Check all constrained open numbers matched
+ // Check all constrained open numbers matched.
+ // Unlike template types, numbers are not constrained, so we're just checking that the
+ // inferred number matches the constraints on the overload. Increments `score` if the
+ // template numbers do not match their constraint matchers.
for (size_t on = 0; on < overload->num_template_numbers; on++) {
auto* matcher_index = &overload->template_numbers[on].matcher_index;
if (*matcher_index != kNoMatcher) {
@@ -1332,6 +1382,18 @@
}
}
+ // Now that all the template types have been finalized, we can construct the parameters.
+ std::vector<IntrinsicPrototype::Parameter> parameters;
+ if (score == 0) {
+ parameters.reserve(num_params);
+ for (size_t p = 0; p < num_params; p++) {
+ auto& parameter = overload->parameters[p];
+ auto* indices = parameter.matcher_indices;
+ auto* ty = Match(templates, overload, indices).Type(args[p]->UnwrapRef());
+ parameters.emplace_back(IntrinsicPrototype::Parameter{ty, parameter.usage});
+ }
+ }
+
return Candidate{overload, templates, parameters, score};
}
diff --git a/src/tint/resolver/intrinsic_table.inl b/src/tint/resolver/intrinsic_table.inl
index 5f6203c..f6502a1 100644
--- a/src/tint/resolver/intrinsic_table.inl
+++ b/src/tint/resolver/intrinsic_table.inl
@@ -29,7 +29,7 @@
class Bool : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -56,7 +56,7 @@
class I32 : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -83,7 +83,7 @@
class U32 : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -110,7 +110,7 @@
class F32 : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -137,7 +137,7 @@
class Vec2 : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -170,7 +170,7 @@
class Vec3 : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -203,7 +203,7 @@
class Vec4 : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -236,7 +236,7 @@
class Mat2X2 : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -269,7 +269,7 @@
class Mat2X3 : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -302,7 +302,7 @@
class Mat2X4 : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -335,7 +335,7 @@
class Mat3X2 : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -368,7 +368,7 @@
class Mat3X3 : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -401,7 +401,7 @@
class Mat3X4 : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -434,7 +434,7 @@
class Mat4X2 : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -467,7 +467,7 @@
class Mat4X3 : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -500,7 +500,7 @@
class Mat4X4 : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -533,7 +533,7 @@
class Vec : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -574,7 +574,7 @@
class Mat : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -621,7 +621,7 @@
class Ptr : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -666,7 +666,7 @@
class Atomic : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -699,7 +699,7 @@
class Array : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -732,7 +732,7 @@
class Sampler : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -759,7 +759,7 @@
class SamplerComparison : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -786,7 +786,7 @@
class Texture1D : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -819,7 +819,7 @@
class Texture2D : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -852,7 +852,7 @@
class Texture2DArray : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -885,7 +885,7 @@
class Texture3D : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -918,7 +918,7 @@
class TextureCube : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -951,7 +951,7 @@
class TextureCubeArray : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -984,7 +984,7 @@
class TextureMultisampled2D : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1017,7 +1017,7 @@
class TextureDepth2D : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1044,7 +1044,7 @@
class TextureDepth2DArray : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1071,7 +1071,7 @@
class TextureDepthCube : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1098,7 +1098,7 @@
class TextureDepthCubeArray : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1125,7 +1125,7 @@
class TextureDepthMultisampled2D : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1152,7 +1152,7 @@
class TextureStorage1D : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1191,7 +1191,7 @@
class TextureStorage2D : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1230,7 +1230,7 @@
class TextureStorage2DArray : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1269,7 +1269,7 @@
class TextureStorage3D : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1308,7 +1308,7 @@
class TextureExternal : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1335,7 +1335,7 @@
class ModfResult : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1362,7 +1362,7 @@
class ModfResultVec : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1397,7 +1397,7 @@
class FrexpResult : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1424,7 +1424,7 @@
class FrexpResultVec : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1460,7 +1460,7 @@
public:
/// Checks whether the given type matches the matcher rules, and returns the
/// expected, canonicalized type on success.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1494,7 +1494,7 @@
public:
/// Checks whether the given type matches the matcher rules, and returns the
/// expected, canonicalized type on success.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1525,7 +1525,7 @@
public:
/// Checks whether the given type matches the matcher rules, and returns the
/// expected, canonicalized type on success.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1556,7 +1556,7 @@
public:
/// Checks whether the given type matches the matcher rules, and returns the
/// expected, canonicalized type on success.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1593,7 +1593,7 @@
public:
/// Checks whether the given type matches the matcher rules, and returns the
/// expected, canonicalized type on success.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1627,7 +1627,7 @@
public:
/// Checks whether the given type matches the matcher rules, and returns the
/// expected, canonicalized type on success.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1661,7 +1661,7 @@
public:
/// Checks whether the given type matches the matcher rules, and returns the
/// expected, canonicalized type on success.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1695,7 +1695,7 @@
public:
/// Checks whether the given type matches the matcher rules, and returns the
/// expected, canonicalized type on success.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -1728,7 +1728,7 @@
class F32TexelFormat : public NumberMatcher {
public:
/// Checks whether the given number matches the enum matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define template numbers in state.
/// @param state the MatchState
/// @param number the enum value as a Number
/// @return true if the enum value matches the set
@@ -1761,7 +1761,7 @@
class I32TexelFormat : public NumberMatcher {
public:
/// Checks whether the given number matches the enum matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define template numbers in state.
/// @param state the MatchState
/// @param number the enum value as a Number
/// @return true if the enum value matches the set
@@ -1793,7 +1793,7 @@
class U32TexelFormat : public NumberMatcher {
public:
/// Checks whether the given number matches the enum matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define template numbers in state.
/// @param state the MatchState
/// @param number the enum value as a Number
/// @return true if the enum value matches the set
@@ -1825,7 +1825,7 @@
class WriteOnly : public NumberMatcher {
public:
/// Checks whether the given number matches the enum matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define template numbers in state.
/// @param state the MatchState
/// @param number the enum value as a Number
/// @return true if the enum value matches the set
@@ -1851,7 +1851,7 @@
class FunctionPrivateWorkgroup : public NumberMatcher {
public:
/// Checks whether the given number matches the enum matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define template numbers in state.
/// @param state the MatchState
/// @param number the enum value as a Number
/// @return true if the enum value matches the set
@@ -1881,7 +1881,7 @@
class WorkgroupOrStorage : public NumberMatcher {
public:
/// Checks whether the given number matches the enum matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define template numbers in state.
/// @param state the MatchState
/// @param number the enum value as a Number
/// @return true if the enum value matches the set
@@ -1909,7 +1909,7 @@
class Storage : public NumberMatcher {
public:
/// Checks whether the given number matches the enum matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define template numbers in state.
/// @param state the MatchState
/// @param number the enum value as a Number
/// @return true if the enum value matches the set
@@ -1934,7 +1934,7 @@
class Write : public NumberMatcher {
public:
/// Checks whether the given number matches the enum matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define template numbers in state.
/// @param state the MatchState
/// @param number the enum value as a Number
/// @return true if the enum value matches the set
@@ -1959,7 +1959,7 @@
class ReadWrite : public NumberMatcher {
public:
/// Checks whether the given number matches the enum matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define template numbers in state.
/// @param state the MatchState
/// @param number the enum value as a Number
/// @return true if the enum value matches the set
diff --git a/src/tint/resolver/intrinsic_table.inl.tmpl b/src/tint/resolver/intrinsic_table.inl.tmpl
index 832e696..d09d340 100644
--- a/src/tint/resolver/intrinsic_table.inl.tmpl
+++ b/src/tint/resolver/intrinsic_table.inl.tmpl
@@ -180,7 +180,7 @@
class {{$class}} : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -236,7 +236,7 @@
public:
/// Checks whether the given type matches the matcher rules, and returns the
/// expected, canonicalized type on success.
- /// Match may define template types and numbers in state.
+ /// Match may define and refine the template types and numbers in state.
/// @param state the MatchState
/// @param type the type to match
/// @returns the canonicalized type on match, otherwise nullptr
@@ -280,7 +280,7 @@
class {{$class}} : public NumberMatcher {
public:
/// Checks whether the given number matches the enum matcher rules.
- /// Match may define template types and numbers in state.
+ /// Match may define template numbers in state.
/// @param state the MatchState
/// @param number the enum value as a Number
/// @return true if the enum value matches the set
diff --git a/src/tint/resolver/intrinsic_table_test.cc b/src/tint/resolver/intrinsic_table_test.cc
index 206c951..a72eb00 100644
--- a/src/tint/resolver/intrinsic_table_test.cc
+++ b/src/tint/resolver/intrinsic_table_test.cc
@@ -18,6 +18,7 @@
#include "gmock/gmock.h"
#include "src/tint/program_builder.h"
+#include "src/tint/resolver/resolver_test_helper.h"
#include "src/tint/sem/atomic.h"
#include "src/tint/sem/depth_multisampled_texture.h"
#include "src/tint/sem/depth_texture.h"
@@ -32,12 +33,27 @@
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;
using Parameter = sem::Parameter;
using ParameterUsage = sem::ParameterUsage;
+using AFloatV = builder::vec<3, AFloat>;
+using AIntV = builder::vec<3, AInt>;
+using f32V = builder::vec<3, f32>;
+using i32V = builder::vec<3, i32>;
+using u32V = builder::vec<3, u32>;
+
class IntrinsicTableTest : public testing::Test, public ProgramBuilder {
public:
std::unique_ptr<IntrinsicTable> table = IntrinsicTable::Create(*this);
@@ -418,7 +434,7 @@
EXPECT_EQ(result->Parameters()[0]->Type(), f32);
}
-TEST_F(IntrinsicTableTest, MatchOpenType) {
+TEST_F(IntrinsicTableTest, MatchTemplateType) {
auto* f32 = create<sem::F32>();
auto* result = table->Lookup(BuiltinType::kClamp, {f32, f32, f32}, Source{});
ASSERT_NE(result, nullptr) << Diagnostics().str();
@@ -430,7 +446,7 @@
EXPECT_EQ(result->Parameters()[2]->Type(), f32);
}
-TEST_F(IntrinsicTableTest, MismatchOpenType) {
+TEST_F(IntrinsicTableTest, MismatchTemplateType) {
auto* f32 = create<sem::F32>();
auto* u32 = create<sem::U32>();
auto* result = table->Lookup(BuiltinType::kClamp, {f32, u32, f32}, Source{});
@@ -785,5 +801,461 @@
ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
}
+////////////////////////////////////////////////////////////////////////////////
+// AbstractBinaryTests
+////////////////////////////////////////////////////////////////////////////////
+namespace AbstractBinaryTests {
+
+struct Case {
+ template <typename RESULT,
+ typename PARAM_LHS,
+ typename PARAM_RHS,
+ typename ARG_LHS,
+ typename ARG_RHS>
+ static Case Create(bool match = true) {
+ return {
+ match, //
+ builder::DataType<RESULT>::Sem, //
+ builder::DataType<PARAM_LHS>::Sem, //
+ builder::DataType<PARAM_RHS>::Sem, //
+ builder::DataType<ARG_LHS>::Sem, //
+ builder::DataType<ARG_RHS>::Sem, //
+ };
+ }
+ bool expected_match;
+ builder::sem_type_func_ptr expected_result;
+ builder::sem_type_func_ptr expected_param_lhs;
+ builder::sem_type_func_ptr expected_param_rhs;
+ builder::sem_type_func_ptr arg_lhs;
+ 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>";
+ }
+
+ std::unique_ptr<IntrinsicTable> table = IntrinsicTable::Create(*this);
+};
+
+TEST_P(IntrinsicTableAbstractBinaryTest, MatchAdd) {
+ auto* arg_lhs = GetParam().arg_lhs(*this);
+ auto* arg_rhs = GetParam().arg_rhs(*this);
+ auto result = table->Lookup(ast::BinaryOp::kAdd, arg_lhs, arg_rhs, Source{{12, 34}},
+ /* is_compound */ false);
+
+ bool matched = result.result != nullptr;
+ bool expected_match = GetParam().expected_match;
+ EXPECT_EQ(matched, expected_match) << Diagnostics().str();
+
+ auto* expected_result = GetParam().expected_result(*this);
+ EXPECT_TYPE(result.result, expected_result);
+
+ auto* expected_param_lhs = GetParam().expected_param_lhs(*this);
+ EXPECT_TYPE(result.lhs, expected_param_lhs);
+
+ auto* expected_param_rhs = GetParam().expected_param_rhs(*this);
+ EXPECT_TYPE(result.rhs, expected_param_rhs);
+}
+
+INSTANTIATE_TEST_SUITE_P(AFloat_AInt,
+ IntrinsicTableAbstractBinaryTest,
+ testing::Values( // clang-format off
+// result | param lhs | param rhs | arg lhs | arg rhs
+Case::Create<f32, f32, f32, AFloat, AFloat>(),
+Case::Create<f32, f32, f32, AFloat, AInt>(),
+Case::Create<f32, f32, f32, AInt, AFloat>(),
+Case::Create<i32, i32, i32, AInt, AInt>()
+ )); // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(VecAFloat_VecAInt,
+ IntrinsicTableAbstractBinaryTest,
+ testing::Values( // clang-format off
+// result | param lhs | param rhs | arg lhs | arg rhs
+Case::Create<f32V, f32V, f32V, AFloatV, AFloatV>(),
+Case::Create<f32V, f32V, f32V, AFloatV, AIntV>(),
+Case::Create<f32V, f32V, f32V, AIntV, AFloatV>(),
+Case::Create<i32V, i32V, i32V, AIntV, AIntV>()
+ )); // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(AFloat_f32,
+ IntrinsicTableAbstractBinaryTest,
+ testing::Values( // clang-format off
+// result | param lhs | param rhs | arg lhs | arg rhs
+Case::Create<f32, f32, f32, AFloat, f32>(),
+Case::Create<f32, f32, f32, f32, AFloat>()
+ )); // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(VecAFloat_Vecf32,
+ IntrinsicTableAbstractBinaryTest,
+ testing::Values( // clang-format off
+// result | param lhs | param rhs | arg lhs | arg rhs
+Case::Create<f32V, f32V, f32V, AFloatV, f32V>(),
+Case::Create<f32V, f32V, f32V, f32V, AFloatV>()
+ )); // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(
+ AFloat_i32,
+ IntrinsicTableAbstractBinaryTest,
+ testing::Values( // clang-format off
+// result | param lhs | param rhs | arg lhs | arg rhs
+Case::Create<void, void, void, AFloat, i32>(false),
+Case::Create<void, void, void, i32, AFloat>(false)
+ )); // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(
+ VecAFloat_Veci32,
+ IntrinsicTableAbstractBinaryTest,
+ testing::Values( // clang-format off
+// result | param lhs | param rhs | arg lhs | arg rhs
+Case::Create<void, void, void, AFloatV, i32V>(false),
+Case::Create<void, void, void, i32V, AFloatV>(false)
+ )); // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(
+ AFloat_u32,
+ IntrinsicTableAbstractBinaryTest,
+ testing::Values( // clang-format off
+// result | param lhs | param rhs | arg lhs | arg rhs
+Case::Create<void, void, void, AFloat, u32>(false),
+Case::Create<void, void, void, u32, AFloat>(false)
+ )); // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(
+ VecAFloat_Vecu32,
+ IntrinsicTableAbstractBinaryTest,
+ testing::Values( // clang-format off
+// result | param lhs | param rhs | arg lhs | arg rhs
+Case::Create<void, void, void, AFloatV, u32V>(false),
+Case::Create<void, void, void, u32V, AFloatV>(false)
+ )); // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(AInt_f32,
+ IntrinsicTableAbstractBinaryTest,
+ testing::Values( // clang-format off
+// result | param lhs | param rhs | arg lhs | arg rhs
+Case::Create<f32, f32, f32, AInt, f32>(),
+Case::Create<f32, f32, f32, f32, AInt>()
+ )); // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(VecAInt_Vecf32,
+ IntrinsicTableAbstractBinaryTest,
+ testing::Values( // clang-format off
+// result | param lhs | param rhs | arg lhs | arg rhs
+Case::Create<f32V, f32V, f32V, AIntV, f32V>(),
+Case::Create<f32V, f32V, f32V, f32V, AIntV>()
+ )); // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(AInt_i32,
+ IntrinsicTableAbstractBinaryTest,
+ testing::Values( // clang-format off
+// result | param lhs | param rhs | arg lhs | arg rhs
+Case::Create<i32, i32, i32, AInt, i32>(),
+Case::Create<i32, i32, i32, i32, AInt>()
+ )); // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(VecAInt_Veci32,
+ IntrinsicTableAbstractBinaryTest,
+ testing::Values( // clang-format off
+// result | param lhs | param rhs | arg lhs | arg rhs
+Case::Create<i32V, i32V, i32V, AIntV, i32V>(),
+Case::Create<i32V, i32V, i32V, i32V, AIntV>()
+ )); // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(AInt_u32,
+ IntrinsicTableAbstractBinaryTest,
+ testing::Values( // clang-format off
+// result | param lhs | param rhs | arg lhs | arg rhs
+Case::Create<u32, u32, u32, AInt, u32>(),
+Case::Create<u32, u32, u32, u32, AInt>()
+ )); // clang-format on
+
+INSTANTIATE_TEST_SUITE_P(VecAInt_Vecu32,
+ IntrinsicTableAbstractBinaryTest,
+ testing::Values( // clang-format off
+// result | param lhs | param rhs | arg lhs | arg rhs
+Case::Create<u32V, u32V, u32V, AIntV, u32V>(),
+Case::Create<u32V, u32V, u32V, u32V, AIntV>()
+ )); // clang-format on
+
+} // namespace AbstractBinaryTests
+
+////////////////////////////////////////////////////////////////////////////////
+// AbstractTernaryTests
+////////////////////////////////////////////////////////////////////////////////
+namespace AbstractTernaryTests {
+
+struct Case {
+ template <typename RESULT,
+ typename PARAM_A,
+ typename PARAM_B,
+ typename PARAM_C,
+ typename ARG_A,
+ typename ARG_B,
+ typename ARG_C>
+ static Case Create(bool match = true) {
+ return {
+ match,
+ builder::DataType<RESULT>::Sem, //
+ builder::DataType<PARAM_A>::Sem, //
+ builder::DataType<PARAM_B>::Sem, //
+ builder::DataType<PARAM_C>::Sem, //
+ builder::DataType<ARG_A>::Sem, //
+ builder::DataType<ARG_B>::Sem, //
+ builder::DataType<ARG_C>::Sem, //
+ };
+ }
+ bool expected_match;
+ builder::sem_type_func_ptr expected_result;
+ builder::sem_type_func_ptr expected_param_a;
+ builder::sem_type_func_ptr expected_param_b;
+ builder::sem_type_func_ptr expected_param_c;
+ builder::sem_type_func_ptr arg_a;
+ builder::sem_type_func_ptr arg_b;
+ 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>";
+ }
+
+ std::unique_ptr<IntrinsicTable> table = IntrinsicTable::Create(*this);
+};
+
+TEST_P(IntrinsicTableAbstractTernaryTest, MatchClamp) {
+ auto* arg_a = GetParam().arg_a(*this);
+ auto* arg_b = GetParam().arg_b(*this);
+ auto* arg_c = GetParam().arg_c(*this);
+ auto* builtin =
+ table->Lookup(sem::BuiltinType::kClamp, {arg_a, arg_b, arg_c}, Source{{12, 34}});
+
+ bool matched = builtin != nullptr;
+ bool expected_match = GetParam().expected_match;
+ EXPECT_EQ(matched, expected_match) << Diagnostics().str();
+
+ auto* result = builtin ? builtin->ReturnType() : nullptr;
+ auto* expected_result = GetParam().expected_result(*this);
+ EXPECT_TYPE(result, expected_result);
+
+ auto* param_a = builtin ? builtin->Parameters()[0]->Type() : nullptr;
+ auto* expected_param_a = GetParam().expected_param_a(*this);
+ EXPECT_TYPE(param_a, expected_param_a);
+
+ auto* param_b = builtin ? builtin->Parameters()[1]->Type() : nullptr;
+ auto* expected_param_b = GetParam().expected_param_b(*this);
+ EXPECT_TYPE(param_b, expected_param_b);
+
+ auto* param_c = builtin ? builtin->Parameters()[2]->Type() : nullptr;
+ auto* expected_param_c = GetParam().expected_param_c(*this);
+ EXPECT_TYPE(param_c, expected_param_c);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AFloat_AInt,
+ IntrinsicTableAbstractTernaryTest,
+ testing::Values( // clang-format off
+// result | param a | param b | param c | arg a | arg b | arg c
+Case::Create<f32, f32, f32, f32, AFloat, AFloat, AFloat>(),
+Case::Create<f32, f32, f32, f32, AFloat, AFloat, AInt>(),
+Case::Create<f32, f32, f32, f32, AFloat, AInt, AFloat>(),
+Case::Create<f32, f32, f32, f32, AFloat, AInt, AInt>(),
+Case::Create<f32, f32, f32, f32, AInt, AFloat, AFloat>(),
+Case::Create<f32, f32, f32, f32, AInt, AFloat, AInt>(),
+Case::Create<f32, f32, f32, f32, AInt, AInt, AFloat>(),
+Case::Create<i32, i32, i32, i32, AInt, AInt, AInt>()
+ // clang-format on
+ ));
+
+INSTANTIATE_TEST_SUITE_P(
+ VecAFloat_VecAInt,
+ IntrinsicTableAbstractTernaryTest,
+ testing::Values( // clang-format off
+// result | param a | param b | param c | arg a | arg b | arg c
+Case::Create<f32V, f32V, f32V, f32V, AFloatV, AFloatV, AFloatV>(),
+Case::Create<f32V, f32V, f32V, f32V, AFloatV, AFloatV, AIntV>(),
+Case::Create<f32V, f32V, f32V, f32V, AFloatV, AIntV, AFloatV>(),
+Case::Create<f32V, f32V, f32V, f32V, AFloatV, AIntV, AIntV>(),
+Case::Create<f32V, f32V, f32V, f32V, AIntV, AFloatV, AFloatV>(),
+Case::Create<f32V, f32V, f32V, f32V, AIntV, AFloatV, AIntV>(),
+Case::Create<f32V, f32V, f32V, f32V, AIntV, AIntV, AFloatV>(),
+Case::Create<i32V, i32V, i32V, i32V, AIntV, AIntV, AIntV>()
+ // clang-format on
+ ));
+
+INSTANTIATE_TEST_SUITE_P(
+ AFloat_f32,
+ IntrinsicTableAbstractTernaryTest,
+ testing::Values( // clang-format off
+// result | param a | param b | param c | arg a | arg b | arg c
+Case::Create<f32, f32, f32, f32, AFloat, AFloat, f32>(),
+Case::Create<f32, f32, f32, f32, AFloat, f32, AFloat>(),
+Case::Create<f32, f32, f32, f32, AFloat, f32, f32>(),
+Case::Create<f32, f32, f32, f32, f32, AFloat, AFloat>(),
+Case::Create<f32, f32, f32, f32, f32, AFloat, f32>(),
+Case::Create<f32, f32, f32, f32, f32, f32, AFloat>()
+ // clang-format on
+ ));
+
+INSTANTIATE_TEST_SUITE_P(
+ VecAFloat_Vecf32,
+ IntrinsicTableAbstractTernaryTest,
+ testing::Values( // clang-format off
+// result | param a | param b | param c | arg a | arg b | arg c
+Case::Create<f32V, f32V, f32V, f32V, AFloatV, AFloatV, f32V>(),
+Case::Create<f32V, f32V, f32V, f32V, AFloatV, f32V, AFloatV>(),
+Case::Create<f32V, f32V, f32V, f32V, AFloatV, f32V, f32V>(),
+Case::Create<f32V, f32V, f32V, f32V, f32V, AFloatV, AFloatV>(),
+Case::Create<f32V, f32V, f32V, f32V, f32V, AFloatV, f32V>(),
+Case::Create<f32V, f32V, f32V, f32V, f32V, f32V, AFloatV> ()
+ // clang-format on
+ ));
+
+INSTANTIATE_TEST_SUITE_P(
+ AFloat_i32,
+ IntrinsicTableAbstractTernaryTest,
+ testing::Values( // clang-format off
+// result | param a | param b | param c | arg a | arg b | arg c
+Case::Create<void, void, void, void, AFloat, AFloat, i32>(false),
+Case::Create<void, void, void, void, AFloat, i32, AFloat>(false),
+Case::Create<void, void, void, void, AFloat, i32, i32>(false),
+Case::Create<void, void, void, void, i32, AFloat, AFloat>(false),
+Case::Create<void, void, void, void, i32, AFloat, i32>(false),
+Case::Create<void, void, void, void, i32, i32, AFloat>(false)
+ // clang-format on
+ ));
+
+INSTANTIATE_TEST_SUITE_P(
+ VecAFloat_Veci32,
+ IntrinsicTableAbstractTernaryTest,
+ testing::Values( // clang-format off
+// result | param a | param b | param c | arg a | arg b | arg c
+Case::Create<void, void, void, void, AFloatV, AFloatV, i32V>(false),
+Case::Create<void, void, void, void, AFloatV, i32V, AFloatV>(false),
+Case::Create<void, void, void, void, AFloatV, i32V, i32V>(false),
+Case::Create<void, void, void, void, i32V, AFloatV, AFloatV>(false),
+Case::Create<void, void, void, void, i32V, AFloatV, i32V>(false),
+Case::Create<void, void, void, void, i32V, i32V, AFloatV>(false)
+ // clang-format on
+ ));
+
+INSTANTIATE_TEST_SUITE_P(
+ AFloat_u32,
+ IntrinsicTableAbstractTernaryTest,
+ testing::Values( // clang-format off
+// result | param a | param b | param c | arg a | arg b | arg c
+Case::Create<void, void, void, void, AFloat, AFloat, u32>(false),
+Case::Create<void, void, void, void, AFloat, u32, AFloat>(false),
+Case::Create<void, void, void, void, AFloat, u32, u32>(false),
+Case::Create<void, void, void, void, u32, AFloat, AFloat>(false),
+Case::Create<void, void, void, void, u32, AFloat, u32>(false),
+Case::Create<void, void, void, void, u32, u32, AFloat>(false)
+ // clang-format on
+ ));
+
+INSTANTIATE_TEST_SUITE_P(
+ VecAFloat_Vecu32,
+ IntrinsicTableAbstractTernaryTest,
+ testing::Values( // clang-format off
+// result | param a | param b | param c | arg a | arg b | arg c
+Case::Create<void, void, void, void, AFloatV, AFloatV, u32V>(false),
+Case::Create<void, void, void, void, AFloatV, u32V, AFloatV>(false),
+Case::Create<void, void, void, void, AFloatV, u32V, u32V>(false),
+Case::Create<void, void, void, void, u32V, AFloatV, AFloatV>(false),
+Case::Create<void, void, void, void, u32V, AFloatV, u32V>(false),
+Case::Create<void, void, void, void, u32V, u32V, AFloatV>(false)
+ // clang-format on
+ ));
+
+INSTANTIATE_TEST_SUITE_P(
+ AInt_f32,
+ IntrinsicTableAbstractTernaryTest,
+ testing::Values( // clang-format off
+// result | param a | param b | param c | arg a | arg b | arg c
+Case::Create<f32, f32, f32, f32, AInt, AInt, f32>(),
+Case::Create<f32, f32, f32, f32, AInt, f32, AInt>(),
+Case::Create<f32, f32, f32, f32, AInt, f32, f32>(),
+Case::Create<f32, f32, f32, f32, f32, AInt, AInt>(),
+Case::Create<f32, f32, f32, f32, f32, AInt, f32>(),
+Case::Create<f32, f32, f32, f32, f32, f32, AInt>()
+ // clang-format on
+ ));
+
+INSTANTIATE_TEST_SUITE_P(
+ VecAInt_Vecf32,
+ IntrinsicTableAbstractTernaryTest,
+ testing::Values( // clang-format off
+// result | param a | param b | param c | arg a | arg b | arg c
+Case::Create<f32V, f32V, f32V, f32V, AIntV, AIntV, f32V>(),
+Case::Create<f32V, f32V, f32V, f32V, AIntV, f32V, AIntV>(),
+Case::Create<f32V, f32V, f32V, f32V, AIntV, f32V, f32V>(),
+Case::Create<f32V, f32V, f32V, f32V, f32V, AIntV, AIntV>(),
+Case::Create<f32V, f32V, f32V, f32V, f32V, AIntV, f32V>(),
+Case::Create<f32V, f32V, f32V, f32V, f32V, f32V, AIntV>()
+ // clang-format on
+ ));
+
+INSTANTIATE_TEST_SUITE_P(
+ AInt_i32,
+ IntrinsicTableAbstractTernaryTest,
+ testing::Values( // clang-format off
+// result | param a | param b | param c | arg a | arg b | arg c
+Case::Create<i32, i32, i32, i32, AInt, AInt, i32>(),
+Case::Create<i32, i32, i32, i32, AInt, i32, AInt>(),
+Case::Create<i32, i32, i32, i32, AInt, i32, i32>(),
+Case::Create<i32, i32, i32, i32, i32, AInt, AInt>(),
+Case::Create<i32, i32, i32, i32, i32, AInt, i32>(),
+Case::Create<i32, i32, i32, i32, i32, i32, AInt>()
+ // clang-format on
+ ));
+
+INSTANTIATE_TEST_SUITE_P(
+ VecAInt_Veci32,
+ IntrinsicTableAbstractTernaryTest,
+ testing::Values( // clang-format off
+// result | param a | param b | param c | arg a | arg b | arg c
+Case::Create<i32V, i32V, i32V, i32V, AIntV, AIntV, i32V>(),
+Case::Create<i32V, i32V, i32V, i32V, AIntV, i32V, AIntV>(),
+Case::Create<i32V, i32V, i32V, i32V, AIntV, i32V, i32V>(),
+Case::Create<i32V, i32V, i32V, i32V, i32V, AIntV, AIntV>(),
+Case::Create<i32V, i32V, i32V, i32V, i32V, AIntV, i32V>(),
+Case::Create<i32V, i32V, i32V, i32V, i32V, i32V, AIntV>()
+ // clang-format on
+ ));
+
+INSTANTIATE_TEST_SUITE_P(
+ AInt_u32,
+ IntrinsicTableAbstractTernaryTest,
+ testing::Values( // clang-format off
+// result | param a | param b | param c | arg a | arg b | arg c
+Case::Create<u32, u32, u32, u32, AInt, AInt, u32>(),
+Case::Create<u32, u32, u32, u32, AInt, u32, AInt>(),
+Case::Create<u32, u32, u32, u32, AInt, u32, u32>(),
+Case::Create<u32, u32, u32, u32, u32, AInt, AInt>(),
+Case::Create<u32, u32, u32, u32, u32, AInt, u32>(),
+Case::Create<u32, u32, u32, u32, u32, u32, AInt>()
+ // clang-format on
+ ));
+
+INSTANTIATE_TEST_SUITE_P(
+ VecAInt_Vecu32,
+ IntrinsicTableAbstractTernaryTest,
+ testing::Values( // clang-format off
+// result | param a | param b | param c | arg a | arg b | arg c
+Case::Create<u32V, u32V, u32V, u32V, AIntV, AIntV, u32V>(),
+Case::Create<u32V, u32V, u32V, u32V, AIntV, u32V, AIntV>(),
+Case::Create<u32V, u32V, u32V, u32V, AIntV, u32V, u32V>(),
+Case::Create<u32V, u32V, u32V, u32V, u32V, AIntV, AIntV>(),
+Case::Create<u32V, u32V, u32V, u32V, u32V, AIntV, u32V>(),
+Case::Create<u32V, u32V, u32V, u32V, u32V, u32V, AIntV>()
+ // clang-format on
+ ));
+
+} // namespace AbstractTernaryTests
+
} // namespace
} // namespace tint::resolver
diff --git a/src/tint/resolver/resolver_test_helper.h b/src/tint/resolver/resolver_test_helper.h
index d54e57c..aca1b29 100644
--- a/src/tint/resolver/resolver_test_helper.h
+++ b/src/tint/resolver/resolver_test_helper.h
@@ -22,6 +22,8 @@
#include "gtest/gtest.h"
#include "src/tint/program_builder.h"
#include "src/tint/resolver/resolver.h"
+#include "src/tint/sem/abstract_float.h"
+#include "src/tint/sem/abstract_int.h"
#include "src/tint/sem/expression.h"
#include "src/tint/sem/statement.h"
#include "src/tint/sem/variable.h"
@@ -174,6 +176,15 @@
template <typename T>
struct DataType {};
+/// Helper that represents no-type. Returns nullptr for all static methods.
+template <>
+struct DataType<void> {
+ /// @return nullptr
+ static inline const ast::Type* AST(ProgramBuilder&) { return nullptr; }
+ /// @return nullptr
+ static inline const sem::Type* Sem(ProgramBuilder&) { return nullptr; }
+};
+
/// Helper for building bool types and expressions
template <>
struct DataType<bool> {
@@ -254,6 +265,40 @@
}
};
+/// Helper for building abstract float types and expressions
+template <>
+struct DataType<AFloat> {
+ /// false as AFloat is not a composite type
+ static constexpr bool is_composite = false;
+
+ /// @param b the ProgramBuilder
+ /// @return the semantic abstract-float type
+ static inline const sem::Type* Sem(ProgramBuilder& b) { return b.create<sem::AbstractFloat>(); }
+ /// @param b the ProgramBuilder
+ /// @param elem_value the abstract-float value
+ /// @return a new AST abstract-float literal value expression
+ static inline const ast::Expression* Expr(ProgramBuilder& b, AFloat elem_value) {
+ return b.Expr(elem_value);
+ }
+};
+
+/// Helper for building abstract integer types and expressions
+template <>
+struct DataType<AInt> {
+ /// false as AFloat is not a composite type
+ static constexpr bool is_composite = false;
+
+ /// @param b the ProgramBuilder
+ /// @return the semantic abstract-int type
+ static inline const sem::Type* Sem(ProgramBuilder& b) { return b.create<sem::AbstractInt>(); }
+ /// @param b the ProgramBuilder
+ /// @param elem_value the abstract-int value
+ /// @return a new AST abstract-int literal value expression
+ static inline const ast::Expression* Expr(ProgramBuilder& b, AInt elem_value) {
+ return b.Expr(elem_value);
+ }
+};
+
/// Helper for building vector types and expressions
template <uint32_t N, typename T>
struct DataType<vec<N, T>> {