IntrinsicTable: Fix a number of TODOs
And add tests for IntrinsicTable.
Drop all the type unwrapping - be precise:
* Display the actual argument types in the signature mismatch message
* Only dereference pointer arguments if the parameter does not expect a pointer
Correctly match access control on storage types
Note that I was mistaken in tint:486 - the TypeDeterminer is resolving identifiers to variables correctly as pointer types. The confustion here was probably due to all the UnwrapAll() calls, which have now all gone.
Fixed: tint:486
Change-Id: I239eabd1fedfc082566c4af616ccfc58786cae25
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/41280
Commit-Queue: Ben Clayton <bclayton@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 4147124..d2b2637 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -835,6 +835,7 @@
"src/diagnostic/formatter_test.cc",
"src/diagnostic/printer_test.cc",
"src/inspector/inspector_test.cc",
+ "src/intrinsic_table_test.cc",
"src/namer_test.cc",
"src/program_builder_test.cc",
"src/program_test.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 4026aac..e4b81d4 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -465,6 +465,7 @@
diagnostic/formatter_test.cc
diagnostic/printer_test.cc
inspector/inspector_test.cc
+ intrinsic_table_test.cc
namer_test.cc
program_test.cc
scope_stack_test.cc
diff --git a/src/intrinsic_table.cc b/src/intrinsic_table.cc
index a0f3cc2..74c2242 100644
--- a/src/intrinsic_table.cc
+++ b/src/intrinsic_table.cc
@@ -23,6 +23,7 @@
#include "src/block_allocator.h"
#include "src/program_builder.h"
#include "src/semantic/intrinsic.h"
+#include "src/type/access_control_type.h"
#include "src/type/depth_texture_type.h"
#include "src/type/f32_type.h"
#include "src/type/multisampled_texture_type.h"
@@ -91,15 +92,33 @@
virtual ~Matcher() = default;
/// Checks whether the given argument type matches.
+ /// Aliases are automatically unwrapped before matching.
/// Match may add to, or compare against the open types and numbers in state.
/// @returns true if the argument type is as expected.
- virtual bool Match(MatchState& state, type::Type* argument_type) const = 0;
+ bool Match(MatchState& state, type::Type* argument_type) const {
+ auto* unwrapped = argument_type;
+ while (auto* alias = unwrapped->As<type::Alias>()) {
+ unwrapped = alias->type();
+ }
+ return MatchUnwrapped(state, unwrapped);
+ }
+
+ /// @return true if the matcher is expecting a pointer. If this method returns
+ /// false and the argument is a pointer type, then the argument should be
+ /// dereferenced before calling.
+ virtual bool ExpectsPointer() const { return false; }
/// @return a string representation of the matcher. Used for printing error
/// messages when no overload is found.
virtual std::string str() const = 0;
protected:
+ /// Checks whether the given alias-unwrapped argument type matches.
+ /// Match may add to, or compare against the open types and numbers in state.
+ /// @returns true if the argument type is as expected.
+ virtual bool MatchUnwrapped(MatchState& state,
+ type::Type* argument_type) const = 0;
+
/// Checks `state.open_type` to see if the OpenType `t` is equal to the type
/// `ty`. If `state.open_type` does not contain an entry for `t`, then `ty`
/// is added and returns true.
@@ -154,7 +173,7 @@
public:
explicit OpenTypeBuilder(OpenType open_type) : open_type_(open_type) {}
- bool Match(MatchState& state, type::Type* ty) const override {
+ bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
return MatchOpenType(state, open_type_, ty);
}
@@ -171,7 +190,7 @@
/// VoidBuilder is a Matcher / Builder for void types.
class VoidBuilder : public Builder {
public:
- bool Match(MatchState&, type::Type* ty) const override {
+ bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
return ty->Is<type::Void>();
}
type::Type* Build(BuildState& state) const override {
@@ -183,7 +202,7 @@
/// BoolBuilder is a Matcher / Builder for boolean types.
class BoolBuilder : public Builder {
public:
- bool Match(MatchState&, type::Type* ty) const override {
+ bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
return ty->Is<type::Bool>();
}
type::Type* Build(BuildState& state) const override {
@@ -195,7 +214,7 @@
/// F32Builder is a Matcher / Builder for f32 types.
class F32Builder : public Builder {
public:
- bool Match(MatchState&, type::Type* ty) const override {
+ bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
return ty->Is<type::F32>();
}
type::Type* Build(BuildState& state) const override {
@@ -207,7 +226,7 @@
/// U32Builder is a Matcher / Builder for u32 types.
class U32Builder : public Builder {
public:
- bool Match(MatchState&, type::Type* ty) const override {
+ bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
return ty->Is<type::U32>();
}
type::Type* Build(BuildState& state) const override {
@@ -219,7 +238,7 @@
/// I32Builder is a Matcher / Builder for i32 types.
class I32Builder : public Builder {
public:
- bool Match(MatchState&, type::Type* ty) const override {
+ bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
return ty->Is<type::I32>();
}
type::Type* Build(BuildState& state) const override {
@@ -231,7 +250,7 @@
/// IU32Matcher is a Matcher for i32 or u32 types.
class IU32Matcher : public Matcher {
public:
- bool Match(MatchState&, type::Type* ty) const override {
+ bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
return ty->Is<type::I32>() || ty->Is<type::U32>();
}
std::string str() const override { return "i32 or u32"; }
@@ -240,7 +259,7 @@
/// FIU32Matcher is a Matcher for f32, i32 or u32 types.
class FIU32Matcher : public Matcher {
public:
- bool Match(MatchState&, type::Type* ty) const override {
+ bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
return ty->Is<type::F32>() || ty->Is<type::I32>() || ty->Is<type::U32>();
}
std::string str() const override { return "f32, i32 or u32"; }
@@ -249,7 +268,7 @@
/// ScalarMatcher is a Matcher for f32, i32, u32 or boolean types.
class ScalarMatcher : public Matcher {
public:
- bool Match(MatchState&, type::Type* ty) const override {
+ bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
return ty->is_scalar();
}
std::string str() const override { return "scalar"; }
@@ -262,8 +281,8 @@
OpenSizeVecBuilder(OpenNumber size, Builder* element_builder)
: size_(size), element_builder_(element_builder) {}
- bool Match(MatchState& state, type::Type* ty) const override {
- if (auto* vec = ty->UnwrapAll()->As<type::Vector>()) {
+ bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
+ if (auto* vec = ty->As<type::Vector>()) {
if (!MatchOpenNumber(state, size_, vec->size())) {
return false;
}
@@ -294,8 +313,8 @@
VecBuilder(uint32_t size, Builder* element_builder)
: size_(size), element_builder_(element_builder) {}
- bool Match(MatchState& state, type::Type* ty) const override {
- if (auto* vec = ty->UnwrapAll()->As<type::Vector>()) {
+ bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
+ if (auto* vec = ty->As<type::Vector>()) {
if (vec->size() == size_) {
return element_builder_->Match(state, vec->type());
}
@@ -326,8 +345,8 @@
Builder* element_builder)
: columns_(columns), rows_(rows), element_builder_(element_builder) {}
- bool Match(MatchState& state, type::Type* ty) const override {
- if (auto* mat = ty->UnwrapAll()->As<type::Matrix>()) {
+ bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
+ if (auto* mat = ty->As<type::Matrix>()) {
if (!MatchOpenNumber(state, columns_, mat->columns())) {
return false;
}
@@ -347,7 +366,7 @@
}
std::string str() const override {
- return "max" + std::string(tint::str(columns_)) + "x" +
+ return "mat" + std::string(tint::str(columns_)) + "x" +
std::string(tint::str(rows_)) + "<" + element_builder_->str() + ">";
}
@@ -363,15 +382,11 @@
explicit PtrBuilder(Builder* element_builder)
: element_builder_(element_builder) {}
- bool Match(MatchState& state, type::Type* ty) const override {
+ bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
if (auto* ptr = ty->As<type::Pointer>()) {
return element_builder_->Match(state, ptr->type());
}
- // TODO(bclayton): https://crbug.com/tint/486
- // TypeDeterminer currently folds away the pointers on expressions.
- // We'll need to fix this to ensure that pointer parameters are not fed
- // non-pointer arguments, but for now just accept them.
- return element_builder_->Match(state, ty);
+ return false;
}
type::Type* Build(BuildState& state) const override {
@@ -379,6 +394,8 @@
return state.ty_mgr.Get<type::Pointer>(el, ast::StorageClass::kNone);
}
+ bool ExpectsPointer() const override { return true; }
+
std::string str() const override {
return "ptr<" + element_builder_->str() + ">";
}
@@ -393,7 +410,7 @@
explicit ArrayBuilder(Builder* element_builder)
: element_builder_(element_builder) {}
- bool Match(MatchState& state, type::Type* ty) const override {
+ bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
if (auto* arr = ty->As<type::Array>()) {
if (arr->size() == 0) {
return element_builder_->Match(state, arr->type());
@@ -422,7 +439,7 @@
Builder* type_builder)
: dimensions_(dimensions), type_builder_(type_builder) {}
- bool Match(MatchState& state, type::Type* ty) const override {
+ bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
if (auto* tex = ty->As<type::SampledTexture>()) {
if (tex->dim() == dimensions_) {
return type_builder_->Match(state, tex->type());
@@ -455,7 +472,7 @@
Builder* type_builder)
: dimensions_(dimensions), type_builder_(type_builder) {}
- bool Match(MatchState& state, type::Type* ty) const override {
+ bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
if (auto* tex = ty->As<type::MultisampledTexture>()) {
if (tex->dim() == dimensions_) {
return type_builder_->Match(state, tex->type());
@@ -487,7 +504,7 @@
explicit DepthTextureBuilder(type::TextureDimension dimensions)
: dimensions_(dimensions) {}
- bool Match(MatchState&, type::Type* ty) const override {
+ bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
if (auto* tex = ty->As<type::DepthTexture>()) {
return tex->dim() == dimensions_;
}
@@ -520,7 +537,15 @@
texel_format_(texel_format),
channel_format_(channel_format) {}
- bool Match(MatchState& state, type::Type* ty) const override {
+ bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
+ if (auto* ac = ty->As<type::AccessControl>()) {
+ // If we have an storage texture argument that's got an access control
+ // type wrapped around it, accept it. Signatures that don't include an
+ // access control imply any access. Example:
+ // textureDimensions(t : texture_storage_1d<F>) -> i32
+ ty = ac->type();
+ }
+
if (auto* tex = ty->As<type::StorageTexture>()) {
if (MatchOpenNumber(state, texel_format_,
static_cast<uint32_t>(tex->image_format()))) {
@@ -557,7 +582,7 @@
public:
explicit SamplerBuilder(type::SamplerKind kind) : kind_(kind) {}
- bool Match(MatchState&, type::Type* ty) const override {
+ bool MatchUnwrapped(MatchState&, type::Type* ty) const override {
if (auto* sampler = ty->As<type::Sampler>()) {
return sampler->kind() == kind_;
}
@@ -582,6 +607,38 @@
type::SamplerKind const kind_;
};
+/// AccessControlBuilder is a Matcher / Builder for AccessControl types
+class AccessControlBuilder : public Builder {
+ public:
+ explicit AccessControlBuilder(ast::AccessControl access_control,
+ Builder* type)
+ : access_control_(access_control), type_(type) {}
+
+ bool MatchUnwrapped(MatchState& state, type::Type* ty) const override {
+ if (auto* ac = ty->As<type::AccessControl>()) {
+ if (ac->access_control() == access_control_) {
+ return type_->Match(state, ty);
+ }
+ }
+ return false;
+ }
+
+ type::Type* Build(BuildState& state) const override {
+ auto* ty = type_->Build(state);
+ return state.ty_mgr.Get<type::AccessControl>(access_control_, ty);
+ }
+
+ std::string str() const override {
+ std::stringstream ss;
+ ss << "[[access(" << access_control_ << ")]] " << type_->str();
+ return ss.str();
+ }
+
+ private:
+ ast::AccessControl const access_control_;
+ Builder* const type_;
+};
+
/// Impl is the private implementation of the IntrinsicTable interface.
class Impl : public IntrinsicTable {
public:
@@ -717,6 +774,12 @@
return matcher_allocator_.Create<SamplerBuilder>(kind);
}
+ /// @returns a Matcher / Builder that matches an access control type
+ Builder* access_control(ast::AccessControl access_control, Builder* type) {
+ return matcher_allocator_.Create<AccessControlBuilder>(access_control,
+ type);
+ }
+
/// Registers an overload with the given intrinsic type, return type Matcher /
/// Builder, and parameter Matcher / Builders.
/// This overload of Register does not constrain any OpenTypes.
@@ -1044,6 +1107,26 @@
storage_texture(Dim::k2dArray, OpenNumber::F, OpenType::T);
auto* tex_storage_3d_FT =
storage_texture(Dim::k3d, OpenNumber::F, OpenType::T);
+ auto* tex_storage_ro_1d_FT =
+ access_control(ast::AccessControl::kReadOnly, tex_storage_1d_FT);
+ auto* tex_storage_ro_1d_array_FT =
+ access_control(ast::AccessControl::kReadOnly, tex_storage_1d_array_FT);
+ auto* tex_storage_ro_2d_FT =
+ access_control(ast::AccessControl::kReadOnly, tex_storage_2d_FT);
+ auto* tex_storage_ro_2d_array_FT =
+ access_control(ast::AccessControl::kReadOnly, tex_storage_2d_array_FT);
+ auto* tex_storage_ro_3d_FT =
+ access_control(ast::AccessControl::kReadOnly, tex_storage_3d_FT);
+ auto* tex_storage_wo_1d_FT =
+ access_control(ast::AccessControl::kWriteOnly, tex_storage_1d_FT);
+ auto* tex_storage_wo_1d_array_FT =
+ access_control(ast::AccessControl::kWriteOnly, tex_storage_1d_array_FT);
+ auto* tex_storage_wo_2d_FT =
+ access_control(ast::AccessControl::kWriteOnly, tex_storage_2d_FT);
+ auto* tex_storage_wo_2d_array_FT =
+ access_control(ast::AccessControl::kWriteOnly, tex_storage_2d_array_FT);
+ auto* tex_storage_wo_3d_FT =
+ access_control(ast::AccessControl::kWriteOnly, tex_storage_3d_FT);
auto* sampler = this->sampler(type::SamplerKind::kSampler);
auto* sampler_comparison =
this->sampler(type::SamplerKind::kComparisonSampler);
@@ -1170,14 +1253,12 @@
Register(I::kTextureSampleLevel, f32, {{t, tex_depth_cube}, {s, sampler}, {coords, vec3_f32}, {level, i32}, }); // NOLINT
Register(I::kTextureSampleLevel, f32, {{t, tex_depth_cube_array},{s, sampler}, {coords, vec3_f32}, {array_index, i32}, {level, i32}, }); // NOLINT
- // TODO(bclayton): Check for [[access(write)]]
- Register(I::kTextureStore, void_, {{t, tex_storage_1d_FT}, {coords, i32}, {value, vec4_T}, }); // NOLINT
- Register(I::kTextureStore, void_, {{t, tex_storage_1d_array_FT},{coords, i32}, {array_index, i32}, {value, vec4_T}, }); // NOLINT
- Register(I::kTextureStore, void_, {{t, tex_storage_2d_FT}, {coords, vec2_i32}, {value, vec4_T}, }); // NOLINT
- Register(I::kTextureStore, void_, {{t, tex_storage_2d_array_FT},{coords, vec2_i32}, {array_index, i32}, {value, vec4_T}, }); // NOLINT
- Register(I::kTextureStore, void_, {{t, tex_storage_3d_FT}, {coords, vec3_i32}, {value, vec4_T}, }); // NOLINT
+ Register(I::kTextureStore, void_, {{t, tex_storage_wo_1d_FT}, {coords, i32}, {value, vec4_T}, }); // NOLINT
+ Register(I::kTextureStore, void_, {{t, tex_storage_wo_1d_array_FT},{coords, i32}, {array_index, i32}, {value, vec4_T}, }); // NOLINT
+ Register(I::kTextureStore, void_, {{t, tex_storage_wo_2d_FT}, {coords, vec2_i32}, {value, vec4_T}, }); // NOLINT
+ Register(I::kTextureStore, void_, {{t, tex_storage_wo_2d_array_FT},{coords, vec2_i32}, {array_index, i32}, {value, vec4_T}, }); // NOLINT
+ Register(I::kTextureStore, void_, {{t, tex_storage_wo_3d_FT}, {coords, vec3_i32}, {value, vec4_T}, }); // NOLINT
- // TODO(bclayton): Check for [[access(read)]]
Register(I::kTextureLoad, vec4_T, {{t, tex_2d_T}, {coords, vec2_i32}, {level, i32}, }); // NOLINT
Register(I::kTextureLoad, vec4_T, {{t, tex_2d_array_T}, {coords, vec2_i32}, {array_index, i32}, {level, i32}, }); // NOLINT
Register(I::kTextureLoad, vec4_T, {{t, tex_3d_T}, {coords, vec3_i32}, {level, i32}, }); // NOLINT
@@ -1185,11 +1266,11 @@
Register(I::kTextureLoad, vec4_T, {{t, tex_ms_2d_array_T}, {coords, vec2_i32}, {array_index, i32}, {sample_index, i32}, }); // NOLINT
Register(I::kTextureLoad, f32, {{t, tex_depth_2d}, {coords, vec2_i32}, {level, i32}, }); // NOLINT
Register(I::kTextureLoad, f32, {{t, tex_depth_2d_array}, {coords, vec2_i32}, {array_index, i32}, {level, i32}, }); // NOLINT
- Register(I::kTextureLoad, vec4_T, {{t, tex_storage_1d_FT}, {coords, i32}, }); // NOLINT
- Register(I::kTextureLoad, vec4_T, {{t, tex_storage_1d_array_FT},{coords, i32}, {array_index, i32}, }); // NOLINT
- Register(I::kTextureLoad, vec4_T, {{t, tex_storage_2d_FT}, {coords, vec2_i32}, }); // NOLINT
- Register(I::kTextureLoad, vec4_T, {{t, tex_storage_2d_array_FT},{coords, vec2_i32}, {array_index, i32}, }); // NOLINT
- Register(I::kTextureLoad, vec4_T, {{t, tex_storage_3d_FT}, {coords, vec3_i32}, }); // NOLINT
+ Register(I::kTextureLoad, vec4_T, {{t, tex_storage_ro_1d_FT}, {coords, i32}, }); // NOLINT
+ Register(I::kTextureLoad, vec4_T, {{t, tex_storage_ro_1d_array_FT},{coords, i32}, {array_index, i32}, }); // NOLINT
+ Register(I::kTextureLoad, vec4_T, {{t, tex_storage_ro_2d_FT}, {coords, vec2_i32}, }); // NOLINT
+ Register(I::kTextureLoad, vec4_T, {{t, tex_storage_ro_2d_array_FT},{coords, vec2_i32}, {array_index, i32}, }); // NOLINT
+ Register(I::kTextureLoad, vec4_T, {{t, tex_storage_ro_3d_FT}, {coords, vec3_i32}, }); // NOLINT
// TODO(bclayton): Update the rest of tint to reflect the spec changes made in
// https://github.com/gpuweb/gpuweb/pull/1301:
@@ -1287,7 +1368,7 @@
ss << ", ";
}
first = false;
- ss << arg->UnwrapAll()->FriendlyName(builder.Symbols());
+ ss << arg->FriendlyName(builder.Symbols());
}
}
ss << ")" << std::endl;
@@ -1327,13 +1408,21 @@
auto count = std::min(parameters.size(), args.size());
for (size_t i = 0; i < count; i++) {
assert(args[i]);
- auto* arg_ty = args[i]->UnwrapAll();
- if (!parameters[i].matcher->Match(matcher_state, arg_ty)) {
- matched = false;
- continue;
+ auto* arg_ty = args[i];
+ if (auto* ptr = arg_ty->As<type::Pointer>()) {
+ if (!parameters[i].matcher->ExpectsPointer()) {
+ // Argument is a pointer, but the matcher isn't expecting one.
+ // Perform an implicit dereference.
+ arg_ty = ptr->type();
+ }
}
- // Weight correct parameter matches more than exact number of arguments
- match_score += 2;
+ if (parameters[i].matcher->Match(matcher_state, arg_ty)) {
+ // A correct parameter match is scored higher than number of parameters to
+ // arguments.
+ match_score += 2;
+ } else {
+ matched = false;
+ }
}
if (!matched) {
return nullptr;
diff --git a/src/intrinsic_table_test.cc b/src/intrinsic_table_test.cc
new file mode 100644
index 0000000..fc75e0c
--- /dev/null
+++ b/src/intrinsic_table_test.cc
@@ -0,0 +1,488 @@
+// Copyright 2021 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/intrinsic_table.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "src/program_builder.h"
+#include "src/type/access_control_type.h"
+#include "src/type/depth_texture_type.h"
+#include "src/type/multisampled_texture_type.h"
+#include "src/type/sampled_texture_type.h"
+#include "src/type/storage_texture_type.h"
+
+namespace tint {
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::HasSubstr;
+
+using IntrinsicType = semantic::IntrinsicType;
+using Parameter = semantic::Parameter;
+
+class IntrinsicTableTest : public testing::Test, public ProgramBuilder {
+ public:
+ std::unique_ptr<IntrinsicTable> table = IntrinsicTable::Create();
+};
+
+TEST_F(IntrinsicTableTest, MatchF32) {
+ auto result = table->Lookup(*this, IntrinsicType::kCos, {ty.f32()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCos);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
+ EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.f32()}));
+}
+
+TEST_F(IntrinsicTableTest, MismatchF32) {
+ auto result = table->Lookup(*this, IntrinsicType::kCos, {ty.i32()});
+ ASSERT_EQ(result.intrinsic, nullptr);
+ ASSERT_THAT(result.error, HasSubstr("no matching call"));
+}
+
+TEST_F(IntrinsicTableTest, MatchU32) {
+ auto result =
+ table->Lookup(*this, IntrinsicType::kUnpack2x16Float, {ty.u32()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kUnpack2x16Float);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec2<f32>());
+ EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.u32()}));
+}
+
+TEST_F(IntrinsicTableTest, MismatchU32) {
+ auto result =
+ table->Lookup(*this, IntrinsicType::kUnpack2x16Float, {ty.f32()});
+ ASSERT_EQ(result.intrinsic, nullptr);
+ ASSERT_THAT(result.error, HasSubstr("no matching call"));
+}
+
+TEST_F(IntrinsicTableTest, MatchI32) {
+ auto* tex =
+ create<type::SampledTexture>(type::TextureDimension::k1d, ty.f32());
+ auto result =
+ table->Lookup(*this, IntrinsicType::kTextureLoad, {tex, ty.i32()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec4<f32>());
+ EXPECT_THAT(result.intrinsic->Parameters(),
+ ElementsAre(Parameter{tex, Parameter::Usage::kTexture},
+ Parameter{ty.i32(), Parameter::Usage::kCoords}));
+}
+
+TEST_F(IntrinsicTableTest, MismatchI32) {
+ auto* tex =
+ create<type::SampledTexture>(type::TextureDimension::k1d, ty.f32());
+ auto result =
+ table->Lookup(*this, IntrinsicType::kTextureLoad, {tex, ty.f32()});
+ ASSERT_EQ(result.intrinsic, nullptr);
+ ASSERT_THAT(result.error, HasSubstr("no matching call"));
+}
+
+TEST_F(IntrinsicTableTest, MatchIU32AsI32) {
+ auto result = table->Lookup(*this, IntrinsicType::kCountOneBits, {ty.i32()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCountOneBits);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.i32());
+ EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.i32()}));
+}
+
+TEST_F(IntrinsicTableTest, MatchIU32AsU32) {
+ auto result = table->Lookup(*this, IntrinsicType::kCountOneBits, {ty.u32()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCountOneBits);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.u32());
+ EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.u32()}));
+}
+
+TEST_F(IntrinsicTableTest, MismatchIU32) {
+ auto result = table->Lookup(*this, IntrinsicType::kCountOneBits, {ty.f32()});
+ ASSERT_EQ(result.intrinsic, nullptr);
+ ASSERT_THAT(result.error, HasSubstr("no matching call"));
+}
+
+TEST_F(IntrinsicTableTest, MatchFIU32AsI32) {
+ auto result = table->Lookup(*this, IntrinsicType::kClamp,
+ {ty.i32(), ty.i32(), ty.i32()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.i32());
+ EXPECT_THAT(result.intrinsic->Parameters(),
+ ElementsAre(Parameter{ty.i32()}, Parameter{ty.i32()},
+ Parameter{ty.i32()}));
+}
+
+TEST_F(IntrinsicTableTest, MatchFIU32AsU32) {
+ auto result = table->Lookup(*this, IntrinsicType::kClamp,
+ {ty.u32(), ty.u32(), ty.u32()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.u32());
+ EXPECT_THAT(result.intrinsic->Parameters(),
+ ElementsAre(Parameter{ty.u32()}, Parameter{ty.u32()},
+ Parameter{ty.u32()}));
+}
+
+TEST_F(IntrinsicTableTest, MatchFIU32AsF32) {
+ auto result = table->Lookup(*this, IntrinsicType::kClamp,
+ {ty.f32(), ty.f32(), ty.f32()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
+ EXPECT_THAT(result.intrinsic->Parameters(),
+ ElementsAre(Parameter{ty.f32()}, Parameter{ty.f32()},
+ Parameter{ty.f32()}));
+}
+
+TEST_F(IntrinsicTableTest, MismatchFIU32) {
+ auto result = table->Lookup(*this, IntrinsicType::kClamp,
+ {ty.bool_(), ty.bool_(), ty.bool_()});
+ ASSERT_EQ(result.intrinsic, nullptr);
+ ASSERT_THAT(result.error, HasSubstr("no matching call"));
+}
+
+TEST_F(IntrinsicTableTest, MatchBool) {
+ auto result = table->Lookup(*this, IntrinsicType::kSelect,
+ {ty.f32(), ty.f32(), ty.bool_()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kSelect);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
+ EXPECT_THAT(result.intrinsic->Parameters(),
+ ElementsAre(Parameter{ty.f32()}, Parameter{ty.f32()},
+ Parameter{ty.bool_()}));
+}
+
+TEST_F(IntrinsicTableTest, MismatchBool) {
+ auto result = table->Lookup(*this, IntrinsicType::kSelect,
+ {ty.f32(), ty.f32(), ty.f32()});
+ ASSERT_EQ(result.intrinsic, nullptr);
+ ASSERT_THAT(result.error, HasSubstr("no matching call"));
+}
+
+TEST_F(IntrinsicTableTest, MatchPointer) {
+ auto result =
+ table->Lookup(*this, IntrinsicType::kModf,
+ {ty.f32(), ty.pointer<f32>(ast::StorageClass::kNone)});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kModf);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
+ EXPECT_THAT(
+ result.intrinsic->Parameters(),
+ ElementsAre(Parameter{ty.f32()},
+ Parameter{ty.pointer<f32>(ast::StorageClass::kNone)}));
+}
+
+TEST_F(IntrinsicTableTest, MismatchPointer) {
+ auto result =
+ table->Lookup(*this, IntrinsicType::kModf, {ty.f32(), ty.f32()});
+ ASSERT_EQ(result.intrinsic, nullptr);
+ ASSERT_THAT(result.error, HasSubstr("no matching call"));
+}
+
+TEST_F(IntrinsicTableTest, MatchArray) {
+ auto result =
+ table->Lookup(*this, IntrinsicType::kArrayLength, {ty.array<f32>()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kArrayLength);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.u32());
+ EXPECT_THAT(result.intrinsic->Parameters(),
+ ElementsAre(Parameter{ty.array<f32>()}));
+}
+
+TEST_F(IntrinsicTableTest, MismatchArray) {
+ auto result = table->Lookup(*this, IntrinsicType::kArrayLength, {ty.f32()});
+ ASSERT_EQ(result.intrinsic, nullptr);
+ ASSERT_THAT(result.error, HasSubstr("no matching call"));
+}
+
+TEST_F(IntrinsicTableTest, MatchSampler) {
+ auto* tex =
+ create<type::SampledTexture>(type::TextureDimension::k2d, ty.f32());
+ auto* sampler = create<type::Sampler>(type::SamplerKind::kSampler);
+ auto result = table->Lookup(*this, IntrinsicType::kTextureSample,
+ {tex, sampler, ty.vec2<f32>()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureSample);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec4<f32>());
+ EXPECT_THAT(
+ result.intrinsic->Parameters(),
+ ElementsAre(Parameter{tex, Parameter::Usage::kTexture},
+ Parameter{sampler, Parameter::Usage::kSampler},
+ Parameter{ty.vec2<f32>(), Parameter::Usage::kCoords}));
+}
+
+TEST_F(IntrinsicTableTest, MismatchSampler) {
+ auto* tex =
+ create<type::SampledTexture>(type::TextureDimension::k2d, ty.f32());
+ auto result = table->Lookup(*this, IntrinsicType::kTextureSample,
+ {tex, ty.f32(), ty.vec2<f32>()});
+ ASSERT_EQ(result.intrinsic, nullptr);
+ ASSERT_THAT(result.error, HasSubstr("no matching call"));
+}
+
+TEST_F(IntrinsicTableTest, MatchSampledTexture) {
+ auto* tex =
+ create<type::SampledTexture>(type::TextureDimension::k2d, ty.f32());
+ auto result =
+ table->Lookup(*this, IntrinsicType::kTextureLoad, {tex, ty.vec2<i32>()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec4<f32>());
+ EXPECT_THAT(
+ result.intrinsic->Parameters(),
+ ElementsAre(Parameter{tex, Parameter::Usage::kTexture},
+ Parameter{ty.vec2<i32>(), Parameter::Usage::kCoords}));
+}
+
+TEST_F(IntrinsicTableTest, MatchMultisampledTexture) {
+ auto* tex =
+ create<type::MultisampledTexture>(type::TextureDimension::k2d, ty.f32());
+ auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
+ {tex, ty.vec2<i32>(), ty.i32()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec4<f32>());
+ EXPECT_THAT(result.intrinsic->Parameters(),
+ ElementsAre(Parameter{tex, Parameter::Usage::kTexture},
+ Parameter{ty.vec2<i32>(), Parameter::Usage::kCoords},
+ Parameter{ty.i32(), Parameter::Usage::kSampleIndex}));
+}
+
+TEST_F(IntrinsicTableTest, MatchDepthTexture) {
+ auto* tex = create<type::DepthTexture>(type::TextureDimension::k2d);
+ auto result =
+ table->Lookup(*this, IntrinsicType::kTextureLoad, {tex, ty.vec2<i32>()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
+ EXPECT_THAT(
+ result.intrinsic->Parameters(),
+ ElementsAre(Parameter{tex, Parameter::Usage::kTexture},
+ Parameter{ty.vec2<i32>(), Parameter::Usage::kCoords}));
+}
+
+TEST_F(IntrinsicTableTest, MatchROStorageTexture) {
+ auto* tex = create<type::StorageTexture>(
+ type::TextureDimension::k2d, type::ImageFormat::kR16Float,
+ type::StorageTexture::SubtypeFor(type::ImageFormat::kR16Float, Types()));
+ auto* tex_ac =
+ create<type::AccessControl>(ast::AccessControl::kReadOnly, tex);
+ auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
+ {tex_ac, ty.vec2<i32>()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec4<f32>());
+ EXPECT_THAT(
+ result.intrinsic->Parameters(),
+ ElementsAre(Parameter{tex_ac, Parameter::Usage::kTexture},
+ Parameter{ty.vec2<i32>(), Parameter::Usage::kCoords}));
+}
+
+TEST_F(IntrinsicTableTest, MatchWOStorageTexture) {
+ auto* tex = create<type::StorageTexture>(
+ type::TextureDimension::k2d, type::ImageFormat::kR16Float,
+ type::StorageTexture::SubtypeFor(type::ImageFormat::kR16Float, Types()));
+ auto* tex_ac =
+ create<type::AccessControl>(ast::AccessControl::kWriteOnly, tex);
+ auto result = table->Lookup(*this, IntrinsicType::kTextureStore,
+ {tex_ac, ty.vec2<i32>(), ty.vec4<f32>()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureStore);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.void_());
+ EXPECT_THAT(result.intrinsic->Parameters(),
+ ElementsAre(Parameter{tex_ac, Parameter::Usage::kTexture},
+ Parameter{ty.vec2<i32>(), Parameter::Usage::kCoords},
+ Parameter{ty.vec4<f32>(), Parameter::Usage::kValue}));
+}
+
+TEST_F(IntrinsicTableTest, MismatchTexture) {
+ auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
+ {ty.f32(), ty.vec2<i32>()});
+ ASSERT_EQ(result.intrinsic, nullptr);
+ ASSERT_THAT(result.error, HasSubstr("no matching call"));
+}
+
+TEST_F(IntrinsicTableTest, MatchAutoPointerDereference) {
+ auto result = table->Lookup(*this, IntrinsicType::kCos,
+ {ty.pointer<f32>(ast::StorageClass::kNone)});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCos);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
+ EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.f32()}));
+}
+
+TEST_F(IntrinsicTableTest, MatchWithAliasUnwrapping) {
+ auto* alias_a = ty.alias("alias_a", ty.f32());
+ auto* alias_b = ty.alias("alias_b", alias_a);
+ auto* alias_c = ty.alias("alias_c", alias_b);
+ auto result = table->Lookup(*this, IntrinsicType::kCos, {alias_c});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kCos);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
+ EXPECT_THAT(result.intrinsic->Parameters(), ElementsAre(Parameter{ty.f32()}));
+}
+
+TEST_F(IntrinsicTableTest, MatchOpenType) {
+ auto result = table->Lookup(*this, IntrinsicType::kClamp,
+ {ty.f32(), ty.f32(), ty.f32()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
+ EXPECT_THAT(result.intrinsic->Parameters(),
+ ElementsAre(Parameter{ty.f32()}, Parameter{ty.f32()},
+ Parameter{ty.f32()}));
+}
+
+TEST_F(IntrinsicTableTest, MismatchOpenType) {
+ auto result = table->Lookup(*this, IntrinsicType::kClamp,
+ {ty.f32(), ty.u32(), ty.f32()});
+ ASSERT_EQ(result.intrinsic, nullptr);
+ ASSERT_THAT(result.error, HasSubstr("no matching call"));
+}
+
+TEST_F(IntrinsicTableTest, MatchOpenSizeVector) {
+ auto result = table->Lookup(*this, IntrinsicType::kClamp,
+ {ty.vec2<f32>(), ty.vec2<f32>(), ty.vec2<f32>()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kClamp);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec2<f32>());
+ EXPECT_THAT(result.intrinsic->Parameters(),
+ ElementsAre(Parameter{ty.vec2<f32>()}, Parameter{ty.vec2<f32>()},
+ Parameter{ty.vec2<f32>()}));
+}
+
+TEST_F(IntrinsicTableTest, MismatchOpenSizeVector) {
+ auto result = table->Lookup(*this, IntrinsicType::kClamp,
+ {ty.vec2<f32>(), ty.vec2<u32>(), ty.vec2<f32>()});
+ ASSERT_EQ(result.intrinsic, nullptr);
+ ASSERT_THAT(result.error, HasSubstr("no matching call"));
+}
+
+TEST_F(IntrinsicTableTest, MatchOpenSizeMatrix) {
+ auto result =
+ table->Lookup(*this, IntrinsicType::kDeterminant, {ty.mat3x3<f32>()});
+ ASSERT_NE(result.intrinsic, nullptr);
+ ASSERT_EQ(result.error, "");
+ EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kDeterminant);
+ EXPECT_THAT(result.intrinsic->ReturnType(), ty.f32());
+ EXPECT_THAT(result.intrinsic->Parameters(),
+ ElementsAre(Parameter{ty.mat3x3<f32>()}));
+}
+
+TEST_F(IntrinsicTableTest, MismatchOpenSizeMatrix) {
+ auto result =
+ table->Lookup(*this, IntrinsicType::kDeterminant, {ty.mat3x2<f32>()});
+ ASSERT_EQ(result.intrinsic, nullptr);
+ ASSERT_THAT(result.error, HasSubstr("no matching call"));
+}
+
+TEST_F(IntrinsicTableTest, OverloadOrderByNumberOfParameters) {
+ // None of the arguments match, so expect the overloads with 2 parameters to
+ // come first
+ auto result = table->Lookup(*this, IntrinsicType::kTextureDimensions,
+ {ty.bool_(), ty.bool_()});
+ ASSERT_EQ(result.error,
+ R"(no matching call to textureDimensions(bool, bool)
+
+27 candidate functions:
+ textureDimensions(texture : texture_2d<T>, level : i32) -> vec2<i32>
+ textureDimensions(texture : texture_2d_array<T>, level : i32) -> vec2<i32>
+ textureDimensions(texture : texture_3d<T>, level : i32) -> vec3<i32>
+ textureDimensions(texture : texture_cube<T>, level : i32) -> vec3<i32>
+ textureDimensions(texture : texture_cube_array<T>, level : i32) -> vec3<i32>
+ textureDimensions(texture : texture_depth_2d, level : i32) -> vec2<i32>
+ textureDimensions(texture : texture_depth_2d_array, level : i32) -> vec2<i32>
+ textureDimensions(texture : texture_depth_cube, level : i32) -> vec3<i32>
+ textureDimensions(texture : texture_depth_cube_array, level : i32) -> vec3<i32>
+ textureDimensions(texture : texture_1d<T>) -> i32
+ textureDimensions(texture : texture_1d_array<T>) -> i32
+ textureDimensions(texture : texture_2d<T>) -> vec2<i32>
+ textureDimensions(texture : texture_2d_array<T>) -> vec2<i32>
+ textureDimensions(texture : texture_3d<T>) -> vec3<i32>
+ textureDimensions(texture : texture_cube<T>) -> vec3<i32>
+ textureDimensions(texture : texture_cube_array<T>) -> vec3<i32>
+ textureDimensions(texture : texture_multisampled_2d<T>) -> vec2<i32>
+ textureDimensions(texture : texture_multisampled_2d_array<T>) -> vec2<i32>
+ textureDimensions(texture : texture_depth_2d) -> vec2<i32>
+ textureDimensions(texture : texture_depth_2d_array) -> vec2<i32>
+ textureDimensions(texture : texture_depth_cube) -> vec3<i32>
+ textureDimensions(texture : texture_depth_cube_array) -> vec3<i32>
+ textureDimensions(texture : texture_storage_1d<F>) -> i32
+ textureDimensions(texture : texture_storage_1d_array<F>) -> i32
+ textureDimensions(texture : texture_storage_2d<F>) -> vec2<i32>
+ textureDimensions(texture : texture_storage_2d_array<F>) -> vec2<i32>
+ textureDimensions(texture : texture_storage_3d<F>) -> vec3<i32>
+)");
+}
+
+TEST_F(IntrinsicTableTest, OverloadOrderByMatchingParameter) {
+ auto* tex = create<type::DepthTexture>(type::TextureDimension::k2d);
+ auto result = table->Lookup(*this, IntrinsicType::kTextureDimensions,
+ {tex, ty.bool_()});
+ ASSERT_EQ(result.error,
+ R"(no matching call to textureDimensions(texture_depth_2d, bool)
+
+27 candidate functions:
+ textureDimensions(texture : texture_depth_2d, level : i32) -> vec2<i32>
+ textureDimensions(texture : texture_depth_2d) -> vec2<i32>
+ textureDimensions(texture : texture_2d<T>, level : i32) -> vec2<i32>
+ textureDimensions(texture : texture_2d_array<T>, level : i32) -> vec2<i32>
+ textureDimensions(texture : texture_3d<T>, level : i32) -> vec3<i32>
+ textureDimensions(texture : texture_cube<T>, level : i32) -> vec3<i32>
+ textureDimensions(texture : texture_cube_array<T>, level : i32) -> vec3<i32>
+ textureDimensions(texture : texture_depth_2d_array, level : i32) -> vec2<i32>
+ textureDimensions(texture : texture_depth_cube, level : i32) -> vec3<i32>
+ textureDimensions(texture : texture_depth_cube_array, level : i32) -> vec3<i32>
+ textureDimensions(texture : texture_1d<T>) -> i32
+ textureDimensions(texture : texture_1d_array<T>) -> i32
+ textureDimensions(texture : texture_2d<T>) -> vec2<i32>
+ textureDimensions(texture : texture_2d_array<T>) -> vec2<i32>
+ textureDimensions(texture : texture_3d<T>) -> vec3<i32>
+ textureDimensions(texture : texture_cube<T>) -> vec3<i32>
+ textureDimensions(texture : texture_cube_array<T>) -> vec3<i32>
+ textureDimensions(texture : texture_multisampled_2d<T>) -> vec2<i32>
+ textureDimensions(texture : texture_multisampled_2d_array<T>) -> vec2<i32>
+ textureDimensions(texture : texture_depth_2d_array) -> vec2<i32>
+ textureDimensions(texture : texture_depth_cube) -> vec3<i32>
+ textureDimensions(texture : texture_depth_cube_array) -> vec3<i32>
+ textureDimensions(texture : texture_storage_1d<F>) -> i32
+ textureDimensions(texture : texture_storage_1d_array<F>) -> i32
+ textureDimensions(texture : texture_storage_2d<F>) -> vec2<i32>
+ textureDimensions(texture : texture_storage_2d_array<F>) -> vec2<i32>
+ textureDimensions(texture : texture_storage_3d<F>) -> vec3<i32>
+)");
+}
+
+} // namespace
+} // namespace tint
diff --git a/src/semantic/call_target.h b/src/semantic/call_target.h
index 16cae05..ce0587e 100644
--- a/src/semantic/call_target.h
+++ b/src/semantic/call_target.h
@@ -56,6 +56,13 @@
Usage const usage = Usage::kNone;
};
+std::ostream& operator<<(std::ostream& out, Parameter parameter);
+
+/// Comparison operator for Parameters
+static inline bool operator==(const Parameter& a, const Parameter& b) {
+ return a.type == b.type && a.usage == b.usage;
+}
+
/// @returns a string representation of the given parameter usage.
const char* str(Parameter::Usage usage);
diff --git a/src/semantic/sem_call_target.cc b/src/semantic/sem_call_target.cc
index 2ae9ad1..5c8011d 100644
--- a/src/semantic/sem_call_target.cc
+++ b/src/semantic/sem_call_target.cc
@@ -14,6 +14,7 @@
#include "src/semantic/call_target.h"
+#include "src/symbol_table.h"
#include "src/type/type.h"
TINT_INSTANTIATE_CLASS_ID(tint::semantic::CallTarget);
@@ -65,5 +66,12 @@
return "<unknown>";
}
}
+
+std::ostream& operator<<(std::ostream& out, Parameter parameter) {
+ out << "[type: " << parameter.type->FriendlyName(SymbolTable{})
+ << ", usage: " << str(parameter.usage) << "]";
+ return out;
+}
+
} // namespace semantic
} // namespace tint
diff --git a/src/type_determiner_test.cc b/src/type_determiner_test.cc
index fc46468..f6e8859 100644
--- a/src/type_determiner_test.cc
+++ b/src/type_determiner_test.cc
@@ -55,6 +55,7 @@
#include "src/semantic/expression.h"
#include "src/semantic/function.h"
#include "src/semantic/variable.h"
+#include "src/type/access_control_type.h"
#include "src/type/alias_type.h"
#include "src/type/array_type.h"
#include "src/type/bool_type.h"
@@ -1379,13 +1380,13 @@
Global("my_var", ast::StorageClass::kNone, ty.f32());
- auto* expr = Call(name, "my_var", "my_var");
+ auto* expr = Call(name, "my_var", 1.23f);
WrapInFunction(expr);
EXPECT_FALSE(td()->Determine());
EXPECT_EQ(td()->error(), "no matching call to " + name +
- "(f32, f32)\n\n"
+ "(ptr<f32>, f32)\n\n"
"2 candidate functions:\n " +
name + "(f32) -> bool\n " + name +
"(vecN<f32>) -> vecN<bool>\n");
@@ -1469,11 +1470,13 @@
auto* coords_type = GetCoordsType(dim, ty.i32());
auto* subtype = type::StorageTexture::SubtypeFor(format, Types());
- type::Type* texture_type = create<type::StorageTexture>(dim, format, subtype);
+ auto* texture_type = create<type::StorageTexture>(dim, format, subtype);
+ auto* ro_texture_type =
+ create<type::AccessControl>(ast::AccessControl::kReadOnly, texture_type);
ast::ExpressionList call_params;
- add_call_param("texture", texture_type, &call_params);
+ add_call_param("texture", ro_texture_type, &call_params);
add_call_param("coords", coords_type, &call_params);
if (type::IsTextureArray(dim)) {
@@ -2504,7 +2507,7 @@
"no matching call to " + std::string(param.name) + R"(()
1 candidate function:
- determinant(maxNxN<f32>) -> f32
+ determinant(matNxN<f32>) -> f32
)");
}