Import Tint changes from Dawn
Changes:
- aecf1a2ab5106c4a08f8e2ae79717483f3abf8ee tint/inspector: Fix GetOverrideDefaultValues() by Ben Clayton <bclayton@google.com>
- 723da2aac5934a47fb85b3bae607eb52af9cee58 tint: split up const eval unit tests into multiple files by Antonio Maiorano <amaiorano@google.com>
- c84d06e8603ce9c4b5c8d86e42e9ec0acf3bd689 tint/resolver: Improve errors for expr eval-stages by Ben Clayton <bclayton@google.com>
- 559a2482339c2034ee85c6ba06bbd7ff70466c8c tint/resolver: Allow texture 'offset' to be const-expr by Ben Clayton <bclayton@google.com>
- d5139b44631f52bbae71790fb237cd35bf1db83b [msl] Handle packed conversions in shift expressions. by dan sinclair <dsinclair@chromium.org>
- 840e42477d9d8683ef1130dd51e0b71e23816f83 tint: update natvis by Antonio Maiorano <amaiorano@google.com>
- f10a57908aeaccbe5f3348183752dc2519e40b39 tint: Use `const-expression` and `override-expression` te... by Ben Clayton <bclayton@google.com>
- 92264f8bb26250ea201e454898ba4761e51fc410 tint/resolver: Fix NPE in CollectTextureSamplerPairs() by Ben Clayton <bclayton@google.com>
- 4fe330fff4481164f4733a2d4431c890dbd6d5e9 tint: s/analysing/analyzing by Ben Clayton <bclayton@google.com>
- 6988e894d2c033d1c0eaa9ddb78cb1746ccbb7ae spirv-reader: track access mode for ptr/ref by David Neto <dneto@google.com>
- cc0c67bce8d8b782d3a83940a1e86d40a94d0d10 spirv-reader: Support GLSL.std.450 FindSMsb by David Neto <dneto@google.com>
- 042711b2b1db99c9944199ba8f894fdc1ba344c2 Remove redundant file in tint/BUILD.gn by Corentin Wallez <cwallez@chromium.org>
- ba384f0383502bb182f810c3007235a6d0aadc3f tint/transform: Fix array materialization when indexing w... by Ben Clayton <bclayton@google.com>
- d6daefc3798532cdd31cbcc9d006e41dc0ca43ce wgsl: Print abstract-floats with full precision. by Ben Clayton <bclayton@google.com>
- 2b8c9d7c2a1ce0463db6feef8fc57c3c866336cb spirv-reader: Support GLSL.std.450 FindUMsb by David Neto <dneto@google.com>
- d2e0db3af2c61efa01254d895d4f335b50d80e6e tint: Rename kInvalid to kUndefined for enums by Ben Clayton <bclayton@google.com>
- 78c839be976a332cd651bbfc651768d426ecfea0 tint/resolver: Ensure that total workgroup size fits in u32 by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: aecf1a2ab5106c4a08f8e2ae79717483f3abf8ee
Change-Id: I6865b5a6e90a527fe223fa4623a897f2cc2a3c90
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/105700
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Commit-Queue: Copybara Prod <copybara-worker-blackhole@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 9e39630..e034c34 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -482,7 +482,6 @@
"transform/builtin_polyfill.h",
"transform/calculate_array_length.cc",
"transform/calculate_array_length.h",
- "transform/calculate_array_length.h",
"transform/canonicalize_entry_point_io.cc",
"transform/canonicalize_entry_point_io.h",
"transform/clamp_frag_depth.cc",
@@ -1116,7 +1115,14 @@
"resolver/call_validation_test.cc",
"resolver/compound_assignment_validation_test.cc",
"resolver/compound_statement_test.cc",
- "resolver/const_eval_test.cc",
+ "resolver/const_eval_binary_op_test.cc",
+ "resolver/const_eval_builtin_test.cc",
+ "resolver/const_eval_construction_test.cc",
+ "resolver/const_eval_conversion_test.cc",
+ "resolver/const_eval_indexing_test.cc",
+ "resolver/const_eval_member_access_test.cc",
+ "resolver/const_eval_test.h",
+ "resolver/const_eval_unary_op_test.cc",
"resolver/control_block_validation_test.cc",
"resolver/dependency_graph_test.cc",
"resolver/entry_point_validation_test.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index c331ccf..fc62717 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -794,7 +794,14 @@
resolver/call_validation_test.cc
resolver/compound_assignment_validation_test.cc
resolver/compound_statement_test.cc
- resolver/const_eval_test.cc
+ resolver/const_eval_binary_op_test.cc
+ resolver/const_eval_builtin_test.cc
+ resolver/const_eval_construction_test.cc
+ resolver/const_eval_conversion_test.cc
+ resolver/const_eval_indexing_test.cc
+ resolver/const_eval_member_access_test.cc
+ resolver/const_eval_test.h
+ resolver/const_eval_unary_op_test.cc
resolver/control_block_validation_test.cc
resolver/dependency_graph_test.cc
resolver/entry_point_validation_test.cc
diff --git a/src/tint/ast/access.cc b/src/tint/ast/access.cc
index 3997715..7ca9b52 100644
--- a/src/tint/ast/access.cc
+++ b/src/tint/ast/access.cc
@@ -26,7 +26,7 @@
/// ParseAccess parses a Access from a string.
/// @param str the string to parse
-/// @returns the parsed enum, or Access::kInvalid if the string could not be parsed.
+/// @returns the parsed enum, or Access::kUndefined if the string could not be parsed.
Access ParseAccess(std::string_view str) {
if (str == "read") {
return Access::kRead;
@@ -37,13 +37,13 @@
if (str == "write") {
return Access::kWrite;
}
- return Access::kInvalid;
+ return Access::kUndefined;
}
std::ostream& operator<<(std::ostream& out, Access value) {
switch (value) {
- case Access::kInvalid:
- return out << "invalid";
+ case Access::kUndefined:
+ return out << "undefined";
case Access::kRead:
return out << "read";
case Access::kReadWrite:
diff --git a/src/tint/ast/access.h b/src/tint/ast/access.h
index 12e1a20..e213691 100644
--- a/src/tint/ast/access.h
+++ b/src/tint/ast/access.h
@@ -29,7 +29,7 @@
/// Address space of a given pointer.
enum class Access {
- kInvalid,
+ kUndefined,
kRead,
kReadWrite,
kWrite,
@@ -42,7 +42,7 @@
/// ParseAccess parses a Access from a string.
/// @param str the string to parse
-/// @returns the parsed enum, or Access::kInvalid if the string could not be parsed.
+/// @returns the parsed enum, or Access::kUndefined if the string could not be parsed.
Access ParseAccess(std::string_view str);
constexpr const char* kAccessStrings[] = {
diff --git a/src/tint/ast/address_space.cc b/src/tint/ast/address_space.cc
index d8174a2..260f86b 100644
--- a/src/tint/ast/address_space.cc
+++ b/src/tint/ast/address_space.cc
@@ -26,7 +26,7 @@
/// ParseAddressSpace parses a AddressSpace from a string.
/// @param str the string to parse
-/// @returns the parsed enum, or AddressSpace::kInvalid if the string could not be parsed.
+/// @returns the parsed enum, or AddressSpace::kUndefined if the string could not be parsed.
AddressSpace ParseAddressSpace(std::string_view str) {
if (str == "function") {
return AddressSpace::kFunction;
@@ -46,13 +46,13 @@
if (str == "workgroup") {
return AddressSpace::kWorkgroup;
}
- return AddressSpace::kInvalid;
+ return AddressSpace::kUndefined;
}
std::ostream& operator<<(std::ostream& out, AddressSpace value) {
switch (value) {
- case AddressSpace::kInvalid:
- return out << "invalid";
+ case AddressSpace::kUndefined:
+ return out << "undefined";
case AddressSpace::kFunction:
return out << "function";
case AddressSpace::kHandle:
diff --git a/src/tint/ast/address_space.h b/src/tint/ast/address_space.h
index 8b96f0f..d599975 100644
--- a/src/tint/ast/address_space.h
+++ b/src/tint/ast/address_space.h
@@ -29,7 +29,7 @@
/// Address space of a given pointer.
enum class AddressSpace {
- kInvalid,
+ kUndefined,
kFunction,
kHandle, // Tint-internal enum entry - not parsed
kIn, // Tint-internal enum entry - not parsed
@@ -49,7 +49,7 @@
/// ParseAddressSpace parses a AddressSpace from a string.
/// @param str the string to parse
-/// @returns the parsed enum, or AddressSpace::kInvalid if the string could not be parsed.
+/// @returns the parsed enum, or AddressSpace::kUndefined if the string could not be parsed.
AddressSpace ParseAddressSpace(std::string_view str);
constexpr const char* kAddressSpaceStrings[] = {
diff --git a/src/tint/ast/address_space_test.cc b/src/tint/ast/address_space_test.cc
index 271cf33..83646be 100644
--- a/src/tint/ast/address_space_test.cc
+++ b/src/tint/ast/address_space_test.cc
@@ -51,15 +51,15 @@
};
static constexpr Case kInvalidCases[] = {
- {"fccnctin", AddressSpace::kInvalid}, {"ucti3", AddressSpace::kInvalid},
- {"functVon", AddressSpace::kInvalid}, {"priv1te", AddressSpace::kInvalid},
- {"pqiJate", AddressSpace::kInvalid}, {"privat7ll", AddressSpace::kInvalid},
- {"pqqsh_pponstHnt", AddressSpace::kInvalid}, {"pus_cnstat", AddressSpace::kInvalid},
- {"bus_Gonstant", AddressSpace::kInvalid}, {"storiive", AddressSpace::kInvalid},
- {"8WWorage", AddressSpace::kInvalid}, {"sxxrage", AddressSpace::kInvalid},
- {"uXforgg", AddressSpace::kInvalid}, {"nfoXm", AddressSpace::kInvalid},
- {"unif3rm", AddressSpace::kInvalid}, {"workgroEp", AddressSpace::kInvalid},
- {"woTTPkroup", AddressSpace::kInvalid}, {"ddorkroxxp", AddressSpace::kInvalid},
+ {"fccnctin", AddressSpace::kUndefined}, {"ucti3", AddressSpace::kUndefined},
+ {"functVon", AddressSpace::kUndefined}, {"priv1te", AddressSpace::kUndefined},
+ {"pqiJate", AddressSpace::kUndefined}, {"privat7ll", AddressSpace::kUndefined},
+ {"pqqsh_pponstHnt", AddressSpace::kUndefined}, {"pus_cnstat", AddressSpace::kUndefined},
+ {"bus_Gonstant", AddressSpace::kUndefined}, {"storiive", AddressSpace::kUndefined},
+ {"8WWorage", AddressSpace::kUndefined}, {"sxxrage", AddressSpace::kUndefined},
+ {"uXforgg", AddressSpace::kUndefined}, {"nfoXm", AddressSpace::kUndefined},
+ {"unif3rm", AddressSpace::kUndefined}, {"workgroEp", AddressSpace::kUndefined},
+ {"woTTPkroup", AddressSpace::kUndefined}, {"ddorkroxxp", AddressSpace::kUndefined},
};
using AddressSpaceParseTest = testing::TestWithParam<Case>;
diff --git a/src/tint/ast/builtin_texture_helper_test.h b/src/tint/ast/builtin_texture_helper_test.h
index bb61875..2c4515b 100644
--- a/src/tint/ast/builtin_texture_helper_test.h
+++ b/src/tint/ast/builtin_texture_helper_test.h
@@ -241,7 +241,7 @@
Access const access = Access::kReadWrite;
/// The image format for the storage texture
/// Used only when texture_kind is kStorage
- ast::TexelFormat const texel_format = ast::TexelFormat::kInvalid;
+ ast::TexelFormat const texel_format = ast::TexelFormat::kUndefined;
/// The dimensions of the texture parameter
ast::TextureDimension const texture_dimension;
/// The data type of the texture parameter
diff --git a/src/tint/ast/builtin_value.cc b/src/tint/ast/builtin_value.cc
index d4eaec0..0a4439b 100644
--- a/src/tint/ast/builtin_value.cc
+++ b/src/tint/ast/builtin_value.cc
@@ -26,7 +26,7 @@
/// ParseBuiltinValue parses a BuiltinValue from a string.
/// @param str the string to parse
-/// @returns the parsed enum, or BuiltinValue::kInvalid if the string could not be parsed.
+/// @returns the parsed enum, or BuiltinValue::kUndefined if the string could not be parsed.
BuiltinValue ParseBuiltinValue(std::string_view str) {
if (str == "frag_depth") {
return BuiltinValue::kFragDepth;
@@ -64,13 +64,13 @@
if (str == "workgroup_id") {
return BuiltinValue::kWorkgroupId;
}
- return BuiltinValue::kInvalid;
+ return BuiltinValue::kUndefined;
}
std::ostream& operator<<(std::ostream& out, BuiltinValue value) {
switch (value) {
- case BuiltinValue::kInvalid:
- return out << "invalid";
+ case BuiltinValue::kUndefined:
+ return out << "undefined";
case BuiltinValue::kFragDepth:
return out << "frag_depth";
case BuiltinValue::kFrontFacing:
diff --git a/src/tint/ast/builtin_value.h b/src/tint/ast/builtin_value.h
index f7dd660..3a3c786 100644
--- a/src/tint/ast/builtin_value.h
+++ b/src/tint/ast/builtin_value.h
@@ -29,7 +29,7 @@
/// Builtin value defined with `@builtin(<name>)`.
enum class BuiltinValue {
- kInvalid,
+ kUndefined,
kFragDepth,
kFrontFacing,
kGlobalInvocationId,
@@ -52,7 +52,7 @@
/// ParseBuiltinValue parses a BuiltinValue from a string.
/// @param str the string to parse
-/// @returns the parsed enum, or BuiltinValue::kInvalid if the string could not be parsed.
+/// @returns the parsed enum, or BuiltinValue::kUndefined if the string could not be parsed.
BuiltinValue ParseBuiltinValue(std::string_view str);
constexpr const char* kBuiltinValueStrings[] = {
diff --git a/src/tint/ast/builtin_value_test.cc b/src/tint/ast/builtin_value_test.cc
index 55f8e4d..0ee2206 100644
--- a/src/tint/ast/builtin_value_test.cc
+++ b/src/tint/ast/builtin_value_test.cc
@@ -57,42 +57,42 @@
};
static constexpr Case kInvalidCases[] = {
- {"fragdeccth", BuiltinValue::kInvalid},
- {"flaget3", BuiltinValue::kInvalid},
- {"fVag_depth", BuiltinValue::kInvalid},
- {"1ront_facing", BuiltinValue::kInvalid},
- {"front_fJcqng", BuiltinValue::kInvalid},
- {"frllnt_facin77", BuiltinValue::kInvalid},
- {"global_invoqqtionppHid", BuiltinValue::kInvalid},
- {"clvbal_inocaionid", BuiltinValue::kInvalid},
- {"global_Gvocation_id", BuiltinValue::kInvalid},
- {"invtance_iniiex", BuiltinValue::kInvalid},
- {"8nstanceWWindex", BuiltinValue::kInvalid},
- {"insxxanceindex", BuiltinValue::kInvalid},
- {"lXcal_invoation_igg", BuiltinValue::kInvalid},
- {"Xocal_nvocatin_Vd", BuiltinValue::kInvalid},
- {"local_invoca3ion_id", BuiltinValue::kInvalid},
- {"local_invocation_indeE", BuiltinValue::kInvalid},
- {"loTTal_invPPcatin_index", BuiltinValue::kInvalid},
- {"loal_invocadxxion_index", BuiltinValue::kInvalid},
- {"num_work44roups", BuiltinValue::kInvalid},
- {"num_wVVrkgSSoups", BuiltinValue::kInvalid},
- {"Rum_wokgrou2Rs", BuiltinValue::kInvalid},
- {"oFi9ion", BuiltinValue::kInvalid},
- {"postion", BuiltinValue::kInvalid},
- {"ROOoHiiVn", BuiltinValue::kInvalid},
- {"samply_inde", BuiltinValue::kInvalid},
- {"snrrmpl77l_indGx", BuiltinValue::kInvalid},
- {"00ample4index", BuiltinValue::kInvalid},
- {"smoo_mask", BuiltinValue::kInvalid},
- {"sampzemask", BuiltinValue::kInvalid},
- {"ppaplii1_mas", BuiltinValue::kInvalid},
- {"vertex_iXXdex", BuiltinValue::kInvalid},
- {"5nnertex_99IIdex", BuiltinValue::kInvalid},
- {"verYeaaHHrrndeSS", BuiltinValue::kInvalid},
- {"workkgHo_i", BuiltinValue::kInvalid},
- {"worRgoupjid", BuiltinValue::kInvalid},
- {"wrkgrupbid", BuiltinValue::kInvalid},
+ {"fragdeccth", BuiltinValue::kUndefined},
+ {"flaget3", BuiltinValue::kUndefined},
+ {"fVag_depth", BuiltinValue::kUndefined},
+ {"1ront_facing", BuiltinValue::kUndefined},
+ {"front_fJcqng", BuiltinValue::kUndefined},
+ {"frllnt_facin77", BuiltinValue::kUndefined},
+ {"global_invoqqtionppHid", BuiltinValue::kUndefined},
+ {"clvbal_inocaionid", BuiltinValue::kUndefined},
+ {"global_Gvocation_id", BuiltinValue::kUndefined},
+ {"invtance_iniiex", BuiltinValue::kUndefined},
+ {"8nstanceWWindex", BuiltinValue::kUndefined},
+ {"insxxanceindex", BuiltinValue::kUndefined},
+ {"lXcal_invoation_igg", BuiltinValue::kUndefined},
+ {"Xocal_nvocatin_Vd", BuiltinValue::kUndefined},
+ {"local_invoca3ion_id", BuiltinValue::kUndefined},
+ {"local_invocation_indeE", BuiltinValue::kUndefined},
+ {"loTTal_invPPcatin_index", BuiltinValue::kUndefined},
+ {"loal_invocadxxion_index", BuiltinValue::kUndefined},
+ {"num_work44roups", BuiltinValue::kUndefined},
+ {"num_wVVrkgSSoups", BuiltinValue::kUndefined},
+ {"Rum_wokgrou2Rs", BuiltinValue::kUndefined},
+ {"oFi9ion", BuiltinValue::kUndefined},
+ {"postion", BuiltinValue::kUndefined},
+ {"ROOoHiiVn", BuiltinValue::kUndefined},
+ {"samply_inde", BuiltinValue::kUndefined},
+ {"snrrmpl77l_indGx", BuiltinValue::kUndefined},
+ {"00ample4index", BuiltinValue::kUndefined},
+ {"smoo_mask", BuiltinValue::kUndefined},
+ {"sampzemask", BuiltinValue::kUndefined},
+ {"ppaplii1_mas", BuiltinValue::kUndefined},
+ {"vertex_iXXdex", BuiltinValue::kUndefined},
+ {"5nnertex_99IIdex", BuiltinValue::kUndefined},
+ {"verYeaaHHrrndeSS", BuiltinValue::kUndefined},
+ {"workkgHo_i", BuiltinValue::kUndefined},
+ {"worRgoupjid", BuiltinValue::kUndefined},
+ {"wrkgrupbid", BuiltinValue::kUndefined},
};
using BuiltinValueParseTest = testing::TestWithParam<Case>;
diff --git a/src/tint/ast/extension.cc b/src/tint/ast/extension.cc
index a4d823f..be16806 100644
--- a/src/tint/ast/extension.cc
+++ b/src/tint/ast/extension.cc
@@ -26,7 +26,7 @@
/// ParseExtension parses a Extension from a string.
/// @param str the string to parse
-/// @returns the parsed enum, or Extension::kInvalid if the string could not be parsed.
+/// @returns the parsed enum, or Extension::kUndefined if the string could not be parsed.
Extension ParseExtension(std::string_view str) {
if (str == "chromium_disable_uniformity_analysis") {
return Extension::kChromiumDisableUniformityAnalysis;
@@ -40,13 +40,13 @@
if (str == "f16") {
return Extension::kF16;
}
- return Extension::kInvalid;
+ return Extension::kUndefined;
}
std::ostream& operator<<(std::ostream& out, Extension value) {
switch (value) {
- case Extension::kInvalid:
- return out << "invalid";
+ case Extension::kUndefined:
+ return out << "undefined";
case Extension::kChromiumDisableUniformityAnalysis:
return out << "chromium_disable_uniformity_analysis";
case Extension::kChromiumExperimentalDp4A:
diff --git a/src/tint/ast/extension.h b/src/tint/ast/extension.h
index 4d1a937..f6fa03f 100644
--- a/src/tint/ast/extension.h
+++ b/src/tint/ast/extension.h
@@ -32,7 +32,7 @@
/// An enumerator of WGSL extensions
/// @see src/tint/intrinsics.def for extension descriptions
enum class Extension {
- kInvalid,
+ kUndefined,
kChromiumDisableUniformityAnalysis,
kChromiumExperimentalDp4A,
kChromiumExperimentalPushConstant,
@@ -46,7 +46,7 @@
/// ParseExtension parses a Extension from a string.
/// @param str the string to parse
-/// @returns the parsed enum, or Extension::kInvalid if the string could not be parsed.
+/// @returns the parsed enum, or Extension::kUndefined if the string could not be parsed.
Extension ParseExtension(std::string_view str);
constexpr const char* kExtensionStrings[] = {
diff --git a/src/tint/ast/extension_test.cc b/src/tint/ast/extension_test.cc
index e43266c..2879bf8 100644
--- a/src/tint/ast/extension_test.cc
+++ b/src/tint/ast/extension_test.cc
@@ -49,18 +49,18 @@
};
static constexpr Case kInvalidCases[] = {
- {"chromium_disableuniformiccy_analysis", Extension::kInvalid},
- {"chromil3_disable_unifority_analss", Extension::kInvalid},
- {"chromium_disable_Vniformity_analysis", Extension::kInvalid},
- {"chro1ium_experimental_dp4a", Extension::kInvalid},
- {"chrJmium_experiqqetal_dp4a", Extension::kInvalid},
- {"chromium_experimenll77l_dp4a", Extension::kInvalid},
- {"cppromium_experiHHenal_qqush_constant", Extension::kInvalid},
- {"chromium_xpericental_sh_vonstant", Extension::kInvalid},
- {"chromium_experimental_Gsh_cbnstant", Extension::kInvalid},
- {"f1vi", Extension::kInvalid},
- {"f8WW", Extension::kInvalid},
- {"fxx", Extension::kInvalid},
+ {"chromium_disableuniformiccy_analysis", Extension::kUndefined},
+ {"chromil3_disable_unifority_analss", Extension::kUndefined},
+ {"chromium_disable_Vniformity_analysis", Extension::kUndefined},
+ {"chro1ium_experimental_dp4a", Extension::kUndefined},
+ {"chrJmium_experiqqetal_dp4a", Extension::kUndefined},
+ {"chromium_experimenll77l_dp4a", Extension::kUndefined},
+ {"cppromium_experiHHenal_qqush_constant", Extension::kUndefined},
+ {"chromium_xpericental_sh_vonstant", Extension::kUndefined},
+ {"chromium_experimental_Gsh_cbnstant", Extension::kUndefined},
+ {"f1vi", Extension::kUndefined},
+ {"f8WW", Extension::kUndefined},
+ {"fxx", Extension::kUndefined},
};
using ExtensionParseTest = testing::TestWithParam<Case>;
diff --git a/src/tint/ast/interpolate_attribute.cc b/src/tint/ast/interpolate_attribute.cc
index 7c89ee1..2e9ef2a 100644
--- a/src/tint/ast/interpolate_attribute.cc
+++ b/src/tint/ast/interpolate_attribute.cc
@@ -51,7 +51,7 @@
/// ParseInterpolationType parses a InterpolationType from a string.
/// @param str the string to parse
-/// @returns the parsed enum, or InterpolationType::kInvalid if the string could not be parsed.
+/// @returns the parsed enum, or InterpolationType::kUndefined if the string could not be parsed.
InterpolationType ParseInterpolationType(std::string_view str) {
if (str == "flat") {
return InterpolationType::kFlat;
@@ -62,13 +62,13 @@
if (str == "perspective") {
return InterpolationType::kPerspective;
}
- return InterpolationType::kInvalid;
+ return InterpolationType::kUndefined;
}
std::ostream& operator<<(std::ostream& out, InterpolationType value) {
switch (value) {
- case InterpolationType::kInvalid:
- return out << "invalid";
+ case InterpolationType::kUndefined:
+ return out << "undefined";
case InterpolationType::kFlat:
return out << "flat";
case InterpolationType::kLinear:
@@ -81,7 +81,8 @@
/// ParseInterpolationSampling parses a InterpolationSampling from a string.
/// @param str the string to parse
-/// @returns the parsed enum, or InterpolationSampling::kInvalid if the string could not be parsed.
+/// @returns the parsed enum, or InterpolationSampling::kUndefined if the string could not be
+/// parsed.
InterpolationSampling ParseInterpolationSampling(std::string_view str) {
if (str == "center") {
return InterpolationSampling::kCenter;
@@ -92,13 +93,13 @@
if (str == "sample") {
return InterpolationSampling::kSample;
}
- return InterpolationSampling::kInvalid;
+ return InterpolationSampling::kUndefined;
}
std::ostream& operator<<(std::ostream& out, InterpolationSampling value) {
switch (value) {
- case InterpolationSampling::kInvalid:
- return out << "invalid";
+ case InterpolationSampling::kUndefined:
+ return out << "undefined";
case InterpolationSampling::kCenter:
return out << "center";
case InterpolationSampling::kCentroid:
diff --git a/src/tint/ast/interpolate_attribute.h b/src/tint/ast/interpolate_attribute.h
index c6b4af0..dd60156 100644
--- a/src/tint/ast/interpolate_attribute.h
+++ b/src/tint/ast/interpolate_attribute.h
@@ -32,7 +32,7 @@
/// The interpolation type.
enum class InterpolationType {
- kInvalid,
+ kUndefined,
kFlat,
kLinear,
kPerspective,
@@ -45,7 +45,7 @@
/// ParseInterpolationType parses a InterpolationType from a string.
/// @param str the string to parse
-/// @returns the parsed enum, or InterpolationType::kInvalid if the string could not be parsed.
+/// @returns the parsed enum, or InterpolationType::kUndefined if the string could not be parsed.
InterpolationType ParseInterpolationType(std::string_view str);
constexpr const char* kInterpolationTypeStrings[] = {
@@ -56,7 +56,7 @@
/// The interpolation sampling.
enum class InterpolationSampling {
- kInvalid,
+ kUndefined,
kCenter,
kCentroid,
kSample,
@@ -69,7 +69,8 @@
/// ParseInterpolationSampling parses a InterpolationSampling from a string.
/// @param str the string to parse
-/// @returns the parsed enum, or InterpolationSampling::kInvalid if the string could not be parsed.
+/// @returns the parsed enum, or InterpolationSampling::kUndefined if the string could not be
+/// parsed.
InterpolationSampling ParseInterpolationSampling(std::string_view str);
constexpr const char* kInterpolationSamplingStrings[] = {
diff --git a/src/tint/ast/pointer.cc b/src/tint/ast/pointer.cc
index 96bbdaf..71b7004 100644
--- a/src/tint/ast/pointer.cc
+++ b/src/tint/ast/pointer.cc
@@ -35,7 +35,7 @@
out << address_space << ", ";
}
out << type->FriendlyName(symbols);
- if (access != ast::Access::kInvalid) {
+ if (access != ast::Access::kUndefined) {
out << ", " << access;
}
out << ">";
diff --git a/src/tint/ast/pointer_test.cc b/src/tint/ast/pointer_test.cc
index 56a04b0..aea260a 100644
--- a/src/tint/ast/pointer_test.cc
+++ b/src/tint/ast/pointer_test.cc
@@ -32,7 +32,7 @@
TEST_F(AstPointerTest, FriendlyName) {
auto* i32 = create<I32>();
- auto* p = create<Pointer>(i32, ast::AddressSpace::kWorkgroup, Access::kInvalid);
+ auto* p = create<Pointer>(i32, ast::AddressSpace::kWorkgroup, Access::kUndefined);
EXPECT_EQ(p->FriendlyName(Symbols()), "ptr<workgroup, i32>");
}
diff --git a/src/tint/ast/storage_texture.cc b/src/tint/ast/storage_texture.cc
index 9c223c3..c9d26c2 100644
--- a/src/tint/ast/storage_texture.cc
+++ b/src/tint/ast/storage_texture.cc
@@ -76,7 +76,7 @@
return builder.create<F32>();
}
- case TexelFormat::kInvalid:
+ case TexelFormat::kUndefined:
break;
}
diff --git a/src/tint/ast/texel_format.cc b/src/tint/ast/texel_format.cc
index 201cad3..587cf29 100644
--- a/src/tint/ast/texel_format.cc
+++ b/src/tint/ast/texel_format.cc
@@ -26,7 +26,7 @@
/// ParseTexelFormat parses a TexelFormat from a string.
/// @param str the string to parse
-/// @returns the parsed enum, or TexelFormat::kInvalid if the string could not be parsed.
+/// @returns the parsed enum, or TexelFormat::kUndefined if the string could not be parsed.
TexelFormat ParseTexelFormat(std::string_view str) {
if (str == "r32float") {
return TexelFormat::kR32Float;
@@ -76,13 +76,13 @@
if (str == "rgba8unorm") {
return TexelFormat::kRgba8Unorm;
}
- return TexelFormat::kInvalid;
+ return TexelFormat::kUndefined;
}
std::ostream& operator<<(std::ostream& out, TexelFormat value) {
switch (value) {
- case TexelFormat::kInvalid:
- return out << "invalid";
+ case TexelFormat::kUndefined:
+ return out << "undefined";
case TexelFormat::kR32Float:
return out << "r32float";
case TexelFormat::kR32Sint:
diff --git a/src/tint/ast/texel_format.h b/src/tint/ast/texel_format.h
index 9c65fb6..fff88a3 100644
--- a/src/tint/ast/texel_format.h
+++ b/src/tint/ast/texel_format.h
@@ -29,7 +29,7 @@
/// Enumerator of texel formats
enum class TexelFormat {
- kInvalid,
+ kUndefined,
kR32Float,
kR32Sint,
kR32Uint,
@@ -55,7 +55,7 @@
/// ParseTexelFormat parses a TexelFormat from a string.
/// @param str the string to parse
-/// @returns the parsed enum, or TexelFormat::kInvalid if the string could not be parsed.
+/// @returns the parsed enum, or TexelFormat::kUndefined if the string could not be parsed.
TexelFormat ParseTexelFormat(std::string_view str);
constexpr const char* kTexelFormatStrings[] = {
diff --git a/src/tint/ast/texel_format_test.cc b/src/tint/ast/texel_format_test.cc
index d431918..d257d5e 100644
--- a/src/tint/ast/texel_format_test.cc
+++ b/src/tint/ast/texel_format_test.cc
@@ -53,30 +53,30 @@
};
static constexpr Case kInvalidCases[] = {
- {"rcc2flot", TexelFormat::kInvalid}, {"3flo3", TexelFormat::kInvalid},
- {"r32flVat", TexelFormat::kInvalid}, {"r32s1nt", TexelFormat::kInvalid},
- {"rq2Jint", TexelFormat::kInvalid}, {"r32sin7ll", TexelFormat::kInvalid},
- {"ppqq2unHH", TexelFormat::kInvalid}, {"r3cv", TexelFormat::kInvalid},
- {"b2uiGt", TexelFormat::kInvalid}, {"rgvi2float", TexelFormat::kInvalid},
- {"rg328WWoat", TexelFormat::kInvalid}, {"rg32lxxMt", TexelFormat::kInvalid},
- {"rgXggsnt", TexelFormat::kInvalid}, {"rgXsnu", TexelFormat::kInvalid},
- {"rg32s3nt", TexelFormat::kInvalid}, {"rg3Euint", TexelFormat::kInvalid},
- {"PP32TTint", TexelFormat::kInvalid}, {"xxg32ddnt", TexelFormat::kInvalid},
- {"rgba446float", TexelFormat::kInvalid}, {"SSVVba16float", TexelFormat::kInvalid},
- {"rgbRR6float", TexelFormat::kInvalid}, {"rga16Fint", TexelFormat::kInvalid},
- {"rgb16sint", TexelFormat::kInvalid}, {"ORVHa16sint", TexelFormat::kInvalid},
- {"ryba1uint", TexelFormat::kInvalid}, {"r77ba1nnullrrt", TexelFormat::kInvalid},
- {"rgb4006uint", TexelFormat::kInvalid}, {"rboofloat", TexelFormat::kInvalid},
- {"rgbaz2loat", TexelFormat::kInvalid}, {"ppga3ii1floa", TexelFormat::kInvalid},
- {"XXgba32sint", TexelFormat::kInvalid}, {"IIgb9932nni55t", TexelFormat::kInvalid},
- {"rYbaSSrrsiHHat", TexelFormat::kInvalid}, {"rbkk2Hit", TexelFormat::kInvalid},
- {"jgba3ugRR", TexelFormat::kInvalid}, {"rgbab2ui", TexelFormat::kInvalid},
- {"rgba8sijt", TexelFormat::kInvalid}, {"rba8sint", TexelFormat::kInvalid},
- {"rba8sqt", TexelFormat::kInvalid}, {"rgba8NNnom", TexelFormat::kInvalid},
- {"rga8vvorm", TexelFormat::kInvalid}, {"rgba8snorQ", TexelFormat::kInvalid},
- {"rgbauirf", TexelFormat::kInvalid}, {"rgbajuint", TexelFormat::kInvalid},
- {"wNNgbauin2", TexelFormat::kInvalid}, {"rgba8unrm", TexelFormat::kInvalid},
- {"rgba8urrorm", TexelFormat::kInvalid}, {"rgba8Gnorm", TexelFormat::kInvalid},
+ {"rcc2flot", TexelFormat::kUndefined}, {"3flo3", TexelFormat::kUndefined},
+ {"r32flVat", TexelFormat::kUndefined}, {"r32s1nt", TexelFormat::kUndefined},
+ {"rq2Jint", TexelFormat::kUndefined}, {"r32sin7ll", TexelFormat::kUndefined},
+ {"ppqq2unHH", TexelFormat::kUndefined}, {"r3cv", TexelFormat::kUndefined},
+ {"b2uiGt", TexelFormat::kUndefined}, {"rgvi2float", TexelFormat::kUndefined},
+ {"rg328WWoat", TexelFormat::kUndefined}, {"rg32lxxMt", TexelFormat::kUndefined},
+ {"rgXggsnt", TexelFormat::kUndefined}, {"rgXsnu", TexelFormat::kUndefined},
+ {"rg32s3nt", TexelFormat::kUndefined}, {"rg3Euint", TexelFormat::kUndefined},
+ {"PP32TTint", TexelFormat::kUndefined}, {"xxg32ddnt", TexelFormat::kUndefined},
+ {"rgba446float", TexelFormat::kUndefined}, {"SSVVba16float", TexelFormat::kUndefined},
+ {"rgbRR6float", TexelFormat::kUndefined}, {"rga16Fint", TexelFormat::kUndefined},
+ {"rgb16sint", TexelFormat::kUndefined}, {"ORVHa16sint", TexelFormat::kUndefined},
+ {"ryba1uint", TexelFormat::kUndefined}, {"r77ba1nnullrrt", TexelFormat::kUndefined},
+ {"rgb4006uint", TexelFormat::kUndefined}, {"rboofloat", TexelFormat::kUndefined},
+ {"rgbaz2loat", TexelFormat::kUndefined}, {"ppga3ii1floa", TexelFormat::kUndefined},
+ {"XXgba32sint", TexelFormat::kUndefined}, {"IIgb9932nni55t", TexelFormat::kUndefined},
+ {"rYbaSSrrsiHHat", TexelFormat::kUndefined}, {"rbkk2Hit", TexelFormat::kUndefined},
+ {"jgba3ugRR", TexelFormat::kUndefined}, {"rgbab2ui", TexelFormat::kUndefined},
+ {"rgba8sijt", TexelFormat::kUndefined}, {"rba8sint", TexelFormat::kUndefined},
+ {"rba8sqt", TexelFormat::kUndefined}, {"rgba8NNnom", TexelFormat::kUndefined},
+ {"rga8vvorm", TexelFormat::kUndefined}, {"rgba8snorQ", TexelFormat::kUndefined},
+ {"rgbauirf", TexelFormat::kUndefined}, {"rgbajuint", TexelFormat::kUndefined},
+ {"wNNgbauin2", TexelFormat::kUndefined}, {"rgba8unrm", TexelFormat::kUndefined},
+ {"rgba8urrorm", TexelFormat::kUndefined}, {"rgba8Gnorm", TexelFormat::kUndefined},
};
using TexelFormatParseTest = testing::TestWithParam<Case>;
diff --git a/src/tint/inspector/inspector.cc b/src/tint/inspector/inspector.cc
index ddb5b11..687ac91 100644
--- a/src/tint/inspector/inspector.cc
+++ b/src/tint/inspector/inspector.cc
@@ -29,6 +29,7 @@
#include "src/tint/ast/override.h"
#include "src/tint/ast/var.h"
#include "src/tint/sem/array.h"
+#include "src/tint/sem/bool.h"
#include "src/tint/sem/call.h"
#include "src/tint/sem/depth_multisampled_texture.h"
#include "src/tint/sem/depth_texture.h"
@@ -120,7 +121,7 @@
auto ast_interpolation_type = interpolation_attribute->type;
auto ast_sampling_type = interpolation_attribute->sampling;
if (ast_interpolation_type != ast::InterpolationType::kFlat &&
- ast_sampling_type == ast::InterpolationSampling::kInvalid) {
+ ast_sampling_type == ast::InterpolationSampling::kUndefined) {
ast_sampling_type = ast::InterpolationSampling::kCenter;
}
@@ -135,13 +136,13 @@
case ast::InterpolationType::kFlat:
interpolation_type = InterpolationType::kFlat;
break;
- case ast::InterpolationType::kInvalid:
+ case ast::InterpolationType::kUndefined:
break;
}
auto sampling_type = InterpolationSampling::kUnknown;
switch (ast_sampling_type) {
- case ast::InterpolationSampling::kInvalid:
+ case ast::InterpolationSampling::kUndefined:
sampling_type = InterpolationSampling::kNone;
break;
case ast::InterpolationSampling::kCenter:
@@ -301,40 +302,19 @@
continue;
}
- if (!var->constructor) {
- result[override_id] = Scalar();
- continue;
- }
-
- auto* literal = var->constructor->As<ast::LiteralExpression>();
- if (!literal) {
- // This is invalid WGSL, but handling gracefully.
- result[override_id] = Scalar();
- continue;
- }
-
- if (auto* l = literal->As<ast::BoolLiteralExpression>()) {
- result[override_id] = Scalar(l->value);
- continue;
- }
-
- if (auto* l = literal->As<ast::IntLiteralExpression>()) {
- switch (l->suffix) {
- case ast::IntLiteralExpression::Suffix::kNone:
- case ast::IntLiteralExpression::Suffix::kI:
- result[override_id] = Scalar(static_cast<int32_t>(l->value));
- continue;
- case ast::IntLiteralExpression::Suffix::kU:
- result[override_id] = Scalar(static_cast<uint32_t>(l->value));
- continue;
+ if (global->Constructor()) {
+ if (auto* value = global->Constructor()->ConstantValue()) {
+ result[override_id] = Switch(
+ value->Type(), //
+ [&](const sem::I32*) { return Scalar(value->As<i32>()); },
+ [&](const sem::U32*) { return Scalar(value->As<u32>()); },
+ [&](const sem::F32*) { return Scalar(value->As<f32>()); },
+ [&](const sem::Bool*) { return Scalar(value->As<bool>()); });
+ continue;
}
}
- if (auto* l = literal->As<ast::FloatLiteralExpression>()) {
- result[override_id] = Scalar(static_cast<float>(l->value));
- continue;
- }
-
+ // No const-expression initializer for the override
result[override_id] = Scalar();
}
diff --git a/src/tint/inspector/inspector_test.cc b/src/tint/inspector/inspector_test.cc
index 7aa017c..1daec37 100644
--- a/src/tint/inspector/inspector_test.cc
+++ b/src/tint/inspector/inspector_test.cc
@@ -1250,7 +1250,7 @@
ast::InterpolationType::kPerspective, ast::InterpolationSampling::kSample,
InterpolationType::kPerspective, InterpolationSampling::kSample},
InspectorGetEntryPointInterpolateTestParams{
- ast::InterpolationType::kPerspective, ast::InterpolationSampling::kInvalid,
+ ast::InterpolationType::kPerspective, ast::InterpolationSampling::kUndefined,
InterpolationType::kPerspective, InterpolationSampling::kCenter},
InspectorGetEntryPointInterpolateTestParams{
ast::InterpolationType::kLinear, ast::InterpolationSampling::kCenter,
@@ -1262,21 +1262,24 @@
ast::InterpolationType::kLinear, ast::InterpolationSampling::kSample,
InterpolationType::kLinear, InterpolationSampling::kSample},
InspectorGetEntryPointInterpolateTestParams{
- ast::InterpolationType::kLinear, ast::InterpolationSampling::kInvalid,
+ ast::InterpolationType::kLinear, ast::InterpolationSampling::kUndefined,
InterpolationType::kLinear, InterpolationSampling::kCenter},
InspectorGetEntryPointInterpolateTestParams{
- ast::InterpolationType::kFlat, ast::InterpolationSampling::kInvalid,
+ ast::InterpolationType::kFlat, ast::InterpolationSampling::kUndefined,
InterpolationType::kFlat, InterpolationSampling::kNone}));
TEST_F(InspectorGetOverrideDefaultValuesTest, Bool) {
- Override("foo", ty.bool_(), Id(1_a));
- Override("bar", ty.bool_(), Expr(true), Id(20_a));
- Override("baz", ty.bool_(), Expr(false), Id(300_a));
+ GlobalConst("C", Expr(true));
+ Override("a", ty.bool_(), Id(1_a));
+ Override("b", ty.bool_(), Expr(true), Id(20_a));
+ Override("c", Expr(false), Id(300_a));
+ Override("d", Or(true, false), Id(400_a));
+ Override("e", Expr("C"), Id(500_a));
Inspector& inspector = Build();
auto result = inspector.GetOverrideDefaultValues();
- ASSERT_EQ(3u, result.size());
+ ASSERT_EQ(5u, result.size());
ASSERT_TRUE(result.find(OverrideId{1}) != result.end());
EXPECT_TRUE(result[OverrideId{1}].IsNull());
@@ -1288,16 +1291,29 @@
ASSERT_TRUE(result.find(OverrideId{300}) != result.end());
EXPECT_TRUE(result[OverrideId{300}].IsBool());
EXPECT_FALSE(result[OverrideId{300}].AsBool());
+
+ ASSERT_TRUE(result.find(OverrideId{400}) != result.end());
+ EXPECT_TRUE(result[OverrideId{400}].IsBool());
+ EXPECT_TRUE(result[OverrideId{400}].AsBool());
+
+ ASSERT_TRUE(result.find(OverrideId{500}) != result.end());
+ EXPECT_TRUE(result[OverrideId{500}].IsBool());
+ EXPECT_TRUE(result[OverrideId{500}].AsBool());
}
TEST_F(InspectorGetOverrideDefaultValuesTest, U32) {
- Override("foo", ty.u32(), Id(1_a));
- Override("bar", ty.u32(), Expr(42_u), Id(20_a));
+ GlobalConst("C", Expr(100_u));
+ Override("a", ty.u32(), Id(1_a));
+ Override("b", ty.u32(), Expr(42_u), Id(20_a));
+ Override("c", ty.u32(), Expr(42_a), Id(30_a));
+ Override("d", ty.u32(), Add(42_a, 10_a), Id(40_a));
+ Override("e", Add(42_a, 10_u), Id(50_a));
+ Override("f", Expr("C"), Id(60_a));
Inspector& inspector = Build();
auto result = inspector.GetOverrideDefaultValues();
- ASSERT_EQ(2u, result.size());
+ ASSERT_EQ(6u, result.size());
ASSERT_TRUE(result.find(OverrideId{1}) != result.end());
EXPECT_TRUE(result[OverrideId{1}].IsNull());
@@ -1305,17 +1321,37 @@
ASSERT_TRUE(result.find(OverrideId{20}) != result.end());
EXPECT_TRUE(result[OverrideId{20}].IsU32());
EXPECT_EQ(42u, result[OverrideId{20}].AsU32());
+
+ ASSERT_TRUE(result.find(OverrideId{30}) != result.end());
+ EXPECT_TRUE(result[OverrideId{30}].IsU32());
+ EXPECT_EQ(42u, result[OverrideId{30}].AsU32());
+
+ ASSERT_TRUE(result.find(OverrideId{40}) != result.end());
+ EXPECT_TRUE(result[OverrideId{40}].IsU32());
+ EXPECT_EQ(52u, result[OverrideId{40}].AsU32());
+
+ ASSERT_TRUE(result.find(OverrideId{50}) != result.end());
+ EXPECT_TRUE(result[OverrideId{50}].IsU32());
+ EXPECT_EQ(52u, result[OverrideId{50}].AsU32());
+
+ ASSERT_TRUE(result.find(OverrideId{60}) != result.end());
+ EXPECT_TRUE(result[OverrideId{60}].IsU32());
+ EXPECT_EQ(100u, result[OverrideId{60}].AsU32());
}
TEST_F(InspectorGetOverrideDefaultValuesTest, I32) {
- Override("foo", ty.i32(), Id(1_a));
- Override("bar", ty.i32(), Expr(-42_i), Id(20_a));
- Override("baz", ty.i32(), Expr(42_i), Id(300_a));
+ GlobalConst("C", Expr(100_a));
+ Override("a", ty.i32(), Id(1_a));
+ Override("b", ty.i32(), Expr(-42_i), Id(20_a));
+ Override("c", ty.i32(), Expr(42_i), Id(300_a));
+ Override("d", Expr(42_a), Id(400_a));
+ Override("e", Add(42_a, 7_a), Id(500_a));
+ Override("f", Expr("C"), Id(6000_a));
Inspector& inspector = Build();
auto result = inspector.GetOverrideDefaultValues();
- ASSERT_EQ(3u, result.size());
+ ASSERT_EQ(6u, result.size());
ASSERT_TRUE(result.find(OverrideId{1}) != result.end());
EXPECT_TRUE(result[OverrideId{1}].IsNull());
@@ -1327,18 +1363,32 @@
ASSERT_TRUE(result.find(OverrideId{300}) != result.end());
EXPECT_TRUE(result[OverrideId{300}].IsI32());
EXPECT_EQ(42, result[OverrideId{300}].AsI32());
+
+ ASSERT_TRUE(result.find(OverrideId{400}) != result.end());
+ EXPECT_TRUE(result[OverrideId{400}].IsI32());
+ EXPECT_EQ(42, result[OverrideId{400}].AsI32());
+
+ ASSERT_TRUE(result.find(OverrideId{500}) != result.end());
+ EXPECT_TRUE(result[OverrideId{500}].IsI32());
+ EXPECT_EQ(49, result[OverrideId{500}].AsI32());
+
+ ASSERT_TRUE(result.find(OverrideId{6000}) != result.end());
+ EXPECT_TRUE(result[OverrideId{6000}].IsI32());
+ EXPECT_EQ(100, result[OverrideId{6000}].AsI32());
}
TEST_F(InspectorGetOverrideDefaultValuesTest, Float) {
- Override("foo", ty.f32(), Id(1_a));
- Override("bar", ty.f32(), Expr(0_f), Id(20_a));
- Override("baz", ty.f32(), Expr(-10_f), Id(300_a));
- Override("x", ty.f32(), Expr(15_f), Id(4000_a));
+ Override("a", ty.f32(), Id(1_a));
+ Override("b", ty.f32(), Expr(0_f), Id(20_a));
+ Override("c", ty.f32(), Expr(-10_f), Id(300_a));
+ Override("d", Expr(15_f), Id(4000_a));
+ Override("3", Expr(42.0_a), Id(5000_a));
+ Override("e", ty.f32(), Mul(15_f, 10_a), Id(6000_a));
Inspector& inspector = Build();
auto result = inspector.GetOverrideDefaultValues();
- ASSERT_EQ(4u, result.size());
+ ASSERT_EQ(6u, result.size());
ASSERT_TRUE(result.find(OverrideId{1}) != result.end());
EXPECT_TRUE(result[OverrideId{1}].IsNull());
@@ -1354,6 +1404,14 @@
ASSERT_TRUE(result.find(OverrideId{4000}) != result.end());
EXPECT_TRUE(result[OverrideId{4000}].IsFloat());
EXPECT_FLOAT_EQ(15.0f, result[OverrideId{4000}].AsFloat());
+
+ ASSERT_TRUE(result.find(OverrideId{5000}) != result.end());
+ EXPECT_TRUE(result[OverrideId{5000}].IsFloat());
+ EXPECT_FLOAT_EQ(42.0f, result[OverrideId{5000}].AsFloat());
+
+ ASSERT_TRUE(result.find(OverrideId{6000}) != result.end());
+ EXPECT_TRUE(result[OverrideId{6000}].IsFloat());
+ EXPECT_FLOAT_EQ(150.0f, result[OverrideId{6000}].AsFloat());
}
TEST_F(InspectorGetConstantNameToIdMapTest, WithAndWithoutIds) {
diff --git a/src/tint/inspector/resource_binding.cc b/src/tint/inspector/resource_binding.cc
index 6434ac2..a0e89e2 100644
--- a/src/tint/inspector/resource_binding.cc
+++ b/src/tint/inspector/resource_binding.cc
@@ -104,7 +104,7 @@
return ResourceBinding::TexelFormat::kRgba32Sint;
case ast::TexelFormat::kRgba32Float:
return ResourceBinding::TexelFormat::kRgba32Float;
- case ast::TexelFormat::kInvalid:
+ case ast::TexelFormat::kUndefined:
return ResourceBinding::TexelFormat::kNone;
}
return ResourceBinding::TexelFormat::kNone;
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index 2c99b3b..8430698 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -173,7 +173,7 @@
const ast::Type* type = nullptr;
ast::AddressSpace address_space = ast::AddressSpace::kNone;
- ast::Access access = ast::Access::kInvalid;
+ ast::Access access = ast::Access::kUndefined;
const ast::Expression* constructor = nullptr;
utils::Vector<const ast::Attribute*, 4> attributes;
@@ -902,7 +902,7 @@
/// @return the pointer to `type` with the given ast::AddressSpace
const ast::Pointer* pointer(const ast::Type* type,
ast::AddressSpace address_space,
- ast::Access access = ast::Access::kInvalid) const {
+ ast::Access access = ast::Access::kUndefined) const {
return builder->create<ast::Pointer>(type, address_space, access);
}
@@ -914,7 +914,7 @@
const ast::Pointer* pointer(const Source& source,
const ast::Type* type,
ast::AddressSpace address_space,
- ast::Access access = ast::Access::kInvalid) const {
+ ast::Access access = ast::Access::kUndefined) const {
return builder->create<ast::Pointer>(source, type, address_space, access);
}
@@ -923,7 +923,7 @@
/// @return the pointer to type `T` with the given ast::AddressSpace.
template <typename T>
const ast::Pointer* pointer(ast::AddressSpace address_space,
- ast::Access access = ast::Access::kInvalid) const {
+ ast::Access access = ast::Access::kUndefined) const {
return pointer(Of<T>(), address_space, access);
}
@@ -934,7 +934,7 @@
template <typename T>
const ast::Pointer* pointer(const Source& source,
ast::AddressSpace address_space,
- ast::Access access = ast::Access::kInvalid) const {
+ ast::Access access = ast::Access::kUndefined) const {
return pointer(source, Of<T>(), address_space, access);
}
@@ -2909,7 +2909,7 @@
const ast::InterpolateAttribute* Interpolate(
const Source& source,
ast::InterpolationType type,
- ast::InterpolationSampling sampling = ast::InterpolationSampling::kInvalid) {
+ ast::InterpolationSampling sampling = ast::InterpolationSampling::kUndefined) {
return create<ast::InterpolateAttribute>(source, type, sampling);
}
@@ -2919,7 +2919,7 @@
/// @returns the interpolate attribute pointer
const ast::InterpolateAttribute* Interpolate(
ast::InterpolationType type,
- ast::InterpolationSampling sampling = ast::InterpolationSampling::kInvalid) {
+ ast::InterpolationSampling sampling = ast::InterpolationSampling::kUndefined) {
return create<ast::InterpolateAttribute>(source_, type, sampling);
}
diff --git a/src/tint/reader/spirv/enum_converter.cc b/src/tint/reader/spirv/enum_converter.cc
index bc899d7..923f250 100644
--- a/src/tint/reader/spirv/enum_converter.cc
+++ b/src/tint/reader/spirv/enum_converter.cc
@@ -59,7 +59,7 @@
}
Fail() << "unknown SPIR-V storage class: " << uint32_t(sc);
- return ast::AddressSpace::kInvalid;
+ return ast::AddressSpace::kUndefined;
}
ast::BuiltinValue EnumConverter::ToBuiltin(SpvBuiltIn b) {
@@ -93,7 +93,7 @@
}
Fail() << "unknown SPIR-V builtin: " << uint32_t(b);
- return ast::BuiltinValue::kInvalid;
+ return ast::BuiltinValue::kUndefined;
}
ast::TextureDimension EnumConverter::ToDim(SpvDim dim, bool arrayed) {
@@ -129,7 +129,7 @@
ast::TexelFormat EnumConverter::ToTexelFormat(SpvImageFormat fmt) {
switch (fmt) {
case SpvImageFormatUnknown:
- return ast::TexelFormat::kInvalid;
+ return ast::TexelFormat::kUndefined;
// 8 bit channels
case SpvImageFormatRgba8:
@@ -172,7 +172,7 @@
break;
}
Fail() << "invalid image format: " << int(fmt);
- return ast::TexelFormat::kInvalid;
+ return ast::TexelFormat::kUndefined;
}
} // namespace tint::reader::spirv
diff --git a/src/tint/reader/spirv/enum_converter_test.cc b/src/tint/reader/spirv/enum_converter_test.cc
index 623f6ea..112dc4a 100644
--- a/src/tint/reader/spirv/enum_converter_test.cc
+++ b/src/tint/reader/spirv/enum_converter_test.cc
@@ -137,7 +137,7 @@
INSTANTIATE_TEST_SUITE_P(EnumConverterBad,
SpvStorageClassTest,
testing::Values(StorageClassCase{static_cast<SpvStorageClass>(9999), false,
- ast::AddressSpace::kInvalid}));
+ ast::AddressSpace::kUndefined}));
// Builtin
@@ -202,12 +202,14 @@
BuiltinCase{SpvBuiltInFragDepth, true, ast::BuiltinValue::kFragDepth},
BuiltinCase{SpvBuiltInSampleMask, true, ast::BuiltinValue::kSampleMask}));
-INSTANTIATE_TEST_SUITE_P(
- EnumConverterBad,
- SpvBuiltinTest,
- testing::Values(BuiltinCase{static_cast<SpvBuiltIn>(9999), false, ast::BuiltinValue::kInvalid},
- BuiltinCase{static_cast<SpvBuiltIn>(9999), false, ast::BuiltinValue::kInvalid},
- BuiltinCase{SpvBuiltInNumWorkgroups, false, ast::BuiltinValue::kInvalid}));
+INSTANTIATE_TEST_SUITE_P(EnumConverterBad,
+ SpvBuiltinTest,
+ testing::Values(BuiltinCase{static_cast<SpvBuiltIn>(9999), false,
+ ast::BuiltinValue::kUndefined},
+ BuiltinCase{static_cast<SpvBuiltIn>(9999), false,
+ ast::BuiltinValue::kUndefined},
+ BuiltinCase{SpvBuiltInNumWorkgroups, false,
+ ast::BuiltinValue::kUndefined}));
// Dim
@@ -326,7 +328,7 @@
SpvImageFormatTest,
testing::Values(
// Unknown. This is used for sampled images.
- TexelFormatCase{SpvImageFormatUnknown, true, ast::TexelFormat::kInvalid},
+ TexelFormatCase{SpvImageFormatUnknown, true, ast::TexelFormat::kUndefined},
// 8 bit channels
TexelFormatCase{SpvImageFormatRgba8, true, ast::TexelFormat::kRgba8Unorm},
TexelFormatCase{SpvImageFormatRgba8Snorm, true, ast::TexelFormat::kRgba8Snorm},
@@ -355,23 +357,23 @@
SpvImageFormatTest,
testing::Values(
// Scanning in order from the SPIR-V spec.
- TexelFormatCase{SpvImageFormatRg16f, false, ast::TexelFormat::kInvalid},
- TexelFormatCase{SpvImageFormatR11fG11fB10f, false, ast::TexelFormat::kInvalid},
- TexelFormatCase{SpvImageFormatR16f, false, ast::TexelFormat::kInvalid},
- TexelFormatCase{SpvImageFormatRgb10A2, false, ast::TexelFormat::kInvalid},
- TexelFormatCase{SpvImageFormatRg16, false, ast::TexelFormat::kInvalid},
- TexelFormatCase{SpvImageFormatRg8, false, ast::TexelFormat::kInvalid},
- TexelFormatCase{SpvImageFormatR16, false, ast::TexelFormat::kInvalid},
- TexelFormatCase{SpvImageFormatR8, false, ast::TexelFormat::kInvalid},
- TexelFormatCase{SpvImageFormatRgba16Snorm, false, ast::TexelFormat::kInvalid},
- TexelFormatCase{SpvImageFormatRg16Snorm, false, ast::TexelFormat::kInvalid},
- TexelFormatCase{SpvImageFormatRg8Snorm, false, ast::TexelFormat::kInvalid},
- TexelFormatCase{SpvImageFormatRg16i, false, ast::TexelFormat::kInvalid},
- TexelFormatCase{SpvImageFormatRg8i, false, ast::TexelFormat::kInvalid},
- TexelFormatCase{SpvImageFormatR8i, false, ast::TexelFormat::kInvalid},
- TexelFormatCase{SpvImageFormatRgb10a2ui, false, ast::TexelFormat::kInvalid},
- TexelFormatCase{SpvImageFormatRg16ui, false, ast::TexelFormat::kInvalid},
- TexelFormatCase{SpvImageFormatRg8ui, false, ast::TexelFormat::kInvalid}));
+ TexelFormatCase{SpvImageFormatRg16f, false, ast::TexelFormat::kUndefined},
+ TexelFormatCase{SpvImageFormatR11fG11fB10f, false, ast::TexelFormat::kUndefined},
+ TexelFormatCase{SpvImageFormatR16f, false, ast::TexelFormat::kUndefined},
+ TexelFormatCase{SpvImageFormatRgb10A2, false, ast::TexelFormat::kUndefined},
+ TexelFormatCase{SpvImageFormatRg16, false, ast::TexelFormat::kUndefined},
+ TexelFormatCase{SpvImageFormatRg8, false, ast::TexelFormat::kUndefined},
+ TexelFormatCase{SpvImageFormatR16, false, ast::TexelFormat::kUndefined},
+ TexelFormatCase{SpvImageFormatR8, false, ast::TexelFormat::kUndefined},
+ TexelFormatCase{SpvImageFormatRgba16Snorm, false, ast::TexelFormat::kUndefined},
+ TexelFormatCase{SpvImageFormatRg16Snorm, false, ast::TexelFormat::kUndefined},
+ TexelFormatCase{SpvImageFormatRg8Snorm, false, ast::TexelFormat::kUndefined},
+ TexelFormatCase{SpvImageFormatRg16i, false, ast::TexelFormat::kUndefined},
+ TexelFormatCase{SpvImageFormatRg8i, false, ast::TexelFormat::kUndefined},
+ TexelFormatCase{SpvImageFormatR8i, false, ast::TexelFormat::kUndefined},
+ TexelFormatCase{SpvImageFormatRgb10a2ui, false, ast::TexelFormat::kUndefined},
+ TexelFormatCase{SpvImageFormatRg16ui, false, ast::TexelFormat::kUndefined},
+ TexelFormatCase{SpvImageFormatRg8ui, false, ast::TexelFormat::kUndefined}));
} // namespace
} // namespace tint::reader::spirv
diff --git a/src/tint/reader/spirv/function.cc b/src/tint/reader/spirv/function.cc
index 7bbb5df..9c38818 100644
--- a/src/tint/reader/spirv/function.cc
+++ b/src/tint/reader/spirv/function.cc
@@ -329,6 +329,10 @@
return "faceForward";
case GLSLstd450FindILsb:
return "firstTrailingBit";
+ case GLSLstd450FindSMsb:
+ return "firstLeadingBit";
+ case GLSLstd450FindUMsb:
+ return "firstLeadingBit";
case GLSLstd450Floor:
return "floor";
case GLSLstd450Fma:
@@ -429,9 +433,6 @@
case GLSLstd450PackDouble2x32:
case GLSLstd450UnpackDouble2x32:
- case GLSLstd450FindSMsb:
- case GLSLstd450FindUMsb:
-
case GLSLstd450InterpolateAtCentroid:
case GLSLstd450InterpolateAtSample:
case GLSLstd450InterpolateAtOffset:
@@ -2592,7 +2593,6 @@
// Construct the reference type, mapping storage class correctly.
const auto* type =
RemapPointerProperties(parser_impl_.ConvertType(inst->type_id(), PtrAs::Ref), id);
- // TODO(crbug.com/tint/1041): Fix access mode
return TypedExpression{type, create<ast::IdentifierExpression>(
Source{}, builder_.Symbols().Register(name))};
}
@@ -4854,15 +4854,20 @@
}
// Local variables are always Function storage class, with default
// access mode.
- return DefInfo::Pointer{ast::AddressSpace::kFunction, ast::Access::kInvalid};
+ return DefInfo::Pointer{ast::AddressSpace::kFunction, ast::Access::kUndefined};
}
case SpvOpFunctionParameter: {
const auto* type = As<Pointer>(parser_impl_.ConvertType(inst.type_id()));
- // TODO(crbug.com/tint/1041): Add access mode.
- // Using kUndefined is ok for now, since the only non-default access mode
- // on a pointer would be for a storage buffer, and baseline SPIR-V doesn't
- // allow passing pointers to buffers as function parameters.
- return DefInfo::Pointer{type->address_space, ast::Access::kInvalid};
+ // For access mode, kUndefined is ok for now, since the
+ // only non-default access mode on a pointer would be for a storage
+ // buffer, and baseline SPIR-V doesn't allow passing pointers to
+ // buffers as function parameters.
+ // If/when the SPIR-V path supports variable pointers, then we
+ // can pointers to read-only storage buffers passed as
+ // parameters. In that case we need to do a global analysis to
+ // determine what the formal argument parameter type should be,
+ // whether it has read_only or read_write access mode.
+ return DefInfo::Pointer{type->address_space, ast::Access::kUndefined};
}
default:
break;
@@ -4898,13 +4903,11 @@
const Type* FunctionEmitter::RemapPointerProperties(const Type* type, uint32_t result_id) {
if (auto* ast_ptr_type = As<Pointer>(type)) {
const auto pi = GetPointerInfo(result_id);
- // TODO(crbug.com/tin/t1041): also do access mode
- return ty_.Pointer(ast_ptr_type->type, pi.address_space);
+ return ty_.Pointer(ast_ptr_type->type, pi.address_space, pi.access);
}
if (auto* ast_ptr_type = As<Reference>(type)) {
const auto pi = GetPointerInfo(result_id);
- // TODO(crbug.com/tin/t1041): also do access mode
- return ty_.Reference(ast_ptr_type->type, pi.address_space);
+ return ty_.Reference(ast_ptr_type->type, pi.address_space, pi.access);
}
return type;
}
@@ -5086,7 +5089,7 @@
// Avoid moving combinatorial values across constructs. This is a
// simple heuristic to avoid changing the cost of an operation
// by moving it into or out of a loop, for example.
- if ((def_info->pointer.address_space == ast::AddressSpace::kInvalid) &&
+ if ((def_info->pointer.address_space == ast::AddressSpace::kUndefined) &&
local_def.used_in_another_construct) {
should_hoist_to_let = true;
}
diff --git a/src/tint/reader/spirv/function.h b/src/tint/reader/spirv/function.h
index b8240cf..9c9c832 100644
--- a/src/tint/reader/spirv/function.h
+++ b/src/tint/reader/spirv/function.h
@@ -332,10 +332,10 @@
/// buffer expressed in the old style (with Uniform address space)
/// that needs to be remapped to StorageBuffer address space.
/// This is kInvalid for non-pointers.
- ast::AddressSpace address_space = ast::AddressSpace::kInvalid;
+ ast::AddressSpace address_space = ast::AddressSpace::kUndefined;
/// The declared access mode.
- ast::Access access = ast::Access::kInvalid;
+ ast::Access access = ast::Access::kUndefined;
};
/// The expression to use when sinking pointers into their use.
diff --git a/src/tint/reader/spirv/function_glsl_std_450_test.cc b/src/tint/reader/spirv/function_glsl_std_450_test.cc
index 7a4fd94..584ec2a 100644
--- a/src/tint/reader/spirv/function_glsl_std_450_test.cc
+++ b/src/tint/reader/spirv/function_glsl_std_450_test.cc
@@ -583,7 +583,8 @@
INSTANTIATE_TEST_SUITE_P(Samples,
SpvParserTest_GlslStd450_Inting_Inting,
::testing::Values(GlslStd450Case{"SAbs", "abs"},
- GlslStd450Case{"FindILsb", "firstTrailingBit"}));
+ GlslStd450Case{"FindILsb", "firstTrailingBit"},
+ GlslStd450Case{"FindSMsb", "firstLeadingBit"}));
INSTANTIATE_TEST_SUITE_P(Samples,
SpvParserTest_GlslStd450_Inting_IntingInting,
@@ -701,7 +702,8 @@
INSTANTIATE_TEST_SUITE_P(Samples,
SpvParserTest_GlslStd450_Uinting_Uinting,
- ::testing::Values(GlslStd450Case{"FindILsb", "firstTrailingBit"}));
+ ::testing::Values(GlslStd450Case{"FindILsb", "firstTrailingBit"},
+ GlslStd450Case{"FindUMsb", "firstLeadingBit"}));
INSTANTIATE_TEST_SUITE_P(Samples,
SpvParserTest_GlslStd450_Uinting_UintingUinting,
@@ -977,6 +979,106 @@
<< body;
}
+TEST_F(SpvParserTest, RectifyOperandsAndResult_FindSMsb) {
+ // Check signedness conversion of arguments and results.
+ // SPIR-V signed arg -> keep it
+ // signed result -> keep it
+ // unsigned result -> cast result to unsigned
+ //
+ // SPIR-V unsigned arg -> cast it to signed
+ // signed result -> keept it
+ // unsigned result -> cast result to unsigned
+ const auto assembly = Preamble() + R"(
+ ; signed arg
+ ; signed result
+ %1 = OpExtInst %int %glsl FindSMsb %i1
+ %2 = OpExtInst %v2int %glsl FindSMsb %v2i1
+
+ ; signed arg
+ ; unsigned result
+ %3 = OpExtInst %uint %glsl FindSMsb %i1
+ %4 = OpExtInst %v2uint %glsl FindSMsb %v2i1
+
+ ; unsigned arg
+ ; signed result
+ %5 = OpExtInst %int %glsl FindSMsb %u1
+ %6 = OpExtInst %v2int %glsl FindSMsb %v2u1
+
+ ; unsigned arg
+ ; unsigned result
+ %7 = OpExtInst %uint %glsl FindSMsb %u1
+ %8 = OpExtInst %v2uint %glsl FindSMsb %v2u1
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+ auto fe = p->function_emitter(100);
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ auto ast_body = fe.ast_body();
+ const auto body = test::ToString(p->program(), ast_body);
+ EXPECT_THAT(body, HasSubstr(R"(
+let x_1 : i32 = firstLeadingBit(i1);
+let x_2 : vec2<i32> = firstLeadingBit(v2i1);
+let x_3 : u32 = bitcast<u32>(firstLeadingBit(i1));
+let x_4 : vec2<u32> = bitcast<vec2<u32>>(firstLeadingBit(v2i1));
+let x_5 : i32 = firstLeadingBit(bitcast<i32>(u1));
+let x_6 : vec2<i32> = firstLeadingBit(bitcast<vec2<i32>>(v2u1));
+let x_7 : u32 = bitcast<u32>(firstLeadingBit(bitcast<i32>(u1)));
+let x_8 : vec2<u32> = bitcast<vec2<u32>>(firstLeadingBit(bitcast<vec2<i32>>(v2u1)));
+)")) << body;
+}
+
+TEST_F(SpvParserTest, RectifyOperandsAndResult_FindUMsb) {
+ // Check signedness conversion of arguments and results.
+ // SPIR-V signed arg -> cast arg to unsigned
+ // signed result -> cast result to signed
+ // unsigned result -> keep it
+ //
+ // SPIR-V unsigned arg -> keep it
+ // signed result -> cast result to signed
+ // unsigned result -> keep it
+ const auto assembly = Preamble() + R"(
+ ; signed arg
+ ; signed result
+ %1 = OpExtInst %int %glsl FindUMsb %i1
+ %2 = OpExtInst %v2int %glsl FindUMsb %v2i1
+
+ ; signed arg
+ ; unsigned result
+ %3 = OpExtInst %uint %glsl FindUMsb %i1
+ %4 = OpExtInst %v2uint %glsl FindUMsb %v2i1
+
+ ; unsigned arg
+ ; signed result
+ %5 = OpExtInst %int %glsl FindUMsb %u1
+ %6 = OpExtInst %v2int %glsl FindUMsb %v2u1
+
+ ; unsigned arg
+ ; unsigned result
+ %7 = OpExtInst %uint %glsl FindUMsb %u1
+ %8 = OpExtInst %v2uint %glsl FindUMsb %v2u1
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+ auto fe = p->function_emitter(100);
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ auto ast_body = fe.ast_body();
+ const auto body = test::ToString(p->program(), ast_body);
+ EXPECT_THAT(body, HasSubstr(R"(
+let x_1 : i32 = bitcast<i32>(firstLeadingBit(bitcast<u32>(i1)));
+let x_2 : vec2<i32> = bitcast<vec2<i32>>(firstLeadingBit(bitcast<vec2<u32>>(v2i1)));
+let x_3 : u32 = firstLeadingBit(bitcast<u32>(i1));
+let x_4 : vec2<u32> = firstLeadingBit(bitcast<vec2<u32>>(v2i1));
+let x_5 : i32 = bitcast<i32>(firstLeadingBit(u1));
+let x_6 : vec2<i32> = bitcast<vec2<i32>>(firstLeadingBit(v2u1));
+let x_7 : u32 = firstLeadingBit(u1);
+let x_8 : vec2<u32> = firstLeadingBit(v2u1);
+)")) << body;
+}
+
struct DataPackingCase {
std::string opcode;
std::string wgsl_func;
diff --git a/src/tint/reader/spirv/function_memory_test.cc b/src/tint/reader/spirv/function_memory_test.cc
index a64aac5..66979ae 100644
--- a/src/tint/reader/spirv/function_memory_test.cc
+++ b/src/tint/reader/spirv/function_memory_test.cc
@@ -858,7 +858,52 @@
EXPECT_EQ(got, expected) << got;
}
-std::string OldStorageBufferPreamble() {
+std::string NewStorageBufferPreamble(bool nonwritable = false) {
+ // Declare a buffer with
+ // StorageBuffer storage class
+ // Block struct decoration
+ return std::string(R"(
+ OpCapability Shader
+ OpExtension "SPV_KHR_storage_buffer_storage_class"
+ OpMemoryModel Logical Simple
+ OpEntryPoint Fragment %100 "main"
+ OpExecutionMode %100 OriginUpperLeft
+ OpName %myvar "myvar"
+
+ OpDecorate %myvar DescriptorSet 0
+ OpDecorate %myvar Binding 0
+
+ OpDecorate %struct Block
+ OpMemberDecorate %struct 0 Offset 0
+ OpMemberDecorate %struct 1 Offset 4
+ OpDecorate %arr ArrayStride 4
+ )") +
+ (nonwritable ? R"(
+ OpMemberDecorate %struct 0 NonWritable
+ OpMemberDecorate %struct 1 NonWritable)"
+ : "") +
+ R"(
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+
+ %uint_0 = OpConstant %uint 0
+ %uint_1 = OpConstant %uint 1
+
+ %arr = OpTypeRuntimeArray %uint
+ %struct = OpTypeStruct %uint %arr
+ %ptr_struct = OpTypePointer StorageBuffer %struct
+ %ptr_uint = OpTypePointer StorageBuffer %uint
+
+ %myvar = OpVariable %ptr_struct StorageBuffer
+ )";
+}
+
+std::string OldStorageBufferPreamble(bool nonwritable = false) {
+ // Declare a buffer with
+ // Unifrom storage class
+ // BufferBlock struct decoration
+ // This is the deprecated way to declare a storage buffer.
return Preamble() + R"(
OpName %myvar "myvar"
@@ -869,7 +914,12 @@
OpMemberDecorate %struct 0 Offset 0
OpMemberDecorate %struct 1 Offset 4
OpDecorate %arr ArrayStride 4
-
+ )" +
+ (nonwritable ? R"(
+ OpMemberDecorate %struct 0 NonWritable
+ OpMemberDecorate %struct 1 NonWritable)"
+ : "") +
+ R"(
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%uint = OpTypeInt 32 0
@@ -938,7 +988,7 @@
)"));
}
-TEST_F(SpvParserMemoryTest, RemapStorageBuffer_ThroughAccessChain_NonCascaded_UsedTwice) {
+TEST_F(SpvParserMemoryTest, RemapStorageBuffer_ThroughAccessChain_NonCascaded_UsedTwice_ReadWrite) {
// Use the pointer value twice, which provokes the spirv-reader
// to make a let declaration for the pointer. The storage class
// must be 'storage', not 'uniform'.
@@ -966,15 +1016,130 @@
EXPECT_TRUE(fe.EmitBody()) << p->error();
auto ast_body = fe.ast_body();
const auto got = test::ToString(p->program(), ast_body);
- EXPECT_THAT(got, HasSubstr(R"(let x_1 : ptr<storage, u32> = &(myvar.field0);
+ EXPECT_THAT(got, HasSubstr(R"(let x_1 : ptr<storage, u32, read_write> = &(myvar.field0);
*(x_1) = 0u;
*(x_1) = 0u;
-let x_2 : ptr<storage, u32> = &(myvar.field1[1u]);
+let x_2 : ptr<storage, u32, read_write> = &(myvar.field1[1u]);
let x_3 : u32 = *(x_2);
*(x_2) = 0u;
)"));
}
+TEST_F(SpvParserMemoryTest, RemapStorageBuffer_ThroughAccessChain_NonCascaded_UsedTwice_ReadOnly) {
+ // Like the previous test, but make the storage buffer read_only.
+ // The pointer type must also be read-only.
+ const auto assembly = OldStorageBufferPreamble(true) + R"(
+ %100 = OpFunction %void None %voidfn
+ %entry = OpLabel
+
+ ; the scalar element
+ %1 = OpAccessChain %ptr_uint %myvar %uint_0
+ OpStore %1 %uint_0
+ OpStore %1 %uint_0
+
+ ; element in the runtime array
+ %2 = OpAccessChain %ptr_uint %myvar %uint_1 %uint_1
+ ; Use the pointer twice
+ %3 = OpLoad %uint %2
+ OpStore %2 %uint_0
+
+ OpReturn
+ OpFunctionEnd
+)";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
+ auto fe = p->function_emitter(100);
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ auto ast_body = fe.ast_body();
+ const auto got = test::ToString(p->program(), ast_body);
+ EXPECT_THAT(got, HasSubstr(R"(let x_1 : ptr<storage, u32, read> = &(myvar.field0);
+*(x_1) = 0u;
+*(x_1) = 0u;
+let x_2 : ptr<storage, u32, read> = &(myvar.field1[1u]);
+let x_3 : u32 = *(x_2);
+*(x_2) = 0u;
+)")) << got
+ << assembly;
+}
+
+TEST_F(SpvParserMemoryTest, StorageBuffer_ThroughAccessChain_NonCascaded_UsedTwice_ReadWrite) {
+ // Use new style storage buffer declaration:
+ // StorageBuffer storage class,
+ // Block decoration
+ // The pointer type must use 'storage' address space, and should use defaulted access
+ const auto assembly = NewStorageBufferPreamble() + R"(
+ %100 = OpFunction %void None %voidfn
+ %entry = OpLabel
+
+ ; the scalar element
+ %1 = OpAccessChain %ptr_uint %myvar %uint_0
+ OpStore %1 %uint_0
+ OpStore %1 %uint_0
+
+ ; element in the runtime array
+ %2 = OpAccessChain %ptr_uint %myvar %uint_1 %uint_1
+ ; Use the pointer twice
+ %3 = OpLoad %uint %2
+ OpStore %2 %uint_0
+
+ OpReturn
+ OpFunctionEnd
+)";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
+ auto fe = p->function_emitter(100);
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ auto ast_body = fe.ast_body();
+ const auto got = test::ToString(p->program(), ast_body);
+ EXPECT_THAT(got, HasSubstr(R"(let x_1 : ptr<storage, u32, read_write> = &(myvar.field0);
+*(x_1) = 0u;
+*(x_1) = 0u;
+let x_2 : ptr<storage, u32, read_write> = &(myvar.field1[1u]);
+let x_3 : u32 = *(x_2);
+*(x_2) = 0u;
+)")) << got;
+}
+
+TEST_F(SpvParserMemoryTest, StorageBuffer_ThroughAccessChain_NonCascaded_UsedTwice_ReadOnly) {
+ // Like the previous test, but make the storage buffer read_only.
+ // Use new style storage buffer declaration:
+ // StorageBuffer storage class,
+ // Block decoration
+ // The pointer type must use 'storage' address space, and must use read_only
+ // access
+ const auto assembly = NewStorageBufferPreamble(true) + R"(
+ %100 = OpFunction %void None %voidfn
+ %entry = OpLabel
+
+ ; the scalar element
+ %1 = OpAccessChain %ptr_uint %myvar %uint_0
+ OpStore %1 %uint_0
+ OpStore %1 %uint_0
+
+ ; element in the runtime array
+ %2 = OpAccessChain %ptr_uint %myvar %uint_1 %uint_1
+ ; Use the pointer twice
+ %3 = OpLoad %uint %2
+ OpStore %2 %uint_0
+
+ OpReturn
+ OpFunctionEnd
+)";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
+ auto fe = p->function_emitter(100);
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ auto ast_body = fe.ast_body();
+ const auto got = test::ToString(p->program(), ast_body);
+ EXPECT_THAT(got, HasSubstr(R"(let x_1 : ptr<storage, u32, read> = &(myvar.field0);
+*(x_1) = 0u;
+*(x_1) = 0u;
+let x_2 : ptr<storage, u32, read> = &(myvar.field1[1u]);
+let x_3 : u32 = *(x_2);
+*(x_2) = 0u;
+)")) << got;
+}
+
TEST_F(SpvParserMemoryTest, RemapStorageBuffer_ThroughAccessChain_NonCascaded_InBoundsAccessChain) {
// Like the previous test, but using OpInBoundsAccessChain.
const auto assembly = OldStorageBufferPreamble() + R"(
@@ -1050,7 +1215,7 @@
EXPECT_TRUE(fe.EmitBody()) << p->error();
auto ast_body = fe.ast_body();
EXPECT_THAT(test::ToString(p->program(), ast_body),
- HasSubstr(R"(let x_2 : ptr<storage, u32> = &(myvar.field1[1u]);
+ HasSubstr(R"(let x_2 : ptr<storage, u32, read_write> = &(myvar.field1[1u]);
*(x_2) = 0u;
)")) << p->error();
@@ -1128,7 +1293,7 @@
EXPECT_TRUE(fe.EmitBody()) << p->error();
auto ast_body = fe.ast_body();
const auto body_str = test::ToString(p->program(), ast_body);
- EXPECT_THAT(body_str, HasSubstr(R"(let x_2 : ptr<storage, S> = &(myvar);
+ EXPECT_THAT(body_str, HasSubstr(R"(let x_2 : ptr<storage, S, read_write> = &(myvar);
let x_1 : u32 = arrayLength(&((*(x_2)).rtarr));
)")) << body_str;
diff --git a/src/tint/reader/spirv/parser_impl.cc b/src/tint/reader/spirv/parser_impl.cc
index 63e5cb4..c64586a 100644
--- a/src/tint/reader/spirv/parser_impl.cc
+++ b/src/tint/reader/spirv/parser_impl.cc
@@ -117,6 +117,7 @@
case GLSLstd450SMin:
case GLSLstd450SMax:
case GLSLstd450SClamp:
+ case GLSLstd450FindSMsb:
return true;
default:
break;
@@ -150,6 +151,7 @@
case GLSLstd450UMin:
case GLSLstd450UMax:
case GLSLstd450UClamp:
+ case GLSLstd450FindUMsb:
return true;
default:
break;
@@ -222,8 +224,8 @@
case GLSLstd450UMax:
case GLSLstd450UClamp:
case GLSLstd450FindILsb:
- // TODO(dneto): FindSMsb?
- // TODO(dneto): FindUMsb?
+ case GLSLstd450FindSMsb:
+ case GLSLstd450FindUMsb:
return true;
default:
break;
@@ -1207,7 +1209,7 @@
}
auto ast_address_space = enum_converter_.ToAddressSpace(storage_class);
- if (ast_address_space == ast::AddressSpace::kInvalid) {
+ if (ast_address_space == ast::AddressSpace::kUndefined) {
Fail() << "SPIR-V pointer type with ID " << type_id << " has invalid storage class "
<< static_cast<uint32_t>(storage_class);
return nullptr;
@@ -1567,7 +1569,7 @@
return nullptr;
}
- ast::Access access = ast::Access::kInvalid;
+ ast::Access access = ast::Access::kUndefined;
if (address_space == ast::AddressSpace::kStorage) {
bool read_only = false;
if (auto* tn = storage_type->As<Named>()) {
@@ -1678,7 +1680,7 @@
break;
}
auto ast_builtin = enum_converter_.ToBuiltin(spv_builtin);
- if (ast_builtin == ast::BuiltinValue::kInvalid) {
+ if (ast_builtin == ast::BuiltinValue::kUndefined) {
// A diagnostic has already been emitted.
return false;
}
@@ -1750,7 +1752,7 @@
AttributeList* attributes) {
// Vulkan defaults to perspective-correct interpolation.
ast::InterpolationType type = ast::InterpolationType::kPerspective;
- ast::InterpolationSampling sampling = ast::InterpolationSampling::kInvalid;
+ ast::InterpolationSampling sampling = ast::InterpolationSampling::kUndefined;
for (const auto& deco : decorations) {
TINT_ASSERT(Reader, deco.size() > 0);
@@ -1805,7 +1807,7 @@
// Apply interpolation.
if (type == ast::InterpolationType::kPerspective &&
- sampling == ast::InterpolationSampling::kInvalid) {
+ sampling == ast::InterpolationSampling::kUndefined) {
// This is the default. Don't add a decoration.
} else {
attributes->Push(create<ast::InterpolateAttribute>(type, sampling));
@@ -2477,7 +2479,7 @@
} else {
const auto access = ast::Access::kWrite;
const auto format = enum_converter_.ToTexelFormat(image_type->format());
- if (format == ast::TexelFormat::kInvalid) {
+ if (format == ast::TexelFormat::kUndefined) {
return nullptr;
}
ast_store_type = ty_.StorageTexture(dim, format, access);
diff --git a/src/tint/reader/spirv/parser_type.cc b/src/tint/reader/spirv/parser_type.cc
index 256444a..ff4b96e 100644
--- a/src/tint/reader/spirv/parser_type.cc
+++ b/src/tint/reader/spirv/parser_type.cc
@@ -50,11 +50,15 @@
namespace {
struct PointerHasher {
- size_t operator()(const Pointer& t) const { return utils::Hash(t.type, t.address_space); }
+ size_t operator()(const Pointer& t) const {
+ return utils::Hash(t.type, t.address_space, t.access);
+ }
};
struct ReferenceHasher {
- size_t operator()(const Reference& t) const { return utils::Hash(t.type, t.address_space); }
+ size_t operator()(const Reference& t) const {
+ return utils::Hash(t.type, t.address_space, t.access);
+ }
};
struct VectorHasher {
@@ -107,10 +111,10 @@
// Equality operators
//! @cond Doxygen_Suppress
static bool operator==(const Pointer& a, const Pointer& b) {
- return a.type == b.type && a.address_space == b.address_space;
+ return a.type == b.type && a.address_space == b.address_space && a.access == b.access;
}
static bool operator==(const Reference& a, const Reference& b) {
- return a.type == b.type && a.address_space == b.address_space;
+ return a.type == b.type && a.address_space == b.address_space && a.access == b.access;
}
static bool operator==(const Vector& a, const Vector& b) {
return a.type == b.type && a.size == b.size;
@@ -170,14 +174,16 @@
Texture::~Texture() = default;
-Pointer::Pointer(const Type* t, ast::AddressSpace s) : type(t), address_space(s) {}
+Pointer::Pointer(const Type* t, ast::AddressSpace s, ast::Access a)
+ : type(t), address_space(s), access(a) {}
Pointer::Pointer(const Pointer&) = default;
const ast::Type* Pointer::Build(ProgramBuilder& b) const {
- return b.ty.pointer(type->Build(b), address_space);
+ return b.ty.pointer(type->Build(b), address_space, access);
}
-Reference::Reference(const Type* t, ast::AddressSpace s) : type(t), address_space(s) {}
+Reference::Reference(const Type* t, ast::AddressSpace s, ast::Access a)
+ : type(t), address_space(s), access(a) {}
Reference::Reference(const Reference&) = default;
const ast::Type* Reference::Build(ProgramBuilder& b) const {
@@ -438,12 +444,16 @@
return state->i32_;
}
-const spirv::Pointer* TypeManager::Pointer(const Type* el, ast::AddressSpace address_space) {
- return state->pointers_.Get(el, address_space);
+const spirv::Pointer* TypeManager::Pointer(const Type* el,
+ ast::AddressSpace address_space,
+ ast::Access access) {
+ return state->pointers_.Get(el, address_space, access);
}
-const spirv::Reference* TypeManager::Reference(const Type* el, ast::AddressSpace address_space) {
- return state->references_.Get(el, address_space);
+const spirv::Reference* TypeManager::Reference(const Type* el,
+ ast::AddressSpace address_space,
+ ast::Access access) {
+ return state->references_.Get(el, address_space, access);
}
const spirv::Vector* TypeManager::Vector(const Type* el, uint32_t size) {
diff --git a/src/tint/reader/spirv/parser_type.h b/src/tint/reader/spirv/parser_type.h
index 4c67f7a..48b6f3f 100644
--- a/src/tint/reader/spirv/parser_type.h
+++ b/src/tint/reader/spirv/parser_type.h
@@ -157,12 +157,13 @@
#endif // NDEBUG
};
-/// `ptr<SC, T>` type
+/// `ptr<SC, T, AM>` type
struct Pointer final : public Castable<Pointer, Type> {
/// Constructor
/// @param ty the store type
/// @param sc the pointer address space
- Pointer(const Type* ty, ast::AddressSpace sc);
+ /// @param access the declared access mode
+ Pointer(const Type* ty, ast::AddressSpace sc, ast::Access access);
/// Copy constructor
/// @param other the other type to copy
@@ -181,16 +182,19 @@
Type const* const type;
/// the pointer address space
ast::AddressSpace const address_space;
+ /// the pointer declared access mode
+ ast::Access const access;
};
-/// `ref<SC, T>` type
+/// `ref<SC, T, AM>` type
/// Note this has no AST representation, but is used for type tracking in the
/// reader.
struct Reference final : public Castable<Reference, Type> {
/// Constructor
/// @param ty the referenced type
/// @param sc the reference address space
- Reference(const Type* ty, ast::AddressSpace sc);
+ /// @param access the reference declared access mode
+ Reference(const Type* ty, ast::AddressSpace sc, ast::Access access);
/// Copy constructor
/// @param other the other type to copy
@@ -209,6 +213,8 @@
Type const* const type;
/// the pointer address space
ast::AddressSpace const address_space;
+ /// the pointer declared access mode
+ ast::Access const access;
};
/// `vecN<T>` type
@@ -535,14 +541,20 @@
const spirv::I32* I32();
/// @param ty the store type
/// @param address_space the pointer address space
+ /// @param access the declared access mode
/// @return a Pointer type. Repeated calls with the same arguments will return
/// the same pointer.
- const spirv::Pointer* Pointer(const Type* ty, ast::AddressSpace address_space);
+ const spirv::Pointer* Pointer(const Type* ty,
+ ast::AddressSpace address_space,
+ ast::Access access = ast::Access::kUndefined);
/// @param ty the referenced type
/// @param address_space the reference address space
+ /// @param access the declared access mode
/// @return a Reference type. Repeated calls with the same arguments will
/// return the same pointer.
- const spirv::Reference* Reference(const Type* ty, ast::AddressSpace address_space);
+ const spirv::Reference* Reference(const Type* ty,
+ ast::AddressSpace address_space,
+ ast::Access access = ast::Access::kUndefined);
/// @param ty the element type
/// @param sz the number of elements in the vector
/// @return a Vector type. Repeated calls with the same arguments will return
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index b042ac2..3dad333 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -383,7 +383,7 @@
return add_error(t.source(), "enable directives don't take parenthesis");
}
- auto extension = ast::Extension::kInvalid;
+ auto extension = ast::Extension::kUndefined;
if (t.Is(Token::Type::kF16)) {
// `f16` is a valid extension name and also a keyword
synchronized_ = true;
@@ -991,7 +991,7 @@
}
return VariableQualifier{sc.value, ac.value};
}
- return Expect<VariableQualifier>{VariableQualifier{sc.value, ast::Access::kInvalid},
+ return Expect<VariableQualifier>{VariableQualifier{sc.value, ast::Access::kUndefined},
source};
});
@@ -1179,7 +1179,7 @@
auto& t = peek();
if (t.IsIdentifier()) {
auto val = parse(t.to_str());
- if (val != ENUM::kInvalid) {
+ if (val != ENUM::kUndefined) {
synchronized_ = true;
next();
return {val, t.source()};
@@ -1247,7 +1247,7 @@
const char* use = "ptr declaration";
auto address_space = ast::AddressSpace::kNone;
- auto access = ast::Access::kInvalid;
+ auto access = ast::Access::kUndefined;
auto subtype = expect_lt_gt_block(use, [&]() -> Expect<const ast::Type*> {
auto sc = expect_address_space(use);
@@ -3512,7 +3512,7 @@
return Failure::kErrored;
}
- ast::InterpolationSampling sampling = ast::InterpolationSampling::kInvalid;
+ ast::InterpolationSampling sampling = ast::InterpolationSampling::kUndefined;
if (match(Token::Type::kComma)) {
if (!peek_is(Token::Type::kParenRight)) {
auto sample = expect_interpolation_sample_name();
diff --git a/src/tint/reader/wgsl/parser_impl.h b/src/tint/reader/wgsl/parser_impl.h
index 77196a0..60b94de 100644
--- a/src/tint/reader/wgsl/parser_impl.h
+++ b/src/tint/reader/wgsl/parser_impl.h
@@ -296,7 +296,7 @@
/// Variable address space
ast::AddressSpace address_space = ast::AddressSpace::kNone;
/// Variable access control
- ast::Access access = ast::Access::kInvalid;
+ ast::Access access = ast::Access::kUndefined;
/// Variable type
const ast::Type* type = nullptr;
};
@@ -306,7 +306,7 @@
/// The variable's address space
ast::AddressSpace address_space = ast::AddressSpace::kNone;
/// The variable's access control
- ast::Access access = ast::Access::kInvalid;
+ ast::Access access = ast::Access::kUndefined;
};
/// MatrixDimensions contains the column and row information for a matrix
diff --git a/src/tint/reader/wgsl/parser_impl_texture_sampler_test.cc b/src/tint/reader/wgsl/parser_impl_texture_sampler_test.cc
index ce31276..a13998a 100644
--- a/src/tint/reader/wgsl/parser_impl_texture_sampler_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_texture_sampler_test.cc
@@ -239,7 +239,8 @@
EXPECT_EQ(t.value, nullptr);
EXPECT_FALSE(t.matched);
EXPECT_TRUE(t.errored);
- EXPECT_EQ(p->error(), R"(1:30: expected access control for storage texture type. Did you mean 'read'?
+ EXPECT_EQ(p->error(),
+ R"(1:30: expected access control for storage texture type. Did you mean 'read'?
Possible values: 'read', 'read_write', 'write')");
}
diff --git a/src/tint/reader/wgsl/parser_impl_variable_attribute_test.cc b/src/tint/reader/wgsl/parser_impl_variable_attribute_test.cc
index 70af3b2..4c68f9b 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_attribute_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_attribute_test.cc
@@ -227,7 +227,7 @@
auto* interp = var_attr->As<ast::InterpolateAttribute>();
EXPECT_EQ(interp->type, ast::InterpolationType::kFlat);
- EXPECT_EQ(interp->sampling, ast::InterpolationSampling::kInvalid);
+ EXPECT_EQ(interp->sampling, ast::InterpolationSampling::kUndefined);
}
TEST_F(ParserImplTest, Attribute_Interpolate_Single_TrailingComma) {
@@ -243,7 +243,7 @@
auto* interp = var_attr->As<ast::InterpolateAttribute>();
EXPECT_EQ(interp->type, ast::InterpolationType::kFlat);
- EXPECT_EQ(interp->sampling, ast::InterpolationSampling::kInvalid);
+ EXPECT_EQ(interp->sampling, ast::InterpolationSampling::kUndefined);
}
TEST_F(ParserImplTest, Attribute_Interpolate_Single_DoubleTrailingComma) {
diff --git a/src/tint/reader/wgsl/parser_impl_variable_qualifier_test.cc b/src/tint/reader/wgsl/parser_impl_variable_qualifier_test.cc
index 32c1a54..5c0cd5e 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_qualifier_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_qualifier_test.cc
@@ -47,11 +47,11 @@
ParserImplTest,
VariableQualifierTest,
testing::Values(
- VariableStorageData{"uniform", ast::AddressSpace::kUniform, ast::Access::kInvalid},
- VariableStorageData{"workgroup", ast::AddressSpace::kWorkgroup, ast::Access::kInvalid},
- VariableStorageData{"storage", ast::AddressSpace::kStorage, ast::Access::kInvalid},
- VariableStorageData{"private", ast::AddressSpace::kPrivate, ast::Access::kInvalid},
- VariableStorageData{"function", ast::AddressSpace::kFunction, ast::Access::kInvalid},
+ VariableStorageData{"uniform", ast::AddressSpace::kUniform, ast::Access::kUndefined},
+ VariableStorageData{"workgroup", ast::AddressSpace::kWorkgroup, ast::Access::kUndefined},
+ VariableStorageData{"storage", ast::AddressSpace::kStorage, ast::Access::kUndefined},
+ VariableStorageData{"private", ast::AddressSpace::kPrivate, ast::Access::kUndefined},
+ VariableStorageData{"function", ast::AddressSpace::kFunction, ast::Access::kUndefined},
VariableStorageData{"storage, read", ast::AddressSpace::kStorage, ast::Access::kRead},
VariableStorageData{"storage, write", ast::AddressSpace::kStorage, ast::Access::kWrite},
VariableStorageData{"storage, read_write", ast::AddressSpace::kStorage,
diff --git a/src/tint/resolver/address_space_validation_test.cc b/src/tint/resolver/address_space_validation_test.cc
index ae68013..3ef19a6 100644
--- a/src/tint/resolver/address_space_validation_test.cc
+++ b/src/tint/resolver/address_space_validation_test.cc
@@ -61,7 +61,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
-note: while analysing structure member S.m
+note: while analyzing structure member S.m
12:34 note: while instantiating 'var' v)");
}
@@ -81,7 +81,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
-note: while analysing structure member S.m
+note: while analyzing structure member S.m
12:34 note: while instantiating 'var' v)");
}
@@ -317,7 +317,7 @@
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(56:78 error: runtime-sized arrays can only be used in the <storage> address space
-note: while analysing structure member S.m
+note: while analyzing structure member S.m
56:78 note: while instantiating 'var' svar)");
}
diff --git a/src/tint/resolver/attribute_validation_test.cc b/src/tint/resolver/attribute_validation_test.cc
index 018dbfd..30615bb 100644
--- a/src/tint/resolver/attribute_validation_test.cc
+++ b/src/tint/resolver/attribute_validation_test.cc
@@ -721,7 +721,7 @@
}
TEST_F(StructMemberAttributeTest, Align_Attribute_Var) {
- GlobalVar(Source{{1, 2}}, "val", ty.f32(), ast::AddressSpace::kPrivate, ast::Access::kInvalid,
+ GlobalVar(Source{{1, 2}}, "val", ty.f32(), ast::AddressSpace::kPrivate, ast::Access::kUndefined,
Expr(1.23_f));
Structure(Source{{6, 4}}, "mystruct",
@@ -738,7 +738,9 @@
Structure("mystruct", utils::Vector{Member(
"a", ty.f32(), utils::Vector{MemberAlign(Source{{12, 34}}, "val")})});
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(12:34 error: 'align' must be an i32 or u32 value)");
+ EXPECT_EQ(
+ r()->error(),
+ R"(error: @align requires a const-expression, but expression is an override-expression)");
}
} // namespace StructAndStructMemberTests
@@ -1386,16 +1388,16 @@
ResolverAttributeValidationTest,
InterpolateParameterTest,
testing::Values(
- Params{ast::InterpolationType::kPerspective, ast::InterpolationSampling::kInvalid, true},
+ Params{ast::InterpolationType::kPerspective, ast::InterpolationSampling::kUndefined, true},
Params{ast::InterpolationType::kPerspective, ast::InterpolationSampling::kCenter, true},
Params{ast::InterpolationType::kPerspective, ast::InterpolationSampling::kCentroid, true},
Params{ast::InterpolationType::kPerspective, ast::InterpolationSampling::kSample, true},
- Params{ast::InterpolationType::kLinear, ast::InterpolationSampling::kInvalid, true},
+ Params{ast::InterpolationType::kLinear, ast::InterpolationSampling::kUndefined, true},
Params{ast::InterpolationType::kLinear, ast::InterpolationSampling::kCenter, true},
Params{ast::InterpolationType::kLinear, ast::InterpolationSampling::kCentroid, true},
Params{ast::InterpolationType::kLinear, ast::InterpolationSampling::kSample, true},
// flat interpolation must not have a sampling type
- Params{ast::InterpolationType::kFlat, ast::InterpolationSampling::kInvalid, true},
+ Params{ast::InterpolationType::kFlat, ast::InterpolationSampling::kUndefined, true},
Params{ast::InterpolationType::kFlat, ast::InterpolationSampling::kCenter, false},
Params{ast::InterpolationType::kFlat, ast::InterpolationSampling::kCentroid, false},
Params{ast::InterpolationType::kFlat, ast::InterpolationSampling::kSample, false}));
@@ -1433,7 +1435,7 @@
EXPECT_EQ(
r()->error(),
R"(12:34 error: integral user-defined vertex outputs must have a flat interpolation attribute
-note: while analysing entry point 'main')");
+note: while analyzing entry point 'main')");
}
TEST_F(InterpolateTest, MissingLocationAttribute_Parameter) {
@@ -1443,7 +1445,7 @@
utils::Vector{
Builtin(ast::BuiltinValue::kPosition),
Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
- ast::InterpolationSampling::kInvalid),
+ ast::InterpolationSampling::kUndefined),
}),
},
ty.void_(), utils::Empty,
@@ -1467,7 +1469,7 @@
utils::Vector{
Builtin(ast::BuiltinValue::kPosition),
Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
- ast::InterpolationSampling::kInvalid),
+ ast::InterpolationSampling::kUndefined),
});
EXPECT_FALSE(r()->Resolve());
@@ -1480,7 +1482,7 @@
utils::Vector{
Member("a", ty.f32(),
utils::Vector{Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
- ast::InterpolationSampling::kInvalid)}),
+ ast::InterpolationSampling::kUndefined)}),
});
EXPECT_FALSE(r()->Resolve());
diff --git a/src/tint/resolver/builtin_test.cc b/src/tint/resolver/builtin_test.cc
index 0883aed..b28521d 100644
--- a/src/tint/resolver/builtin_test.cc
+++ b/src/tint/resolver/builtin_test.cc
@@ -67,7 +67,9 @@
// TODO(crbug.com/tint/1581): Once 'abs' is implemented as @const, this will no longer be an
// error.
- EXPECT_EQ(r()->error(), R"(12:34 error: 'const' initializer must be constant expression)");
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: const initializer requires a const-expression, but expression is a runtime-expression)");
}
// Tests for Logical builtins
diff --git a/src/tint/resolver/builtin_validation_test.cc b/src/tint/resolver/builtin_validation_test.cc
index 6ba48de..e5ad2e2 100644
--- a/src/tint/resolver/builtin_validation_test.cc
+++ b/src/tint/resolver/builtin_validation_test.cc
@@ -260,8 +260,7 @@
// a vector constructor.
bool is_vector = arg_to_replace->Is<ast::CallExpression>();
- // Make the expression to be replaced, reachable. This keeps the resolver
- // happy.
+ // Make the expression to be replaced, reachable. This keeps the resolver happy.
WrapInFunction(arg_to_replace);
arg_to_replace = expr(Source{{12, 34}}, *this);
@@ -310,13 +309,65 @@
auto args = overload.args(this);
auto*& arg_to_replace = (param.position == Position::kFirst) ? args.Front() : args.Back();
- // Make the expression to be replaced, reachable. This keeps the resolver
- // happy.
+ // BuildTextureVariable() uses a Literal for scalars, and a CallExpression for
+ // a vector constructor.
+ bool is_vector = arg_to_replace->Is<ast::CallExpression>();
+
+ // Make the expression to be replaced, reachable. This keeps the resolver happy.
WrapInFunction(arg_to_replace);
arg_to_replace = Expr(Source{{12, 34}}, "G");
- // Call the builtin with the constexpr argument replaced
+ // Call the builtin with the constant-expression argument replaced
+ Func("func", utils::Empty, ty.void_(),
+ utils::Vector{
+ CallStmt(Call(overload.function, args)),
+ },
+ utils::Vector{
+ Stage(ast::PipelineStage::kFragment),
+ });
+
+ if (expr.invalid_index == Constexpr::kValid) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ std::stringstream err;
+ if (is_vector) {
+ err << "12:34 error: each component of the " << param.name
+ << " argument must be at least " << param.min << " and at most " << param.max
+ << ". " << param.name << " component " << expr.invalid_index << " is "
+ << std::to_string(expr.values[static_cast<size_t>(expr.invalid_index)]);
+ } else {
+ err << "12:34 error: the " << param.name << " argument must be at least " << param.min
+ << " and at most " << param.max << ". " << param.name << " is "
+ << std::to_string(expr.values[static_cast<size_t>(expr.invalid_index)]);
+ }
+ EXPECT_EQ(r()->error(), err.str());
+ }
+}
+
+TEST_P(BuiltinTextureConstExprArgValidationTest, GlobalVar) {
+ auto& p = GetParam();
+ auto overload = std::get<0>(p);
+ auto param = std::get<1>(p);
+ auto expr = std::get<2>(p);
+
+ // Build the global texture and sampler variables
+ overload.BuildTextureVariable(this);
+ overload.BuildSamplerVariable(this);
+
+ // Build the module-scope var 'G' with the offset value
+ GlobalVar("G", expr({}, *this), ast::AddressSpace::kPrivate);
+
+ auto args = overload.args(this);
+ auto*& arg_to_replace = (param.position == Position::kFirst) ? args.Front() : args.Back();
+
+ // Make the expression to be replaced, reachable. This keeps the resolver happy.
+ WrapInFunction(arg_to_replace);
+
+ arg_to_replace = Expr(Source{{12, 34}}, "G");
+
+ // Call the builtin with the constant-expression argument replaced
Func("func", utils::Empty, ty.void_(),
utils::Vector{
CallStmt(Call(overload.function, args)),
@@ -327,7 +378,7 @@
EXPECT_FALSE(r()->Resolve());
std::stringstream err;
- err << "12:34 error: the " << param.name << " argument must be a const_expression";
+ err << "12:34 error: the " << param.name << " argument must be a const-expression";
EXPECT_EQ(r()->error(), err.str());
}
INSTANTIATE_TEST_SUITE_P(
diff --git a/src/tint/resolver/builtins_validation_test.cc b/src/tint/resolver/builtins_validation_test.cc
index f2bf191..7190ef1 100644
--- a/src/tint/resolver/builtins_validation_test.cc
+++ b/src/tint/resolver/builtins_validation_test.cc
@@ -204,7 +204,7 @@
EXPECT_EQ(r()->error(),
"12:34 error: builtin(frag_depth) cannot be used in input of "
"fragment pipeline stage\n"
- "note: while analysing entry point 'fragShader'");
+ "note: while analyzing entry point 'fragShader'");
}
TEST_F(ResolverBuiltinsValidationTest, StructBuiltinInsideEntryPoint_Ignored) {
diff --git a/src/tint/resolver/const_eval.h b/src/tint/resolver/const_eval.h
index b58ed41..065cca5 100644
--- a/src/tint/resolver/const_eval.h
+++ b/src/tint/resolver/const_eval.h
@@ -38,7 +38,7 @@
namespace tint::resolver {
-/// ConstEval performs shader creation-time (constant expression) expression evaluation.
+/// ConstEval performs shader creation-time (const-expression) expression evaluation.
/// Methods are called from the resolver, either directly or via member-function pointers indexed by
/// the IntrinsicTable. All child-expression nodes are guaranteed to have been already resolved
/// before calling a method to evaluate an expression's value.
diff --git a/src/tint/resolver/const_eval_binary_op_test.cc b/src/tint/resolver/const_eval_binary_op_test.cc
new file mode 100644
index 0000000..18b28da
--- /dev/null
+++ b/src/tint/resolver/const_eval_binary_op_test.cc
@@ -0,0 +1,918 @@
+// Copyright 2022 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/tint/resolver/const_eval_test.h"
+
+using namespace tint::number_suffixes; // NOLINT
+using ::testing::HasSubstr;
+
+namespace tint::resolver {
+namespace {
+
+// Bring in std::ostream& operator<<(std::ostream& o, const Types& types)
+using resolver::operator<<;
+
+struct Case {
+ Types lhs;
+ Types rhs;
+ Types expected;
+ bool overflow;
+};
+
+/// Creates a Case with Values of any type
+template <typename T, typename U, typename V>
+Case C(Value<T> lhs, Value<U> rhs, Value<V> expected, bool overflow = false) {
+ return Case{std::move(lhs), std::move(rhs), std::move(expected), overflow};
+}
+
+/// Convenience overload that creates a Case with just scalars
+template <typename T, typename U, typename V, typename = std::enable_if_t<!IsValue<T>>>
+Case C(T lhs, U rhs, V expected, bool overflow = false) {
+ return Case{Val(lhs), Val(rhs), Val(expected), overflow};
+}
+
+static std::ostream& operator<<(std::ostream& o, const Case& c) {
+ o << "lhs: " << c.lhs << ", rhs: " << c.rhs << ", expected: " << c.expected
+ << ", overflow: " << c.overflow;
+ return o;
+}
+
+using ResolverConstEvalBinaryOpTest = ResolverTestWithParam<std::tuple<ast::BinaryOp, Case>>;
+TEST_P(ResolverConstEvalBinaryOpTest, Test) {
+ Enable(ast::Extension::kF16);
+ auto op = std::get<0>(GetParam());
+ auto& c = std::get<1>(GetParam());
+
+ std::visit(
+ [&](auto&& expected) {
+ using T = typename std::decay_t<decltype(expected)>::ElementType;
+ if constexpr (std::is_same_v<T, AInt> || std::is_same_v<T, AFloat>) {
+ if (c.overflow) {
+ // Overflow is not allowed for abstract types. This is tested separately.
+ return;
+ }
+ }
+
+ auto* lhs_expr = std::visit([&](auto&& value) { return value.Expr(*this); }, c.lhs);
+ auto* rhs_expr = std::visit([&](auto&& value) { return value.Expr(*this); }, c.rhs);
+ auto* expr = create<ast::BinaryExpression>(op, lhs_expr, rhs_expr);
+
+ GlobalConst("C", expr);
+ auto* expected_expr = expected.Expr(*this);
+ GlobalConst("E", expected_expr);
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ const sem::Constant* value = sem->ConstantValue();
+ ASSERT_NE(value, nullptr);
+ EXPECT_TYPE(value->Type(), sem->Type());
+
+ auto* expected_sem = Sem().Get(expected_expr);
+ const sem::Constant* expected_value = expected_sem->ConstantValue();
+ ASSERT_NE(expected_value, nullptr);
+ EXPECT_TYPE(expected_value->Type(), expected_sem->Type());
+
+ ForEachElemPair(value, expected_value,
+ [&](const sem::Constant* a, const sem::Constant* b) {
+ EXPECT_EQ(a->As<T>(), b->As<T>());
+ if constexpr (IsIntegral<T>) {
+ // Check that the constant's integer doesn't contain unexpected
+ // data in the MSBs that are outside of the bit-width of T.
+ EXPECT_EQ(a->As<AInt>(), b->As<AInt>());
+ }
+ return HasFailure() ? Action::kStop : Action::kContinue;
+ });
+ },
+ c.expected);
+}
+
+INSTANTIATE_TEST_SUITE_P(MixedAbstractArgs,
+ ResolverConstEvalBinaryOpTest,
+ testing::Combine(testing::Values(ast::BinaryOp::kAdd),
+ testing::ValuesIn(std::vector{
+ // Mixed abstract type args
+ C(1_a, 2.3_a, 3.3_a),
+ C(2.3_a, 1_a, 3.3_a),
+ })));
+
+template <typename T>
+std::vector<Case> OpAddIntCases() {
+ static_assert(IsIntegral<T>);
+ return {
+ C(T{0}, T{0}, T{0}),
+ C(T{1}, T{2}, T{3}),
+ C(T::Lowest(), T{1}, T{T::Lowest() + 1}),
+ C(T::Highest(), Negate(T{1}), T{T::Highest() - 1}),
+ C(T::Lowest(), T::Highest(), Negate(T{1})),
+ C(T::Highest(), T{1}, T::Lowest(), true),
+ C(T::Lowest(), Negate(T{1}), T::Highest(), true),
+ };
+}
+template <typename T>
+std::vector<Case> OpAddFloatCases() {
+ static_assert(IsFloatingPoint<T>);
+ return {
+ C(T{0}, T{0}, T{0}),
+ C(T{1}, T{2}, T{3}),
+ C(T::Lowest(), T{1}, T{T::Lowest() + 1}),
+ C(T::Highest(), Negate(T{1}), T{T::Highest() - 1}),
+ C(T::Lowest(), T::Highest(), T{0}),
+ C(T::Highest(), T::Highest(), T::Inf(), true),
+ C(T::Lowest(), Negate(T::Highest()), -T::Inf(), true),
+ };
+}
+INSTANTIATE_TEST_SUITE_P(Add,
+ ResolverConstEvalBinaryOpTest,
+ testing::Combine(testing::Values(ast::BinaryOp::kAdd),
+ testing::ValuesIn(Concat( //
+ OpAddIntCases<AInt>(),
+ OpAddIntCases<i32>(),
+ OpAddIntCases<u32>(),
+ OpAddFloatCases<AFloat>(),
+ OpAddFloatCases<f32>(),
+ OpAddFloatCases<f16>()))));
+
+template <typename T>
+std::vector<Case> OpSubIntCases() {
+ static_assert(IsIntegral<T>);
+ return {
+ C(T{0}, T{0}, T{0}),
+ C(T{3}, T{2}, T{1}),
+ C(T{T::Lowest() + 1}, T{1}, T::Lowest()),
+ C(T{T::Highest() - 1}, Negate(T{1}), T::Highest()),
+ C(Negate(T{1}), T::Highest(), T::Lowest()),
+ C(T::Lowest(), T{1}, T::Highest(), true),
+ C(T::Highest(), Negate(T{1}), T::Lowest(), true),
+ };
+}
+template <typename T>
+std::vector<Case> OpSubFloatCases() {
+ static_assert(IsFloatingPoint<T>);
+ return {
+ C(T{0}, T{0}, T{0}),
+ C(T{3}, T{2}, T{1}),
+ C(T::Highest(), T{1}, T{T::Highest() - 1}),
+ C(T::Lowest(), Negate(T{1}), T{T::Lowest() + 1}),
+ C(T{0}, T::Highest(), T::Lowest()),
+ C(T::Highest(), Negate(T::Highest()), T::Inf(), true),
+ C(T::Lowest(), T::Highest(), -T::Inf(), true),
+ };
+}
+INSTANTIATE_TEST_SUITE_P(Sub,
+ ResolverConstEvalBinaryOpTest,
+ testing::Combine(testing::Values(ast::BinaryOp::kSubtract),
+ testing::ValuesIn(Concat( //
+ OpSubIntCases<AInt>(),
+ OpSubIntCases<i32>(),
+ OpSubIntCases<u32>(),
+ OpSubFloatCases<AFloat>(),
+ OpSubFloatCases<f32>(),
+ OpSubFloatCases<f16>()))));
+
+template <typename T>
+std::vector<Case> OpMulScalarCases() {
+ return {
+ C(T{0}, T{0}, T{0}),
+ C(T{1}, T{2}, T{2}),
+ C(T{2}, T{3}, T{6}),
+ C(Negate(T{2}), T{3}, Negate(T{6})),
+ C(T::Highest(), T{1}, T::Highest()),
+ C(T::Lowest(), T{1}, T::Lowest()),
+ C(T::Highest(), T::Highest(), Mul(T::Highest(), T::Highest()), true),
+ C(T::Lowest(), T::Lowest(), Mul(T::Lowest(), T::Lowest()), true),
+ };
+}
+
+template <typename T>
+std::vector<Case> OpMulVecCases() {
+ return {
+ // s * vec3 = vec3
+ C(Val(T{2.0}), Vec(T{1.25}, T{2.25}, T{3.25}), Vec(T{2.5}, T{4.5}, T{6.5})),
+ // vec3 * s = vec3
+ C(Vec(T{1.25}, T{2.25}, T{3.25}), Val(T{2.0}), Vec(T{2.5}, T{4.5}, T{6.5})),
+ // vec3 * vec3 = vec3
+ C(Vec(T{1.25}, T{2.25}, T{3.25}), Vec(T{2.0}, T{2.0}, T{2.0}), Vec(T{2.5}, T{4.5}, T{6.5})),
+ };
+}
+
+template <typename T>
+std::vector<Case> OpMulMatCases() {
+ return {
+ // s * mat3x2 = mat3x2
+ C(Val(T{2.25}),
+ Mat({T{1.0}, T{4.0}}, //
+ {T{2.0}, T{5.0}}, //
+ {T{3.0}, T{6.0}}),
+ Mat({T{2.25}, T{9.0}}, //
+ {T{4.5}, T{11.25}}, //
+ {T{6.75}, T{13.5}})),
+ // mat3x2 * s = mat3x2
+ C(Mat({T{1.0}, T{4.0}}, //
+ {T{2.0}, T{5.0}}, //
+ {T{3.0}, T{6.0}}),
+ Val(T{2.25}),
+ Mat({T{2.25}, T{9.0}}, //
+ {T{4.5}, T{11.25}}, //
+ {T{6.75}, T{13.5}})),
+ // vec3 * mat2x3 = vec2
+ C(Vec(T{1.25}, T{2.25}, T{3.25}), //
+ Mat({T{1.0}, T{2.0}, T{3.0}}, //
+ {T{4.0}, T{5.0}, T{6.0}}), //
+ Vec(T{15.5}, T{35.75})),
+ // mat2x3 * vec2 = vec3
+ C(Mat({T{1.0}, T{2.0}, T{3.0}}, //
+ {T{4.0}, T{5.0}, T{6.0}}), //
+ Vec(T{1.25}, T{2.25}), //
+ Vec(T{10.25}, T{13.75}, T{17.25})),
+ // mat3x2 * mat2x3 = mat2x2
+ C(Mat({T{1.0}, T{2.0}}, //
+ {T{3.0}, T{4.0}}, //
+ {T{5.0}, T{6.0}}), //
+ Mat({T{1.25}, T{2.25}, T{3.25}}, //
+ {T{4.25}, T{5.25}, T{6.25}}), //
+ Mat({T{24.25}, T{31.0}}, //
+ {T{51.25}, T{67.0}})), //
+ };
+}
+
+INSTANTIATE_TEST_SUITE_P(Mul,
+ ResolverConstEvalBinaryOpTest,
+ testing::Combine( //
+ testing::Values(ast::BinaryOp::kMultiply),
+ testing::ValuesIn(Concat( //
+ OpMulScalarCases<AInt>(),
+ OpMulScalarCases<i32>(),
+ OpMulScalarCases<u32>(),
+ OpMulScalarCases<AFloat>(),
+ OpMulScalarCases<f32>(),
+ OpMulScalarCases<f16>(),
+ OpMulVecCases<AInt>(),
+ OpMulVecCases<i32>(),
+ OpMulVecCases<u32>(),
+ OpMulVecCases<AFloat>(),
+ OpMulVecCases<f32>(),
+ OpMulVecCases<f16>(),
+ OpMulMatCases<AFloat>(),
+ OpMulMatCases<f32>(),
+ OpMulMatCases<f16>()))));
+
+template <typename T>
+std::vector<Case> OpDivIntCases() {
+ std::vector<Case> r = {
+ C(Val(T{0}), Val(T{1}), Val(T{0})),
+ C(Val(T{1}), Val(T{1}), Val(T{1})),
+ C(Val(T{1}), Val(T{1}), Val(T{1})),
+ C(Val(T{2}), Val(T{1}), Val(T{2})),
+ C(Val(T{4}), Val(T{2}), Val(T{2})),
+ C(Val(T::Highest()), Val(T{1}), Val(T::Highest())),
+ C(Val(T::Lowest()), Val(T{1}), Val(T::Lowest())),
+ C(Val(T::Highest()), Val(T::Highest()), Val(T{1})),
+ C(Val(T{0}), Val(T::Highest()), Val(T{0})),
+ C(Val(T{0}), Val(T::Lowest()), Val(T{0})),
+ };
+ ConcatIntoIf<IsIntegral<T>>( //
+ r, std::vector<Case>{
+ // e1, when e2 is zero.
+ C(T{123}, T{0}, T{123}, true),
+ });
+ ConcatIntoIf<IsSignedIntegral<T>>( //
+ r, std::vector<Case>{
+ // e1, when e1 is the most negative value in T, and e2 is -1.
+ C(T::Smallest(), T{-1}, T::Smallest(), true),
+ });
+ return r;
+}
+
+template <typename T>
+std::vector<Case> OpDivFloatCases() {
+ return {
+ C(Val(T{0}), Val(T{1}), Val(T{0})),
+ C(Val(T{1}), Val(T{1}), Val(T{1})),
+ C(Val(T{1}), Val(T{1}), Val(T{1})),
+ C(Val(T{2}), Val(T{1}), Val(T{2})),
+ C(Val(T{4}), Val(T{2}), Val(T{2})),
+ C(Val(T::Highest()), Val(T{1}), Val(T::Highest())),
+ C(Val(T::Lowest()), Val(T{1}), Val(T::Lowest())),
+ C(Val(T::Highest()), Val(T::Highest()), Val(T{1})),
+ C(Val(T{0}), Val(T::Highest()), Val(T{0})),
+ C(Val(T{0}), Val(T::Lowest()), Val(-T{0})),
+ C(T{123}, T{0}, T::Inf(), true),
+ C(T{-123}, -T{0}, T::Inf(), true),
+ C(T{-123}, T{0}, -T::Inf(), true),
+ C(T{123}, -T{0}, -T::Inf(), true),
+ };
+}
+INSTANTIATE_TEST_SUITE_P(Div,
+ ResolverConstEvalBinaryOpTest,
+ testing::Combine( //
+ testing::Values(ast::BinaryOp::kDivide),
+ testing::ValuesIn(Concat( //
+ OpDivIntCases<AInt>(),
+ OpDivIntCases<i32>(),
+ OpDivIntCases<u32>(),
+ OpDivFloatCases<AFloat>(),
+ OpDivFloatCases<f32>(),
+ OpDivFloatCases<f16>()))));
+
+template <typename T, bool equals>
+std::vector<Case> OpEqualCases() {
+ return {
+ C(Val(T{0}), Val(T{0}), Val(true == equals)),
+ C(Val(T{0}), Val(T{1}), Val(false == equals)),
+ C(Val(T{1}), Val(T{0}), Val(false == equals)),
+ C(Val(T{1}), Val(T{1}), Val(true == equals)),
+ C(Vec(T{0}, T{0}), Vec(T{0}, T{0}), Vec(true == equals, true == equals)),
+ C(Vec(T{1}, T{0}), Vec(T{0}, T{1}), Vec(false == equals, false == equals)),
+ C(Vec(T{1}, T{1}), Vec(T{0}, T{1}), Vec(false == equals, true == equals)),
+ };
+}
+INSTANTIATE_TEST_SUITE_P(Equal,
+ ResolverConstEvalBinaryOpTest,
+ testing::Combine( //
+ testing::Values(ast::BinaryOp::kEqual),
+ testing::ValuesIn(Concat( //
+ OpEqualCases<AInt, true>(),
+ OpEqualCases<i32, true>(),
+ OpEqualCases<u32, true>(),
+ OpEqualCases<AFloat, true>(),
+ OpEqualCases<f32, true>(),
+ OpEqualCases<f16, true>(),
+ OpEqualCases<bool, true>()))));
+INSTANTIATE_TEST_SUITE_P(NotEqual,
+ ResolverConstEvalBinaryOpTest,
+ testing::Combine( //
+ testing::Values(ast::BinaryOp::kNotEqual),
+ testing::ValuesIn(Concat( //
+ OpEqualCases<AInt, false>(),
+ OpEqualCases<i32, false>(),
+ OpEqualCases<u32, false>(),
+ OpEqualCases<AFloat, false>(),
+ OpEqualCases<f32, false>(),
+ OpEqualCases<f16, false>(),
+ OpEqualCases<bool, false>()))));
+
+template <typename T, bool less_than>
+std::vector<Case> OpLessThanCases() {
+ return {
+ C(Val(T{0}), Val(T{0}), Val(false == less_than)),
+ C(Val(T{0}), Val(T{1}), Val(true == less_than)),
+ C(Val(T{1}), Val(T{0}), Val(false == less_than)),
+ C(Val(T{1}), Val(T{1}), Val(false == less_than)),
+ C(Vec(T{0}, T{0}), Vec(T{0}, T{0}), Vec(false == less_than, false == less_than)),
+ C(Vec(T{0}, T{0}), Vec(T{1}, T{1}), Vec(true == less_than, true == less_than)),
+ C(Vec(T{1}, T{1}), Vec(T{0}, T{0}), Vec(false == less_than, false == less_than)),
+ C(Vec(T{1}, T{0}), Vec(T{0}, T{1}), Vec(false == less_than, true == less_than)),
+ };
+}
+INSTANTIATE_TEST_SUITE_P(LessThan,
+ ResolverConstEvalBinaryOpTest,
+ testing::Combine( //
+ testing::Values(ast::BinaryOp::kLessThan),
+ testing::ValuesIn(Concat( //
+ OpLessThanCases<AInt, true>(),
+ OpLessThanCases<i32, true>(),
+ OpLessThanCases<u32, true>(),
+ OpLessThanCases<AFloat, true>(),
+ OpLessThanCases<f32, true>(),
+ OpLessThanCases<f16, true>()))));
+INSTANTIATE_TEST_SUITE_P(GreaterThanEqual,
+ ResolverConstEvalBinaryOpTest,
+ testing::Combine( //
+ testing::Values(ast::BinaryOp::kGreaterThanEqual),
+ testing::ValuesIn(Concat( //
+ OpLessThanCases<AInt, false>(),
+ OpLessThanCases<i32, false>(),
+ OpLessThanCases<u32, false>(),
+ OpLessThanCases<AFloat, false>(),
+ OpLessThanCases<f32, false>(),
+ OpLessThanCases<f16, false>()))));
+
+template <typename T, bool greater_than>
+std::vector<Case> OpGreaterThanCases() {
+ return {
+ C(Val(T{0}), Val(T{0}), Val(false == greater_than)),
+ C(Val(T{0}), Val(T{1}), Val(false == greater_than)),
+ C(Val(T{1}), Val(T{0}), Val(true == greater_than)),
+ C(Val(T{1}), Val(T{1}), Val(false == greater_than)),
+ C(Vec(T{0}, T{0}), Vec(T{0}, T{0}), Vec(false == greater_than, false == greater_than)),
+ C(Vec(T{1}, T{1}), Vec(T{0}, T{0}), Vec(true == greater_than, true == greater_than)),
+ C(Vec(T{0}, T{0}), Vec(T{1}, T{1}), Vec(false == greater_than, false == greater_than)),
+ C(Vec(T{1}, T{0}), Vec(T{0}, T{1}), Vec(true == greater_than, false == greater_than)),
+ };
+}
+INSTANTIATE_TEST_SUITE_P(GreaterThan,
+ ResolverConstEvalBinaryOpTest,
+ testing::Combine( //
+ testing::Values(ast::BinaryOp::kGreaterThan),
+ testing::ValuesIn(Concat( //
+ OpGreaterThanCases<AInt, true>(),
+ OpGreaterThanCases<i32, true>(),
+ OpGreaterThanCases<u32, true>(),
+ OpGreaterThanCases<AFloat, true>(),
+ OpGreaterThanCases<f32, true>(),
+ OpGreaterThanCases<f16, true>()))));
+INSTANTIATE_TEST_SUITE_P(LessThanEqual,
+ ResolverConstEvalBinaryOpTest,
+ testing::Combine( //
+ testing::Values(ast::BinaryOp::kLessThanEqual),
+ testing::ValuesIn(Concat( //
+ OpGreaterThanCases<AInt, false>(),
+ OpGreaterThanCases<i32, false>(),
+ OpGreaterThanCases<u32, false>(),
+ OpGreaterThanCases<AFloat, false>(),
+ OpGreaterThanCases<f32, false>(),
+ OpGreaterThanCases<f16, false>()))));
+
+static std::vector<Case> OpAndBoolCases() {
+ return {
+ C(true, true, true),
+ C(true, false, false),
+ C(false, true, false),
+ C(false, false, false),
+ C(Vec(true, true), Vec(true, false), Vec(true, false)),
+ C(Vec(true, true), Vec(false, true), Vec(false, true)),
+ C(Vec(true, false), Vec(true, false), Vec(true, false)),
+ C(Vec(false, true), Vec(true, false), Vec(false, false)),
+ C(Vec(false, false), Vec(true, false), Vec(false, false)),
+ };
+}
+template <typename T>
+std::vector<Case> OpAndIntCases() {
+ using B = BitValues<T>;
+ return {
+ C(T{0b1010}, T{0b1111}, T{0b1010}),
+ C(T{0b1010}, T{0b0000}, T{0b0000}),
+ C(T{0b1010}, T{0b0011}, T{0b0010}),
+ C(T{0b1010}, T{0b1100}, T{0b1000}),
+ C(T{0b1010}, T{0b0101}, T{0b0000}),
+ C(B::All, B::All, B::All),
+ C(B::LeftMost, B::LeftMost, B::LeftMost),
+ C(B::RightMost, B::RightMost, B::RightMost),
+ C(B::All, T{0}, T{0}),
+ C(T{0}, B::All, T{0}),
+ C(B::LeftMost, B::AllButLeftMost, T{0}),
+ C(B::AllButLeftMost, B::LeftMost, T{0}),
+ C(B::RightMost, B::AllButRightMost, T{0}),
+ C(B::AllButRightMost, B::RightMost, T{0}),
+ C(Vec(B::All, B::LeftMost, B::RightMost), //
+ Vec(B::All, B::All, B::All), //
+ Vec(B::All, B::LeftMost, B::RightMost)), //
+ C(Vec(B::All, B::LeftMost, B::RightMost), //
+ Vec(T{0}, T{0}, T{0}), //
+ Vec(T{0}, T{0}, T{0})), //
+ C(Vec(B::LeftMost, B::RightMost), //
+ Vec(B::AllButLeftMost, B::AllButRightMost), //
+ Vec(T{0}, T{0})),
+ };
+}
+INSTANTIATE_TEST_SUITE_P(And,
+ ResolverConstEvalBinaryOpTest,
+ testing::Combine( //
+ testing::Values(ast::BinaryOp::kAnd),
+ testing::ValuesIn( //
+ Concat(OpAndBoolCases(), //
+ OpAndIntCases<AInt>(),
+ OpAndIntCases<i32>(),
+ OpAndIntCases<u32>()))));
+
+static std::vector<Case> OpOrBoolCases() {
+ return {
+ C(true, true, true),
+ C(true, false, true),
+ C(false, true, true),
+ C(false, false, false),
+ C(Vec(true, true), Vec(true, false), Vec(true, true)),
+ C(Vec(true, true), Vec(false, true), Vec(true, true)),
+ C(Vec(true, false), Vec(true, false), Vec(true, false)),
+ C(Vec(false, true), Vec(true, false), Vec(true, true)),
+ C(Vec(false, false), Vec(true, false), Vec(true, false)),
+ };
+}
+template <typename T>
+std::vector<Case> OpOrIntCases() {
+ using B = BitValues<T>;
+ return {
+ C(T{0b1010}, T{0b1111}, T{0b1111}),
+ C(T{0b1010}, T{0b0000}, T{0b1010}),
+ C(T{0b1010}, T{0b0011}, T{0b1011}),
+ C(T{0b1010}, T{0b1100}, T{0b1110}),
+ C(T{0b1010}, T{0b0101}, T{0b1111}),
+ C(B::All, B::All, B::All),
+ C(B::LeftMost, B::LeftMost, B::LeftMost),
+ C(B::RightMost, B::RightMost, B::RightMost),
+ C(B::All, T{0}, B::All),
+ C(T{0}, B::All, B::All),
+ C(B::LeftMost, B::AllButLeftMost, B::All),
+ C(B::AllButLeftMost, B::LeftMost, B::All),
+ C(B::RightMost, B::AllButRightMost, B::All),
+ C(B::AllButRightMost, B::RightMost, B::All),
+ C(Vec(B::All, B::LeftMost, B::RightMost), //
+ Vec(B::All, B::All, B::All), //
+ Vec(B::All, B::All, B::All)), //
+ C(Vec(B::All, B::LeftMost, B::RightMost), //
+ Vec(T{0}, T{0}, T{0}), //
+ Vec(B::All, B::LeftMost, B::RightMost)), //
+ C(Vec(B::LeftMost, B::RightMost), //
+ Vec(B::AllButLeftMost, B::AllButRightMost), //
+ Vec(B::All, B::All)),
+ };
+}
+INSTANTIATE_TEST_SUITE_P(Or,
+ ResolverConstEvalBinaryOpTest,
+ testing::Combine( //
+ testing::Values(ast::BinaryOp::kOr),
+ testing::ValuesIn(Concat(OpOrBoolCases(),
+ OpOrIntCases<AInt>(),
+ OpOrIntCases<i32>(),
+ OpOrIntCases<u32>()))));
+
+TEST_F(ResolverConstEvalTest, NotAndOrOfVecs) {
+ // const C = !((vec2(true, true) & vec2(true, false)) | vec2(false, true));
+ auto v1 = Vec(true, true).Expr(*this);
+ auto v2 = Vec(true, false).Expr(*this);
+ auto v3 = Vec(false, true).Expr(*this);
+ auto expr = Not(Or(And(v1, v2), v3));
+ GlobalConst("C", expr);
+ auto expected_expr = Vec(false, false).Expr(*this);
+ GlobalConst("E", expected_expr);
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ const sem::Constant* value = sem->ConstantValue();
+ ASSERT_NE(value, nullptr);
+ EXPECT_TYPE(value->Type(), sem->Type());
+
+ auto* expected_sem = Sem().Get(expected_expr);
+ const sem::Constant* expected_value = expected_sem->ConstantValue();
+ ASSERT_NE(expected_value, nullptr);
+ EXPECT_TYPE(expected_value->Type(), expected_sem->Type());
+
+ ForEachElemPair(value, expected_value, [&](const sem::Constant* a, const sem::Constant* b) {
+ EXPECT_EQ(a->As<bool>(), b->As<bool>());
+ return HasFailure() ? Action::kStop : Action::kContinue;
+ });
+}
+
+template <typename T>
+std::vector<Case> XorCases() {
+ using B = BitValues<T>;
+ return {
+ C(T{0b1010}, T{0b1111}, T{0b0101}),
+ C(T{0b1010}, T{0b0000}, T{0b1010}),
+ C(T{0b1010}, T{0b0011}, T{0b1001}),
+ C(T{0b1010}, T{0b1100}, T{0b0110}),
+ C(T{0b1010}, T{0b0101}, T{0b1111}),
+ C(B::All, B::All, T{0}),
+ C(B::LeftMost, B::LeftMost, T{0}),
+ C(B::RightMost, B::RightMost, T{0}),
+ C(B::All, T{0}, B::All),
+ C(T{0}, B::All, B::All),
+ C(B::LeftMost, B::AllButLeftMost, B::All),
+ C(B::AllButLeftMost, B::LeftMost, B::All),
+ C(B::RightMost, B::AllButRightMost, B::All),
+ C(B::AllButRightMost, B::RightMost, B::All),
+ C(Vec(B::All, B::LeftMost, B::RightMost), //
+ Vec(B::All, B::All, B::All), //
+ Vec(T{0}, B::AllButLeftMost, B::AllButRightMost)), //
+ C(Vec(B::All, B::LeftMost, B::RightMost), //
+ Vec(T{0}, T{0}, T{0}), //
+ Vec(B::All, B::LeftMost, B::RightMost)), //
+ C(Vec(B::LeftMost, B::RightMost), //
+ Vec(B::AllButLeftMost, B::AllButRightMost), //
+ Vec(B::All, B::All)),
+ };
+}
+INSTANTIATE_TEST_SUITE_P(Xor,
+ ResolverConstEvalBinaryOpTest,
+ testing::Combine( //
+ testing::Values(ast::BinaryOp::kXor),
+ testing::ValuesIn(Concat(XorCases<AInt>(), //
+ XorCases<i32>(), //
+ XorCases<u32>()))));
+
+template <typename T>
+std::vector<Case> ShiftLeftCases() {
+ // Shift type is u32 for non-abstract
+ using ST = std::conditional_t<IsAbstract<T>, T, u32>;
+ using B = BitValues<T>;
+ return {
+ C(T{0b1010}, ST{0}, T{0b0000'0000'1010}), //
+ C(T{0b1010}, ST{1}, T{0b0000'0001'0100}), //
+ C(T{0b1010}, ST{2}, T{0b0000'0010'1000}), //
+ C(T{0b1010}, ST{3}, T{0b0000'0101'0000}), //
+ C(T{0b1010}, ST{4}, T{0b0000'1010'0000}), //
+ C(T{0b1010}, ST{5}, T{0b0001'0100'0000}), //
+ C(T{0b1010}, ST{6}, T{0b0010'1000'0000}), //
+ C(T{0b1010}, ST{7}, T{0b0101'0000'0000}), //
+ C(T{0b1010}, ST{8}, T{0b1010'0000'0000}), //
+ C(B::LeftMost, ST{0}, B::LeftMost), //
+ C(B::TwoLeftMost, ST{1}, B::LeftMost), // No overflow
+ C(B::All, ST{1}, B::AllButRightMost), // No overflow
+ C(B::All, ST{B::NumBits - 1}, B::LeftMost), // No overflow
+
+ C(Vec(T{0b1010}, T{0b1010}), //
+ Vec(ST{0}, ST{1}), //
+ Vec(T{0b0000'0000'1010}, T{0b0000'0001'0100})), //
+ C(Vec(T{0b1010}, T{0b1010}), //
+ Vec(ST{2}, ST{3}), //
+ Vec(T{0b0000'0010'1000}, T{0b0000'0101'0000})), //
+ C(Vec(T{0b1010}, T{0b1010}), //
+ Vec(ST{4}, ST{5}), //
+ Vec(T{0b0000'1010'0000}, T{0b0001'0100'0000})), //
+ C(Vec(T{0b1010}, T{0b1010}, T{0b1010}), //
+ Vec(ST{6}, ST{7}, ST{8}), //
+ Vec(T{0b0010'1000'0000}, T{0b0101'0000'0000}, T{0b1010'0000'0000})), //
+ };
+}
+INSTANTIATE_TEST_SUITE_P(ShiftLeft,
+ ResolverConstEvalBinaryOpTest,
+ testing::Combine( //
+ testing::Values(ast::BinaryOp::kShiftLeft),
+ testing::ValuesIn(Concat(ShiftLeftCases<AInt>(), //
+ ShiftLeftCases<i32>(), //
+ ShiftLeftCases<u32>()))));
+
+// Tests for errors on overflow/underflow of binary operations with abstract numbers
+struct OverflowCase {
+ ast::BinaryOp op;
+ Types lhs;
+ Types rhs;
+};
+
+static std::ostream& operator<<(std::ostream& o, const OverflowCase& c) {
+ o << ast::FriendlyName(c.op) << ", lhs: " << c.lhs << ", rhs: " << c.rhs;
+ return o;
+}
+using ResolverConstEvalBinaryOpTest_Overflow = ResolverTestWithParam<OverflowCase>;
+TEST_P(ResolverConstEvalBinaryOpTest_Overflow, Test) {
+ Enable(ast::Extension::kF16);
+ auto& c = GetParam();
+ auto* lhs_expr = std::visit([&](auto&& value) { return value.Expr(*this); }, c.lhs);
+ auto* rhs_expr = std::visit([&](auto&& value) { return value.Expr(*this); }, c.rhs);
+ auto* expr = create<ast::BinaryExpression>(Source{{1, 1}}, c.op, lhs_expr, rhs_expr);
+ GlobalConst("C", expr);
+ ASSERT_FALSE(r()->Resolve());
+
+ std::string type_name = std::visit(
+ [&](auto&& value) {
+ using ValueType = std::decay_t<decltype(value)>;
+ return builder::FriendlyName<ValueType>();
+ },
+ c.lhs);
+
+ EXPECT_THAT(r()->error(), HasSubstr("1:1 error: '"));
+ EXPECT_THAT(r()->error(), HasSubstr("' cannot be represented as '" + type_name + "'"));
+}
+INSTANTIATE_TEST_SUITE_P(
+ Test,
+ ResolverConstEvalBinaryOpTest_Overflow,
+ testing::Values(
+
+ // scalar-scalar add
+ OverflowCase{ast::BinaryOp::kAdd, Val(AInt::Highest()), Val(1_a)},
+ OverflowCase{ast::BinaryOp::kAdd, Val(AInt::Lowest()), Val(-1_a)},
+ OverflowCase{ast::BinaryOp::kAdd, Val(AFloat::Highest()), Val(AFloat::Highest())},
+ OverflowCase{ast::BinaryOp::kAdd, Val(AFloat::Lowest()), Val(AFloat::Lowest())},
+ // scalar-scalar subtract
+ OverflowCase{ast::BinaryOp::kSubtract, Val(AInt::Lowest()), Val(1_a)},
+ OverflowCase{ast::BinaryOp::kSubtract, Val(AInt::Highest()), Val(-1_a)},
+ OverflowCase{ast::BinaryOp::kSubtract, Val(AFloat::Highest()), Val(AFloat::Lowest())},
+ OverflowCase{ast::BinaryOp::kSubtract, Val(AFloat::Lowest()), Val(AFloat::Highest())},
+
+ // scalar-scalar multiply
+ OverflowCase{ast::BinaryOp::kMultiply, Val(AInt::Highest()), Val(2_a)},
+ OverflowCase{ast::BinaryOp::kMultiply, Val(AInt::Lowest()), Val(-2_a)},
+
+ // scalar-vector multiply
+ OverflowCase{ast::BinaryOp::kMultiply, Val(AInt::Highest()), Vec(2_a, 1_a)},
+ OverflowCase{ast::BinaryOp::kMultiply, Val(AInt::Lowest()), Vec(-2_a, 1_a)},
+
+ // vector-matrix multiply
+
+ // Overflow from first multiplication of dot product of vector and matrix column 0
+ // i.e. (v[0] * m[0][0] + v[1] * m[0][1])
+ // ^
+ OverflowCase{ast::BinaryOp::kMultiply, //
+ Vec(AFloat::Highest(), 1.0_a), //
+ Mat({2.0_a, 1.0_a}, //
+ {1.0_a, 1.0_a})},
+
+ // Overflow from second multiplication of dot product of vector and matrix column 0
+ // i.e. (v[0] * m[0][0] + v[1] * m[0][1])
+ // ^
+ OverflowCase{ast::BinaryOp::kMultiply, //
+ Vec(1.0_a, AFloat::Highest()), //
+ Mat({1.0_a, 2.0_a}, //
+ {1.0_a, 1.0_a})},
+
+ // Overflow from addition of dot product of vector and matrix column 0
+ // i.e. (v[0] * m[0][0] + v[1] * m[0][1])
+ // ^
+ OverflowCase{ast::BinaryOp::kMultiply, //
+ Vec(AFloat::Highest(), AFloat::Highest()), //
+ Mat({1.0_a, 1.0_a}, //
+ {1.0_a, 1.0_a})},
+
+ // matrix-matrix multiply
+
+ // Overflow from first multiplication of dot product of lhs row 0 and rhs column 0
+ // i.e. m1[0][0] * m2[0][0] + m1[0][1] * m[1][0]
+ // ^
+ OverflowCase{ast::BinaryOp::kMultiply, //
+ Mat({AFloat::Highest(), 1.0_a}, //
+ {1.0_a, 1.0_a}), //
+ Mat({2.0_a, 1.0_a}, //
+ {1.0_a, 1.0_a})},
+
+ // Overflow from second multiplication of dot product of lhs row 0 and rhs column 0
+ // i.e. m1[0][0] * m2[0][0] + m1[0][1] * m[1][0]
+ // ^
+ OverflowCase{ast::BinaryOp::kMultiply, //
+ Mat({1.0_a, AFloat::Highest()}, //
+ {1.0_a, 1.0_a}), //
+ Mat({1.0_a, 1.0_a}, //
+ {2.0_a, 1.0_a})},
+
+ // Overflow from addition of dot product of lhs row 0 and rhs column 0
+ // i.e. m1[0][0] * m2[0][0] + m1[0][1] * m[1][0]
+ // ^
+ OverflowCase{ast::BinaryOp::kMultiply, //
+ Mat({AFloat::Highest(), 1.0_a}, //
+ {AFloat::Highest(), 1.0_a}), //
+ Mat({1.0_a, 1.0_a}, //
+ {1.0_a, 1.0_a})},
+
+ // Divide by zero
+ OverflowCase{ast::BinaryOp::kDivide, Val(123_a), Val(0_a)},
+ OverflowCase{ast::BinaryOp::kDivide, Val(-123_a), Val(-0_a)},
+ OverflowCase{ast::BinaryOp::kDivide, Val(-123_a), Val(0_a)},
+ OverflowCase{ast::BinaryOp::kDivide, Val(123_a), Val(-0_a)},
+
+ // Most negative value divided by -1
+ OverflowCase{ast::BinaryOp::kDivide, Val(AInt::Lowest()), Val(-1_a)},
+
+ // ShiftLeft of AInts that result in values not representable as AInts.
+ // Note that for i32/u32, these would error because shift value is larger than 32.
+ OverflowCase{ast::BinaryOp::kShiftLeft, //
+ Val(AInt{BitValues<AInt>::All}), //
+ Val(AInt{BitValues<AInt>::NumBits})}, //
+ OverflowCase{ast::BinaryOp::kShiftLeft, //
+ Val(AInt{BitValues<AInt>::RightMost}), //
+ Val(AInt{BitValues<AInt>::NumBits})}, //
+ OverflowCase{ast::BinaryOp::kShiftLeft, //
+ Val(AInt{BitValues<AInt>::AllButLeftMost}), //
+ Val(AInt{BitValues<AInt>::NumBits})}, //
+ OverflowCase{ast::BinaryOp::kShiftLeft, //
+ Val(AInt{BitValues<AInt>::AllButLeftMost}), //
+ Val(AInt{BitValues<AInt>::NumBits + 1})}, //
+ OverflowCase{ast::BinaryOp::kShiftLeft, //
+ Val(AInt{BitValues<AInt>::AllButLeftMost}), //
+ Val(AInt{BitValues<AInt>::NumBits + 1000})}
+
+ ));
+
+TEST_F(ResolverConstEvalTest, BinaryAbstractAddOverflow_AInt) {
+ GlobalConst("c", Add(Source{{1, 1}}, Expr(AInt::Highest()), 1_a));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "1:1 error: '9223372036854775807 + 1' cannot be represented as 'abstract-int'");
+}
+
+TEST_F(ResolverConstEvalTest, BinaryAbstractAddUnderflow_AInt) {
+ GlobalConst("c", Add(Source{{1, 1}}, Expr(AInt::Lowest()), -1_a));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "1:1 error: '-9223372036854775808 + -1' cannot be represented as 'abstract-int'");
+}
+
+TEST_F(ResolverConstEvalTest, BinaryAbstractAddOverflow_AFloat) {
+ GlobalConst("c", Add(Source{{1, 1}}, Expr(AFloat::Highest()), AFloat::Highest()));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "1:1 error: '1.79769e+308 + 1.79769e+308' cannot be represented as 'abstract-float'");
+}
+
+TEST_F(ResolverConstEvalTest, BinaryAbstractAddUnderflow_AFloat) {
+ GlobalConst("c", Add(Source{{1, 1}}, Expr(AFloat::Lowest()), AFloat::Lowest()));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ "1:1 error: '-1.79769e+308 + -1.79769e+308' cannot be represented as 'abstract-float'");
+}
+
+// Mixed AInt and AFloat args to test implicit conversion to AFloat
+INSTANTIATE_TEST_SUITE_P(
+ AbstractMixed,
+ ResolverConstEvalBinaryOpTest,
+ testing::Combine(
+ testing::Values(ast::BinaryOp::kAdd),
+ testing::Values(C(Val(1_a), Val(2.3_a), Val(3.3_a)),
+ C(Val(2.3_a), Val(1_a), Val(3.3_a)),
+ C(Val(1_a), Vec(2.3_a, 2.3_a, 2.3_a), Vec(3.3_a, 3.3_a, 3.3_a)),
+ C(Vec(2.3_a, 2.3_a, 2.3_a), Val(1_a), Vec(3.3_a, 3.3_a, 3.3_a)),
+ C(Vec(2.3_a, 2.3_a, 2.3_a), Val(1_a), Vec(3.3_a, 3.3_a, 3.3_a)),
+ C(Val(1_a), Vec(2.3_a, 2.3_a, 2.3_a), Vec(3.3_a, 3.3_a, 3.3_a)),
+ C(Mat({1_a, 2_a}, //
+ {1_a, 2_a}, //
+ {1_a, 2_a}), //
+ Mat({1.2_a, 2.3_a}, //
+ {1.2_a, 2.3_a}, //
+ {1.2_a, 2.3_a}), //
+ Mat({2.2_a, 4.3_a}, //
+ {2.2_a, 4.3_a}, //
+ {2.2_a, 4.3_a})), //
+ C(Mat({1.2_a, 2.3_a}, //
+ {1.2_a, 2.3_a}, //
+ {1.2_a, 2.3_a}), //
+ Mat({1_a, 2_a}, //
+ {1_a, 2_a}, //
+ {1_a, 2_a}), //
+ Mat({2.2_a, 4.3_a}, //
+ {2.2_a, 4.3_a}, //
+ {2.2_a, 4.3_a})) //
+ )));
+
+// AInt left shift negative value -> error
+TEST_F(ResolverConstEvalTest, BinaryAbstractShiftLeftByNegativeValue_Error) {
+ GlobalConst("c", Shl(Source{{1, 1}}, Expr(1_a), Expr(-1_a)));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "1:1 error: cannot shift left by a negative value");
+}
+
+// i32/u32 left shift by >= 32 -> error
+using ResolverConstEvalShiftLeftConcreteGeqBitWidthError =
+ ResolverTestWithParam<std::tuple<Types, Types>>;
+TEST_P(ResolverConstEvalShiftLeftConcreteGeqBitWidthError, Test) {
+ auto* lhs_expr =
+ std::visit([&](auto&& value) { return value.Expr(*this); }, std::get<0>(GetParam()));
+ auto* rhs_expr =
+ std::visit([&](auto&& value) { return value.Expr(*this); }, std::get<1>(GetParam()));
+ GlobalConst("c", Shl(Source{{1, 1}}, lhs_expr, rhs_expr));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ "1:1 error: shift left value must be less than the bit width of the lhs, which is 32");
+}
+INSTANTIATE_TEST_SUITE_P(Test,
+ ResolverConstEvalShiftLeftConcreteGeqBitWidthError,
+ testing::Values( //
+ std::make_tuple(Val(1_i), Val(32_u)), //
+ std::make_tuple(Val(1_i), Val(33_u)), //
+ std::make_tuple(Val(1_i), Val(34_u)), //
+ std::make_tuple(Val(1_i), Val(99999999_u)), //
+ std::make_tuple(Val(1_u), Val(32_u)), //
+ std::make_tuple(Val(1_u), Val(33_u)), //
+ std::make_tuple(Val(1_u), Val(34_u)), //
+ std::make_tuple(Val(1_u), Val(99999999_u)) //
+ ));
+
+// AInt left shift results in sign change error
+using ResolverConstEvalShiftLeftSignChangeError = ResolverTestWithParam<std::tuple<Types, Types>>;
+TEST_P(ResolverConstEvalShiftLeftSignChangeError, Test) {
+ auto* lhs_expr =
+ std::visit([&](auto&& value) { return value.Expr(*this); }, std::get<0>(GetParam()));
+ auto* rhs_expr =
+ std::visit([&](auto&& value) { return value.Expr(*this); }, std::get<1>(GetParam()));
+ GlobalConst("c", Shl(Source{{1, 1}}, lhs_expr, rhs_expr));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "1:1 error: shift left operation results in sign change");
+}
+template <typename T>
+std::vector<std::tuple<Types, Types>> ShiftLeftSignChangeErrorCases() {
+ // Shift type is u32 for non-abstract
+ using ST = std::conditional_t<IsAbstract<T>, T, u32>;
+ using B = BitValues<T>;
+ return {
+ {Val(T{0b0001}), Val(ST{B::NumBits - 1})},
+ {Val(T{0b0010}), Val(ST{B::NumBits - 2})},
+ {Val(T{0b0100}), Val(ST{B::NumBits - 3})},
+ {Val(T{0b1000}), Val(ST{B::NumBits - 4})},
+ {Val(T{0b0011}), Val(ST{B::NumBits - 2})},
+ {Val(T{0b0110}), Val(ST{B::NumBits - 3})},
+ {Val(T{0b1100}), Val(ST{B::NumBits - 4})},
+ {Val(B::AllButLeftMost), Val(ST{1})},
+ {Val(B::AllButLeftMost), Val(ST{B::NumBits - 1})},
+ {Val(B::LeftMost), Val(ST{1})},
+ {Val(B::LeftMost), Val(ST{B::NumBits - 1})},
+ };
+}
+INSTANTIATE_TEST_SUITE_P(Test,
+ ResolverConstEvalShiftLeftSignChangeError,
+ testing::ValuesIn(Concat( //
+ ShiftLeftSignChangeErrorCases<AInt>(),
+ ShiftLeftSignChangeErrorCases<i32>(),
+ ShiftLeftSignChangeErrorCases<u32>())));
+
+} // namespace
+} // namespace tint::resolver
diff --git a/src/tint/resolver/const_eval_builtin_test.cc b/src/tint/resolver/const_eval_builtin_test.cc
new file mode 100644
index 0000000..3936fba
--- /dev/null
+++ b/src/tint/resolver/const_eval_builtin_test.cc
@@ -0,0 +1,302 @@
+// 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/tint/resolver/const_eval_test.h"
+
+using namespace tint::number_suffixes; // NOLINT
+
+namespace tint::resolver {
+namespace {
+
+// Bring in std::ostream& operator<<(std::ostream& o, const Types& types)
+using resolver::operator<<;
+
+struct Case {
+ Case(utils::VectorRef<Types> in_args, Types in_expected)
+ : args(std::move(in_args)), expected(std::move(in_expected)) {}
+
+ /// Expected value may be positive or negative
+ Case& PosOrNeg() {
+ expected_pos_or_neg = true;
+ return *this;
+ }
+
+ /// Expected value should be compared using FLOAT_EQ instead of EQ
+ Case& FloatComp() {
+ float_compare = true;
+ return *this;
+ }
+
+ utils::Vector<Types, 8> args;
+ Types expected;
+ bool expected_pos_or_neg = false;
+ bool float_compare = false;
+};
+
+static std::ostream& operator<<(std::ostream& o, const Case& c) {
+ o << "args: ";
+ for (auto& a : c.args) {
+ o << a << ", ";
+ }
+ o << "expected: " << c.expected << ", expected_pos_or_neg: " << c.expected_pos_or_neg;
+ return o;
+}
+
+/// Creates a Case with Values for args and result
+static Case C(std::initializer_list<Types> args, Types result) {
+ return Case{utils::Vector<Types, 8>{args}, std::move(result)};
+}
+
+/// Convenience overload that creates a Case with just scalars
+using ScalarTypes = std::variant<AInt, AFloat, u32, i32, f32, f16>;
+static Case C(std::initializer_list<ScalarTypes> sargs, ScalarTypes sresult) {
+ utils::Vector<Types, 8> args;
+ for (auto& sa : sargs) {
+ std::visit([&](auto&& v) { return args.Push(Val(v)); }, sa);
+ }
+ Types result = Val(0_a);
+ std::visit([&](auto&& v) { result = Val(v); }, sresult);
+ return Case{std::move(args), std::move(result)};
+}
+
+using ResolverConstEvalBuiltinTest = ResolverTestWithParam<std::tuple<sem::BuiltinType, Case>>;
+
+TEST_P(ResolverConstEvalBuiltinTest, Test) {
+ Enable(ast::Extension::kF16);
+
+ auto builtin = std::get<0>(GetParam());
+ auto& c = std::get<1>(GetParam());
+
+ utils::Vector<const ast::Expression*, 8> args;
+ for (auto& a : c.args) {
+ std::visit([&](auto&& v) { args.Push(v.Expr(*this)); }, a);
+ }
+
+ std::visit(
+ [&](auto&& expected) {
+ using T = typename std::decay_t<decltype(expected)>::ElementType;
+ auto* expr = Call(sem::str(builtin), std::move(args));
+
+ GlobalConst("C", expr);
+ auto* expected_expr = expected.Expr(*this);
+ GlobalConst("E", expected_expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ const sem::Constant* value = sem->ConstantValue();
+ ASSERT_NE(value, nullptr);
+ EXPECT_TYPE(value->Type(), sem->Type());
+
+ auto* expected_sem = Sem().Get(expected_expr);
+ const sem::Constant* expected_value = expected_sem->ConstantValue();
+ ASSERT_NE(expected_value, nullptr);
+ EXPECT_TYPE(expected_value->Type(), expected_sem->Type());
+
+ ForEachElemPair(value, expected_value,
+ [&](const sem::Constant* a, const sem::Constant* b) {
+ auto v = a->As<T>();
+ auto e = b->As<T>();
+ if constexpr (std::is_same_v<bool, T>) {
+ EXPECT_EQ(v, e);
+ } else if constexpr (IsFloatingPoint<T>) {
+ if (std::isnan(e)) {
+ EXPECT_TRUE(std::isnan(v));
+ } else {
+ auto vf = (c.expected_pos_or_neg ? Abs(v) : v);
+ if (c.float_compare) {
+ EXPECT_FLOAT_EQ(vf, e);
+ } else {
+ EXPECT_EQ(vf, e);
+ }
+ }
+ } else {
+ EXPECT_EQ((c.expected_pos_or_neg ? Abs(v) : v), e);
+ // Check that the constant's integer doesn't contain unexpected
+ // data in the MSBs that are outside of the bit-width of T.
+ EXPECT_EQ(a->As<AInt>(), b->As<AInt>());
+ }
+ return HasFailure() ? Action::kStop : Action::kContinue;
+ });
+ },
+ c.expected);
+}
+
+INSTANTIATE_TEST_SUITE_P( //
+ MixedAbstractArgs,
+ ResolverConstEvalBuiltinTest,
+ testing::Combine(testing::Values(sem::BuiltinType::kAtan2),
+ testing::ValuesIn(std::vector{
+ C({0_a, -0.0_a}, kPi<AFloat>),
+ C({1.0_a, 0_a}, kPiOver2<AFloat>),
+ })));
+
+template <typename T, bool finite_only>
+std::vector<Case> Atan2Cases() {
+ std::vector<Case> cases = {
+ // If y is +/-0 and x is negative or -0, +/-PI is returned
+ C({T(0.0), -T(0.0)}, kPi<T>).PosOrNeg().FloatComp(),
+
+ // If y is +/-0 and x is positive or +0, +/-0 is returned
+ C({T(0.0), T(0.0)}, T(0.0)).PosOrNeg(),
+
+ // If x is +/-0 and y is negative, -PI/2 is returned
+ C({-T(1.0), T(0.0)}, -kPiOver2<T>).FloatComp(), //
+ C({-T(1.0), -T(0.0)}, -kPiOver2<T>).FloatComp(),
+
+ // If x is +/-0 and y is positive, +PI/2 is returned
+ C({T(1.0), T(0.0)}, kPiOver2<T>).FloatComp(), //
+ C({T(1.0), -T(0.0)}, kPiOver2<T>).FloatComp(),
+
+ // Vector tests
+ C({Vec(T(0.0), T(0.0)), Vec(-T(0.0), T(0.0))}, Vec(kPi<T>, T(0.0))).PosOrNeg().FloatComp(),
+ C({Vec(-T(1.0), -T(1.0)), Vec(T(0.0), -T(0.0))}, Vec(-kPiOver2<T>, -kPiOver2<T>))
+ .FloatComp(),
+ C({Vec(T(1.0), T(1.0)), Vec(T(0.0), -T(0.0))}, Vec(kPiOver2<T>, kPiOver2<T>)).FloatComp(),
+ };
+
+ if constexpr (!finite_only) {
+ std::vector<Case> non_finite_cases = {
+ // If y is +/-INF and x is finite, +/-PI/2 is returned
+ C({T::Inf(), T(0.0)}, kPiOver2<T>).PosOrNeg().FloatComp(),
+ C({-T::Inf(), T(0.0)}, kPiOver2<T>).PosOrNeg().FloatComp(),
+
+ // If y is +/-INF and x is -INF, +/-3PI/4 is returned
+ C({T::Inf(), -T::Inf()}, k3PiOver4<T>).PosOrNeg().FloatComp(),
+ C({-T::Inf(), -T::Inf()}, k3PiOver4<T>).PosOrNeg().FloatComp(),
+
+ // If y is +/-INF and x is +INF, +/-PI/4 is returned
+ C({T::Inf(), T::Inf()}, kPiOver4<T>).PosOrNeg().FloatComp(),
+ C({-T::Inf(), T::Inf()}, kPiOver4<T>).PosOrNeg().FloatComp(),
+
+ // If x is -INF and y is finite and positive, +PI is returned
+ C({T(0.0), -T::Inf()}, kPi<T>).FloatComp(),
+
+ // If x is -INF and y is finite and negative, -PI is returned
+ C({-T(0.0), -T::Inf()}, -kPi<T>).FloatComp(),
+
+ // If x is +INF and y is finite and positive, +0 is returned
+ C({T(0.0), T::Inf()}, T(0.0)),
+
+ // If x is +INF and y is finite and negative, -0 is returned
+ C({-T(0.0), T::Inf()}, -T(0.0)),
+
+ // If either x is NaN or y is NaN, NaN is returned
+ C({T::NaN(), T(0.0)}, T::NaN()),
+ C({T(0.0), T::NaN()}, T::NaN()),
+ C({T::NaN(), T::NaN()}, T::NaN()),
+
+ // Vector tests
+ C({Vec(T::Inf(), -T::Inf(), T::Inf(), -T::Inf()), //
+ Vec(T(0.0), T(0.0), -T::Inf(), -T::Inf())}, //
+ Vec(kPiOver2<T>, kPiOver2<T>, k3PiOver4<T>, k3PiOver4<T>))
+ .PosOrNeg()
+ .FloatComp(),
+ };
+ cases = Concat(cases, non_finite_cases);
+ }
+
+ return cases;
+}
+INSTANTIATE_TEST_SUITE_P( //
+ Atan2,
+ ResolverConstEvalBuiltinTest,
+ testing::Combine(testing::Values(sem::BuiltinType::kAtan2),
+ testing::ValuesIn(Concat(Atan2Cases<AFloat, true>(), //
+ Atan2Cases<f32, false>(),
+ Atan2Cases<f16, false>()))));
+
+template <typename T>
+std::vector<Case> ClampCases() {
+ return {
+ C({T(0), T(0), T(0)}, T(0)),
+ C({T(0), T(42), T::Highest()}, T(42)),
+ C({T::Lowest(), T(0), T(42)}, T(0)),
+ C({T(0), T::Lowest(), T::Highest()}, T(0)),
+ C({T(0), T::Highest(), T::Lowest()}, T::Lowest()),
+ C({T::Highest(), T::Highest(), T::Highest()}, T::Highest()),
+ C({T::Lowest(), T::Lowest(), T::Lowest()}, T::Lowest()),
+ C({T::Highest(), T::Lowest(), T::Highest()}, T::Highest()),
+ C({T::Lowest(), T::Lowest(), T::Highest()}, T::Lowest()),
+
+ // Vector tests
+ C({Vec(T(0), T(0)), //
+ Vec(T(0), T(42)), //
+ Vec(T(0), T::Highest())}, //
+ Vec(T(0), T(42))), //
+ C({Vec(T::Lowest(), T(0), T(0)), //
+ Vec(T(0), T::Lowest(), T::Highest()), //
+ Vec(T(42), T::Highest(), T::Lowest())}, //
+ Vec(T(0), T(0), T::Lowest())),
+ };
+}
+INSTANTIATE_TEST_SUITE_P( //
+ Clamp,
+ ResolverConstEvalBuiltinTest,
+ testing::Combine(testing::Values(sem::BuiltinType::kClamp),
+ testing::ValuesIn(Concat(ClampCases<AInt>(), //
+ ClampCases<i32>(),
+ ClampCases<u32>(),
+ ClampCases<AFloat>(),
+ ClampCases<f32>(),
+ ClampCases<f16>()))));
+
+template <typename T>
+std::vector<Case> SelectCases() {
+ return {
+ C({Val(T{1}), Val(T{2}), Val(false)}, Val(T{1})),
+ C({Val(T{1}), Val(T{2}), Val(true)}, Val(T{2})),
+
+ C({Val(T{2}), Val(T{1}), Val(false)}, Val(T{2})),
+ C({Val(T{2}), Val(T{1}), Val(true)}, Val(T{1})),
+
+ C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(false, false)}, Vec(T{1}, T{2})),
+ C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(false, true)}, Vec(T{1}, T{4})),
+ C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(true, false)}, Vec(T{3}, T{2})),
+ C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(true, true)}, Vec(T{3}, T{4})),
+
+ C({Vec(T{1}, T{1}, T{2}, T{2}), //
+ Vec(T{2}, T{2}, T{1}, T{1}), //
+ Vec(false, true, false, true)}, //
+ Vec(T{1}, T{2}, T{2}, T{1})), //
+ };
+}
+static std::vector<Case> SelectBoolCases() {
+ return {
+ C({Val(true), Val(false), Val(false)}, Val(true)),
+ C({Val(true), Val(false), Val(true)}, Val(false)),
+
+ C({Val(false), Val(true), Val(true)}, Val(true)),
+ C({Val(false), Val(true), Val(false)}, Val(false)),
+
+ C({Vec(true, true, false, false), //
+ Vec(false, false, true, true), //
+ Vec(false, true, true, false)}, //
+ Vec(true, false, true, false)), //
+ };
+}
+INSTANTIATE_TEST_SUITE_P( //
+ Select,
+ ResolverConstEvalBuiltinTest,
+ testing::Combine(testing::Values(sem::BuiltinType::kSelect),
+ testing::ValuesIn(Concat(SelectCases<AInt>(), //
+ SelectCases<i32>(),
+ SelectCases<u32>(),
+ SelectCases<AFloat>(),
+ SelectCases<f32>(),
+ SelectCases<f16>(),
+ SelectBoolCases()))));
+
+} // namespace
+} // namespace tint::resolver
diff --git a/src/tint/resolver/const_eval_construction_test.cc b/src/tint/resolver/const_eval_construction_test.cc
new file mode 100644
index 0000000..9df9807
--- /dev/null
+++ b/src/tint/resolver/const_eval_construction_test.cc
@@ -0,0 +1,2099 @@
+// Copyright 2022 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/tint/resolver/const_eval_test.h"
+
+using namespace tint::number_suffixes; // NOLINT
+
+namespace tint::resolver {
+namespace {
+
+TEST_F(ResolverConstEvalTest, Scalar_i32) {
+ auto* expr = Expr(99_i);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Type()->Is<sem::I32>());
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->As<AInt>(), 99);
+}
+
+TEST_F(ResolverConstEvalTest, Scalar_u32) {
+ auto* expr = Expr(99_u);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Type()->Is<sem::U32>());
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->As<AInt>(), 99u);
+}
+
+TEST_F(ResolverConstEvalTest, Scalar_f32) {
+ auto* expr = Expr(9.9_f);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Type()->Is<sem::F32>());
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->As<AFloat>().value, 9.9f);
+}
+
+TEST_F(ResolverConstEvalTest, Scalar_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = Expr(9.9_h);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Type()->Is<sem::F16>());
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+ // 9.9 is not exactly representable by f16, and should be quantized to 9.8984375
+ EXPECT_EQ(sem->ConstantValue()->As<AFloat>(), 9.8984375f);
+}
+
+TEST_F(ResolverConstEvalTest, Scalar_bool) {
+ auto* expr = Expr(true);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Type()->Is<sem::Bool>());
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->As<bool>(), true);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_ZeroInit_i32) {
+ auto* expr = vec3<i32>();
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::I32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 0);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 0);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 0);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_ZeroInit_u32) {
+ auto* expr = vec3<u32>();
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::U32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 0u);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 0u);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 0u);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_ZeroInit_f32) {
+ auto* expr = vec3<f32>();
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 0._a);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 0._a);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 0._a);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_ZeroInit_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<f16>();
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F16>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 0._a);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 0._a);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 0._a);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_ZeroInit_bool) {
+ auto* expr = vec3<bool>();
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::Bool>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<bool>(), false);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<bool>(), false);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), false);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Splat_i32) {
+ auto* expr = vec3<i32>(99_i);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::I32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 99);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 99);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 99);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Splat_u32) {
+ auto* expr = vec3<u32>(99_u);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::U32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 99u);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 99u);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 99u);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Splat_f32) {
+ auto* expr = vec3<f32>(9.9_f);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 9.9f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 9.9f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 9.9f);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Splat_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<f16>(9.9_h);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F16>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+ // 9.9 is not exactly representable by f16, and should be quantized to 9.8984375
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 9.8984375f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 9.8984375f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 9.8984375f);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Splat_bool) {
+ auto* expr = vec3<bool>(true);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::Bool>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<bool>(), true);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<bool>(), true);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), true);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_FullConstruct_i32) {
+ auto* expr = vec3<i32>(1_i, 2_i, 3_i);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::I32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_FullConstruct_u32) {
+ auto* expr = vec3<u32>(1_u, 2_u, 3_u);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::U32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_FullConstruct_f32) {
+ auto* expr = vec3<f32>(1_f, 2_f, 3_f);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 1.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 2.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 3.f);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_FullConstruct_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<f16>(1_h, 2_h, 3_h);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F16>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 1.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 2.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 3.f);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_FullConstruct_bool) {
+ auto* expr = vec3<bool>(true, false, true);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::Bool>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<bool>(), true);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<bool>(), false);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), true);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_i32) {
+ auto* expr = vec3<i32>(1_i, vec2<i32>(2_i, 3_i));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::I32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_u32) {
+ auto* expr = vec3<u32>(vec2<u32>(1_u, 2_u), 3_u);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::U32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f32) {
+ auto* expr = vec3<f32>(1_f, vec2<f32>(2_f, 3_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 1.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 2.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 3.f);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f32_all_10) {
+ auto* expr = vec3<f32>(10_f, vec2<f32>(10_f, 10_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 10_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 10_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 10_f);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f32_all_positive_0) {
+ auto* expr = vec3<f32>(0_f, vec2<f32>(0_f, 0_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 0_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 0_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 0_f);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f32_all_negative_0) {
+ auto* expr = vec3<f32>(vec2<f32>(-0_f, -0_f), -0_f);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), -0_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), -0_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), -0_f);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f32_mixed_sign_0) {
+ auto* expr = vec3<f32>(0_f, vec2<f32>(-0_f, 0_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 0_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), -0_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 0_f);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<f16>(1_h, vec2<f16>(2_h, 3_h));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F16>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 1.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 2.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 3.f);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f16_all_10) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<f16>(10_h, vec2<f16>(10_h, 10_h));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F16>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f16>(), 10_h);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f16>(), 10_h);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f16>(), 10_h);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f16_all_positive_0) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<f16>(0_h, vec2<f16>(0_h, 0_h));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F16>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f16>(), 0_h);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f16>(), 0_h);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f16>(), 0_h);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f16_all_negative_0) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<f16>(vec2<f16>(-0_h, -0_h), -0_h);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F16>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f16>(), -0_h);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f16>(), -0_h);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f16>(), -0_h);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f16_mixed_sign_0) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<f16>(0_h, vec2<f16>(-0_h, 0_h));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F16>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f16>(), 0_h);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f16>(), -0_h);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f16>(), 0_h);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_bool) {
+ auto* expr = vec3<bool>(vec2<bool>(true, false), true);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::Bool>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<bool>(), true);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<bool>(), false);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), true);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_all_true) {
+ auto* expr = vec3<bool>(true, vec2<bool>(true, true));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::Bool>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<bool>(), true);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<bool>(), true);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), true);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_all_false) {
+ auto* expr = vec3<bool>(false, vec2<bool>(false, false));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::Bool>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<bool>(), false);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<bool>(), false);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), false);
+}
+
+TEST_F(ResolverConstEvalTest, Mat2x3_ZeroInit_f32) {
+ auto* expr = mat2x3<f32>();
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* mat = sem->Type()->As<sem::Matrix>();
+ ASSERT_NE(mat, nullptr);
+ EXPECT_TRUE(mat->type()->Is<sem::F32>());
+ EXPECT_EQ(mat->columns(), 2u);
+ EXPECT_EQ(mat->rows(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<f32>(), 0._f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<f32>(), 0._f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As<f32>(), 0._f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<f32>(), 0._f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<f32>(), 0._f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f32>(), 0._f);
+}
+
+TEST_F(ResolverConstEvalTest, Mat2x3_ZeroInit_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = mat2x3<f16>();
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* mat = sem->Type()->As<sem::Matrix>();
+ ASSERT_NE(mat, nullptr);
+ EXPECT_TRUE(mat->type()->Is<sem::F16>());
+ EXPECT_EQ(mat->columns(), 2u);
+ EXPECT_EQ(mat->rows(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<f16>(), 0._h);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<f16>(), 0._h);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As<f16>(), 0._h);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<f16>(), 0._h);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<f16>(), 0._h);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f16>(), 0._h);
+}
+
+TEST_F(ResolverConstEvalTest, Mat3x2_Construct_Scalars_af) {
+ auto* expr = Construct(ty.mat(nullptr, 3, 2), 1.0_a, 2.0_a, 3.0_a, 4.0_a, 5.0_a, 6.0_a);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* mat = sem->Type()->As<sem::Matrix>();
+ ASSERT_NE(mat, nullptr);
+ EXPECT_TRUE(mat->type()->Is<sem::F32>());
+ EXPECT_EQ(mat->columns(), 3u);
+ EXPECT_EQ(mat->rows(), 2u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<AFloat>(), 1._a);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<AFloat>(), 2._a);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<AFloat>(), 3._a);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<AFloat>(), 4._a);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(0)->As<AFloat>(), 5._a);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(1)->As<AFloat>(), 6._a);
+}
+
+TEST_F(ResolverConstEvalTest, Mat3x2_Construct_Columns_af) {
+ auto* expr = Construct(ty.mat(nullptr, 3, 2), //
+ vec(nullptr, 2u, 1.0_a, 2.0_a), //
+ vec(nullptr, 2u, 3.0_a, 4.0_a), //
+ vec(nullptr, 2u, 5.0_a, 6.0_a));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* mat = sem->Type()->As<sem::Matrix>();
+ ASSERT_NE(mat, nullptr);
+ EXPECT_TRUE(mat->type()->Is<sem::F32>());
+ EXPECT_EQ(mat->columns(), 3u);
+ EXPECT_EQ(mat->rows(), 2u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<AFloat>(), 1._a);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<AFloat>(), 2._a);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<AFloat>(), 3._a);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<AFloat>(), 4._a);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(0)->As<AFloat>(), 5._a);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(1)->As<AFloat>(), 6._a);
+}
+
+TEST_F(ResolverConstEvalTest, Array_i32_Zero) {
+ auto* expr = Construct(ty.array<i32, 4>());
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* arr = sem->Type()->As<sem::Array>();
+ ASSERT_NE(arr, nullptr);
+ EXPECT_TRUE(arr->ElemType()->Is<sem::I32>());
+ EXPECT_EQ(arr->Count(), sem::ConstantArrayCount{4u});
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<i32>(), 0_i);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<i32>(), 0_i);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<i32>(), 0_i);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(3)->As<i32>(), 0_i);
+}
+
+TEST_F(ResolverConstEvalTest, Array_f32_Zero) {
+ auto* expr = Construct(ty.array<f32, 4>());
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* arr = sem->Type()->As<sem::Array>();
+ ASSERT_NE(arr, nullptr);
+ EXPECT_TRUE(arr->ElemType()->Is<sem::F32>());
+ EXPECT_EQ(arr->Count(), sem::ConstantArrayCount{4u});
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 0_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 0_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 0_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(3)->As<f32>(), 0_f);
+}
+
+TEST_F(ResolverConstEvalTest, Array_vec3_f32_Zero) {
+ auto* expr = Construct(ty.array(ty.vec3<f32>(), 2_u));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* arr = sem->Type()->As<sem::Array>();
+ ASSERT_NE(arr, nullptr);
+ EXPECT_TRUE(arr->ElemType()->Is<sem::Vector>());
+ EXPECT_EQ(arr->Count(), sem::ConstantArrayCount{2u});
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<f32>(), 0_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<f32>(), 0_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As<f32>(), 0_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<f32>(), 0_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<f32>(), 0_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f32>(), 0_f);
+}
+
+TEST_F(ResolverConstEvalTest, Array_Struct_f32_Zero) {
+ Structure("S", utils::Vector{
+ Member("m1", ty.f32()),
+ Member("m2", ty.f32()),
+ });
+ auto* expr = Construct(ty.array(ty.type_name("S"), 2_u));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* arr = sem->Type()->As<sem::Array>();
+ ASSERT_NE(arr, nullptr);
+ EXPECT_TRUE(arr->ElemType()->Is<sem::Struct>());
+ EXPECT_EQ(arr->Count(), sem::ConstantArrayCount{2u});
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<f32>(), 0_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<f32>(), 0_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<f32>(), 0_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<f32>(), 0_f);
+}
+
+TEST_F(ResolverConstEvalTest, Array_i32_Elements) {
+ auto* expr = Construct(ty.array<i32, 4>(), 10_i, 20_i, 30_i, 40_i);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* arr = sem->Type()->As<sem::Array>();
+ ASSERT_NE(arr, nullptr);
+ EXPECT_TRUE(arr->ElemType()->Is<sem::I32>());
+ EXPECT_EQ(arr->Count(), sem::ConstantArrayCount{4u});
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<i32>(), 10_i);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<i32>(), 20_i);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<i32>(), 30_i);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(3)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(3)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(3)->As<i32>(), 40_i);
+}
+
+TEST_F(ResolverConstEvalTest, Array_f32_Elements) {
+ auto* expr = Construct(ty.array<f32, 4>(), 10_f, 20_f, 30_f, 40_f);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* arr = sem->Type()->As<sem::Array>();
+ ASSERT_NE(arr, nullptr);
+ EXPECT_TRUE(arr->ElemType()->Is<sem::F32>());
+ EXPECT_EQ(arr->Count(), sem::ConstantArrayCount{4u});
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 10_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 20_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 30_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(3)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(3)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(3)->As<f32>(), 40_f);
+}
+
+TEST_F(ResolverConstEvalTest, Array_vec3_f32_Elements) {
+ auto* expr = Construct(ty.array(ty.vec3<f32>(), 2_u), //
+ vec3<f32>(1_f, 2_f, 3_f), vec3<f32>(4_f, 5_f, 6_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* arr = sem->Type()->As<sem::Array>();
+ ASSERT_NE(arr, nullptr);
+ EXPECT_TRUE(arr->ElemType()->Is<sem::Vector>());
+ EXPECT_EQ(arr->Count(), sem::ConstantArrayCount{2u});
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<f32>(), 1_f);
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<f32>(), 2_f);
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As<f32>(), 3_f);
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<f32>(), 4_f);
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<f32>(), 5_f);
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f32>(), 6_f);
+}
+
+TEST_F(ResolverConstEvalTest, Array_Struct_f32_Elements) {
+ Structure("S", utils::Vector{
+ Member("m1", ty.f32()),
+ Member("m2", ty.f32()),
+ });
+ auto* expr = Construct(ty.array(ty.type_name("S"), 2_u), //
+ Construct(ty.type_name("S"), 1_f, 2_f), //
+ Construct(ty.type_name("S"), 3_f, 4_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* arr = sem->Type()->As<sem::Array>();
+ ASSERT_NE(arr, nullptr);
+ EXPECT_TRUE(arr->ElemType()->Is<sem::Struct>());
+ EXPECT_EQ(arr->Count(), sem::ConstantArrayCount{2u});
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<f32>(), 1_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<f32>(), 2_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<f32>(), 3_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<f32>(), 4_f);
+}
+
+TEST_F(ResolverConstEvalTest, Struct_I32s_ZeroInit) {
+ Structure(
+ "S", utils::Vector{Member("m1", ty.i32()), Member("m2", ty.i32()), Member("m3", ty.i32())});
+ auto* expr = Construct(ty.type_name("S"));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* str = sem->Type()->As<sem::Struct>();
+ ASSERT_NE(str, nullptr);
+ EXPECT_EQ(str->Members().size(), 3u);
+ ASSERT_NE(sem->ConstantValue(), nullptr);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::I32>());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<i32>(), 0_i);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::I32>());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<i32>(), 0_i);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->Type()->Is<sem::I32>());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<i32>(), 0_i);
+}
+
+TEST_F(ResolverConstEvalTest, Struct_MixedScalars_ZeroInit) {
+ Enable(ast::Extension::kF16);
+
+ Structure("S", utils::Vector{
+ Member("m1", ty.i32()),
+ Member("m2", ty.u32()),
+ Member("m3", ty.f32()),
+ Member("m4", ty.f16()),
+ Member("m5", ty.bool_()),
+ });
+ auto* expr = Construct(ty.type_name("S"));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* str = sem->Type()->As<sem::Struct>();
+ ASSERT_NE(str, nullptr);
+ EXPECT_EQ(str->Members().size(), 5u);
+ ASSERT_NE(sem->ConstantValue(), nullptr);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::I32>());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<i32>(), 0_i);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::U32>());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<u32>(), 0_u);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->Type()->Is<sem::F32>());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 0._f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->Type()->Is<sem::F16>());
+ EXPECT_EQ(sem->ConstantValue()->Index(3)->As<f16>(), 0._h);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(4)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(4)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(4)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(4)->Type()->Is<sem::Bool>());
+ EXPECT_EQ(sem->ConstantValue()->Index(4)->As<bool>(), false);
+}
+
+TEST_F(ResolverConstEvalTest, Struct_VectorF32s_ZeroInit) {
+ Structure("S", utils::Vector{
+ Member("m1", ty.vec3<f32>()),
+ Member("m2", ty.vec3<f32>()),
+ Member("m3", ty.vec3<f32>()),
+ });
+ auto* expr = Construct(ty.type_name("S"));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* str = sem->Type()->As<sem::Struct>();
+ ASSERT_NE(str, nullptr);
+ EXPECT_EQ(str->Members().size(), 3u);
+ ASSERT_NE(sem->ConstantValue(), nullptr);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<f32>(), 0._f);
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<f32>(), 0._f);
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As<f32>(), 0._f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<f32>(), 0._f);
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<f32>(), 0._f);
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f32>(), 0._f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(0)->As<f32>(), 0._f);
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(1)->As<f32>(), 0._f);
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(2)->As<f32>(), 0._f);
+}
+
+TEST_F(ResolverConstEvalTest, Struct_MixedVectors_ZeroInit) {
+ Enable(ast::Extension::kF16);
+
+ Structure("S", utils::Vector{
+ Member("m1", ty.vec2<i32>()),
+ Member("m2", ty.vec3<u32>()),
+ Member("m3", ty.vec4<f32>()),
+ Member("m4", ty.vec3<f16>()),
+ Member("m5", ty.vec2<bool>()),
+ });
+ auto* expr = Construct(ty.type_name("S"));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* str = sem->Type()->As<sem::Struct>();
+ ASSERT_NE(str, nullptr);
+ EXPECT_EQ(str->Members().size(), 5u);
+ ASSERT_NE(sem->ConstantValue(), nullptr);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<i32>(), 0_i);
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<i32>(), 0_i);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<u32>(), 0_u);
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<u32>(), 0_u);
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<u32>(), 0_u);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(0)->As<f32>(), 0._f);
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(1)->As<f32>(), 0._f);
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(2)->As<f32>(), 0._f);
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(3)->As<f32>(), 0._f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->Type()->As<sem::Vector>()->type()->Is<sem::F16>());
+ EXPECT_EQ(sem->ConstantValue()->Index(3)->Index(0)->As<f16>(), 0._h);
+ EXPECT_EQ(sem->ConstantValue()->Index(3)->Index(1)->As<f16>(), 0._h);
+ EXPECT_EQ(sem->ConstantValue()->Index(3)->Index(2)->As<f16>(), 0._h);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(4)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(4)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(4)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(4)->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->ConstantValue()->Index(4)->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
+ EXPECT_EQ(sem->ConstantValue()->Index(4)->Index(0)->As<bool>(), false);
+ EXPECT_EQ(sem->ConstantValue()->Index(4)->Index(1)->As<bool>(), false);
+}
+
+TEST_F(ResolverConstEvalTest, Struct_Struct_ZeroInit) {
+ Structure("Inner", utils::Vector{
+ Member("m1", ty.i32()),
+ Member("m2", ty.u32()),
+ Member("m3", ty.f32()),
+ });
+
+ Structure("Outer", utils::Vector{
+ Member("m1", ty.type_name("Inner")),
+ Member("m2", ty.type_name("Inner")),
+ });
+ auto* expr = Construct(ty.type_name("Outer"));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* str = sem->Type()->As<sem::Struct>();
+ ASSERT_NE(str, nullptr);
+ EXPECT_EQ(str->Members().size(), 2u);
+ ASSERT_NE(sem->ConstantValue(), nullptr);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::Struct>());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<i32>(), 0_i);
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<u32>(), 0_u);
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As<f32>(), 0_f);
+
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::Struct>());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<i32>(), 0_i);
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<u32>(), 0_u);
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f32>(), 0_f);
+}
+
+TEST_F(ResolverConstEvalTest, Struct_MixedScalars_Construct) {
+ Enable(ast::Extension::kF16);
+
+ Structure("S", utils::Vector{
+ Member("m1", ty.i32()),
+ Member("m2", ty.u32()),
+ Member("m3", ty.f32()),
+ Member("m4", ty.f16()),
+ Member("m5", ty.bool_()),
+ });
+ auto* expr = Construct(ty.type_name("S"), 1_i, 2_u, 3_f, 4_h, false);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* str = sem->Type()->As<sem::Struct>();
+ ASSERT_NE(str, nullptr);
+ EXPECT_EQ(str->Members().size(), 5u);
+ ASSERT_NE(sem->ConstantValue(), nullptr);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::I32>());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<i32>(), 1_i);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::U32>());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<u32>(), 2_u);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->Type()->Is<sem::F32>());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 3._f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(3)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(3)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->Type()->Is<sem::F16>());
+ EXPECT_EQ(sem->ConstantValue()->Index(3)->As<f16>(), 4._h);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(4)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(4)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(4)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(4)->Type()->Is<sem::Bool>());
+ EXPECT_EQ(sem->ConstantValue()->Index(4)->As<bool>(), false);
+}
+
+TEST_F(ResolverConstEvalTest, Struct_MixedVectors_Construct) {
+ Enable(ast::Extension::kF16);
+
+ Structure("S", utils::Vector{
+ Member("m1", ty.vec2<i32>()),
+ Member("m2", ty.vec3<u32>()),
+ Member("m3", ty.vec4<f32>()),
+ Member("m4", ty.vec3<f16>()),
+ Member("m5", ty.vec2<bool>()),
+ });
+ auto* expr = Construct(ty.type_name("S"), vec2<i32>(1_i), vec3<u32>(2_u), vec4<f32>(3_f),
+ vec3<f16>(4_h), vec2<bool>(false));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* str = sem->Type()->As<sem::Struct>();
+ ASSERT_NE(str, nullptr);
+ EXPECT_EQ(str->Members().size(), 5u);
+ ASSERT_NE(sem->ConstantValue(), nullptr);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<i32>(), 1_i);
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<i32>(), 1_i);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<u32>(), 2_u);
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<u32>(), 2_u);
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<u32>(), 2_u);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(0)->As<f32>(), 3._f);
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(1)->As<f32>(), 3._f);
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(2)->As<f32>(), 3._f);
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(3)->As<f32>(), 3._f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(3)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(3)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->ConstantValue()->Index(3)->Type()->As<sem::Vector>()->type()->Is<sem::F16>());
+ EXPECT_EQ(sem->ConstantValue()->Index(3)->Index(0)->As<f16>(), 4._h);
+ EXPECT_EQ(sem->ConstantValue()->Index(3)->Index(1)->As<f16>(), 4._h);
+ EXPECT_EQ(sem->ConstantValue()->Index(3)->Index(2)->As<f16>(), 4._h);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(4)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(4)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(4)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(4)->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->ConstantValue()->Index(4)->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
+ EXPECT_EQ(sem->ConstantValue()->Index(4)->Index(0)->As<bool>(), false);
+ EXPECT_EQ(sem->ConstantValue()->Index(4)->Index(1)->As<bool>(), false);
+}
+
+TEST_F(ResolverConstEvalTest, Struct_Struct_Construct) {
+ Structure("Inner", utils::Vector{
+ Member("m1", ty.i32()),
+ Member("m2", ty.u32()),
+ Member("m3", ty.f32()),
+ });
+
+ Structure("Outer", utils::Vector{
+ Member("m1", ty.type_name("Inner")),
+ Member("m2", ty.type_name("Inner")),
+ });
+ auto* expr = Construct(ty.type_name("Outer"), //
+ Construct(ty.type_name("Inner"), 1_i, 2_u, 3_f),
+ Construct(ty.type_name("Inner"), 4_i, 0_u, 6_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* str = sem->Type()->As<sem::Struct>();
+ ASSERT_NE(str, nullptr);
+ EXPECT_EQ(str->Members().size(), 2u);
+ ASSERT_NE(sem->ConstantValue(), nullptr);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::Struct>());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<i32>(), 1_i);
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<u32>(), 2_u);
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As<f32>(), 3_f);
+
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::Struct>());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<i32>(), 4_i);
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<u32>(), 0_u);
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f32>(), 6_f);
+}
+
+TEST_F(ResolverConstEvalTest, Struct_Array_Construct) {
+ Structure("S", utils::Vector{
+ Member("m1", ty.array<i32, 2>()),
+ Member("m2", ty.array<f32, 3>()),
+ });
+ auto* expr = Construct(ty.type_name("S"), //
+ Construct(ty.array<i32, 2>(), 1_i, 2_i),
+ Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* str = sem->Type()->As<sem::Struct>();
+ ASSERT_NE(str, nullptr);
+ EXPECT_EQ(str->Members().size(), 2u);
+ ASSERT_NE(sem->ConstantValue(), nullptr);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::Array>());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<i32>(), 1_i);
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<u32>(), 2_i);
+
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::Array>());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<i32>(), 1_f);
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<u32>(), 2_f);
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f32>(), 3_f);
+}
+
+} // namespace
+} // namespace tint::resolver
diff --git a/src/tint/resolver/const_eval_conversion_test.cc b/src/tint/resolver/const_eval_conversion_test.cc
new file mode 100644
index 0000000..35657ee
--- /dev/null
+++ b/src/tint/resolver/const_eval_conversion_test.cc
@@ -0,0 +1,529 @@
+// Copyright 2022 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/tint/resolver/const_eval_test.h"
+
+using namespace tint::number_suffixes; // NOLINT
+
+namespace tint::resolver {
+namespace {
+
+using Scalar = std::variant< //
+ builder::Value<AInt>,
+ builder::Value<AFloat>,
+ builder::Value<u32>,
+ builder::Value<i32>,
+ builder::Value<f32>,
+ builder::Value<f16>,
+ builder::Value<bool>>;
+
+static std::ostream& operator<<(std::ostream& o, const Scalar& scalar) {
+ std::visit(
+ [&](auto&& v) {
+ using ValueType = std::decay_t<decltype(v)>;
+ o << ValueType::DataType::Name() << "(";
+ for (auto& a : v.args.values) {
+ o << std::get<typename ValueType::ElementType>(a);
+ if (&a != &v.args.values.Back()) {
+ o << ", ";
+ }
+ }
+ o << ")";
+ },
+ scalar);
+ return o;
+}
+
+enum class Kind {
+ kScalar,
+ kVector,
+};
+
+static std::ostream& operator<<(std::ostream& o, const Kind& k) {
+ switch (k) {
+ case Kind::kScalar:
+ return o << "scalar";
+ case Kind::kVector:
+ return o << "vector";
+ }
+ return o << "<unknown>";
+}
+
+struct Case {
+ Scalar input;
+ Scalar expected;
+ builder::CreatePtrs type;
+ bool unrepresentable = false;
+};
+
+static std::ostream& operator<<(std::ostream& o, const Case& c) {
+ if (c.unrepresentable) {
+ o << "[unrepresentable] input: " << c.input;
+ } else {
+ o << "input: " << c.input << ", expected: " << c.expected;
+ }
+ return o << ", type: " << c.type;
+}
+
+template <typename TO, typename FROM>
+Case Success(FROM input, TO expected) {
+ return {builder::Val(input), builder::Val(expected), builder::CreatePtrsFor<TO>()};
+}
+
+template <typename TO, typename FROM>
+Case Unrepresentable(FROM input) {
+ return {builder::Val(input), builder::Val(0_i), builder::CreatePtrsFor<TO>(),
+ /* unrepresentable */ true};
+}
+
+using ResolverConstEvalConvTest = ResolverTestWithParam<std::tuple<Kind, Case>>;
+
+TEST_P(ResolverConstEvalConvTest, Test) {
+ const auto& kind = std::get<0>(GetParam());
+ const auto& input = std::get<1>(GetParam()).input;
+ const auto& expected = std::get<1>(GetParam()).expected;
+ const auto& type = std::get<1>(GetParam()).type;
+ const auto unrepresentable = std::get<1>(GetParam()).unrepresentable;
+
+ auto* input_val = std::visit([&](auto val) { return val.Expr(*this); }, input);
+ auto* expr = Construct(type.ast(*this), input_val);
+ if (kind == Kind::kVector) {
+ expr = Construct(ty.vec(nullptr, 3), expr);
+ }
+ WrapInFunction(expr);
+
+ auto* target_sem_ty = type.sem(*this);
+ if (kind == Kind::kVector) {
+ target_sem_ty = create<sem::Vector>(target_sem_ty, 3u);
+ }
+
+ if (unrepresentable) {
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_THAT(r()->error(), testing::HasSubstr("cannot be represented as"));
+ } else {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TYPE(sem->Type(), target_sem_ty);
+ ASSERT_NE(sem->ConstantValue(), nullptr);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), target_sem_ty);
+
+ auto expected_values = std::visit([&](auto&& val) { return val.args; }, expected);
+ if (kind == Kind::kVector) {
+ expected_values.values.Push(expected_values.values[0]);
+ expected_values.values.Push(expected_values.values[0]);
+ }
+ auto got_values = ScalarArgsFrom(sem->ConstantValue());
+ EXPECT_EQ(expected_values, got_values);
+ }
+}
+INSTANTIATE_TEST_SUITE_P(ScalarAndVector,
+ ResolverConstEvalConvTest,
+ testing::Combine(testing::Values(Kind::kScalar, Kind::kVector),
+ testing::ValuesIn({
+ // TODO(crbug.com/tint/1502): Add f16 tests
+ // i32 -> u32
+ Success(0_i, 0_u),
+ Success(1_i, 1_u),
+ Success(-1_i, 0xffffffff_u),
+ Success(2_i, 2_u),
+ Success(-2_i, 0xfffffffe_u),
+ // i32 -> f32
+ Success(0_i, 0_f),
+ Success(1_i, 1_f),
+ Success(-1_i, -1_f),
+ Success(2_i, 2_f),
+ Success(-2_i, -2_f),
+ // i32 -> bool
+ Success(0_i, false),
+ Success(1_i, true),
+ Success(-1_i, true),
+ Success(2_i, true),
+ Success(-2_i, true),
+ // u32 -> i32
+ Success(0_u, 0_i),
+ Success(1_u, 1_i),
+ Success(0xffffffff_u, -1_i),
+ Success(2_u, 2_i),
+ Success(0xfffffffe_u, -2_i),
+ // u32 -> f32
+ Success(0_u, 0_f),
+ Success(1_u, 1_f),
+ Success(2_u, 2_f),
+ Success(0xffffffff_u, 0xffffffff_f),
+ // u32 -> bool
+ Success(0_u, false),
+ Success(1_u, true),
+ Success(2_u, true),
+ Success(0xffffffff_u, true),
+ // f32 -> i32
+ Success(0_f, 0_i),
+ Success(1_f, 1_i),
+ Success(2_f, 2_i),
+ Success(1e20_f, i32::Highest()),
+ Success(-1e20_f, i32::Lowest()),
+ // f32 -> u32
+ Success(0_f, 0_i),
+ Success(1_f, 1_i),
+ Success(-1_f, u32::Lowest()),
+ Success(2_f, 2_i),
+ Success(1e20_f, u32::Highest()),
+ Success(-1e20_f, u32::Lowest()),
+ // f32 -> bool
+ Success(0_f, false),
+ Success(1_f, true),
+ Success(-1_f, true),
+ Success(2_f, true),
+ Success(1e20_f, true),
+ Success(-1e20_f, true),
+ // abstract-int -> i32
+ Success(0_a, 0_i),
+ Success(1_a, 1_i),
+ Success(-1_a, -1_i),
+ Success(0x7fffffff_a, i32::Highest()),
+ Success(-0x80000000_a, i32::Lowest()),
+ Unrepresentable<i32>(0x80000000_a),
+ // abstract-int -> u32
+ Success(0_a, 0_u),
+ Success(1_a, 1_u),
+ Success(0xffffffff_a, 0xffffffff_u),
+ Unrepresentable<u32>(0x100000000_a),
+ Unrepresentable<u32>(-1_a),
+ // abstract-int -> f32
+ Success(0_a, 0_f),
+ Success(1_a, 1_f),
+ Success(0xffffffff_a, 0xffffffff_f),
+ Success(0x100000000_a, 0x100000000_f),
+ Success(-0x100000000_a, -0x100000000_f),
+ Success(0x7fffffffffffffff_a, 0x7fffffffffffffff_f),
+ Success(-0x7fffffffffffffff_a, -0x7fffffffffffffff_f),
+ // abstract-int -> bool
+ Success(0_a, false),
+ Success(1_a, true),
+ Success(0xffffffff_a, true),
+ Success(0x100000000_a, true),
+ Success(-0x100000000_a, true),
+ Success(0x7fffffffffffffff_a, true),
+ Success(-0x7fffffffffffffff_a, true),
+ // abstract-float -> i32
+ Success(0.0_a, 0_i),
+ Success(1.0_a, 1_i),
+ Success(-1.0_a, -1_i),
+ Success(AFloat(0x7fffffff), i32::Highest()),
+ Success(-AFloat(0x80000000), i32::Lowest()),
+ Unrepresentable<i32>(0x80000000_a),
+ // abstract-float -> u32
+ Success(0.0_a, 0_u),
+ Success(1.0_a, 1_u),
+ Success(AFloat(0xffffffff), 0xffffffff_u),
+ Unrepresentable<u32>(AFloat(0x100000000)),
+ Unrepresentable<u32>(AFloat(-1)),
+ // abstract-float -> f32
+ Success(0.0_a, 0_f),
+ Success(1.0_a, 1_f),
+ Success(AFloat(0xffffffff), 0xffffffff_f),
+ Success(AFloat(0x100000000), 0x100000000_f),
+ Success(-AFloat(0x100000000), -0x100000000_f),
+ Unrepresentable<f32>(1e40_a),
+ Unrepresentable<f32>(-1e40_a),
+ // abstract-float -> bool
+ Success(0.0_a, false),
+ Success(1.0_a, true),
+ Success(AFloat(0xffffffff), true),
+ Success(AFloat(0x100000000), true),
+ Success(-AFloat(0x100000000), true),
+ Success(1e40_a, true),
+ Success(-1e40_a, true),
+ })));
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_f32_to_i32) {
+ auto* expr = vec3<i32>(vec3<f32>(1.1_f, 2.2_f, 3.3_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::I32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_u32_to_f32) {
+ auto* expr = vec3<f32>(vec3<u32>(10_u, 20_u, 30_u));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 10.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 20.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 30.f);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_f16_to_i32) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<i32>(vec3<f16>(1.1_h, 2.2_h, 3.3_h));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::I32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1_i);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2_i);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3_i);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_u32_to_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<f16>(vec3<u32>(10_u, 20_u, 30_u));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F16>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 10.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 20.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 30.f);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_i32) {
+ auto* expr = vec3<i32>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::I32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), i32::Highest());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), i32::Lowest());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), i32::Highest());
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_u32) {
+ auto* expr = vec3<u32>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::U32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), u32::Highest());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), u32::Lowest());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), u32::Highest());
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<f16>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ constexpr auto kInfinity = std::numeric_limits<double>::infinity();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F16>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), kInfinity);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), -kInfinity);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), kInfinity);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_Small_f32_to_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<f16>(vec3<f32>(1e-20_f, -2e-30_f, 3e-40_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F16>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 0.0);
+ EXPECT_FALSE(std::signbit(sem->ConstantValue()->Index(0)->As<AFloat>().value));
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), -0.0);
+ EXPECT_TRUE(std::signbit(sem->ConstantValue()->Index(1)->As<AFloat>().value));
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 0.0);
+ EXPECT_FALSE(std::signbit(sem->ConstantValue()->Index(2)->As<AFloat>().value));
+}
+
+} // namespace
+} // namespace tint::resolver
diff --git a/src/tint/resolver/const_eval_indexing_test.cc b/src/tint/resolver/const_eval_indexing_test.cc
new file mode 100644
index 0000000..09447d5
--- /dev/null
+++ b/src/tint/resolver/const_eval_indexing_test.cc
@@ -0,0 +1,314 @@
+// Copyright 2022 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/tint/resolver/const_eval_test.h"
+
+using namespace tint::number_suffixes; // NOLINT
+
+namespace tint::resolver {
+namespace {
+
+TEST_F(ResolverConstEvalTest, Vec3_Index) {
+ auto* expr = IndexAccessor(vec3<i32>(1_i, 2_i, 3_i), 2_i);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::I32>());
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->As<i32>(), 3_i);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Index_OOB_High) {
+ auto* expr = IndexAccessor(vec3<i32>(1_i, 2_i, 3_i), Expr(Source{{12, 34}}, 3_i));
+ WrapInFunction(expr);
+
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), "12:34 error: index 3 out of bounds [0..2]");
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Index_OOB_Low) {
+ auto* expr = IndexAccessor(vec3<i32>(1_i, 2_i, 3_i), Expr(Source{{12, 34}}, -3_i));
+ WrapInFunction(expr);
+
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), "12:34 error: index -3 out of bounds [0..2]");
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Swizzle_Scalar) {
+ auto* expr = MemberAccessor(vec3<i32>(1_i, 2_i, 3_i), "y");
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::I32>());
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->As<i32>(), 2_i);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Swizzle_Vector) {
+ auto* expr = MemberAccessor(vec3<i32>(1_i, 2_i, 3_i), "zx");
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_EQ(vec->Width(), 2u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 3._a);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 1._a);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Swizzle_Chain) {
+ auto* expr = // (1, 2, 3) -> (2, 3, 1) -> (3, 2) -> 2
+ MemberAccessor(MemberAccessor(MemberAccessor(vec3<i32>(1_i, 2_i, 3_i), "gbr"), "yx"), "y");
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::I32>());
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->As<i32>(), 2_i);
+}
+
+TEST_F(ResolverConstEvalTest, Mat3x2_Index) {
+ auto* expr = IndexAccessor(
+ mat3x2<f32>(vec2<f32>(1._a, 2._a), vec2<f32>(3._a, 4._a), vec2<f32>(5._a, 6._a)), 2_i);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_EQ(vec->Width(), 2u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 5._a);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 6._a);
+}
+
+TEST_F(ResolverConstEvalTest, Mat3x2_Index_OOB_High) {
+ auto* expr = IndexAccessor(
+ mat3x2<f32>(vec2<f32>(1._a, 2._a), vec2<f32>(3._a, 4._a), vec2<f32>(5._a, 6._a)),
+ Expr(Source{{12, 34}}, 3_i));
+ WrapInFunction(expr);
+
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), "12:34 error: index 3 out of bounds [0..2]");
+}
+
+TEST_F(ResolverConstEvalTest, Mat3x2_Index_OOB_Low) {
+ auto* expr = IndexAccessor(
+ mat3x2<f32>(vec2<f32>(1._a, 2._a), vec2<f32>(3._a, 4._a), vec2<f32>(5._a, 6._a)),
+ Expr(Source{{12, 34}}, -3_i));
+ WrapInFunction(expr);
+
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), "12:34 error: index -3 out of bounds [0..2]");
+}
+
+TEST_F(ResolverConstEvalTest, Array_vec3_f32_Index) {
+ auto* expr = IndexAccessor(Construct(ty.array(ty.vec3<f32>(), 2_u), //
+ vec3<f32>(1_f, 2_f, 3_f), vec3<f32>(4_f, 5_f, 6_f)),
+ 1_i);
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 4_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 5_f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 6_f);
+}
+
+TEST_F(ResolverConstEvalTest, Array_vec3_f32_Index_OOB_High) {
+ auto* expr = IndexAccessor(Construct(ty.array(ty.vec3<f32>(), 2_u), //
+ vec3<f32>(1_f, 2_f, 3_f), vec3<f32>(4_f, 5_f, 6_f)),
+ Expr(Source{{12, 34}}, 2_i));
+ WrapInFunction(expr);
+
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), "12:34 error: index 2 out of bounds [0..1]");
+}
+
+TEST_F(ResolverConstEvalTest, Array_vec3_f32_Index_OOB_Low) {
+ auto* expr = IndexAccessor(Construct(ty.array(ty.vec3<f32>(), 2_u), //
+ vec3<f32>(1_f, 2_f, 3_f), vec3<f32>(4_f, 5_f, 6_f)),
+ Expr(Source{{12, 34}}, -2_i));
+ WrapInFunction(expr);
+
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), "12:34 error: index -2 out of bounds [0..1]");
+}
+
+TEST_F(ResolverConstEvalTest, RuntimeArray_vec3_f32_Index_OOB_Low) {
+ auto* sb = GlobalVar("sb", ty.array(ty.vec3<f32>()), Group(0_a), Binding(0_a),
+ ast::AddressSpace::kStorage);
+ auto* expr = IndexAccessor(sb, Expr(Source{{12, 34}}, -2_i));
+ WrapInFunction(expr);
+
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), "12:34 error: index -2 out of bounds");
+}
+
+TEST_F(ResolverConstEvalTest, ChainedIndex) {
+ auto* arr_expr = Construct(ty.array(ty.mat2x3<f32>(), 2_u), // array<mat2x3<f32>, 2u>
+ mat2x3<f32>(vec3<f32>(1_f, 2_f, 3_f), //
+ vec3<f32>(4_f, 5_f, 6_f)), //
+ mat2x3<f32>(vec3<f32>(7_f, 0_f, 9_f), //
+ vec3<f32>(10_f, 11_f, 12_f)));
+
+ auto* mat_expr = IndexAccessor(arr_expr, 1_i); // arr[1]
+ auto* vec_expr = IndexAccessor(mat_expr, 0_i); // arr[1][0]
+ auto* f32_expr = IndexAccessor(vec_expr, 2_i); // arr[1][0][2]
+ WrapInFunction(f32_expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ {
+ auto* mat = Sem().Get(mat_expr);
+ EXPECT_NE(mat, nullptr);
+ auto* ty = mat->Type()->As<sem::Matrix>();
+ ASSERT_NE(mat->Type(), nullptr);
+ EXPECT_TRUE(ty->ColumnType()->Is<sem::Vector>());
+ EXPECT_EQ(ty->columns(), 2u);
+ EXPECT_EQ(ty->rows(), 3u);
+ EXPECT_EQ(mat->ConstantValue()->Type(), mat->Type());
+ EXPECT_FALSE(mat->ConstantValue()->AllEqual());
+ EXPECT_TRUE(mat->ConstantValue()->AnyZero());
+ EXPECT_FALSE(mat->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(0)->AllEqual());
+ EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(0)->AnyZero());
+ EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(0)->AllZero());
+ EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(0)->As<f32>(), 7_f);
+
+ EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(1)->AllEqual());
+ EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(1)->AnyZero());
+ EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(1)->AllZero());
+ EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(1)->As<f32>(), 0_f);
+
+ EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(2)->AllEqual());
+ EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(2)->AnyZero());
+ EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(2)->AllZero());
+ EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(2)->As<f32>(), 9_f);
+
+ EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(0)->AllEqual());
+ EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(0)->AnyZero());
+ EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(0)->AllZero());
+ EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(0)->As<f32>(), 10_f);
+
+ EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(1)->AllEqual());
+ EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(1)->AnyZero());
+ EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(1)->AllZero());
+ EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(1)->As<f32>(), 11_f);
+
+ EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(2)->AllEqual());
+ EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(2)->AnyZero());
+ EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(2)->AllZero());
+ EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(2)->As<f32>(), 12_f);
+ }
+ {
+ auto* vec = Sem().Get(vec_expr);
+ EXPECT_NE(vec, nullptr);
+ auto* ty = vec->Type()->As<sem::Vector>();
+ ASSERT_NE(vec->Type(), nullptr);
+ EXPECT_TRUE(ty->type()->Is<sem::F32>());
+ EXPECT_EQ(ty->Width(), 3u);
+ EXPECT_EQ(vec->ConstantValue()->Type(), vec->Type());
+ EXPECT_FALSE(vec->ConstantValue()->AllEqual());
+ EXPECT_TRUE(vec->ConstantValue()->AnyZero());
+ EXPECT_FALSE(vec->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(vec->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(vec->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(vec->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(vec->ConstantValue()->Index(0)->As<f32>(), 7_f);
+
+ EXPECT_TRUE(vec->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(vec->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(vec->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(vec->ConstantValue()->Index(1)->As<f32>(), 0_f);
+
+ EXPECT_TRUE(vec->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(vec->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(vec->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(vec->ConstantValue()->Index(2)->As<f32>(), 9_f);
+ }
+ {
+ auto* f = Sem().Get(f32_expr);
+ EXPECT_NE(f, nullptr);
+ EXPECT_TRUE(f->Type()->Is<sem::F32>());
+ EXPECT_EQ(f->ConstantValue()->Type(), f->Type());
+ EXPECT_TRUE(f->ConstantValue()->AllEqual());
+ EXPECT_FALSE(f->ConstantValue()->AnyZero());
+ EXPECT_FALSE(f->ConstantValue()->AllZero());
+ EXPECT_EQ(f->ConstantValue()->As<f32>(), 9_f);
+ }
+}
+} // namespace
+} // namespace tint::resolver
diff --git a/src/tint/resolver/const_eval_member_access_test.cc b/src/tint/resolver/const_eval_member_access_test.cc
new file mode 100644
index 0000000..25d1f26
--- /dev/null
+++ b/src/tint/resolver/const_eval_member_access_test.cc
@@ -0,0 +1,98 @@
+// Copyright 2022 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/tint/resolver/const_eval_test.h"
+
+using namespace tint::number_suffixes; // NOLINT
+
+namespace tint::resolver {
+namespace {
+
+TEST_F(ResolverConstEvalTest, MemberAccess) {
+ Structure("Inner", utils::Vector{
+ Member("i1", ty.i32()),
+ Member("i2", ty.u32()),
+ Member("i3", ty.f32()),
+ });
+
+ Structure("Outer", utils::Vector{
+ Member("o1", ty.type_name("Inner")),
+ Member("o2", ty.type_name("Inner")),
+ });
+ auto* outer_expr = Construct(ty.type_name("Outer"), //
+ Construct(ty.type_name("Inner"), 1_i, 2_u, 3_f),
+ Construct(ty.type_name("Inner")));
+ auto* o1_expr = MemberAccessor(outer_expr, "o1");
+ auto* i2_expr = MemberAccessor(o1_expr, "i2");
+ WrapInFunction(i2_expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* outer = Sem().Get(outer_expr);
+ ASSERT_NE(outer, nullptr);
+ auto* str = outer->Type()->As<sem::Struct>();
+ ASSERT_NE(str, nullptr);
+ EXPECT_EQ(str->Members().size(), 2u);
+ ASSERT_NE(outer->ConstantValue(), nullptr);
+ EXPECT_TYPE(outer->ConstantValue()->Type(), outer->Type());
+ EXPECT_FALSE(outer->ConstantValue()->AllEqual());
+ EXPECT_TRUE(outer->ConstantValue()->AnyZero());
+ EXPECT_FALSE(outer->ConstantValue()->AllZero());
+
+ auto* o1 = Sem().Get(o1_expr);
+ ASSERT_NE(o1->ConstantValue(), nullptr);
+ EXPECT_FALSE(o1->ConstantValue()->AllEqual());
+ EXPECT_FALSE(o1->ConstantValue()->AnyZero());
+ EXPECT_FALSE(o1->ConstantValue()->AllZero());
+ EXPECT_TRUE(o1->ConstantValue()->Type()->Is<sem::Struct>());
+ EXPECT_EQ(o1->ConstantValue()->Index(0)->As<i32>(), 1_i);
+ EXPECT_EQ(o1->ConstantValue()->Index(1)->As<u32>(), 2_u);
+ EXPECT_EQ(o1->ConstantValue()->Index(2)->As<f32>(), 3_f);
+
+ auto* i2 = Sem().Get(i2_expr);
+ ASSERT_NE(i2->ConstantValue(), nullptr);
+ EXPECT_TRUE(i2->ConstantValue()->AllEqual());
+ EXPECT_FALSE(i2->ConstantValue()->AnyZero());
+ EXPECT_FALSE(i2->ConstantValue()->AllZero());
+ EXPECT_TRUE(i2->ConstantValue()->Type()->Is<sem::U32>());
+ EXPECT_EQ(i2->ConstantValue()->As<u32>(), 2_u);
+}
+
+TEST_F(ResolverConstEvalTest, Matrix_AFloat_Construct_From_AInt_Vectors) {
+ auto* c = Const("a", Construct(ty.mat(nullptr, 2, 2), //
+ Construct(ty.vec(nullptr, 2), Expr(1_a), Expr(2_a)),
+ Construct(ty.vec(nullptr, 2), Expr(3_a), Expr(4_a))));
+ WrapInFunction(c);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(c);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Type()->Is<sem::Matrix>());
+ auto* cv = sem->ConstantValue();
+ EXPECT_TYPE(cv->Type(), sem->Type());
+ EXPECT_TRUE(cv->Index(0)->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(cv->Index(0)->Index(0)->Type()->Is<sem::AbstractFloat>());
+ EXPECT_FALSE(cv->AllEqual());
+ EXPECT_FALSE(cv->AnyZero());
+ EXPECT_FALSE(cv->AllZero());
+ auto* c0 = cv->Index(0);
+ auto* c1 = cv->Index(1);
+ EXPECT_EQ(std::get<AFloat>(c0->Index(0)->Value()), 1.0);
+ EXPECT_EQ(std::get<AFloat>(c0->Index(1)->Value()), 2.0);
+ EXPECT_EQ(std::get<AFloat>(c1->Index(0)->Value()), 3.0);
+ EXPECT_EQ(std::get<AFloat>(c1->Index(1)->Value()), 4.0);
+}
+} // namespace
+} // namespace tint::resolver
diff --git a/src/tint/resolver/const_eval_test.cc b/src/tint/resolver/const_eval_test.cc
deleted file mode 100644
index cf3cda0..0000000
--- a/src/tint/resolver/const_eval_test.cc
+++ /dev/null
@@ -1,4602 +0,0 @@
-// 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 <cmath>
-#include <type_traits>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "src/tint/resolver/resolver_test_helper.h"
-#include "src/tint/sem/builtin_type.h"
-#include "src/tint/sem/expression.h"
-#include "src/tint/sem/index_accessor_expression.h"
-#include "src/tint/sem/member_accessor_expression.h"
-#include "src/tint/sem/test_helper.h"
-#include "src/tint/utils/transform.h"
-
-using ::testing::HasSubstr;
-
-using namespace tint::number_suffixes; // NOLINT
-
-namespace tint::resolver {
-namespace {
-
-template <typename T>
-const auto kPi = T(UnwrapNumber<T>(3.14159265358979323846));
-
-template <typename T>
-const auto kPiOver2 = T(UnwrapNumber<T>(1.57079632679489661923));
-
-template <typename T>
-const auto kPiOver4 = T(UnwrapNumber<T>(0.785398163397448309616));
-
-template <typename T>
-const auto k3PiOver4 = T(UnwrapNumber<T>(2.356194490192344928846));
-
-/// Walks the sem::Constant @p c, accumulating all the inner-most scalar values into @p args
-void CollectScalarArgs(const sem::Constant* c, builder::ScalarArgs& args) {
- Switch(
- c->Type(), //
- [&](const sem::Bool*) { args.values.Push(c->As<bool>()); },
- [&](const sem::I32*) { args.values.Push(c->As<i32>()); },
- [&](const sem::U32*) { args.values.Push(c->As<u32>()); },
- [&](const sem::F32*) { args.values.Push(c->As<f32>()); },
- [&](const sem::F16*) { args.values.Push(c->As<f16>()); },
- [&](Default) {
- size_t i = 0;
- while (auto* child = c->Index(i++)) {
- CollectScalarArgs(child, args);
- }
- });
-}
-
-/// Walks the sem::Constant @p c, returning all the inner-most scalar values.
-builder::ScalarArgs ScalarArgsFrom(const sem::Constant* c) {
- builder::ScalarArgs out;
- CollectScalarArgs(c, out);
- return out;
-}
-
-template <typename T>
-constexpr auto Negate(const Number<T>& v) {
- if constexpr (std::is_integral_v<T>) {
- if constexpr (std::is_signed_v<T>) {
- // For signed integrals, avoid C++ UB by not negating the smallest negative number. In
- // WGSL, this operation is well defined to return the same value, see:
- // https://gpuweb.github.io/gpuweb/wgsl/#arithmetic-expr.
- if (v == std::numeric_limits<T>::min()) {
- return v;
- }
- return -v;
-
- } else {
- // Allow negating unsigned values
- using ST = std::make_signed_t<T>;
- auto as_signed = Number<ST>{static_cast<ST>(v)};
- return Number<T>{static_cast<T>(Negate(as_signed))};
- }
- } else {
- // float case
- return -v;
- }
-}
-
-template <typename T>
-auto Abs(const Number<T>& v) {
- if constexpr (std::is_integral_v<T> && std::is_unsigned_v<T>) {
- return v;
- } else {
- return Number<T>(std::abs(v));
- }
-}
-
-TINT_BEGIN_DISABLE_WARNING(CONSTANT_OVERFLOW);
-template <typename T>
-constexpr Number<T> Mul(Number<T> v1, Number<T> v2) {
- if constexpr (std::is_integral_v<T> && std::is_signed_v<T>) {
- // For signed integrals, avoid C++ UB by multiplying as unsigned
- using UT = std::make_unsigned_t<T>;
- return static_cast<Number<T>>(static_cast<UT>(v1) * static_cast<UT>(v2));
- } else {
- return static_cast<Number<T>>(v1 * v2);
- }
-}
-TINT_END_DISABLE_WARNING(CONSTANT_OVERFLOW);
-
-// Concats any number of std::vectors
-template <typename Vec, typename... Vecs>
-[[nodiscard]] auto Concat(Vec&& v1, Vecs&&... vs) {
- auto total_size = v1.size() + (vs.size() + ...);
- v1.reserve(total_size);
- (std::move(vs.begin(), vs.end(), std::back_inserter(v1)), ...);
- return std::move(v1);
-}
-
-// Concats vectors `vs` into `v1`
-template <typename Vec, typename... Vecs>
-void ConcatInto(Vec& v1, Vecs&&... vs) {
- auto total_size = v1.size() + (vs.size() + ...);
- v1.reserve(total_size);
- (std::move(vs.begin(), vs.end(), std::back_inserter(v1)), ...);
-}
-
-// Concats vectors `vs` into `v1` iff `condition` is true
-template <bool condition, typename Vec, typename... Vecs>
-void ConcatIntoIf([[maybe_unused]] Vec& v1, [[maybe_unused]] Vecs&&... vs) {
- if constexpr (condition) {
- ConcatInto(v1, std::forward<Vecs>(vs)...);
- }
-}
-
-using ResolverConstEvalTest = ResolverTest;
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Construction
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-TEST_F(ResolverConstEvalTest, Scalar_i32) {
- auto* expr = Expr(99_i);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Type()->Is<sem::I32>());
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
- EXPECT_EQ(sem->ConstantValue()->As<AInt>(), 99);
-}
-
-TEST_F(ResolverConstEvalTest, Scalar_u32) {
- auto* expr = Expr(99_u);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Type()->Is<sem::U32>());
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
- EXPECT_EQ(sem->ConstantValue()->As<AInt>(), 99u);
-}
-
-TEST_F(ResolverConstEvalTest, Scalar_f32) {
- auto* expr = Expr(9.9_f);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Type()->Is<sem::F32>());
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
- EXPECT_EQ(sem->ConstantValue()->As<AFloat>().value, 9.9f);
-}
-
-TEST_F(ResolverConstEvalTest, Scalar_f16) {
- Enable(ast::Extension::kF16);
-
- auto* expr = Expr(9.9_h);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Type()->Is<sem::F16>());
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
- // 9.9 is not exactly representable by f16, and should be quantized to 9.8984375
- EXPECT_EQ(sem->ConstantValue()->As<AFloat>(), 9.8984375f);
-}
-
-TEST_F(ResolverConstEvalTest, Scalar_bool) {
- auto* expr = Expr(true);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Type()->Is<sem::Bool>());
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
- EXPECT_EQ(sem->ConstantValue()->As<bool>(), true);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_ZeroInit_i32) {
- auto* expr = vec3<i32>();
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::I32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 0);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 0);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 0);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_ZeroInit_u32) {
- auto* expr = vec3<u32>();
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::U32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 0u);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 0u);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 0u);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_ZeroInit_f32) {
- auto* expr = vec3<f32>();
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 0._a);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 0._a);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 0._a);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_ZeroInit_f16) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<f16>();
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F16>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 0._a);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 0._a);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 0._a);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_ZeroInit_bool) {
- auto* expr = vec3<bool>();
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::Bool>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<bool>(), false);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<bool>(), false);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), false);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Splat_i32) {
- auto* expr = vec3<i32>(99_i);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::I32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 99);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 99);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 99);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Splat_u32) {
- auto* expr = vec3<u32>(99_u);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::U32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 99u);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 99u);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 99u);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Splat_f32) {
- auto* expr = vec3<f32>(9.9_f);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 9.9f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 9.9f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 9.9f);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Splat_f16) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<f16>(9.9_h);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F16>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
- // 9.9 is not exactly representable by f16, and should be quantized to 9.8984375
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 9.8984375f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 9.8984375f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 9.8984375f);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Splat_bool) {
- auto* expr = vec3<bool>(true);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::Bool>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<bool>(), true);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<bool>(), true);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), true);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_FullConstruct_i32) {
- auto* expr = vec3<i32>(1_i, 2_i, 3_i);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::I32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_FullConstruct_u32) {
- auto* expr = vec3<u32>(1_u, 2_u, 3_u);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::U32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_FullConstruct_f32) {
- auto* expr = vec3<f32>(1_f, 2_f, 3_f);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 1.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 2.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 3.f);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_FullConstruct_f16) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<f16>(1_h, 2_h, 3_h);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F16>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 1.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 2.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 3.f);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_FullConstruct_bool) {
- auto* expr = vec3<bool>(true, false, true);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::Bool>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<bool>(), true);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<bool>(), false);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), true);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_i32) {
- auto* expr = vec3<i32>(1_i, vec2<i32>(2_i, 3_i));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::I32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_u32) {
- auto* expr = vec3<u32>(vec2<u32>(1_u, 2_u), 3_u);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::U32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f32) {
- auto* expr = vec3<f32>(1_f, vec2<f32>(2_f, 3_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 1.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 2.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 3.f);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f32_all_10) {
- auto* expr = vec3<f32>(10_f, vec2<f32>(10_f, 10_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 10_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 10_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 10_f);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f32_all_positive_0) {
- auto* expr = vec3<f32>(0_f, vec2<f32>(0_f, 0_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 0_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 0_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 0_f);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f32_all_negative_0) {
- auto* expr = vec3<f32>(vec2<f32>(-0_f, -0_f), -0_f);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), -0_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), -0_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), -0_f);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f32_mixed_sign_0) {
- auto* expr = vec3<f32>(0_f, vec2<f32>(-0_f, 0_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 0_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), -0_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 0_f);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f16) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<f16>(1_h, vec2<f16>(2_h, 3_h));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F16>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 1.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 2.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 3.f);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f16_all_10) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<f16>(10_h, vec2<f16>(10_h, 10_h));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F16>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f16>(), 10_h);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f16>(), 10_h);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f16>(), 10_h);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f16_all_positive_0) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<f16>(0_h, vec2<f16>(0_h, 0_h));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F16>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f16>(), 0_h);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f16>(), 0_h);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f16>(), 0_h);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f16_all_negative_0) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<f16>(vec2<f16>(-0_h, -0_h), -0_h);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F16>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f16>(), -0_h);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f16>(), -0_h);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f16>(), -0_h);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_f16_mixed_sign_0) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<f16>(0_h, vec2<f16>(-0_h, 0_h));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F16>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f16>(), 0_h);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f16>(), -0_h);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f16>(), 0_h);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_bool) {
- auto* expr = vec3<bool>(vec2<bool>(true, false), true);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::Bool>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<bool>(), true);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<bool>(), false);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), true);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_all_true) {
- auto* expr = vec3<bool>(true, vec2<bool>(true, true));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::Bool>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<bool>(), true);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<bool>(), true);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), true);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_MixConstruct_all_false) {
- auto* expr = vec3<bool>(false, vec2<bool>(false, false));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::Bool>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<bool>(), false);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<bool>(), false);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), false);
-}
-
-TEST_F(ResolverConstEvalTest, Mat2x3_ZeroInit_f32) {
- auto* expr = mat2x3<f32>();
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* mat = sem->Type()->As<sem::Matrix>();
- ASSERT_NE(mat, nullptr);
- EXPECT_TRUE(mat->type()->Is<sem::F32>());
- EXPECT_EQ(mat->columns(), 2u);
- EXPECT_EQ(mat->rows(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<f32>(), 0._f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<f32>(), 0._f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As<f32>(), 0._f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<f32>(), 0._f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<f32>(), 0._f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f32>(), 0._f);
-}
-
-TEST_F(ResolverConstEvalTest, Mat2x3_ZeroInit_f16) {
- Enable(ast::Extension::kF16);
-
- auto* expr = mat2x3<f16>();
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* mat = sem->Type()->As<sem::Matrix>();
- ASSERT_NE(mat, nullptr);
- EXPECT_TRUE(mat->type()->Is<sem::F16>());
- EXPECT_EQ(mat->columns(), 2u);
- EXPECT_EQ(mat->rows(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<f16>(), 0._h);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<f16>(), 0._h);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As<f16>(), 0._h);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<f16>(), 0._h);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<f16>(), 0._h);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f16>(), 0._h);
-}
-
-TEST_F(ResolverConstEvalTest, Mat3x2_Construct_Scalars_af) {
- auto* expr = Construct(ty.mat(nullptr, 3, 2), 1.0_a, 2.0_a, 3.0_a, 4.0_a, 5.0_a, 6.0_a);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* mat = sem->Type()->As<sem::Matrix>();
- ASSERT_NE(mat, nullptr);
- EXPECT_TRUE(mat->type()->Is<sem::F32>());
- EXPECT_EQ(mat->columns(), 3u);
- EXPECT_EQ(mat->rows(), 2u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<AFloat>(), 1._a);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<AFloat>(), 2._a);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<AFloat>(), 3._a);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<AFloat>(), 4._a);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(0)->As<AFloat>(), 5._a);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(1)->As<AFloat>(), 6._a);
-}
-
-TEST_F(ResolverConstEvalTest, Mat3x2_Construct_Columns_af) {
- auto* expr = Construct(ty.mat(nullptr, 3, 2), //
- vec(nullptr, 2u, 1.0_a, 2.0_a), //
- vec(nullptr, 2u, 3.0_a, 4.0_a), //
- vec(nullptr, 2u, 5.0_a, 6.0_a));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* mat = sem->Type()->As<sem::Matrix>();
- ASSERT_NE(mat, nullptr);
- EXPECT_TRUE(mat->type()->Is<sem::F32>());
- EXPECT_EQ(mat->columns(), 3u);
- EXPECT_EQ(mat->rows(), 2u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<AFloat>(), 1._a);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<AFloat>(), 2._a);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<AFloat>(), 3._a);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<AFloat>(), 4._a);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(0)->As<AFloat>(), 5._a);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(1)->As<AFloat>(), 6._a);
-}
-
-TEST_F(ResolverConstEvalTest, Array_i32_Zero) {
- auto* expr = Construct(ty.array<i32, 4>());
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* arr = sem->Type()->As<sem::Array>();
- ASSERT_NE(arr, nullptr);
- EXPECT_TRUE(arr->ElemType()->Is<sem::I32>());
- EXPECT_EQ(arr->Count(), sem::ConstantArrayCount{4u});
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<i32>(), 0_i);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<i32>(), 0_i);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<i32>(), 0_i);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(3)->As<i32>(), 0_i);
-}
-
-TEST_F(ResolverConstEvalTest, Array_f32_Zero) {
- auto* expr = Construct(ty.array<f32, 4>());
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* arr = sem->Type()->As<sem::Array>();
- ASSERT_NE(arr, nullptr);
- EXPECT_TRUE(arr->ElemType()->Is<sem::F32>());
- EXPECT_EQ(arr->Count(), sem::ConstantArrayCount{4u});
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 0_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 0_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 0_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(3)->As<f32>(), 0_f);
-}
-
-TEST_F(ResolverConstEvalTest, Array_vec3_f32_Zero) {
- auto* expr = Construct(ty.array(ty.vec3<f32>(), 2_u));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* arr = sem->Type()->As<sem::Array>();
- ASSERT_NE(arr, nullptr);
- EXPECT_TRUE(arr->ElemType()->Is<sem::Vector>());
- EXPECT_EQ(arr->Count(), sem::ConstantArrayCount{2u});
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<f32>(), 0_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<f32>(), 0_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As<f32>(), 0_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<f32>(), 0_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<f32>(), 0_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f32>(), 0_f);
-}
-
-TEST_F(ResolverConstEvalTest, Array_Struct_f32_Zero) {
- Structure("S", utils::Vector{
- Member("m1", ty.f32()),
- Member("m2", ty.f32()),
- });
- auto* expr = Construct(ty.array(ty.type_name("S"), 2_u));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* arr = sem->Type()->As<sem::Array>();
- ASSERT_NE(arr, nullptr);
- EXPECT_TRUE(arr->ElemType()->Is<sem::Struct>());
- EXPECT_EQ(arr->Count(), sem::ConstantArrayCount{2u});
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<f32>(), 0_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<f32>(), 0_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<f32>(), 0_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<f32>(), 0_f);
-}
-
-TEST_F(ResolverConstEvalTest, Array_i32_Elements) {
- auto* expr = Construct(ty.array<i32, 4>(), 10_i, 20_i, 30_i, 40_i);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* arr = sem->Type()->As<sem::Array>();
- ASSERT_NE(arr, nullptr);
- EXPECT_TRUE(arr->ElemType()->Is<sem::I32>());
- EXPECT_EQ(arr->Count(), sem::ConstantArrayCount{4u});
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<i32>(), 10_i);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<i32>(), 20_i);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<i32>(), 30_i);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(3)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(3)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(3)->As<i32>(), 40_i);
-}
-
-TEST_F(ResolverConstEvalTest, Array_f32_Elements) {
- auto* expr = Construct(ty.array<f32, 4>(), 10_f, 20_f, 30_f, 40_f);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* arr = sem->Type()->As<sem::Array>();
- ASSERT_NE(arr, nullptr);
- EXPECT_TRUE(arr->ElemType()->Is<sem::F32>());
- EXPECT_EQ(arr->Count(), sem::ConstantArrayCount{4u});
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 10_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 20_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 30_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(3)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(3)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(3)->As<f32>(), 40_f);
-}
-
-TEST_F(ResolverConstEvalTest, Array_vec3_f32_Elements) {
- auto* expr = Construct(ty.array(ty.vec3<f32>(), 2_u), //
- vec3<f32>(1_f, 2_f, 3_f), vec3<f32>(4_f, 5_f, 6_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* arr = sem->Type()->As<sem::Array>();
- ASSERT_NE(arr, nullptr);
- EXPECT_TRUE(arr->ElemType()->Is<sem::Vector>());
- EXPECT_EQ(arr->Count(), sem::ConstantArrayCount{2u});
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<f32>(), 1_f);
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<f32>(), 2_f);
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As<f32>(), 3_f);
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<f32>(), 4_f);
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<f32>(), 5_f);
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f32>(), 6_f);
-}
-
-TEST_F(ResolverConstEvalTest, Array_Struct_f32_Elements) {
- Structure("S", utils::Vector{
- Member("m1", ty.f32()),
- Member("m2", ty.f32()),
- });
- auto* expr = Construct(ty.array(ty.type_name("S"), 2_u), //
- Construct(ty.type_name("S"), 1_f, 2_f), //
- Construct(ty.type_name("S"), 3_f, 4_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* arr = sem->Type()->As<sem::Array>();
- ASSERT_NE(arr, nullptr);
- EXPECT_TRUE(arr->ElemType()->Is<sem::Struct>());
- EXPECT_EQ(arr->Count(), sem::ConstantArrayCount{2u});
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<f32>(), 1_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<f32>(), 2_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<f32>(), 3_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<f32>(), 4_f);
-}
-
-TEST_F(ResolverConstEvalTest, Struct_I32s_ZeroInit) {
- Structure(
- "S", utils::Vector{Member("m1", ty.i32()), Member("m2", ty.i32()), Member("m3", ty.i32())});
- auto* expr = Construct(ty.type_name("S"));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* str = sem->Type()->As<sem::Struct>();
- ASSERT_NE(str, nullptr);
- EXPECT_EQ(str->Members().size(), 3u);
- ASSERT_NE(sem->ConstantValue(), nullptr);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::I32>());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<i32>(), 0_i);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::I32>());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<i32>(), 0_i);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->Type()->Is<sem::I32>());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<i32>(), 0_i);
-}
-
-TEST_F(ResolverConstEvalTest, Struct_MixedScalars_ZeroInit) {
- Enable(ast::Extension::kF16);
-
- Structure("S", utils::Vector{
- Member("m1", ty.i32()),
- Member("m2", ty.u32()),
- Member("m3", ty.f32()),
- Member("m4", ty.f16()),
- Member("m5", ty.bool_()),
- });
- auto* expr = Construct(ty.type_name("S"));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* str = sem->Type()->As<sem::Struct>();
- ASSERT_NE(str, nullptr);
- EXPECT_EQ(str->Members().size(), 5u);
- ASSERT_NE(sem->ConstantValue(), nullptr);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::I32>());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<i32>(), 0_i);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::U32>());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<u32>(), 0_u);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->Type()->Is<sem::F32>());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 0._f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->Type()->Is<sem::F16>());
- EXPECT_EQ(sem->ConstantValue()->Index(3)->As<f16>(), 0._h);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(4)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(4)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(4)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(4)->Type()->Is<sem::Bool>());
- EXPECT_EQ(sem->ConstantValue()->Index(4)->As<bool>(), false);
-}
-
-TEST_F(ResolverConstEvalTest, Struct_VectorF32s_ZeroInit) {
- Structure("S", utils::Vector{
- Member("m1", ty.vec3<f32>()),
- Member("m2", ty.vec3<f32>()),
- Member("m3", ty.vec3<f32>()),
- });
- auto* expr = Construct(ty.type_name("S"));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* str = sem->Type()->As<sem::Struct>();
- ASSERT_NE(str, nullptr);
- EXPECT_EQ(str->Members().size(), 3u);
- ASSERT_NE(sem->ConstantValue(), nullptr);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<f32>(), 0._f);
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<f32>(), 0._f);
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As<f32>(), 0._f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<f32>(), 0._f);
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<f32>(), 0._f);
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f32>(), 0._f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(0)->As<f32>(), 0._f);
- EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(1)->As<f32>(), 0._f);
- EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(2)->As<f32>(), 0._f);
-}
-
-TEST_F(ResolverConstEvalTest, Struct_MixedVectors_ZeroInit) {
- Enable(ast::Extension::kF16);
-
- Structure("S", utils::Vector{
- Member("m1", ty.vec2<i32>()),
- Member("m2", ty.vec3<u32>()),
- Member("m3", ty.vec4<f32>()),
- Member("m4", ty.vec3<f16>()),
- Member("m5", ty.vec2<bool>()),
- });
- auto* expr = Construct(ty.type_name("S"));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* str = sem->Type()->As<sem::Struct>();
- ASSERT_NE(str, nullptr);
- EXPECT_EQ(str->Members().size(), 5u);
- ASSERT_NE(sem->ConstantValue(), nullptr);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<i32>(), 0_i);
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<i32>(), 0_i);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<u32>(), 0_u);
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<u32>(), 0_u);
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<u32>(), 0_u);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(0)->As<f32>(), 0._f);
- EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(1)->As<f32>(), 0._f);
- EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(2)->As<f32>(), 0._f);
- EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(3)->As<f32>(), 0._f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->Type()->As<sem::Vector>()->type()->Is<sem::F16>());
- EXPECT_EQ(sem->ConstantValue()->Index(3)->Index(0)->As<f16>(), 0._h);
- EXPECT_EQ(sem->ConstantValue()->Index(3)->Index(1)->As<f16>(), 0._h);
- EXPECT_EQ(sem->ConstantValue()->Index(3)->Index(2)->As<f16>(), 0._h);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(4)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(4)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(4)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(4)->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->ConstantValue()->Index(4)->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
- EXPECT_EQ(sem->ConstantValue()->Index(4)->Index(0)->As<bool>(), false);
- EXPECT_EQ(sem->ConstantValue()->Index(4)->Index(1)->As<bool>(), false);
-}
-
-TEST_F(ResolverConstEvalTest, Struct_Struct_ZeroInit) {
- Structure("Inner", utils::Vector{
- Member("m1", ty.i32()),
- Member("m2", ty.u32()),
- Member("m3", ty.f32()),
- });
-
- Structure("Outer", utils::Vector{
- Member("m1", ty.type_name("Inner")),
- Member("m2", ty.type_name("Inner")),
- });
- auto* expr = Construct(ty.type_name("Outer"));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* str = sem->Type()->As<sem::Struct>();
- ASSERT_NE(str, nullptr);
- EXPECT_EQ(str->Members().size(), 2u);
- ASSERT_NE(sem->ConstantValue(), nullptr);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->AllZero());
-
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::Struct>());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<i32>(), 0_i);
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<u32>(), 0_u);
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As<f32>(), 0_f);
-
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::Struct>());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<i32>(), 0_i);
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<u32>(), 0_u);
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f32>(), 0_f);
-}
-
-TEST_F(ResolverConstEvalTest, Struct_MixedScalars_Construct) {
- Enable(ast::Extension::kF16);
-
- Structure("S", utils::Vector{
- Member("m1", ty.i32()),
- Member("m2", ty.u32()),
- Member("m3", ty.f32()),
- Member("m4", ty.f16()),
- Member("m5", ty.bool_()),
- });
- auto* expr = Construct(ty.type_name("S"), 1_i, 2_u, 3_f, 4_h, false);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* str = sem->Type()->As<sem::Struct>();
- ASSERT_NE(str, nullptr);
- EXPECT_EQ(str->Members().size(), 5u);
- ASSERT_NE(sem->ConstantValue(), nullptr);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::I32>());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<i32>(), 1_i);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::U32>());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<u32>(), 2_u);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->Type()->Is<sem::F32>());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 3._f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(3)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(3)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->Type()->Is<sem::F16>());
- EXPECT_EQ(sem->ConstantValue()->Index(3)->As<f16>(), 4._h);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(4)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(4)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(4)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(4)->Type()->Is<sem::Bool>());
- EXPECT_EQ(sem->ConstantValue()->Index(4)->As<bool>(), false);
-}
-
-TEST_F(ResolverConstEvalTest, Struct_MixedVectors_Construct) {
- Enable(ast::Extension::kF16);
-
- Structure("S", utils::Vector{
- Member("m1", ty.vec2<i32>()),
- Member("m2", ty.vec3<u32>()),
- Member("m3", ty.vec4<f32>()),
- Member("m4", ty.vec3<f16>()),
- Member("m5", ty.vec2<bool>()),
- });
- auto* expr = Construct(ty.type_name("S"), vec2<i32>(1_i), vec3<u32>(2_u), vec4<f32>(3_f),
- vec3<f16>(4_h), vec2<bool>(false));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* str = sem->Type()->As<sem::Struct>();
- ASSERT_NE(str, nullptr);
- EXPECT_EQ(str->Members().size(), 5u);
- ASSERT_NE(sem->ConstantValue(), nullptr);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<i32>(), 1_i);
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<i32>(), 1_i);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<u32>(), 2_u);
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<u32>(), 2_u);
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<u32>(), 2_u);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(0)->As<f32>(), 3._f);
- EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(1)->As<f32>(), 3._f);
- EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(2)->As<f32>(), 3._f);
- EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(3)->As<f32>(), 3._f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(3)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(3)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->ConstantValue()->Index(3)->Type()->As<sem::Vector>()->type()->Is<sem::F16>());
- EXPECT_EQ(sem->ConstantValue()->Index(3)->Index(0)->As<f16>(), 4._h);
- EXPECT_EQ(sem->ConstantValue()->Index(3)->Index(1)->As<f16>(), 4._h);
- EXPECT_EQ(sem->ConstantValue()->Index(3)->Index(2)->As<f16>(), 4._h);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(4)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(4)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(4)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(4)->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->ConstantValue()->Index(4)->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
- EXPECT_EQ(sem->ConstantValue()->Index(4)->Index(0)->As<bool>(), false);
- EXPECT_EQ(sem->ConstantValue()->Index(4)->Index(1)->As<bool>(), false);
-}
-
-TEST_F(ResolverConstEvalTest, Struct_Struct_Construct) {
- Structure("Inner", utils::Vector{
- Member("m1", ty.i32()),
- Member("m2", ty.u32()),
- Member("m3", ty.f32()),
- });
-
- Structure("Outer", utils::Vector{
- Member("m1", ty.type_name("Inner")),
- Member("m2", ty.type_name("Inner")),
- });
- auto* expr = Construct(ty.type_name("Outer"), //
- Construct(ty.type_name("Inner"), 1_i, 2_u, 3_f),
- Construct(ty.type_name("Inner"), 4_i, 0_u, 6_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* str = sem->Type()->As<sem::Struct>();
- ASSERT_NE(str, nullptr);
- EXPECT_EQ(str->Members().size(), 2u);
- ASSERT_NE(sem->ConstantValue(), nullptr);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::Struct>());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<i32>(), 1_i);
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<u32>(), 2_u);
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As<f32>(), 3_f);
-
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::Struct>());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<i32>(), 4_i);
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<u32>(), 0_u);
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f32>(), 6_f);
-}
-
-TEST_F(ResolverConstEvalTest, Struct_Array_Construct) {
- Structure("S", utils::Vector{
- Member("m1", ty.array<i32, 2>()),
- Member("m2", ty.array<f32, 3>()),
- });
- auto* expr = Construct(ty.type_name("S"), //
- Construct(ty.array<i32, 2>(), 1_i, 2_i),
- Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* str = sem->Type()->As<sem::Struct>();
- ASSERT_NE(str, nullptr);
- EXPECT_EQ(str->Members().size(), 2u);
- ASSERT_NE(sem->ConstantValue(), nullptr);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::Array>());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<i32>(), 1_i);
- EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<u32>(), 2_i);
-
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::Array>());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<i32>(), 1_f);
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<u32>(), 2_f);
- EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f32>(), 3_f);
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Conversion
-////////////////////////////////////////////////////////////////////////////////////////////////////
-namespace conv {
-
-using Scalar = std::variant< //
- builder::Value<AInt>,
- builder::Value<AFloat>,
- builder::Value<u32>,
- builder::Value<i32>,
- builder::Value<f32>,
- builder::Value<f16>,
- builder::Value<bool>>;
-
-static std::ostream& operator<<(std::ostream& o, const Scalar& scalar) {
- std::visit(
- [&](auto&& v) {
- using ValueType = std::decay_t<decltype(v)>;
- o << ValueType::DataType::Name() << "(";
- for (auto& a : v.args.values) {
- o << std::get<typename ValueType::ElementType>(a);
- if (&a != &v.args.values.Back()) {
- o << ", ";
- }
- }
- o << ")";
- },
- scalar);
- return o;
-}
-
-enum class Kind {
- kScalar,
- kVector,
-};
-
-static std::ostream& operator<<(std::ostream& o, const Kind& k) {
- switch (k) {
- case Kind::kScalar:
- return o << "scalar";
- case Kind::kVector:
- return o << "vector";
- }
- return o << "<unknown>";
-}
-
-struct Case {
- Scalar input;
- Scalar expected;
- builder::CreatePtrs type;
- bool unrepresentable = false;
-};
-
-static std::ostream& operator<<(std::ostream& o, const Case& c) {
- if (c.unrepresentable) {
- o << "[unrepresentable] input: " << c.input;
- } else {
- o << "input: " << c.input << ", expected: " << c.expected;
- }
- return o << ", type: " << c.type;
-}
-
-template <typename TO, typename FROM>
-Case Success(FROM input, TO expected) {
- return {builder::Val(input), builder::Val(expected), builder::CreatePtrsFor<TO>()};
-}
-
-template <typename TO, typename FROM>
-Case Unrepresentable(FROM input) {
- return {builder::Val(input), builder::Val(0_i), builder::CreatePtrsFor<TO>(),
- /* unrepresentable */ true};
-}
-
-using ResolverConstEvalConvTest = ResolverTestWithParam<std::tuple<Kind, Case>>;
-
-TEST_P(ResolverConstEvalConvTest, Test) {
- const auto& kind = std::get<0>(GetParam());
- const auto& input = std::get<1>(GetParam()).input;
- const auto& expected = std::get<1>(GetParam()).expected;
- const auto& type = std::get<1>(GetParam()).type;
- const auto unrepresentable = std::get<1>(GetParam()).unrepresentable;
-
- auto* input_val = std::visit([&](auto val) { return val.Expr(*this); }, input);
- auto* expr = Construct(type.ast(*this), input_val);
- if (kind == Kind::kVector) {
- expr = Construct(ty.vec(nullptr, 3), expr);
- }
- WrapInFunction(expr);
-
- auto* target_sem_ty = type.sem(*this);
- if (kind == Kind::kVector) {
- target_sem_ty = create<sem::Vector>(target_sem_ty, 3u);
- }
-
- if (unrepresentable) {
- ASSERT_FALSE(r()->Resolve());
- EXPECT_THAT(r()->error(), testing::HasSubstr("cannot be represented as"));
- } else {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TYPE(sem->Type(), target_sem_ty);
- ASSERT_NE(sem->ConstantValue(), nullptr);
- EXPECT_TYPE(sem->ConstantValue()->Type(), target_sem_ty);
-
- auto expected_values = std::visit([&](auto&& val) { return val.args; }, expected);
- if (kind == Kind::kVector) {
- expected_values.values.Push(expected_values.values[0]);
- expected_values.values.Push(expected_values.values[0]);
- }
- auto got_values = ScalarArgsFrom(sem->ConstantValue());
- EXPECT_EQ(expected_values, got_values);
- }
-}
-INSTANTIATE_TEST_SUITE_P(ScalarAndVector,
- ResolverConstEvalConvTest,
- testing::Combine(testing::Values(Kind::kScalar, Kind::kVector),
- testing::ValuesIn({
- // TODO(crbug.com/tint/1502): Add f16 tests
- // i32 -> u32
- Success(0_i, 0_u),
- Success(1_i, 1_u),
- Success(-1_i, 0xffffffff_u),
- Success(2_i, 2_u),
- Success(-2_i, 0xfffffffe_u),
- // i32 -> f32
- Success(0_i, 0_f),
- Success(1_i, 1_f),
- Success(-1_i, -1_f),
- Success(2_i, 2_f),
- Success(-2_i, -2_f),
- // i32 -> bool
- Success(0_i, false),
- Success(1_i, true),
- Success(-1_i, true),
- Success(2_i, true),
- Success(-2_i, true),
- // u32 -> i32
- Success(0_u, 0_i),
- Success(1_u, 1_i),
- Success(0xffffffff_u, -1_i),
- Success(2_u, 2_i),
- Success(0xfffffffe_u, -2_i),
- // u32 -> f32
- Success(0_u, 0_f),
- Success(1_u, 1_f),
- Success(2_u, 2_f),
- Success(0xffffffff_u, 0xffffffff_f),
- // u32 -> bool
- Success(0_u, false),
- Success(1_u, true),
- Success(2_u, true),
- Success(0xffffffff_u, true),
- // f32 -> i32
- Success(0_f, 0_i),
- Success(1_f, 1_i),
- Success(2_f, 2_i),
- Success(1e20_f, i32::Highest()),
- Success(-1e20_f, i32::Lowest()),
- // f32 -> u32
- Success(0_f, 0_i),
- Success(1_f, 1_i),
- Success(-1_f, u32::Lowest()),
- Success(2_f, 2_i),
- Success(1e20_f, u32::Highest()),
- Success(-1e20_f, u32::Lowest()),
- // f32 -> bool
- Success(0_f, false),
- Success(1_f, true),
- Success(-1_f, true),
- Success(2_f, true),
- Success(1e20_f, true),
- Success(-1e20_f, true),
- // abstract-int -> i32
- Success(0_a, 0_i),
- Success(1_a, 1_i),
- Success(-1_a, -1_i),
- Success(0x7fffffff_a, i32::Highest()),
- Success(-0x80000000_a, i32::Lowest()),
- Unrepresentable<i32>(0x80000000_a),
- // abstract-int -> u32
- Success(0_a, 0_u),
- Success(1_a, 1_u),
- Success(0xffffffff_a, 0xffffffff_u),
- Unrepresentable<u32>(0x100000000_a),
- Unrepresentable<u32>(-1_a),
- // abstract-int -> f32
- Success(0_a, 0_f),
- Success(1_a, 1_f),
- Success(0xffffffff_a, 0xffffffff_f),
- Success(0x100000000_a, 0x100000000_f),
- Success(-0x100000000_a, -0x100000000_f),
- Success(0x7fffffffffffffff_a, 0x7fffffffffffffff_f),
- Success(-0x7fffffffffffffff_a, -0x7fffffffffffffff_f),
- // abstract-int -> bool
- Success(0_a, false),
- Success(1_a, true),
- Success(0xffffffff_a, true),
- Success(0x100000000_a, true),
- Success(-0x100000000_a, true),
- Success(0x7fffffffffffffff_a, true),
- Success(-0x7fffffffffffffff_a, true),
- // abstract-float -> i32
- Success(0.0_a, 0_i),
- Success(1.0_a, 1_i),
- Success(-1.0_a, -1_i),
- Success(AFloat(0x7fffffff), i32::Highest()),
- Success(-AFloat(0x80000000), i32::Lowest()),
- Unrepresentable<i32>(0x80000000_a),
- // abstract-float -> u32
- Success(0.0_a, 0_u),
- Success(1.0_a, 1_u),
- Success(AFloat(0xffffffff), 0xffffffff_u),
- Unrepresentable<u32>(AFloat(0x100000000)),
- Unrepresentable<u32>(AFloat(-1)),
- // abstract-float -> f32
- Success(0.0_a, 0_f),
- Success(1.0_a, 1_f),
- Success(AFloat(0xffffffff), 0xffffffff_f),
- Success(AFloat(0x100000000), 0x100000000_f),
- Success(-AFloat(0x100000000), -0x100000000_f),
- Unrepresentable<f32>(1e40_a),
- Unrepresentable<f32>(-1e40_a),
- // abstract-float -> bool
- Success(0.0_a, false),
- Success(1.0_a, true),
- Success(AFloat(0xffffffff), true),
- Success(AFloat(0x100000000), true),
- Success(-AFloat(0x100000000), true),
- Success(1e40_a, true),
- Success(-1e40_a, true),
- })));
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_f32_to_i32) {
- auto* expr = vec3<i32>(vec3<f32>(1.1_f, 2.2_f, 3.3_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::I32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_u32_to_f32) {
- auto* expr = vec3<f32>(vec3<u32>(10_u, 20_u, 30_u));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 10.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 20.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 30.f);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_f16_to_i32) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<i32>(vec3<f16>(1.1_h, 2.2_h, 3.3_h));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::I32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1_i);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2_i);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3_i);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_u32_to_f16) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<f16>(vec3<u32>(10_u, 20_u, 30_u));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F16>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 10.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 20.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 30.f);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_i32) {
- auto* expr = vec3<i32>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::I32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), i32::Highest());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), i32::Lowest());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), i32::Highest());
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_u32) {
- auto* expr = vec3<u32>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::U32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), u32::Highest());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), u32::Lowest());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), u32::Highest());
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_f16) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<f16>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- constexpr auto kInfinity = std::numeric_limits<double>::infinity();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F16>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), kInfinity);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), -kInfinity);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), kInfinity);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_Small_f32_to_f16) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<f16>(vec3<f32>(1e-20_f, -2e-30_f, 3e-40_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F16>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 0.0);
- EXPECT_FALSE(std::signbit(sem->ConstantValue()->Index(0)->As<AFloat>().value));
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), -0.0);
- EXPECT_TRUE(std::signbit(sem->ConstantValue()->Index(1)->As<AFloat>().value));
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 0.0);
- EXPECT_FALSE(std::signbit(sem->ConstantValue()->Index(2)->As<AFloat>().value));
-}
-
-} // namespace conv
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Indexing
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-TEST_F(ResolverConstEvalTest, Vec3_Index) {
- auto* expr = IndexAccessor(vec3<i32>(1_i, 2_i, 3_i), 2_i);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::I32>());
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
- EXPECT_EQ(sem->ConstantValue()->As<i32>(), 3_i);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Index_OOB_High) {
- auto* expr = IndexAccessor(vec3<i32>(1_i, 2_i, 3_i), Expr(Source{{12, 34}}, 3_i));
- WrapInFunction(expr);
-
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(), "12:34 error: index 3 out of bounds [0..2]");
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Index_OOB_Low) {
- auto* expr = IndexAccessor(vec3<i32>(1_i, 2_i, 3_i), Expr(Source{{12, 34}}, -3_i));
- WrapInFunction(expr);
-
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(), "12:34 error: index -3 out of bounds [0..2]");
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Swizzle_Scalar) {
- auto* expr = MemberAccessor(vec3<i32>(1_i, 2_i, 3_i), "y");
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::I32>());
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
- EXPECT_EQ(sem->ConstantValue()->As<i32>(), 2_i);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Swizzle_Vector) {
- auto* expr = MemberAccessor(vec3<i32>(1_i, 2_i, 3_i), "zx");
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_EQ(vec->Width(), 2u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 3._a);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 1._a);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Swizzle_Chain) {
- auto* expr = // (1, 2, 3) -> (2, 3, 1) -> (3, 2) -> 2
- MemberAccessor(MemberAccessor(MemberAccessor(vec3<i32>(1_i, 2_i, 3_i), "gbr"), "yx"), "y");
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::I32>());
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
- EXPECT_EQ(sem->ConstantValue()->As<i32>(), 2_i);
-}
-
-TEST_F(ResolverConstEvalTest, Mat3x2_Index) {
- auto* expr = IndexAccessor(
- mat3x2<f32>(vec2<f32>(1._a, 2._a), vec2<f32>(3._a, 4._a), vec2<f32>(5._a, 6._a)), 2_i);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_EQ(vec->Width(), 2u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 5._a);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 6._a);
-}
-
-TEST_F(ResolverConstEvalTest, Mat3x2_Index_OOB_High) {
- auto* expr = IndexAccessor(
- mat3x2<f32>(vec2<f32>(1._a, 2._a), vec2<f32>(3._a, 4._a), vec2<f32>(5._a, 6._a)),
- Expr(Source{{12, 34}}, 3_i));
- WrapInFunction(expr);
-
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(), "12:34 error: index 3 out of bounds [0..2]");
-}
-
-TEST_F(ResolverConstEvalTest, Mat3x2_Index_OOB_Low) {
- auto* expr = IndexAccessor(
- mat3x2<f32>(vec2<f32>(1._a, 2._a), vec2<f32>(3._a, 4._a), vec2<f32>(5._a, 6._a)),
- Expr(Source{{12, 34}}, -3_i));
- WrapInFunction(expr);
-
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(), "12:34 error: index -3 out of bounds [0..2]");
-}
-
-TEST_F(ResolverConstEvalTest, Array_vec3_f32_Index) {
- auto* expr = IndexAccessor(Construct(ty.array(ty.vec3<f32>(), 2_u), //
- vec3<f32>(1_f, 2_f, 3_f), vec3<f32>(4_f, 5_f, 6_f)),
- 1_i);
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 4_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 5_f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 6_f);
-}
-
-TEST_F(ResolverConstEvalTest, Array_vec3_f32_Index_OOB_High) {
- auto* expr = IndexAccessor(Construct(ty.array(ty.vec3<f32>(), 2_u), //
- vec3<f32>(1_f, 2_f, 3_f), vec3<f32>(4_f, 5_f, 6_f)),
- Expr(Source{{12, 34}}, 2_i));
- WrapInFunction(expr);
-
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(), "12:34 error: index 2 out of bounds [0..1]");
-}
-
-TEST_F(ResolverConstEvalTest, Array_vec3_f32_Index_OOB_Low) {
- auto* expr = IndexAccessor(Construct(ty.array(ty.vec3<f32>(), 2_u), //
- vec3<f32>(1_f, 2_f, 3_f), vec3<f32>(4_f, 5_f, 6_f)),
- Expr(Source{{12, 34}}, -2_i));
- WrapInFunction(expr);
-
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(), "12:34 error: index -2 out of bounds [0..1]");
-}
-
-TEST_F(ResolverConstEvalTest, RuntimeArray_vec3_f32_Index_OOB_Low) {
- auto* sb = GlobalVar("sb", ty.array(ty.vec3<f32>()), Group(0_a), Binding(0_a),
- ast::AddressSpace::kStorage);
- auto* expr = IndexAccessor(sb, Expr(Source{{12, 34}}, -2_i));
- WrapInFunction(expr);
-
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(), "12:34 error: index -2 out of bounds");
-}
-
-TEST_F(ResolverConstEvalTest, ChainedIndex) {
- auto* arr_expr = Construct(ty.array(ty.mat2x3<f32>(), 2_u), // array<mat2x3<f32>, 2u>
- mat2x3<f32>(vec3<f32>(1_f, 2_f, 3_f), //
- vec3<f32>(4_f, 5_f, 6_f)), //
- mat2x3<f32>(vec3<f32>(7_f, 0_f, 9_f), //
- vec3<f32>(10_f, 11_f, 12_f)));
-
- auto* mat_expr = IndexAccessor(arr_expr, 1_i); // arr[1]
- auto* vec_expr = IndexAccessor(mat_expr, 0_i); // arr[1][0]
- auto* f32_expr = IndexAccessor(vec_expr, 2_i); // arr[1][0][2]
- WrapInFunction(f32_expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- {
- auto* mat = Sem().Get(mat_expr);
- EXPECT_NE(mat, nullptr);
- auto* ty = mat->Type()->As<sem::Matrix>();
- ASSERT_NE(mat->Type(), nullptr);
- EXPECT_TRUE(ty->ColumnType()->Is<sem::Vector>());
- EXPECT_EQ(ty->columns(), 2u);
- EXPECT_EQ(ty->rows(), 3u);
- EXPECT_EQ(mat->ConstantValue()->Type(), mat->Type());
- EXPECT_FALSE(mat->ConstantValue()->AllEqual());
- EXPECT_TRUE(mat->ConstantValue()->AnyZero());
- EXPECT_FALSE(mat->ConstantValue()->AllZero());
-
- EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(0)->AllEqual());
- EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(0)->AnyZero());
- EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(0)->AllZero());
- EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(0)->As<f32>(), 7_f);
-
- EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(1)->AllEqual());
- EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(1)->AnyZero());
- EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(1)->AllZero());
- EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(1)->As<f32>(), 0_f);
-
- EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(2)->AllEqual());
- EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(2)->AnyZero());
- EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(2)->AllZero());
- EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(2)->As<f32>(), 9_f);
-
- EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(0)->AllEqual());
- EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(0)->AnyZero());
- EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(0)->AllZero());
- EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(0)->As<f32>(), 10_f);
-
- EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(1)->AllEqual());
- EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(1)->AnyZero());
- EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(1)->AllZero());
- EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(1)->As<f32>(), 11_f);
-
- EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(2)->AllEqual());
- EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(2)->AnyZero());
- EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(2)->AllZero());
- EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(2)->As<f32>(), 12_f);
- }
- {
- auto* vec = Sem().Get(vec_expr);
- EXPECT_NE(vec, nullptr);
- auto* ty = vec->Type()->As<sem::Vector>();
- ASSERT_NE(vec->Type(), nullptr);
- EXPECT_TRUE(ty->type()->Is<sem::F32>());
- EXPECT_EQ(ty->Width(), 3u);
- EXPECT_EQ(vec->ConstantValue()->Type(), vec->Type());
- EXPECT_FALSE(vec->ConstantValue()->AllEqual());
- EXPECT_TRUE(vec->ConstantValue()->AnyZero());
- EXPECT_FALSE(vec->ConstantValue()->AllZero());
-
- EXPECT_TRUE(vec->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(vec->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(vec->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(vec->ConstantValue()->Index(0)->As<f32>(), 7_f);
-
- EXPECT_TRUE(vec->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(vec->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(vec->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(vec->ConstantValue()->Index(1)->As<f32>(), 0_f);
-
- EXPECT_TRUE(vec->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(vec->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(vec->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(vec->ConstantValue()->Index(2)->As<f32>(), 9_f);
- }
- {
- auto* f = Sem().Get(f32_expr);
- EXPECT_NE(f, nullptr);
- EXPECT_TRUE(f->Type()->Is<sem::F32>());
- EXPECT_EQ(f->ConstantValue()->Type(), f->Type());
- EXPECT_TRUE(f->ConstantValue()->AllEqual());
- EXPECT_FALSE(f->ConstantValue()->AnyZero());
- EXPECT_FALSE(f->ConstantValue()->AllZero());
- EXPECT_EQ(f->ConstantValue()->As<f32>(), 9_f);
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Member accessing
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-TEST_F(ResolverConstEvalTest, MemberAccess) {
- Structure("Inner", utils::Vector{
- Member("i1", ty.i32()),
- Member("i2", ty.u32()),
- Member("i3", ty.f32()),
- });
-
- Structure("Outer", utils::Vector{
- Member("o1", ty.type_name("Inner")),
- Member("o2", ty.type_name("Inner")),
- });
- auto* outer_expr = Construct(ty.type_name("Outer"), //
- Construct(ty.type_name("Inner"), 1_i, 2_u, 3_f),
- Construct(ty.type_name("Inner")));
- auto* o1_expr = MemberAccessor(outer_expr, "o1");
- auto* i2_expr = MemberAccessor(o1_expr, "i2");
- WrapInFunction(i2_expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* outer = Sem().Get(outer_expr);
- ASSERT_NE(outer, nullptr);
- auto* str = outer->Type()->As<sem::Struct>();
- ASSERT_NE(str, nullptr);
- EXPECT_EQ(str->Members().size(), 2u);
- ASSERT_NE(outer->ConstantValue(), nullptr);
- EXPECT_TYPE(outer->ConstantValue()->Type(), outer->Type());
- EXPECT_FALSE(outer->ConstantValue()->AllEqual());
- EXPECT_TRUE(outer->ConstantValue()->AnyZero());
- EXPECT_FALSE(outer->ConstantValue()->AllZero());
-
- auto* o1 = Sem().Get(o1_expr);
- ASSERT_NE(o1->ConstantValue(), nullptr);
- EXPECT_FALSE(o1->ConstantValue()->AllEqual());
- EXPECT_FALSE(o1->ConstantValue()->AnyZero());
- EXPECT_FALSE(o1->ConstantValue()->AllZero());
- EXPECT_TRUE(o1->ConstantValue()->Type()->Is<sem::Struct>());
- EXPECT_EQ(o1->ConstantValue()->Index(0)->As<i32>(), 1_i);
- EXPECT_EQ(o1->ConstantValue()->Index(1)->As<u32>(), 2_u);
- EXPECT_EQ(o1->ConstantValue()->Index(2)->As<f32>(), 3_f);
-
- auto* i2 = Sem().Get(i2_expr);
- ASSERT_NE(i2->ConstantValue(), nullptr);
- EXPECT_TRUE(i2->ConstantValue()->AllEqual());
- EXPECT_FALSE(i2->ConstantValue()->AnyZero());
- EXPECT_FALSE(i2->ConstantValue()->AllZero());
- EXPECT_TRUE(i2->ConstantValue()->Type()->Is<sem::U32>());
- EXPECT_EQ(i2->ConstantValue()->As<u32>(), 2_u);
-}
-
-TEST_F(ResolverConstEvalTest, Matrix_AFloat_Construct_From_AInt_Vectors) {
- auto* c = Const("a", Construct(ty.mat(nullptr, 2, 2), //
- Construct(ty.vec(nullptr, 2), Expr(1_a), Expr(2_a)),
- Construct(ty.vec(nullptr, 2), Expr(3_a), Expr(4_a))));
- WrapInFunction(c);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(c);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Type()->Is<sem::Matrix>());
- auto* cv = sem->ConstantValue();
- EXPECT_TYPE(cv->Type(), sem->Type());
- EXPECT_TRUE(cv->Index(0)->Type()->Is<sem::Vector>());
- EXPECT_TRUE(cv->Index(0)->Index(0)->Type()->Is<sem::AbstractFloat>());
- EXPECT_FALSE(cv->AllEqual());
- EXPECT_FALSE(cv->AnyZero());
- EXPECT_FALSE(cv->AllZero());
- auto* c0 = cv->Index(0);
- auto* c1 = cv->Index(1);
- EXPECT_EQ(std::get<AFloat>(c0->Index(0)->Value()), 1.0);
- EXPECT_EQ(std::get<AFloat>(c0->Index(1)->Value()), 2.0);
- EXPECT_EQ(std::get<AFloat>(c1->Index(0)->Value()), 3.0);
- EXPECT_EQ(std::get<AFloat>(c1->Index(1)->Value()), 4.0);
-}
-
-using builder::IsValue;
-using builder::Mat;
-using builder::Val;
-using builder::Value;
-using builder::Vec;
-
-using Types = std::variant< //
- Value<AInt>,
- Value<AFloat>,
- Value<u32>,
- Value<i32>,
- Value<f32>,
- Value<f16>,
- Value<bool>,
-
- Value<builder::vec2<AInt>>,
- Value<builder::vec2<AFloat>>,
- Value<builder::vec2<u32>>,
- Value<builder::vec2<i32>>,
- Value<builder::vec2<f32>>,
- Value<builder::vec2<f16>>,
- Value<builder::vec2<bool>>,
-
- Value<builder::vec3<AInt>>,
- Value<builder::vec3<AFloat>>,
- Value<builder::vec3<u32>>,
- Value<builder::vec3<i32>>,
- Value<builder::vec3<f32>>,
- Value<builder::vec3<f16>>,
- Value<builder::vec3<bool>>,
-
- Value<builder::vec4<AInt>>,
- Value<builder::vec4<AFloat>>,
- Value<builder::vec4<u32>>,
- Value<builder::vec4<i32>>,
- Value<builder::vec4<f32>>,
- Value<builder::vec4<f16>>,
- Value<builder::vec4<bool>>,
-
- Value<builder::mat2x2<AInt>>,
- Value<builder::mat2x2<AFloat>>,
- Value<builder::mat2x2<f32>>,
- Value<builder::mat2x2<f16>>,
-
- Value<builder::mat2x3<AInt>>,
- Value<builder::mat2x3<AFloat>>,
- Value<builder::mat2x3<f32>>,
- Value<builder::mat2x3<f16>>,
-
- Value<builder::mat3x2<AInt>>,
- Value<builder::mat3x2<AFloat>>,
- Value<builder::mat3x2<f32>>,
- Value<builder::mat3x2<f16>>
- //
- >;
-
-std::ostream& operator<<(std::ostream& o, const Types& types) {
- std::visit(
- [&](auto&& v) {
- using ValueType = std::decay_t<decltype(v)>;
- o << ValueType::DataType::Name() << "(";
- for (auto& a : v.args.values) {
- o << std::get<typename ValueType::ElementType>(a);
- if (&a != &v.args.values.Back()) {
- o << ", ";
- }
- }
- o << ")";
- },
- types);
- return o;
-}
-
-// Calls `f` on deepest elements of both `a` and `b`. If function returns Action::kStop, it stops
-// traversing, and return Action::kStop; if the function returns Action::kContinue, it continues and
-// returns Action::kContinue when done.
-// TODO(amaiorano): Move to Constant.h?
-enum class Action { kStop, kContinue };
-template <typename Func>
-Action ForEachElemPair(const sem::Constant* a, const sem::Constant* b, Func&& f) {
- EXPECT_EQ(a->Type(), b->Type());
- size_t i = 0;
- while (true) {
- auto* a_elem = a->Index(i);
- if (!a_elem) {
- break;
- }
- auto* b_elem = b->Index(i);
- if (ForEachElemPair(a_elem, b_elem, f) == Action::kStop) {
- return Action::kStop;
- }
- i++;
- }
- if (i == 0) {
- return f(a, b);
- }
- return Action::kContinue;
-}
-
-template <typename NumberT>
-struct BitValues {
- using T = UnwrapNumber<NumberT>;
- struct detail {
- using UT = std::make_unsigned_t<T>;
- static constexpr size_t NumBits = sizeof(T) * 8;
- static constexpr T All = T{~T{0}};
- static constexpr T LeftMost = static_cast<T>(UT{1} << (NumBits - 1u));
- static constexpr T AllButLeftMost = T{~LeftMost};
- static constexpr T TwoLeftMost = static_cast<T>(UT{0b11} << (NumBits - 2u));
- static constexpr T AllButTwoLeftMost = T{~TwoLeftMost};
- static constexpr T RightMost = T{1};
- static constexpr T AllButRightMost = T{~RightMost};
- };
-
- static inline const size_t NumBits = detail::NumBits;
- static inline const NumberT All = NumberT{detail::All};
- static inline const NumberT LeftMost = NumberT{detail::LeftMost};
- static inline const NumberT AllButLeftMost = NumberT{detail::AllButLeftMost};
- static inline const NumberT TwoLeftMost = NumberT{detail::TwoLeftMost};
- static inline const NumberT AllButTwoLeftMost = NumberT{detail::AllButTwoLeftMost};
- static inline const NumberT RightMost = NumberT{detail::RightMost};
- static inline const NumberT AllButRightMost = NumberT{detail::AllButRightMost};
-
- template <typename U, typename V>
- static constexpr NumberT Lsh(U val, V shiftBy) {
- return NumberT{T{val} << T{shiftBy}};
- }
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Unary op
-////////////////////////////////////////////////////////////////////////////////////////////////////
-namespace unary_op {
-// Bring in std::ostream& operator<<(std::ostream& o, const Types& types)
-using resolver::operator<<;
-
-struct Case {
- Types input;
- Types expected;
-};
-
-static std::ostream& operator<<(std::ostream& o, const Case& c) {
- o << "input: " << c.input << ", expected: " << c.expected;
- return o;
-}
-
-/// Creates a Case with Values of any type
-template <typename T, typename U>
-Case C(Value<T> input, Value<U> expected) {
- return Case{std::move(input), std::move(expected)};
-}
-
-/// Convenience overload to creates a Case with just scalars
-template <typename T, typename U, typename = std::enable_if_t<!IsValue<T>>>
-Case C(T input, U expected) {
- return Case{Val(input), Val(expected)};
-}
-
-using ResolverConstEvalUnaryOpTest = ResolverTestWithParam<std::tuple<ast::UnaryOp, Case>>;
-
-TEST_P(ResolverConstEvalUnaryOpTest, Test) {
- Enable(ast::Extension::kF16);
-
- auto op = std::get<0>(GetParam());
- auto& c = std::get<1>(GetParam());
- std::visit(
- [&](auto&& expected) {
- using T = typename std::decay_t<decltype(expected)>::ElementType;
-
- auto* input_expr = std::visit([&](auto&& value) { return value.Expr(*this); }, c.input);
- auto* expr = create<ast::UnaryOpExpression>(op, input_expr);
-
- GlobalConst("C", expr);
- auto* expected_expr = expected.Expr(*this);
- GlobalConst("E", expected_expr);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- const sem::Constant* value = sem->ConstantValue();
- ASSERT_NE(value, nullptr);
- EXPECT_TYPE(value->Type(), sem->Type());
-
- auto* expected_sem = Sem().Get(expected_expr);
- const sem::Constant* expected_value = expected_sem->ConstantValue();
- ASSERT_NE(expected_value, nullptr);
- EXPECT_TYPE(expected_value->Type(), expected_sem->Type());
-
- ForEachElemPair(value, expected_value,
- [&](const sem::Constant* a, const sem::Constant* b) {
- EXPECT_EQ(a->As<T>(), b->As<T>());
- if constexpr (IsIntegral<T>) {
- // Check that the constant's integer doesn't contain unexpected
- // data in the MSBs that are outside of the bit-width of T.
- EXPECT_EQ(a->As<AInt>(), b->As<AInt>());
- }
- return HasFailure() ? Action::kStop : Action::kContinue;
- });
- },
- c.expected);
-}
-INSTANTIATE_TEST_SUITE_P(Complement,
- ResolverConstEvalUnaryOpTest,
- testing::Combine(testing::Values(ast::UnaryOp::kComplement),
- testing::ValuesIn({
- // AInt
- C(0_a, 0xffffffffffffffff_a),
- C(0xffffffffffffffff_a, 0_a),
- C(0xf0f0f0f0f0f0f0f0_a, 0x0f0f0f0f0f0f0f0f_a),
- C(0xaaaaaaaaaaaaaaaa_a, 0x5555555555555555_a),
- C(0x5555555555555555_a, 0xaaaaaaaaaaaaaaaa_a),
- // u32
- C(0_u, 0xffffffff_u),
- C(0xffffffff_u, 0_u),
- C(0xf0f0f0f0_u, 0x0f0f0f0f_u),
- C(0xaaaaaaaa_u, 0x55555555_u),
- C(0x55555555_u, 0xaaaaaaaa_u),
- // i32
- C(0_i, -1_i),
- C(-1_i, 0_i),
- C(1_i, -2_i),
- C(-2_i, 1_i),
- C(2_i, -3_i),
- C(-3_i, 2_i),
- })));
-
-INSTANTIATE_TEST_SUITE_P(Negation,
- ResolverConstEvalUnaryOpTest,
- testing::Combine(testing::Values(ast::UnaryOp::kNegation),
- testing::ValuesIn({
- // AInt
- C(0_a, -0_a),
- C(-0_a, 0_a),
- C(1_a, -1_a),
- C(-1_a, 1_a),
- C(AInt::Highest(), -AInt::Highest()),
- C(-AInt::Highest(), AInt::Highest()),
- C(AInt::Lowest(), Negate(AInt::Lowest())),
- C(Negate(AInt::Lowest()), AInt::Lowest()),
- // i32
- C(0_i, -0_i),
- C(-0_i, 0_i),
- C(1_i, -1_i),
- C(-1_i, 1_i),
- C(i32::Highest(), -i32::Highest()),
- C(-i32::Highest(), i32::Highest()),
- C(i32::Lowest(), Negate(i32::Lowest())),
- C(Negate(i32::Lowest()), i32::Lowest()),
- // AFloat
- C(0.0_a, -0.0_a),
- C(-0.0_a, 0.0_a),
- C(1.0_a, -1.0_a),
- C(-1.0_a, 1.0_a),
- C(AFloat::Highest(), -AFloat::Highest()),
- C(-AFloat::Highest(), AFloat::Highest()),
- C(AFloat::Lowest(), Negate(AFloat::Lowest())),
- C(Negate(AFloat::Lowest()), AFloat::Lowest()),
- // f32
- C(0.0_f, -0.0_f),
- C(-0.0_f, 0.0_f),
- C(1.0_f, -1.0_f),
- C(-1.0_f, 1.0_f),
- C(f32::Highest(), -f32::Highest()),
- C(-f32::Highest(), f32::Highest()),
- C(f32::Lowest(), Negate(f32::Lowest())),
- C(Negate(f32::Lowest()), f32::Lowest()),
- // f16
- C(0.0_h, -0.0_h),
- C(-0.0_h, 0.0_h),
- C(1.0_h, -1.0_h),
- C(-1.0_h, 1.0_h),
- C(f16::Highest(), -f16::Highest()),
- C(-f16::Highest(), f16::Highest()),
- C(f16::Lowest(), Negate(f16::Lowest())),
- C(Negate(f16::Lowest()), f16::Lowest()),
- })));
-
-// Make sure UBSan doesn't trip on C++'s undefined behaviour of negating the smallest negative
-// number.
-TEST_F(ResolverConstEvalTest, UnaryNegateLowestAbstract) {
- // const break_me = -(-9223372036854775808);
- auto* c = GlobalConst("break_me", Negation(Negation(Expr(9223372036854775808_a))));
- (void)c;
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(c);
- EXPECT_EQ(sem->ConstantValue()->As<AInt>(), 9223372036854775808_a);
-}
-
-INSTANTIATE_TEST_SUITE_P(Not,
- ResolverConstEvalUnaryOpTest,
- testing::Combine(testing::Values(ast::UnaryOp::kNot),
- testing::ValuesIn({
- C(true, false),
- C(false, true),
- C(Vec(true, true), Vec(false, false)),
- C(Vec(true, false), Vec(false, true)),
- C(Vec(false, true), Vec(true, false)),
- C(Vec(false, false), Vec(true, true)),
- })));
-
-} // namespace unary_op
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Binary op
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-namespace binary_op {
-// Bring in std::ostream& operator<<(std::ostream& o, const Types& types)
-using resolver::operator<<;
-
-struct Case {
- Types lhs;
- Types rhs;
- Types expected;
- bool overflow;
-};
-
-/// Creates a Case with Values of any type
-template <typename T, typename U, typename V>
-Case C(Value<T> lhs, Value<U> rhs, Value<V> expected, bool overflow = false) {
- return Case{std::move(lhs), std::move(rhs), std::move(expected), overflow};
-}
-
-/// Convenience overload that creates a Case with just scalars
-template <typename T, typename U, typename V, typename = std::enable_if_t<!IsValue<T>>>
-Case C(T lhs, U rhs, V expected, bool overflow = false) {
- return Case{Val(lhs), Val(rhs), Val(expected), overflow};
-}
-
-static std::ostream& operator<<(std::ostream& o, const Case& c) {
- o << "lhs: " << c.lhs << ", rhs: " << c.rhs << ", expected: " << c.expected
- << ", overflow: " << c.overflow;
- return o;
-}
-
-using ResolverConstEvalBinaryOpTest = ResolverTestWithParam<std::tuple<ast::BinaryOp, Case>>;
-TEST_P(ResolverConstEvalBinaryOpTest, Test) {
- Enable(ast::Extension::kF16);
- auto op = std::get<0>(GetParam());
- auto& c = std::get<1>(GetParam());
-
- std::visit(
- [&](auto&& expected) {
- using T = typename std::decay_t<decltype(expected)>::ElementType;
- if constexpr (std::is_same_v<T, AInt> || std::is_same_v<T, AFloat>) {
- if (c.overflow) {
- // Overflow is not allowed for abstract types. This is tested separately.
- return;
- }
- }
-
- auto* lhs_expr = std::visit([&](auto&& value) { return value.Expr(*this); }, c.lhs);
- auto* rhs_expr = std::visit([&](auto&& value) { return value.Expr(*this); }, c.rhs);
- auto* expr = create<ast::BinaryExpression>(op, lhs_expr, rhs_expr);
-
- GlobalConst("C", expr);
- auto* expected_expr = expected.Expr(*this);
- GlobalConst("E", expected_expr);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- const sem::Constant* value = sem->ConstantValue();
- ASSERT_NE(value, nullptr);
- EXPECT_TYPE(value->Type(), sem->Type());
-
- auto* expected_sem = Sem().Get(expected_expr);
- const sem::Constant* expected_value = expected_sem->ConstantValue();
- ASSERT_NE(expected_value, nullptr);
- EXPECT_TYPE(expected_value->Type(), expected_sem->Type());
-
- ForEachElemPair(value, expected_value,
- [&](const sem::Constant* a, const sem::Constant* b) {
- EXPECT_EQ(a->As<T>(), b->As<T>());
- if constexpr (IsIntegral<T>) {
- // Check that the constant's integer doesn't contain unexpected
- // data in the MSBs that are outside of the bit-width of T.
- EXPECT_EQ(a->As<AInt>(), b->As<AInt>());
- }
- return HasFailure() ? Action::kStop : Action::kContinue;
- });
- },
- c.expected);
-}
-
-INSTANTIATE_TEST_SUITE_P(MixedAbstractArgs,
- ResolverConstEvalBinaryOpTest,
- testing::Combine(testing::Values(ast::BinaryOp::kAdd),
- testing::ValuesIn(std::vector{
- // Mixed abstract type args
- C(1_a, 2.3_a, 3.3_a),
- C(2.3_a, 1_a, 3.3_a),
- })));
-
-template <typename T>
-std::vector<Case> OpAddIntCases() {
- static_assert(IsIntegral<T>);
- return {
- C(T{0}, T{0}, T{0}),
- C(T{1}, T{2}, T{3}),
- C(T::Lowest(), T{1}, T{T::Lowest() + 1}),
- C(T::Highest(), Negate(T{1}), T{T::Highest() - 1}),
- C(T::Lowest(), T::Highest(), Negate(T{1})),
- C(T::Highest(), T{1}, T::Lowest(), true),
- C(T::Lowest(), Negate(T{1}), T::Highest(), true),
- };
-}
-template <typename T>
-std::vector<Case> OpAddFloatCases() {
- static_assert(IsFloatingPoint<T>);
- return {
- C(T{0}, T{0}, T{0}),
- C(T{1}, T{2}, T{3}),
- C(T::Lowest(), T{1}, T{T::Lowest() + 1}),
- C(T::Highest(), Negate(T{1}), T{T::Highest() - 1}),
- C(T::Lowest(), T::Highest(), T{0}),
- C(T::Highest(), T::Highest(), T::Inf(), true),
- C(T::Lowest(), Negate(T::Highest()), -T::Inf(), true),
- };
-}
-INSTANTIATE_TEST_SUITE_P(Add,
- ResolverConstEvalBinaryOpTest,
- testing::Combine(testing::Values(ast::BinaryOp::kAdd),
- testing::ValuesIn(Concat( //
- OpAddIntCases<AInt>(),
- OpAddIntCases<i32>(),
- OpAddIntCases<u32>(),
- OpAddFloatCases<AFloat>(),
- OpAddFloatCases<f32>(),
- OpAddFloatCases<f16>()))));
-
-template <typename T>
-std::vector<Case> OpSubIntCases() {
- static_assert(IsIntegral<T>);
- return {
- C(T{0}, T{0}, T{0}),
- C(T{3}, T{2}, T{1}),
- C(T{T::Lowest() + 1}, T{1}, T::Lowest()),
- C(T{T::Highest() - 1}, Negate(T{1}), T::Highest()),
- C(Negate(T{1}), T::Highest(), T::Lowest()),
- C(T::Lowest(), T{1}, T::Highest(), true),
- C(T::Highest(), Negate(T{1}), T::Lowest(), true),
- };
-}
-template <typename T>
-std::vector<Case> OpSubFloatCases() {
- static_assert(IsFloatingPoint<T>);
- return {
- C(T{0}, T{0}, T{0}),
- C(T{3}, T{2}, T{1}),
- C(T::Highest(), T{1}, T{T::Highest() - 1}),
- C(T::Lowest(), Negate(T{1}), T{T::Lowest() + 1}),
- C(T{0}, T::Highest(), T::Lowest()),
- C(T::Highest(), Negate(T::Highest()), T::Inf(), true),
- C(T::Lowest(), T::Highest(), -T::Inf(), true),
- };
-}
-INSTANTIATE_TEST_SUITE_P(Sub,
- ResolverConstEvalBinaryOpTest,
- testing::Combine(testing::Values(ast::BinaryOp::kSubtract),
- testing::ValuesIn(Concat( //
- OpSubIntCases<AInt>(),
- OpSubIntCases<i32>(),
- OpSubIntCases<u32>(),
- OpSubFloatCases<AFloat>(),
- OpSubFloatCases<f32>(),
- OpSubFloatCases<f16>()))));
-
-template <typename T>
-std::vector<Case> OpMulScalarCases() {
- return {
- C(T{0}, T{0}, T{0}),
- C(T{1}, T{2}, T{2}),
- C(T{2}, T{3}, T{6}),
- C(Negate(T{2}), T{3}, Negate(T{6})),
- C(T::Highest(), T{1}, T::Highest()),
- C(T::Lowest(), T{1}, T::Lowest()),
- C(T::Highest(), T::Highest(), Mul(T::Highest(), T::Highest()), true),
- C(T::Lowest(), T::Lowest(), Mul(T::Lowest(), T::Lowest()), true),
- };
-}
-
-template <typename T>
-std::vector<Case> OpMulVecCases() {
- return {
- // s * vec3 = vec3
- C(Val(T{2.0}), Vec(T{1.25}, T{2.25}, T{3.25}), Vec(T{2.5}, T{4.5}, T{6.5})),
- // vec3 * s = vec3
- C(Vec(T{1.25}, T{2.25}, T{3.25}), Val(T{2.0}), Vec(T{2.5}, T{4.5}, T{6.5})),
- // vec3 * vec3 = vec3
- C(Vec(T{1.25}, T{2.25}, T{3.25}), Vec(T{2.0}, T{2.0}, T{2.0}), Vec(T{2.5}, T{4.5}, T{6.5})),
- };
-}
-
-template <typename T>
-std::vector<Case> OpMulMatCases() {
- return {
- // s * mat3x2 = mat3x2
- C(Val(T{2.25}),
- Mat({T{1.0}, T{4.0}}, //
- {T{2.0}, T{5.0}}, //
- {T{3.0}, T{6.0}}),
- Mat({T{2.25}, T{9.0}}, //
- {T{4.5}, T{11.25}}, //
- {T{6.75}, T{13.5}})),
- // mat3x2 * s = mat3x2
- C(Mat({T{1.0}, T{4.0}}, //
- {T{2.0}, T{5.0}}, //
- {T{3.0}, T{6.0}}),
- Val(T{2.25}),
- Mat({T{2.25}, T{9.0}}, //
- {T{4.5}, T{11.25}}, //
- {T{6.75}, T{13.5}})),
- // vec3 * mat2x3 = vec2
- C(Vec(T{1.25}, T{2.25}, T{3.25}), //
- Mat({T{1.0}, T{2.0}, T{3.0}}, //
- {T{4.0}, T{5.0}, T{6.0}}), //
- Vec(T{15.5}, T{35.75})),
- // mat2x3 * vec2 = vec3
- C(Mat({T{1.0}, T{2.0}, T{3.0}}, //
- {T{4.0}, T{5.0}, T{6.0}}), //
- Vec(T{1.25}, T{2.25}), //
- Vec(T{10.25}, T{13.75}, T{17.25})),
- // mat3x2 * mat2x3 = mat2x2
- C(Mat({T{1.0}, T{2.0}}, //
- {T{3.0}, T{4.0}}, //
- {T{5.0}, T{6.0}}), //
- Mat({T{1.25}, T{2.25}, T{3.25}}, //
- {T{4.25}, T{5.25}, T{6.25}}), //
- Mat({T{24.25}, T{31.0}}, //
- {T{51.25}, T{67.0}})), //
- };
-}
-
-INSTANTIATE_TEST_SUITE_P(Mul,
- ResolverConstEvalBinaryOpTest,
- testing::Combine( //
- testing::Values(ast::BinaryOp::kMultiply),
- testing::ValuesIn(Concat( //
- OpMulScalarCases<AInt>(),
- OpMulScalarCases<i32>(),
- OpMulScalarCases<u32>(),
- OpMulScalarCases<AFloat>(),
- OpMulScalarCases<f32>(),
- OpMulScalarCases<f16>(),
- OpMulVecCases<AInt>(),
- OpMulVecCases<i32>(),
- OpMulVecCases<u32>(),
- OpMulVecCases<AFloat>(),
- OpMulVecCases<f32>(),
- OpMulVecCases<f16>(),
- OpMulMatCases<AFloat>(),
- OpMulMatCases<f32>(),
- OpMulMatCases<f16>()))));
-
-template <typename T>
-std::vector<Case> OpDivIntCases() {
- std::vector<Case> r = {
- C(Val(T{0}), Val(T{1}), Val(T{0})),
- C(Val(T{1}), Val(T{1}), Val(T{1})),
- C(Val(T{1}), Val(T{1}), Val(T{1})),
- C(Val(T{2}), Val(T{1}), Val(T{2})),
- C(Val(T{4}), Val(T{2}), Val(T{2})),
- C(Val(T::Highest()), Val(T{1}), Val(T::Highest())),
- C(Val(T::Lowest()), Val(T{1}), Val(T::Lowest())),
- C(Val(T::Highest()), Val(T::Highest()), Val(T{1})),
- C(Val(T{0}), Val(T::Highest()), Val(T{0})),
- C(Val(T{0}), Val(T::Lowest()), Val(T{0})),
- };
- ConcatIntoIf<IsIntegral<T>>( //
- r, std::vector<Case>{
- // e1, when e2 is zero.
- C(T{123}, T{0}, T{123}, true),
- });
- ConcatIntoIf<IsSignedIntegral<T>>( //
- r, std::vector<Case>{
- // e1, when e1 is the most negative value in T, and e2 is -1.
- C(T::Smallest(), T{-1}, T::Smallest(), true),
- });
- return r;
-}
-
-template <typename T>
-std::vector<Case> OpDivFloatCases() {
- return {
- C(Val(T{0}), Val(T{1}), Val(T{0})),
- C(Val(T{1}), Val(T{1}), Val(T{1})),
- C(Val(T{1}), Val(T{1}), Val(T{1})),
- C(Val(T{2}), Val(T{1}), Val(T{2})),
- C(Val(T{4}), Val(T{2}), Val(T{2})),
- C(Val(T::Highest()), Val(T{1}), Val(T::Highest())),
- C(Val(T::Lowest()), Val(T{1}), Val(T::Lowest())),
- C(Val(T::Highest()), Val(T::Highest()), Val(T{1})),
- C(Val(T{0}), Val(T::Highest()), Val(T{0})),
- C(Val(T{0}), Val(T::Lowest()), Val(-T{0})),
- C(T{123}, T{0}, T::Inf(), true),
- C(T{-123}, -T{0}, T::Inf(), true),
- C(T{-123}, T{0}, -T::Inf(), true),
- C(T{123}, -T{0}, -T::Inf(), true),
- };
-}
-INSTANTIATE_TEST_SUITE_P(Div,
- ResolverConstEvalBinaryOpTest,
- testing::Combine( //
- testing::Values(ast::BinaryOp::kDivide),
- testing::ValuesIn(Concat( //
- OpDivIntCases<AInt>(),
- OpDivIntCases<i32>(),
- OpDivIntCases<u32>(),
- OpDivFloatCases<AFloat>(),
- OpDivFloatCases<f32>(),
- OpDivFloatCases<f16>()))));
-
-template <typename T, bool equals>
-std::vector<Case> OpEqualCases() {
- return {
- C(Val(T{0}), Val(T{0}), Val(true == equals)),
- C(Val(T{0}), Val(T{1}), Val(false == equals)),
- C(Val(T{1}), Val(T{0}), Val(false == equals)),
- C(Val(T{1}), Val(T{1}), Val(true == equals)),
- C(Vec(T{0}, T{0}), Vec(T{0}, T{0}), Vec(true == equals, true == equals)),
- C(Vec(T{1}, T{0}), Vec(T{0}, T{1}), Vec(false == equals, false == equals)),
- C(Vec(T{1}, T{1}), Vec(T{0}, T{1}), Vec(false == equals, true == equals)),
- };
-}
-INSTANTIATE_TEST_SUITE_P(Equal,
- ResolverConstEvalBinaryOpTest,
- testing::Combine( //
- testing::Values(ast::BinaryOp::kEqual),
- testing::ValuesIn(Concat( //
- OpEqualCases<AInt, true>(),
- OpEqualCases<i32, true>(),
- OpEqualCases<u32, true>(),
- OpEqualCases<AFloat, true>(),
- OpEqualCases<f32, true>(),
- OpEqualCases<f16, true>(),
- OpEqualCases<bool, true>()))));
-INSTANTIATE_TEST_SUITE_P(NotEqual,
- ResolverConstEvalBinaryOpTest,
- testing::Combine( //
- testing::Values(ast::BinaryOp::kNotEqual),
- testing::ValuesIn(Concat( //
- OpEqualCases<AInt, false>(),
- OpEqualCases<i32, false>(),
- OpEqualCases<u32, false>(),
- OpEqualCases<AFloat, false>(),
- OpEqualCases<f32, false>(),
- OpEqualCases<f16, false>(),
- OpEqualCases<bool, false>()))));
-
-template <typename T, bool less_than>
-std::vector<Case> OpLessThanCases() {
- return {
- C(Val(T{0}), Val(T{0}), Val(false == less_than)),
- C(Val(T{0}), Val(T{1}), Val(true == less_than)),
- C(Val(T{1}), Val(T{0}), Val(false == less_than)),
- C(Val(T{1}), Val(T{1}), Val(false == less_than)),
- C(Vec(T{0}, T{0}), Vec(T{0}, T{0}), Vec(false == less_than, false == less_than)),
- C(Vec(T{0}, T{0}), Vec(T{1}, T{1}), Vec(true == less_than, true == less_than)),
- C(Vec(T{1}, T{1}), Vec(T{0}, T{0}), Vec(false == less_than, false == less_than)),
- C(Vec(T{1}, T{0}), Vec(T{0}, T{1}), Vec(false == less_than, true == less_than)),
- };
-}
-INSTANTIATE_TEST_SUITE_P(LessThan,
- ResolverConstEvalBinaryOpTest,
- testing::Combine( //
- testing::Values(ast::BinaryOp::kLessThan),
- testing::ValuesIn(Concat( //
- OpLessThanCases<AInt, true>(),
- OpLessThanCases<i32, true>(),
- OpLessThanCases<u32, true>(),
- OpLessThanCases<AFloat, true>(),
- OpLessThanCases<f32, true>(),
- OpLessThanCases<f16, true>()))));
-INSTANTIATE_TEST_SUITE_P(GreaterThanEqual,
- ResolverConstEvalBinaryOpTest,
- testing::Combine( //
- testing::Values(ast::BinaryOp::kGreaterThanEqual),
- testing::ValuesIn(Concat( //
- OpLessThanCases<AInt, false>(),
- OpLessThanCases<i32, false>(),
- OpLessThanCases<u32, false>(),
- OpLessThanCases<AFloat, false>(),
- OpLessThanCases<f32, false>(),
- OpLessThanCases<f16, false>()))));
-
-template <typename T, bool greater_than>
-std::vector<Case> OpGreaterThanCases() {
- return {
- C(Val(T{0}), Val(T{0}), Val(false == greater_than)),
- C(Val(T{0}), Val(T{1}), Val(false == greater_than)),
- C(Val(T{1}), Val(T{0}), Val(true == greater_than)),
- C(Val(T{1}), Val(T{1}), Val(false == greater_than)),
- C(Vec(T{0}, T{0}), Vec(T{0}, T{0}), Vec(false == greater_than, false == greater_than)),
- C(Vec(T{1}, T{1}), Vec(T{0}, T{0}), Vec(true == greater_than, true == greater_than)),
- C(Vec(T{0}, T{0}), Vec(T{1}, T{1}), Vec(false == greater_than, false == greater_than)),
- C(Vec(T{1}, T{0}), Vec(T{0}, T{1}), Vec(true == greater_than, false == greater_than)),
- };
-}
-INSTANTIATE_TEST_SUITE_P(GreaterThan,
- ResolverConstEvalBinaryOpTest,
- testing::Combine( //
- testing::Values(ast::BinaryOp::kGreaterThan),
- testing::ValuesIn(Concat( //
- OpGreaterThanCases<AInt, true>(),
- OpGreaterThanCases<i32, true>(),
- OpGreaterThanCases<u32, true>(),
- OpGreaterThanCases<AFloat, true>(),
- OpGreaterThanCases<f32, true>(),
- OpGreaterThanCases<f16, true>()))));
-INSTANTIATE_TEST_SUITE_P(LessThanEqual,
- ResolverConstEvalBinaryOpTest,
- testing::Combine( //
- testing::Values(ast::BinaryOp::kLessThanEqual),
- testing::ValuesIn(Concat( //
- OpGreaterThanCases<AInt, false>(),
- OpGreaterThanCases<i32, false>(),
- OpGreaterThanCases<u32, false>(),
- OpGreaterThanCases<AFloat, false>(),
- OpGreaterThanCases<f32, false>(),
- OpGreaterThanCases<f16, false>()))));
-
-static std::vector<Case> OpAndBoolCases() {
- return {
- C(true, true, true),
- C(true, false, false),
- C(false, true, false),
- C(false, false, false),
- C(Vec(true, true), Vec(true, false), Vec(true, false)),
- C(Vec(true, true), Vec(false, true), Vec(false, true)),
- C(Vec(true, false), Vec(true, false), Vec(true, false)),
- C(Vec(false, true), Vec(true, false), Vec(false, false)),
- C(Vec(false, false), Vec(true, false), Vec(false, false)),
- };
-}
-template <typename T>
-std::vector<Case> OpAndIntCases() {
- using B = BitValues<T>;
- return {
- C(T{0b1010}, T{0b1111}, T{0b1010}),
- C(T{0b1010}, T{0b0000}, T{0b0000}),
- C(T{0b1010}, T{0b0011}, T{0b0010}),
- C(T{0b1010}, T{0b1100}, T{0b1000}),
- C(T{0b1010}, T{0b0101}, T{0b0000}),
- C(B::All, B::All, B::All),
- C(B::LeftMost, B::LeftMost, B::LeftMost),
- C(B::RightMost, B::RightMost, B::RightMost),
- C(B::All, T{0}, T{0}),
- C(T{0}, B::All, T{0}),
- C(B::LeftMost, B::AllButLeftMost, T{0}),
- C(B::AllButLeftMost, B::LeftMost, T{0}),
- C(B::RightMost, B::AllButRightMost, T{0}),
- C(B::AllButRightMost, B::RightMost, T{0}),
- C(Vec(B::All, B::LeftMost, B::RightMost), //
- Vec(B::All, B::All, B::All), //
- Vec(B::All, B::LeftMost, B::RightMost)), //
- C(Vec(B::All, B::LeftMost, B::RightMost), //
- Vec(T{0}, T{0}, T{0}), //
- Vec(T{0}, T{0}, T{0})), //
- C(Vec(B::LeftMost, B::RightMost), //
- Vec(B::AllButLeftMost, B::AllButRightMost), //
- Vec(T{0}, T{0})),
- };
-}
-INSTANTIATE_TEST_SUITE_P(And,
- ResolverConstEvalBinaryOpTest,
- testing::Combine( //
- testing::Values(ast::BinaryOp::kAnd),
- testing::ValuesIn( //
- Concat(OpAndBoolCases(), //
- OpAndIntCases<AInt>(),
- OpAndIntCases<i32>(),
- OpAndIntCases<u32>()))));
-
-static std::vector<Case> OpOrBoolCases() {
- return {
- C(true, true, true),
- C(true, false, true),
- C(false, true, true),
- C(false, false, false),
- C(Vec(true, true), Vec(true, false), Vec(true, true)),
- C(Vec(true, true), Vec(false, true), Vec(true, true)),
- C(Vec(true, false), Vec(true, false), Vec(true, false)),
- C(Vec(false, true), Vec(true, false), Vec(true, true)),
- C(Vec(false, false), Vec(true, false), Vec(true, false)),
- };
-}
-template <typename T>
-std::vector<Case> OpOrIntCases() {
- using B = BitValues<T>;
- return {
- C(T{0b1010}, T{0b1111}, T{0b1111}),
- C(T{0b1010}, T{0b0000}, T{0b1010}),
- C(T{0b1010}, T{0b0011}, T{0b1011}),
- C(T{0b1010}, T{0b1100}, T{0b1110}),
- C(T{0b1010}, T{0b0101}, T{0b1111}),
- C(B::All, B::All, B::All),
- C(B::LeftMost, B::LeftMost, B::LeftMost),
- C(B::RightMost, B::RightMost, B::RightMost),
- C(B::All, T{0}, B::All),
- C(T{0}, B::All, B::All),
- C(B::LeftMost, B::AllButLeftMost, B::All),
- C(B::AllButLeftMost, B::LeftMost, B::All),
- C(B::RightMost, B::AllButRightMost, B::All),
- C(B::AllButRightMost, B::RightMost, B::All),
- C(Vec(B::All, B::LeftMost, B::RightMost), //
- Vec(B::All, B::All, B::All), //
- Vec(B::All, B::All, B::All)), //
- C(Vec(B::All, B::LeftMost, B::RightMost), //
- Vec(T{0}, T{0}, T{0}), //
- Vec(B::All, B::LeftMost, B::RightMost)), //
- C(Vec(B::LeftMost, B::RightMost), //
- Vec(B::AllButLeftMost, B::AllButRightMost), //
- Vec(B::All, B::All)),
- };
-}
-INSTANTIATE_TEST_SUITE_P(Or,
- ResolverConstEvalBinaryOpTest,
- testing::Combine( //
- testing::Values(ast::BinaryOp::kOr),
- testing::ValuesIn(Concat(OpOrBoolCases(),
- OpOrIntCases<AInt>(),
- OpOrIntCases<i32>(),
- OpOrIntCases<u32>()))));
-
-TEST_F(ResolverConstEvalTest, NotAndOrOfVecs) {
- // const C = !((vec2(true, true) & vec2(true, false)) | vec2(false, true));
- auto v1 = Vec(true, true).Expr(*this);
- auto v2 = Vec(true, false).Expr(*this);
- auto v3 = Vec(false, true).Expr(*this);
- auto expr = Not(Or(And(v1, v2), v3));
- GlobalConst("C", expr);
- auto expected_expr = Vec(false, false).Expr(*this);
- GlobalConst("E", expected_expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- const sem::Constant* value = sem->ConstantValue();
- ASSERT_NE(value, nullptr);
- EXPECT_TYPE(value->Type(), sem->Type());
-
- auto* expected_sem = Sem().Get(expected_expr);
- const sem::Constant* expected_value = expected_sem->ConstantValue();
- ASSERT_NE(expected_value, nullptr);
- EXPECT_TYPE(expected_value->Type(), expected_sem->Type());
-
- ForEachElemPair(value, expected_value, [&](const sem::Constant* a, const sem::Constant* b) {
- EXPECT_EQ(a->As<bool>(), b->As<bool>());
- return HasFailure() ? Action::kStop : Action::kContinue;
- });
-}
-
-template <typename T>
-std::vector<Case> XorCases() {
- using B = BitValues<T>;
- return {
- C(T{0b1010}, T{0b1111}, T{0b0101}),
- C(T{0b1010}, T{0b0000}, T{0b1010}),
- C(T{0b1010}, T{0b0011}, T{0b1001}),
- C(T{0b1010}, T{0b1100}, T{0b0110}),
- C(T{0b1010}, T{0b0101}, T{0b1111}),
- C(B::All, B::All, T{0}),
- C(B::LeftMost, B::LeftMost, T{0}),
- C(B::RightMost, B::RightMost, T{0}),
- C(B::All, T{0}, B::All),
- C(T{0}, B::All, B::All),
- C(B::LeftMost, B::AllButLeftMost, B::All),
- C(B::AllButLeftMost, B::LeftMost, B::All),
- C(B::RightMost, B::AllButRightMost, B::All),
- C(B::AllButRightMost, B::RightMost, B::All),
- C(Vec(B::All, B::LeftMost, B::RightMost), //
- Vec(B::All, B::All, B::All), //
- Vec(T{0}, B::AllButLeftMost, B::AllButRightMost)), //
- C(Vec(B::All, B::LeftMost, B::RightMost), //
- Vec(T{0}, T{0}, T{0}), //
- Vec(B::All, B::LeftMost, B::RightMost)), //
- C(Vec(B::LeftMost, B::RightMost), //
- Vec(B::AllButLeftMost, B::AllButRightMost), //
- Vec(B::All, B::All)),
- };
-}
-INSTANTIATE_TEST_SUITE_P(Xor,
- ResolverConstEvalBinaryOpTest,
- testing::Combine( //
- testing::Values(ast::BinaryOp::kXor),
- testing::ValuesIn(Concat(XorCases<AInt>(), //
- XorCases<i32>(), //
- XorCases<u32>()))));
-
-template <typename T>
-std::vector<Case> ShiftLeftCases() {
- // Shift type is u32 for non-abstract
- using ST = std::conditional_t<IsAbstract<T>, T, u32>;
- using B = BitValues<T>;
- return {
- C(T{0b1010}, ST{0}, T{0b0000'0000'1010}), //
- C(T{0b1010}, ST{1}, T{0b0000'0001'0100}), //
- C(T{0b1010}, ST{2}, T{0b0000'0010'1000}), //
- C(T{0b1010}, ST{3}, T{0b0000'0101'0000}), //
- C(T{0b1010}, ST{4}, T{0b0000'1010'0000}), //
- C(T{0b1010}, ST{5}, T{0b0001'0100'0000}), //
- C(T{0b1010}, ST{6}, T{0b0010'1000'0000}), //
- C(T{0b1010}, ST{7}, T{0b0101'0000'0000}), //
- C(T{0b1010}, ST{8}, T{0b1010'0000'0000}), //
- C(B::LeftMost, ST{0}, B::LeftMost), //
- C(B::TwoLeftMost, ST{1}, B::LeftMost), // No overflow
- C(B::All, ST{1}, B::AllButRightMost), // No overflow
- C(B::All, ST{B::NumBits - 1}, B::LeftMost), // No overflow
-
- C(Vec(T{0b1010}, T{0b1010}), //
- Vec(ST{0}, ST{1}), //
- Vec(T{0b0000'0000'1010}, T{0b0000'0001'0100})), //
- C(Vec(T{0b1010}, T{0b1010}), //
- Vec(ST{2}, ST{3}), //
- Vec(T{0b0000'0010'1000}, T{0b0000'0101'0000})), //
- C(Vec(T{0b1010}, T{0b1010}), //
- Vec(ST{4}, ST{5}), //
- Vec(T{0b0000'1010'0000}, T{0b0001'0100'0000})), //
- C(Vec(T{0b1010}, T{0b1010}, T{0b1010}), //
- Vec(ST{6}, ST{7}, ST{8}), //
- Vec(T{0b0010'1000'0000}, T{0b0101'0000'0000}, T{0b1010'0000'0000})), //
- };
-}
-INSTANTIATE_TEST_SUITE_P(ShiftLeft,
- ResolverConstEvalBinaryOpTest,
- testing::Combine( //
- testing::Values(ast::BinaryOp::kShiftLeft),
- testing::ValuesIn(Concat(ShiftLeftCases<AInt>(), //
- ShiftLeftCases<i32>(), //
- ShiftLeftCases<u32>()))));
-
-// Tests for errors on overflow/underflow of binary operations with abstract numbers
-struct OverflowCase {
- ast::BinaryOp op;
- Types lhs;
- Types rhs;
-};
-
-static std::ostream& operator<<(std::ostream& o, const OverflowCase& c) {
- o << ast::FriendlyName(c.op) << ", lhs: " << c.lhs << ", rhs: " << c.rhs;
- return o;
-}
-using ResolverConstEvalBinaryOpTest_Overflow = ResolverTestWithParam<OverflowCase>;
-TEST_P(ResolverConstEvalBinaryOpTest_Overflow, Test) {
- Enable(ast::Extension::kF16);
- auto& c = GetParam();
- auto* lhs_expr = std::visit([&](auto&& value) { return value.Expr(*this); }, c.lhs);
- auto* rhs_expr = std::visit([&](auto&& value) { return value.Expr(*this); }, c.rhs);
- auto* expr = create<ast::BinaryExpression>(Source{{1, 1}}, c.op, lhs_expr, rhs_expr);
- GlobalConst("C", expr);
- ASSERT_FALSE(r()->Resolve());
-
- std::string type_name = std::visit(
- [&](auto&& value) {
- using ValueType = std::decay_t<decltype(value)>;
- return builder::FriendlyName<ValueType>();
- },
- c.lhs);
-
- EXPECT_THAT(r()->error(), HasSubstr("1:1 error: '"));
- EXPECT_THAT(r()->error(), HasSubstr("' cannot be represented as '" + type_name + "'"));
-}
-INSTANTIATE_TEST_SUITE_P(
- Test,
- ResolverConstEvalBinaryOpTest_Overflow,
- testing::Values(
-
- // scalar-scalar add
- OverflowCase{ast::BinaryOp::kAdd, Val(AInt::Highest()), Val(1_a)},
- OverflowCase{ast::BinaryOp::kAdd, Val(AInt::Lowest()), Val(-1_a)},
- OverflowCase{ast::BinaryOp::kAdd, Val(AFloat::Highest()), Val(AFloat::Highest())},
- OverflowCase{ast::BinaryOp::kAdd, Val(AFloat::Lowest()), Val(AFloat::Lowest())},
- // scalar-scalar subtract
- OverflowCase{ast::BinaryOp::kSubtract, Val(AInt::Lowest()), Val(1_a)},
- OverflowCase{ast::BinaryOp::kSubtract, Val(AInt::Highest()), Val(-1_a)},
- OverflowCase{ast::BinaryOp::kSubtract, Val(AFloat::Highest()), Val(AFloat::Lowest())},
- OverflowCase{ast::BinaryOp::kSubtract, Val(AFloat::Lowest()), Val(AFloat::Highest())},
-
- // scalar-scalar multiply
- OverflowCase{ast::BinaryOp::kMultiply, Val(AInt::Highest()), Val(2_a)},
- OverflowCase{ast::BinaryOp::kMultiply, Val(AInt::Lowest()), Val(-2_a)},
-
- // scalar-vector multiply
- OverflowCase{ast::BinaryOp::kMultiply, Val(AInt::Highest()), Vec(2_a, 1_a)},
- OverflowCase{ast::BinaryOp::kMultiply, Val(AInt::Lowest()), Vec(-2_a, 1_a)},
-
- // vector-matrix multiply
-
- // Overflow from first multiplication of dot product of vector and matrix column 0
- // i.e. (v[0] * m[0][0] + v[1] * m[0][1])
- // ^
- OverflowCase{ast::BinaryOp::kMultiply, //
- Vec(AFloat::Highest(), 1.0_a), //
- Mat({2.0_a, 1.0_a}, //
- {1.0_a, 1.0_a})},
-
- // Overflow from second multiplication of dot product of vector and matrix column 0
- // i.e. (v[0] * m[0][0] + v[1] * m[0][1])
- // ^
- OverflowCase{ast::BinaryOp::kMultiply, //
- Vec(1.0_a, AFloat::Highest()), //
- Mat({1.0_a, 2.0_a}, //
- {1.0_a, 1.0_a})},
-
- // Overflow from addition of dot product of vector and matrix column 0
- // i.e. (v[0] * m[0][0] + v[1] * m[0][1])
- // ^
- OverflowCase{ast::BinaryOp::kMultiply, //
- Vec(AFloat::Highest(), AFloat::Highest()), //
- Mat({1.0_a, 1.0_a}, //
- {1.0_a, 1.0_a})},
-
- // matrix-matrix multiply
-
- // Overflow from first multiplication of dot product of lhs row 0 and rhs column 0
- // i.e. m1[0][0] * m2[0][0] + m1[0][1] * m[1][0]
- // ^
- OverflowCase{ast::BinaryOp::kMultiply, //
- Mat({AFloat::Highest(), 1.0_a}, //
- {1.0_a, 1.0_a}), //
- Mat({2.0_a, 1.0_a}, //
- {1.0_a, 1.0_a})},
-
- // Overflow from second multiplication of dot product of lhs row 0 and rhs column 0
- // i.e. m1[0][0] * m2[0][0] + m1[0][1] * m[1][0]
- // ^
- OverflowCase{ast::BinaryOp::kMultiply, //
- Mat({1.0_a, AFloat::Highest()}, //
- {1.0_a, 1.0_a}), //
- Mat({1.0_a, 1.0_a}, //
- {2.0_a, 1.0_a})},
-
- // Overflow from addition of dot product of lhs row 0 and rhs column 0
- // i.e. m1[0][0] * m2[0][0] + m1[0][1] * m[1][0]
- // ^
- OverflowCase{ast::BinaryOp::kMultiply, //
- Mat({AFloat::Highest(), 1.0_a}, //
- {AFloat::Highest(), 1.0_a}), //
- Mat({1.0_a, 1.0_a}, //
- {1.0_a, 1.0_a})},
-
- // Divide by zero
- OverflowCase{ast::BinaryOp::kDivide, Val(123_a), Val(0_a)},
- OverflowCase{ast::BinaryOp::kDivide, Val(-123_a), Val(-0_a)},
- OverflowCase{ast::BinaryOp::kDivide, Val(-123_a), Val(0_a)},
- OverflowCase{ast::BinaryOp::kDivide, Val(123_a), Val(-0_a)},
-
- // Most negative value divided by -1
- OverflowCase{ast::BinaryOp::kDivide, Val(AInt::Lowest()), Val(-1_a)},
-
- // ShiftLeft of AInts that result in values not representable as AInts.
- // Note that for i32/u32, these would error because shift value is larger than 32.
- OverflowCase{ast::BinaryOp::kShiftLeft, //
- Val(AInt{BitValues<AInt>::All}), //
- Val(AInt{BitValues<AInt>::NumBits})}, //
- OverflowCase{ast::BinaryOp::kShiftLeft, //
- Val(AInt{BitValues<AInt>::RightMost}), //
- Val(AInt{BitValues<AInt>::NumBits})}, //
- OverflowCase{ast::BinaryOp::kShiftLeft, //
- Val(AInt{BitValues<AInt>::AllButLeftMost}), //
- Val(AInt{BitValues<AInt>::NumBits})}, //
- OverflowCase{ast::BinaryOp::kShiftLeft, //
- Val(AInt{BitValues<AInt>::AllButLeftMost}), //
- Val(AInt{BitValues<AInt>::NumBits + 1})}, //
- OverflowCase{ast::BinaryOp::kShiftLeft, //
- Val(AInt{BitValues<AInt>::AllButLeftMost}), //
- Val(AInt{BitValues<AInt>::NumBits + 1000})}
-
- ));
-
-TEST_F(ResolverConstEvalTest, BinaryAbstractAddOverflow_AInt) {
- GlobalConst("c", Add(Source{{1, 1}}, Expr(AInt::Highest()), 1_a));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "1:1 error: '9223372036854775807 + 1' cannot be represented as 'abstract-int'");
-}
-
-TEST_F(ResolverConstEvalTest, BinaryAbstractAddUnderflow_AInt) {
- GlobalConst("c", Add(Source{{1, 1}}, Expr(AInt::Lowest()), -1_a));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "1:1 error: '-9223372036854775808 + -1' cannot be represented as 'abstract-int'");
-}
-
-TEST_F(ResolverConstEvalTest, BinaryAbstractAddOverflow_AFloat) {
- GlobalConst("c", Add(Source{{1, 1}}, Expr(AFloat::Highest()), AFloat::Highest()));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "1:1 error: '1.79769e+308 + 1.79769e+308' cannot be represented as 'abstract-float'");
-}
-
-TEST_F(ResolverConstEvalTest, BinaryAbstractAddUnderflow_AFloat) {
- GlobalConst("c", Add(Source{{1, 1}}, Expr(AFloat::Lowest()), AFloat::Lowest()));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "1:1 error: '-1.79769e+308 + -1.79769e+308' cannot be represented as 'abstract-float'");
-}
-
-// Mixed AInt and AFloat args to test implicit conversion to AFloat
-INSTANTIATE_TEST_SUITE_P(
- AbstractMixed,
- ResolverConstEvalBinaryOpTest,
- testing::Combine(
- testing::Values(ast::BinaryOp::kAdd),
- testing::Values(C(Val(1_a), Val(2.3_a), Val(3.3_a)),
- C(Val(2.3_a), Val(1_a), Val(3.3_a)),
- C(Val(1_a), Vec(2.3_a, 2.3_a, 2.3_a), Vec(3.3_a, 3.3_a, 3.3_a)),
- C(Vec(2.3_a, 2.3_a, 2.3_a), Val(1_a), Vec(3.3_a, 3.3_a, 3.3_a)),
- C(Vec(2.3_a, 2.3_a, 2.3_a), Val(1_a), Vec(3.3_a, 3.3_a, 3.3_a)),
- C(Val(1_a), Vec(2.3_a, 2.3_a, 2.3_a), Vec(3.3_a, 3.3_a, 3.3_a)),
- C(Mat({1_a, 2_a}, //
- {1_a, 2_a}, //
- {1_a, 2_a}), //
- Mat({1.2_a, 2.3_a}, //
- {1.2_a, 2.3_a}, //
- {1.2_a, 2.3_a}), //
- Mat({2.2_a, 4.3_a}, //
- {2.2_a, 4.3_a}, //
- {2.2_a, 4.3_a})), //
- C(Mat({1.2_a, 2.3_a}, //
- {1.2_a, 2.3_a}, //
- {1.2_a, 2.3_a}), //
- Mat({1_a, 2_a}, //
- {1_a, 2_a}, //
- {1_a, 2_a}), //
- Mat({2.2_a, 4.3_a}, //
- {2.2_a, 4.3_a}, //
- {2.2_a, 4.3_a})) //
- )));
-
-// AInt left shift negative value -> error
-TEST_F(ResolverConstEvalTest, BinaryAbstractShiftLeftByNegativeValue_Error) {
- GlobalConst("c", Shl(Source{{1, 1}}, Expr(1_a), Expr(-1_a)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "1:1 error: cannot shift left by a negative value");
-}
-
-// i32/u32 left shift by >= 32 -> error
-using ResolverConstEvalShiftLeftConcreteGeqBitWidthError =
- ResolverTestWithParam<std::tuple<Types, Types>>;
-TEST_P(ResolverConstEvalShiftLeftConcreteGeqBitWidthError, Test) {
- auto* lhs_expr =
- std::visit([&](auto&& value) { return value.Expr(*this); }, std::get<0>(GetParam()));
- auto* rhs_expr =
- std::visit([&](auto&& value) { return value.Expr(*this); }, std::get<1>(GetParam()));
- GlobalConst("c", Shl(Source{{1, 1}}, lhs_expr, rhs_expr));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "1:1 error: shift left value must be less than the bit width of the lhs, which is 32");
-}
-INSTANTIATE_TEST_SUITE_P(Test,
- ResolverConstEvalShiftLeftConcreteGeqBitWidthError,
- testing::Values( //
- std::make_tuple(Val(1_i), Val(32_u)), //
- std::make_tuple(Val(1_i), Val(33_u)), //
- std::make_tuple(Val(1_i), Val(34_u)), //
- std::make_tuple(Val(1_i), Val(99999999_u)), //
- std::make_tuple(Val(1_u), Val(32_u)), //
- std::make_tuple(Val(1_u), Val(33_u)), //
- std::make_tuple(Val(1_u), Val(34_u)), //
- std::make_tuple(Val(1_u), Val(99999999_u)) //
- ));
-
-// AInt left shift results in sign change error
-using ResolverConstEvalShiftLeftSignChangeError = ResolverTestWithParam<std::tuple<Types, Types>>;
-TEST_P(ResolverConstEvalShiftLeftSignChangeError, Test) {
- auto* lhs_expr =
- std::visit([&](auto&& value) { return value.Expr(*this); }, std::get<0>(GetParam()));
- auto* rhs_expr =
- std::visit([&](auto&& value) { return value.Expr(*this); }, std::get<1>(GetParam()));
- GlobalConst("c", Shl(Source{{1, 1}}, lhs_expr, rhs_expr));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "1:1 error: shift left operation results in sign change");
-}
-template <typename T>
-std::vector<std::tuple<Types, Types>> ShiftLeftSignChangeErrorCases() {
- // Shift type is u32 for non-abstract
- using ST = std::conditional_t<IsAbstract<T>, T, u32>;
- using B = BitValues<T>;
- return {
- {Val(T{0b0001}), Val(ST{B::NumBits - 1})},
- {Val(T{0b0010}), Val(ST{B::NumBits - 2})},
- {Val(T{0b0100}), Val(ST{B::NumBits - 3})},
- {Val(T{0b1000}), Val(ST{B::NumBits - 4})},
- {Val(T{0b0011}), Val(ST{B::NumBits - 2})},
- {Val(T{0b0110}), Val(ST{B::NumBits - 3})},
- {Val(T{0b1100}), Val(ST{B::NumBits - 4})},
- {Val(B::AllButLeftMost), Val(ST{1})},
- {Val(B::AllButLeftMost), Val(ST{B::NumBits - 1})},
- {Val(B::LeftMost), Val(ST{1})},
- {Val(B::LeftMost), Val(ST{B::NumBits - 1})},
- };
-}
-INSTANTIATE_TEST_SUITE_P(Test,
- ResolverConstEvalShiftLeftSignChangeError,
- testing::ValuesIn(Concat( //
- ShiftLeftSignChangeErrorCases<AInt>(),
- ShiftLeftSignChangeErrorCases<i32>(),
- ShiftLeftSignChangeErrorCases<u32>())));
-
-} // namespace binary_op
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Builtin
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-namespace builtin {
-// Bring in std::ostream& operator<<(std::ostream& o, const Types& types)
-using resolver::operator<<;
-
-struct Case {
- Case(utils::VectorRef<Types> in_args, Types in_expected)
- : args(std::move(in_args)), expected(std::move(in_expected)) {}
-
- /// Expected value may be positive or negative
- Case& PosOrNeg() {
- expected_pos_or_neg = true;
- return *this;
- }
-
- /// Expected value should be compared using FLOAT_EQ instead of EQ
- Case& FloatComp() {
- float_compare = true;
- return *this;
- }
-
- utils::Vector<Types, 8> args;
- Types expected;
- bool expected_pos_or_neg = false;
- bool float_compare = false;
-};
-
-static std::ostream& operator<<(std::ostream& o, const Case& c) {
- o << "args: ";
- for (auto& a : c.args) {
- o << a << ", ";
- }
- o << "expected: " << c.expected << ", expected_pos_or_neg: " << c.expected_pos_or_neg;
- return o;
-}
-
-/// Creates a Case with Values for args and result
-static Case C(std::initializer_list<Types> args, Types result) {
- return Case{utils::Vector<Types, 8>{args}, std::move(result)};
-}
-
-/// Convenience overload that creates a Case with just scalars
-using ScalarTypes = std::variant<AInt, AFloat, u32, i32, f32, f16>;
-static Case C(std::initializer_list<ScalarTypes> sargs, ScalarTypes sresult) {
- utils::Vector<Types, 8> args;
- for (auto& sa : sargs) {
- std::visit([&](auto&& v) { return args.Push(Val(v)); }, sa);
- }
- Types result = Val(0_a);
- std::visit([&](auto&& v) { result = Val(v); }, sresult);
- return Case{std::move(args), std::move(result)};
-}
-
-using ResolverConstEvalBuiltinTest = ResolverTestWithParam<std::tuple<sem::BuiltinType, Case>>;
-
-TEST_P(ResolverConstEvalBuiltinTest, Test) {
- Enable(ast::Extension::kF16);
-
- auto builtin = std::get<0>(GetParam());
- auto& c = std::get<1>(GetParam());
-
- utils::Vector<const ast::Expression*, 8> args;
- for (auto& a : c.args) {
- std::visit([&](auto&& v) { args.Push(v.Expr(*this)); }, a);
- }
-
- std::visit(
- [&](auto&& expected) {
- using T = typename std::decay_t<decltype(expected)>::ElementType;
- auto* expr = Call(sem::str(builtin), std::move(args));
-
- GlobalConst("C", expr);
- auto* expected_expr = expected.Expr(*this);
- GlobalConst("E", expected_expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- const sem::Constant* value = sem->ConstantValue();
- ASSERT_NE(value, nullptr);
- EXPECT_TYPE(value->Type(), sem->Type());
-
- auto* expected_sem = Sem().Get(expected_expr);
- const sem::Constant* expected_value = expected_sem->ConstantValue();
- ASSERT_NE(expected_value, nullptr);
- EXPECT_TYPE(expected_value->Type(), expected_sem->Type());
-
- ForEachElemPair(value, expected_value,
- [&](const sem::Constant* a, const sem::Constant* b) {
- auto v = a->As<T>();
- auto e = b->As<T>();
- if constexpr (std::is_same_v<bool, T>) {
- EXPECT_EQ(v, e);
- } else if constexpr (IsFloatingPoint<T>) {
- if (std::isnan(e)) {
- EXPECT_TRUE(std::isnan(v));
- } else {
- auto vf = (c.expected_pos_or_neg ? Abs(v) : v);
- if (c.float_compare) {
- EXPECT_FLOAT_EQ(vf, e);
- } else {
- EXPECT_EQ(vf, e);
- }
- }
- } else {
- EXPECT_EQ((c.expected_pos_or_neg ? Abs(v) : v), e);
- // Check that the constant's integer doesn't contain unexpected
- // data in the MSBs that are outside of the bit-width of T.
- EXPECT_EQ(a->As<AInt>(), b->As<AInt>());
- }
- return HasFailure() ? Action::kStop : Action::kContinue;
- });
- },
- c.expected);
-}
-
-INSTANTIATE_TEST_SUITE_P( //
- MixedAbstractArgs,
- ResolverConstEvalBuiltinTest,
- testing::Combine(testing::Values(sem::BuiltinType::kAtan2),
- testing::ValuesIn(std::vector{
- C({0_a, -0.0_a}, kPi<AFloat>),
- C({1.0_a, 0_a}, kPiOver2<AFloat>),
- })));
-
-template <typename T, bool finite_only>
-std::vector<Case> Atan2Cases() {
- std::vector<Case> cases = {
- // If y is +/-0 and x is negative or -0, +/-PI is returned
- C({T(0.0), -T(0.0)}, kPi<T>).PosOrNeg().FloatComp(),
-
- // If y is +/-0 and x is positive or +0, +/-0 is returned
- C({T(0.0), T(0.0)}, T(0.0)).PosOrNeg(),
-
- // If x is +/-0 and y is negative, -PI/2 is returned
- C({-T(1.0), T(0.0)}, -kPiOver2<T>).FloatComp(), //
- C({-T(1.0), -T(0.0)}, -kPiOver2<T>).FloatComp(),
-
- // If x is +/-0 and y is positive, +PI/2 is returned
- C({T(1.0), T(0.0)}, kPiOver2<T>).FloatComp(), //
- C({T(1.0), -T(0.0)}, kPiOver2<T>).FloatComp(),
-
- // Vector tests
- C({Vec(T(0.0), T(0.0)), Vec(-T(0.0), T(0.0))}, Vec(kPi<T>, T(0.0))).PosOrNeg().FloatComp(),
- C({Vec(-T(1.0), -T(1.0)), Vec(T(0.0), -T(0.0))}, Vec(-kPiOver2<T>, -kPiOver2<T>))
- .FloatComp(),
- C({Vec(T(1.0), T(1.0)), Vec(T(0.0), -T(0.0))}, Vec(kPiOver2<T>, kPiOver2<T>)).FloatComp(),
- };
-
- if constexpr (!finite_only) {
- std::vector<Case> non_finite_cases = {
- // If y is +/-INF and x is finite, +/-PI/2 is returned
- C({T::Inf(), T(0.0)}, kPiOver2<T>).PosOrNeg().FloatComp(),
- C({-T::Inf(), T(0.0)}, kPiOver2<T>).PosOrNeg().FloatComp(),
-
- // If y is +/-INF and x is -INF, +/-3PI/4 is returned
- C({T::Inf(), -T::Inf()}, k3PiOver4<T>).PosOrNeg().FloatComp(),
- C({-T::Inf(), -T::Inf()}, k3PiOver4<T>).PosOrNeg().FloatComp(),
-
- // If y is +/-INF and x is +INF, +/-PI/4 is returned
- C({T::Inf(), T::Inf()}, kPiOver4<T>).PosOrNeg().FloatComp(),
- C({-T::Inf(), T::Inf()}, kPiOver4<T>).PosOrNeg().FloatComp(),
-
- // If x is -INF and y is finite and positive, +PI is returned
- C({T(0.0), -T::Inf()}, kPi<T>).FloatComp(),
-
- // If x is -INF and y is finite and negative, -PI is returned
- C({-T(0.0), -T::Inf()}, -kPi<T>).FloatComp(),
-
- // If x is +INF and y is finite and positive, +0 is returned
- C({T(0.0), T::Inf()}, T(0.0)),
-
- // If x is +INF and y is finite and negative, -0 is returned
- C({-T(0.0), T::Inf()}, -T(0.0)),
-
- // If either x is NaN or y is NaN, NaN is returned
- C({T::NaN(), T(0.0)}, T::NaN()),
- C({T(0.0), T::NaN()}, T::NaN()),
- C({T::NaN(), T::NaN()}, T::NaN()),
-
- // Vector tests
- C({Vec(T::Inf(), -T::Inf(), T::Inf(), -T::Inf()), //
- Vec(T(0.0), T(0.0), -T::Inf(), -T::Inf())}, //
- Vec(kPiOver2<T>, kPiOver2<T>, k3PiOver4<T>, k3PiOver4<T>))
- .PosOrNeg()
- .FloatComp(),
- };
- cases = Concat(cases, non_finite_cases);
- }
-
- return cases;
-}
-INSTANTIATE_TEST_SUITE_P( //
- Atan2,
- ResolverConstEvalBuiltinTest,
- testing::Combine(testing::Values(sem::BuiltinType::kAtan2),
- testing::ValuesIn(Concat(Atan2Cases<AFloat, true>(), //
- Atan2Cases<f32, false>(),
- Atan2Cases<f16, false>()))));
-
-template <typename T>
-std::vector<Case> ClampCases() {
- return {
- C({T(0), T(0), T(0)}, T(0)),
- C({T(0), T(42), T::Highest()}, T(42)),
- C({T::Lowest(), T(0), T(42)}, T(0)),
- C({T(0), T::Lowest(), T::Highest()}, T(0)),
- C({T(0), T::Highest(), T::Lowest()}, T::Lowest()),
- C({T::Highest(), T::Highest(), T::Highest()}, T::Highest()),
- C({T::Lowest(), T::Lowest(), T::Lowest()}, T::Lowest()),
- C({T::Highest(), T::Lowest(), T::Highest()}, T::Highest()),
- C({T::Lowest(), T::Lowest(), T::Highest()}, T::Lowest()),
-
- // Vector tests
- C({Vec(T(0), T(0)), //
- Vec(T(0), T(42)), //
- Vec(T(0), T::Highest())}, //
- Vec(T(0), T(42))), //
- C({Vec(T::Lowest(), T(0), T(0)), //
- Vec(T(0), T::Lowest(), T::Highest()), //
- Vec(T(42), T::Highest(), T::Lowest())}, //
- Vec(T(0), T(0), T::Lowest())),
- };
-}
-INSTANTIATE_TEST_SUITE_P( //
- Clamp,
- ResolverConstEvalBuiltinTest,
- testing::Combine(testing::Values(sem::BuiltinType::kClamp),
- testing::ValuesIn(Concat(ClampCases<AInt>(), //
- ClampCases<i32>(),
- ClampCases<u32>(),
- ClampCases<AFloat>(),
- ClampCases<f32>(),
- ClampCases<f16>()))));
-
-template <typename T>
-std::vector<Case> SelectCases() {
- return {
- C({Val(T{1}), Val(T{2}), Val(false)}, Val(T{1})),
- C({Val(T{1}), Val(T{2}), Val(true)}, Val(T{2})),
-
- C({Val(T{2}), Val(T{1}), Val(false)}, Val(T{2})),
- C({Val(T{2}), Val(T{1}), Val(true)}, Val(T{1})),
-
- C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(false, false)}, Vec(T{1}, T{2})),
- C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(false, true)}, Vec(T{1}, T{4})),
- C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(true, false)}, Vec(T{3}, T{2})),
- C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(true, true)}, Vec(T{3}, T{4})),
-
- C({Vec(T{1}, T{1}, T{2}, T{2}), //
- Vec(T{2}, T{2}, T{1}, T{1}), //
- Vec(false, true, false, true)}, //
- Vec(T{1}, T{2}, T{2}, T{1})), //
- };
-}
-static std::vector<Case> SelectBoolCases() {
- return {
- C({Val(true), Val(false), Val(false)}, Val(true)),
- C({Val(true), Val(false), Val(true)}, Val(false)),
-
- C({Val(false), Val(true), Val(true)}, Val(true)),
- C({Val(false), Val(true), Val(false)}, Val(false)),
-
- C({Vec(true, true, false, false), //
- Vec(false, false, true, true), //
- Vec(false, true, true, false)}, //
- Vec(true, false, true, false)), //
- };
-}
-INSTANTIATE_TEST_SUITE_P( //
- Select,
- ResolverConstEvalBuiltinTest,
- testing::Combine(testing::Values(sem::BuiltinType::kSelect),
- testing::ValuesIn(Concat(SelectCases<AInt>(), //
- SelectCases<i32>(),
- SelectCases<u32>(),
- SelectCases<AFloat>(),
- SelectCases<f32>(),
- SelectCases<f16>(),
- SelectBoolCases()))));
-
-} // namespace builtin
-
-} // namespace
-} // namespace tint::resolver
diff --git a/src/tint/resolver/const_eval_test.h b/src/tint/resolver/const_eval_test.h
new file mode 100644
index 0000000..3840540
--- /dev/null
+++ b/src/tint/resolver/const_eval_test.h
@@ -0,0 +1,292 @@
+// Copyright 2022 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.
+
+#ifndef SRC_TINT_RESOLVER_CONST_EVAL_TEST_H_
+#define SRC_TINT_RESOLVER_CONST_EVAL_TEST_H_
+
+#include <limits>
+#include <utility>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "src/tint/resolver/resolver_test_helper.h"
+#include "src/tint/sem/test_helper.h"
+
+namespace tint::resolver {
+
+template <typename T>
+inline const auto kPi = T(UnwrapNumber<T>(3.14159265358979323846));
+
+template <typename T>
+inline const auto kPiOver2 = T(UnwrapNumber<T>(1.57079632679489661923));
+
+template <typename T>
+inline const auto kPiOver4 = T(UnwrapNumber<T>(0.785398163397448309616));
+
+template <typename T>
+inline const auto k3PiOver4 = T(UnwrapNumber<T>(2.356194490192344928846));
+
+/// Walks the sem::Constant @p c, accumulating all the inner-most scalar values into @p args
+inline void CollectScalarArgs(const sem::Constant* c, builder::ScalarArgs& args) {
+ Switch(
+ c->Type(), //
+ [&](const sem::Bool*) { args.values.Push(c->As<bool>()); },
+ [&](const sem::I32*) { args.values.Push(c->As<i32>()); },
+ [&](const sem::U32*) { args.values.Push(c->As<u32>()); },
+ [&](const sem::F32*) { args.values.Push(c->As<f32>()); },
+ [&](const sem::F16*) { args.values.Push(c->As<f16>()); },
+ [&](Default) {
+ size_t i = 0;
+ while (auto* child = c->Index(i++)) {
+ CollectScalarArgs(child, args);
+ }
+ });
+}
+
+/// Walks the sem::Constant @p c, returning all the inner-most scalar values.
+inline builder::ScalarArgs ScalarArgsFrom(const sem::Constant* c) {
+ builder::ScalarArgs out;
+ CollectScalarArgs(c, out);
+ return out;
+}
+
+template <typename T>
+inline constexpr auto Negate(const Number<T>& v) {
+ if constexpr (std::is_integral_v<T>) {
+ if constexpr (std::is_signed_v<T>) {
+ // For signed integrals, avoid C++ UB by not negating the smallest negative number. In
+ // WGSL, this operation is well defined to return the same value, see:
+ // https://gpuweb.github.io/gpuweb/wgsl/#arithmetic-expr.
+ if (v == std::numeric_limits<T>::min()) {
+ return v;
+ }
+ return -v;
+
+ } else {
+ // Allow negating unsigned values
+ using ST = std::make_signed_t<T>;
+ auto as_signed = Number<ST>{static_cast<ST>(v)};
+ return Number<T>{static_cast<T>(Negate(as_signed))};
+ }
+ } else {
+ // float case
+ return -v;
+ }
+}
+
+template <typename T>
+inline auto Abs(const Number<T>& v) {
+ if constexpr (std::is_integral_v<T> && std::is_unsigned_v<T>) {
+ return v;
+ } else {
+ return Number<T>(std::abs(v));
+ }
+}
+
+TINT_BEGIN_DISABLE_WARNING(CONSTANT_OVERFLOW);
+template <typename T>
+inline constexpr Number<T> Mul(Number<T> v1, Number<T> v2) {
+ if constexpr (std::is_integral_v<T> && std::is_signed_v<T>) {
+ // For signed integrals, avoid C++ UB by multiplying as unsigned
+ using UT = std::make_unsigned_t<T>;
+ return static_cast<Number<T>>(static_cast<UT>(v1) * static_cast<UT>(v2));
+ } else {
+ return static_cast<Number<T>>(v1 * v2);
+ }
+}
+TINT_END_DISABLE_WARNING(CONSTANT_OVERFLOW);
+
+// Concats any number of std::vectors
+template <typename Vec, typename... Vecs>
+[[nodiscard]] inline auto Concat(Vec&& v1, Vecs&&... vs) {
+ auto total_size = v1.size() + (vs.size() + ...);
+ v1.reserve(total_size);
+ (std::move(vs.begin(), vs.end(), std::back_inserter(v1)), ...);
+ return std::move(v1);
+}
+
+// Concats vectors `vs` into `v1`
+template <typename Vec, typename... Vecs>
+inline void ConcatInto(Vec& v1, Vecs&&... vs) {
+ auto total_size = v1.size() + (vs.size() + ...);
+ v1.reserve(total_size);
+ (std::move(vs.begin(), vs.end(), std::back_inserter(v1)), ...);
+}
+
+// Concats vectors `vs` into `v1` iff `condition` is true
+template <bool condition, typename Vec, typename... Vecs>
+inline void ConcatIntoIf([[maybe_unused]] Vec& v1, [[maybe_unused]] Vecs&&... vs) {
+ if constexpr (condition) {
+ ConcatInto(v1, std::forward<Vecs>(vs)...);
+ }
+}
+
+using builder::IsValue;
+using builder::Mat;
+using builder::Val;
+using builder::Value;
+using builder::Vec;
+
+using Types = std::variant< //
+ Value<AInt>,
+ Value<AFloat>,
+ Value<u32>,
+ Value<i32>,
+ Value<f32>,
+ Value<f16>,
+ Value<bool>,
+
+ Value<builder::vec2<AInt>>,
+ Value<builder::vec2<AFloat>>,
+ Value<builder::vec2<u32>>,
+ Value<builder::vec2<i32>>,
+ Value<builder::vec2<f32>>,
+ Value<builder::vec2<f16>>,
+ Value<builder::vec2<bool>>,
+
+ Value<builder::vec3<AInt>>,
+ Value<builder::vec3<AFloat>>,
+ Value<builder::vec3<u32>>,
+ Value<builder::vec3<i32>>,
+ Value<builder::vec3<f32>>,
+ Value<builder::vec3<f16>>,
+ Value<builder::vec3<bool>>,
+
+ Value<builder::vec4<AInt>>,
+ Value<builder::vec4<AFloat>>,
+ Value<builder::vec4<u32>>,
+ Value<builder::vec4<i32>>,
+ Value<builder::vec4<f32>>,
+ Value<builder::vec4<f16>>,
+ Value<builder::vec4<bool>>,
+
+ Value<builder::mat2x2<AInt>>,
+ Value<builder::mat2x2<AFloat>>,
+ Value<builder::mat2x2<f32>>,
+ Value<builder::mat2x2<f16>>,
+
+ Value<builder::mat2x3<AInt>>,
+ Value<builder::mat2x3<AFloat>>,
+ Value<builder::mat2x3<f32>>,
+ Value<builder::mat2x3<f16>>,
+
+ Value<builder::mat3x2<AInt>>,
+ Value<builder::mat3x2<AFloat>>,
+ Value<builder::mat3x2<f32>>,
+ Value<builder::mat3x2<f16>>
+ //
+ >;
+
+inline std::ostream& operator<<(std::ostream& o, const Types& types) {
+ std::visit(
+ [&](auto&& v) {
+ using ValueType = std::decay_t<decltype(v)>;
+ o << ValueType::DataType::Name() << "(";
+ for (auto& a : v.args.values) {
+ o << std::get<typename ValueType::ElementType>(a);
+ if (&a != &v.args.values.Back()) {
+ o << ", ";
+ }
+ }
+ o << ")";
+ },
+ types);
+ return o;
+}
+
+// Calls `f` on deepest elements of both `a` and `b`. If function returns Action::kStop, it stops
+// traversing, and return Action::kStop; if the function returns Action::kContinue, it continues and
+// returns Action::kContinue when done.
+// TODO(amaiorano): Move to Constant.h?
+enum class Action { kStop, kContinue };
+template <typename Func>
+inline Action ForEachElemPair(const sem::Constant* a, const sem::Constant* b, Func&& f) {
+ EXPECT_EQ(a->Type(), b->Type());
+ size_t i = 0;
+ while (true) {
+ auto* a_elem = a->Index(i);
+ if (!a_elem) {
+ break;
+ }
+ auto* b_elem = b->Index(i);
+ if (ForEachElemPair(a_elem, b_elem, f) == Action::kStop) {
+ return Action::kStop;
+ }
+ i++;
+ }
+ if (i == 0) {
+ return f(a, b);
+ }
+ return Action::kContinue;
+}
+
+/// Defines common bit value patterns for the input `NumberT` type used for testing.
+template <typename NumberT>
+struct BitValues {
+ /// The unwrapped number type
+ using T = UnwrapNumber<NumberT>;
+ /// Details
+ struct detail {
+ /// Unsigned type of `T`
+ using UT = std::make_unsigned_t<T>;
+ /// Size in bits of type T
+ static constexpr size_t NumBits = sizeof(T) * 8;
+ /// All bits set 1
+ static constexpr T All = T{~T{0}};
+ /// Only left-most bits set to 1, rest set to 0
+ static constexpr T LeftMost = static_cast<T>(UT{1} << (NumBits - 1u));
+ /// Only left-most bits set to 0, rest set to 1
+ static constexpr T AllButLeftMost = T{~LeftMost};
+ /// Only two left-most bits set to 1, rest set to 0
+ static constexpr T TwoLeftMost = static_cast<T>(UT{0b11} << (NumBits - 2u));
+ /// Only two left-most bits set to 0, rest set to 1
+ static constexpr T AllButTwoLeftMost = T{~TwoLeftMost};
+ /// Only right-most bit set to 1, rest set to 0
+ static constexpr T RightMost = T{1};
+ /// Only right-most bit set to 0, rest set to 1
+ static constexpr T AllButRightMost = T{~RightMost};
+ };
+
+ /// Size in bits of type NumberT
+ static inline const size_t NumBits = detail::NumBits;
+ /// All bits set 1
+ static inline const NumberT All = NumberT{detail::All};
+ /// Only left-most bits set to 1, rest set to 0
+ static inline const NumberT LeftMost = NumberT{detail::LeftMost};
+ /// Only left-most bits set to 0, rest set to 1
+ static inline const NumberT AllButLeftMost = NumberT{detail::AllButLeftMost};
+ /// Only two left-most bits set to 1, rest set to 0
+ static inline const NumberT TwoLeftMost = NumberT{detail::TwoLeftMost};
+ /// Only two left-most bits set to 0, rest set to 1
+ static inline const NumberT AllButTwoLeftMost = NumberT{detail::AllButTwoLeftMost};
+ /// Only right-most bit set to 1, rest set to 0
+ static inline const NumberT RightMost = NumberT{detail::RightMost};
+ /// Only right-most bit set to 0, rest set to 1
+ static inline const NumberT AllButRightMost = NumberT{detail::AllButRightMost};
+
+ /// Performs a left-shift of `val` by `shiftBy`, both of varying type cast to `T`.
+ /// @param val value to shift left
+ /// @param shiftBy number of bits to shift left by
+ /// @returns the shifted value
+ template <typename U, typename V>
+ static constexpr NumberT Lsh(U val, V shiftBy) {
+ return NumberT{T{val} << T{shiftBy}};
+ }
+};
+
+using ResolverConstEvalTest = ResolverTest;
+
+} // namespace tint::resolver
+
+#endif // SRC_TINT_RESOLVER_CONST_EVAL_TEST_H_
diff --git a/src/tint/resolver/const_eval_unary_op_test.cc b/src/tint/resolver/const_eval_unary_op_test.cc
new file mode 100644
index 0000000..80b8caa
--- /dev/null
+++ b/src/tint/resolver/const_eval_unary_op_test.cc
@@ -0,0 +1,189 @@
+// Copyright 2022 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/tint/resolver/const_eval_test.h"
+
+using namespace tint::number_suffixes; // NOLINT
+
+namespace tint::resolver {
+namespace {
+
+// Bring in std::ostream& operator<<(std::ostream& o, const Types& types)
+using resolver::operator<<;
+
+struct Case {
+ Types input;
+ Types expected;
+};
+
+static std::ostream& operator<<(std::ostream& o, const Case& c) {
+ o << "input: " << c.input << ", expected: " << c.expected;
+ return o;
+}
+
+/// Creates a Case with Values of any type
+template <typename T, typename U>
+Case C(Value<T> input, Value<U> expected) {
+ return Case{std::move(input), std::move(expected)};
+}
+
+/// Convenience overload to creates a Case with just scalars
+template <typename T, typename U, typename = std::enable_if_t<!IsValue<T>>>
+Case C(T input, U expected) {
+ return Case{Val(input), Val(expected)};
+}
+
+using ResolverConstEvalUnaryOpTest = ResolverTestWithParam<std::tuple<ast::UnaryOp, Case>>;
+
+TEST_P(ResolverConstEvalUnaryOpTest, Test) {
+ Enable(ast::Extension::kF16);
+
+ auto op = std::get<0>(GetParam());
+ auto& c = std::get<1>(GetParam());
+ std::visit(
+ [&](auto&& expected) {
+ using T = typename std::decay_t<decltype(expected)>::ElementType;
+
+ auto* input_expr = std::visit([&](auto&& value) { return value.Expr(*this); }, c.input);
+ auto* expr = create<ast::UnaryOpExpression>(op, input_expr);
+
+ GlobalConst("C", expr);
+ auto* expected_expr = expected.Expr(*this);
+ GlobalConst("E", expected_expr);
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ const sem::Constant* value = sem->ConstantValue();
+ ASSERT_NE(value, nullptr);
+ EXPECT_TYPE(value->Type(), sem->Type());
+
+ auto* expected_sem = Sem().Get(expected_expr);
+ const sem::Constant* expected_value = expected_sem->ConstantValue();
+ ASSERT_NE(expected_value, nullptr);
+ EXPECT_TYPE(expected_value->Type(), expected_sem->Type());
+
+ ForEachElemPair(value, expected_value,
+ [&](const sem::Constant* a, const sem::Constant* b) {
+ EXPECT_EQ(a->As<T>(), b->As<T>());
+ if constexpr (IsIntegral<T>) {
+ // Check that the constant's integer doesn't contain unexpected
+ // data in the MSBs that are outside of the bit-width of T.
+ EXPECT_EQ(a->As<AInt>(), b->As<AInt>());
+ }
+ return HasFailure() ? Action::kStop : Action::kContinue;
+ });
+ },
+ c.expected);
+}
+INSTANTIATE_TEST_SUITE_P(Complement,
+ ResolverConstEvalUnaryOpTest,
+ testing::Combine(testing::Values(ast::UnaryOp::kComplement),
+ testing::ValuesIn({
+ // AInt
+ C(0_a, 0xffffffffffffffff_a),
+ C(0xffffffffffffffff_a, 0_a),
+ C(0xf0f0f0f0f0f0f0f0_a, 0x0f0f0f0f0f0f0f0f_a),
+ C(0xaaaaaaaaaaaaaaaa_a, 0x5555555555555555_a),
+ C(0x5555555555555555_a, 0xaaaaaaaaaaaaaaaa_a),
+ // u32
+ C(0_u, 0xffffffff_u),
+ C(0xffffffff_u, 0_u),
+ C(0xf0f0f0f0_u, 0x0f0f0f0f_u),
+ C(0xaaaaaaaa_u, 0x55555555_u),
+ C(0x55555555_u, 0xaaaaaaaa_u),
+ // i32
+ C(0_i, -1_i),
+ C(-1_i, 0_i),
+ C(1_i, -2_i),
+ C(-2_i, 1_i),
+ C(2_i, -3_i),
+ C(-3_i, 2_i),
+ })));
+
+INSTANTIATE_TEST_SUITE_P(Negation,
+ ResolverConstEvalUnaryOpTest,
+ testing::Combine(testing::Values(ast::UnaryOp::kNegation),
+ testing::ValuesIn({
+ // AInt
+ C(0_a, -0_a),
+ C(-0_a, 0_a),
+ C(1_a, -1_a),
+ C(-1_a, 1_a),
+ C(AInt::Highest(), -AInt::Highest()),
+ C(-AInt::Highest(), AInt::Highest()),
+ C(AInt::Lowest(), Negate(AInt::Lowest())),
+ C(Negate(AInt::Lowest()), AInt::Lowest()),
+ // i32
+ C(0_i, -0_i),
+ C(-0_i, 0_i),
+ C(1_i, -1_i),
+ C(-1_i, 1_i),
+ C(i32::Highest(), -i32::Highest()),
+ C(-i32::Highest(), i32::Highest()),
+ C(i32::Lowest(), Negate(i32::Lowest())),
+ C(Negate(i32::Lowest()), i32::Lowest()),
+ // AFloat
+ C(0.0_a, -0.0_a),
+ C(-0.0_a, 0.0_a),
+ C(1.0_a, -1.0_a),
+ C(-1.0_a, 1.0_a),
+ C(AFloat::Highest(), -AFloat::Highest()),
+ C(-AFloat::Highest(), AFloat::Highest()),
+ C(AFloat::Lowest(), Negate(AFloat::Lowest())),
+ C(Negate(AFloat::Lowest()), AFloat::Lowest()),
+ // f32
+ C(0.0_f, -0.0_f),
+ C(-0.0_f, 0.0_f),
+ C(1.0_f, -1.0_f),
+ C(-1.0_f, 1.0_f),
+ C(f32::Highest(), -f32::Highest()),
+ C(-f32::Highest(), f32::Highest()),
+ C(f32::Lowest(), Negate(f32::Lowest())),
+ C(Negate(f32::Lowest()), f32::Lowest()),
+ // f16
+ C(0.0_h, -0.0_h),
+ C(-0.0_h, 0.0_h),
+ C(1.0_h, -1.0_h),
+ C(-1.0_h, 1.0_h),
+ C(f16::Highest(), -f16::Highest()),
+ C(-f16::Highest(), f16::Highest()),
+ C(f16::Lowest(), Negate(f16::Lowest())),
+ C(Negate(f16::Lowest()), f16::Lowest()),
+ })));
+
+// Make sure UBSan doesn't trip on C++'s undefined behaviour of negating the smallest negative
+// number.
+TEST_F(ResolverConstEvalTest, UnaryNegateLowestAbstract) {
+ // const break_me = -(-9223372036854775808);
+ auto* c = GlobalConst("break_me", Negation(Negation(Expr(9223372036854775808_a))));
+ (void)c;
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(c);
+ EXPECT_EQ(sem->ConstantValue()->As<AInt>(), 9223372036854775808_a);
+}
+
+INSTANTIATE_TEST_SUITE_P(Not,
+ ResolverConstEvalUnaryOpTest,
+ testing::Combine(testing::Values(ast::UnaryOp::kNot),
+ testing::ValuesIn({
+ C(true, false),
+ C(false, true),
+ C(Vec(true, true), Vec(false, false)),
+ C(Vec(true, false), Vec(false, true)),
+ C(Vec(false, true), Vec(true, false)),
+ C(Vec(false, false), Vec(true, true)),
+ })));
+
+} // namespace
+} // namespace tint::resolver
diff --git a/src/tint/resolver/entry_point_validation_test.cc b/src/tint/resolver/entry_point_validation_test.cc
index 2a9f25b..79b41d7 100644
--- a/src/tint/resolver/entry_point_validation_test.cc
+++ b/src/tint/resolver/entry_point_validation_test.cc
@@ -170,7 +170,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
13:43 note: previously consumed location(0)
-12:34 note: while analysing entry point 'main')");
+12:34 note: while analyzing entry point 'main')");
}
TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_MemberMissingAttribute) {
@@ -198,7 +198,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(14:52 error: missing entry point IO attribute
-12:34 note: while analysing entry point 'main')");
+12:34 note: while analyzing entry point 'main')");
}
TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_DuplicateBuiltins) {
@@ -227,7 +227,7 @@
EXPECT_EQ(
r()->error(),
R"(12:34 error: builtin(frag_depth) attribute appears multiple times as pipeline output
-12:34 note: while analysing entry point 'main')");
+12:34 note: while analyzing entry point 'main')");
}
TEST_F(ResolverEntryPointValidationTest, ParameterAttribute_Location) {
@@ -339,7 +339,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
13:43 note: previously consumed location(0)
-12:34 note: while analysing entry point 'main')");
+12:34 note: while analyzing entry point 'main')");
}
TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_MemberMissingAttribute) {
@@ -366,7 +366,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(14:52 error: missing entry point IO attribute
-12:34 note: while analysing entry point 'main')");
+12:34 note: while analyzing entry point 'main')");
}
TEST_F(ResolverEntryPointValidationTest, Parameter_DuplicateBuiltins) {
@@ -432,7 +432,7 @@
EXPECT_EQ(
r()->error(),
R"(12:34 error: builtin(sample_index) attribute appears multiple times as pipeline input
-12:34 note: while analysing entry point 'main')");
+12:34 note: while analyzing entry point 'main')");
}
TEST_F(ResolverEntryPointValidationTest, VertexShaderMustReturnPosition) {
@@ -873,7 +873,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"14:52 error: nested structures cannot be used for entry point IO\n"
- "12:34 note: while analysing entry point 'main'");
+ "12:34 note: while analyzing entry point 'main'");
}
TEST_F(LocationAttributeTests, BadType_Input_Struct_RuntimeArray) {
@@ -1059,7 +1059,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"14:52 error: nested structures cannot be used for entry point IO\n"
- "12:34 note: while analysing entry point 'main'");
+ "12:34 note: while analyzing entry point 'main'");
}
TEST_F(LocationAttributeTests, ReturnType_Struct_RuntimeArray) {
@@ -1140,7 +1140,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: attribute is not valid for compute shader output\n"
- "56:78 note: while analysing entry point 'main'");
+ "56:78 note: while analyzing entry point 'main'");
}
TEST_F(LocationAttributeTests, ComputeShaderLocationStructMember_Input) {
@@ -1159,7 +1159,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: attribute is not valid for compute shader inputs\n"
- "56:78 note: while analysing entry point 'main'");
+ "56:78 note: while analyzing entry point 'main'");
}
TEST_F(LocationAttributeTests, Duplicate_input) {
@@ -1219,7 +1219,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"34:56 error: location(1) attribute appears multiple times\n"
- "12:34 note: while analysing entry point 'main'");
+ "12:34 note: while analyzing entry point 'main'");
}
} // namespace
diff --git a/src/tint/resolver/function_validation_test.cc b/src/tint/resolver/function_validation_test.cc
index 2fddc09..9fb0da4 100644
--- a/src/tint/resolver/function_validation_test.cc
+++ b/src/tint/resolver/function_validation_test.cc
@@ -672,7 +672,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
- "12:34 error: workgroup_size argument must be a constant or override expression of type "
+ "12:34 error: workgroup_size argument must be a constant or override-expression of type "
"abstract-integer, i32 or u32");
}
@@ -718,7 +718,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
- "12:34 error: workgroup_size argument must be a constant or override expression of type "
+ "12:34 error: workgroup_size argument must be a constant or override-expression of type "
"abstract-integer, i32 or u32");
}
@@ -767,6 +767,77 @@
EXPECT_EQ(r()->error(), "12:34 error: workgroup_size argument must be at least 1");
}
+TEST_F(ResolverFunctionValidationTest, WorkgroupSize_OverflowsU32_0x10000_0x100_0x100) {
+ // @compute @workgroup_size(0x10000, 0x100, 0x100)
+ // fn main() {}
+ Func("main", utils::Empty, ty.void_(), utils::Empty,
+ utils::Vector{
+ Stage(ast::PipelineStage::kCompute),
+ WorkgroupSize(0x10000_a, 0x100_a, Expr(Source{{12, 34}}, 0x100_a)),
+ });
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: total workgroup grid size cannot exceed 0xffffffff");
+}
+
+TEST_F(ResolverFunctionValidationTest, WorkgroupSize_OverflowsU32_0x10000_0x10000) {
+ // @compute @workgroup_size(0x10000, 0x10000)
+ // fn main() {}
+ Func("main", utils::Empty, ty.void_(), utils::Empty,
+ utils::Vector{
+ Stage(ast::PipelineStage::kCompute),
+ WorkgroupSize(0x10000_a, Expr(Source{{12, 34}}, 0x10000_a)),
+ });
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: total workgroup grid size cannot exceed 0xffffffff");
+}
+
+TEST_F(ResolverFunctionValidationTest, WorkgroupSize_OverflowsU32_0x10000_C_0x10000) {
+ // const C = 1;
+ // @compute @workgroup_size(0x10000, C, 0x10000)
+ // fn main() {}
+ GlobalConst("C", ty.u32(), Expr(1_a));
+ Func("main", utils::Empty, ty.void_(), utils::Empty,
+ utils::Vector{
+ Stage(ast::PipelineStage::kCompute),
+ WorkgroupSize(0x10000_a, "C", Expr(Source{{12, 34}}, 0x10000_a)),
+ });
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: total workgroup grid size cannot exceed 0xffffffff");
+}
+
+TEST_F(ResolverFunctionValidationTest, WorkgroupSize_OverflowsU32_0x10000_C) {
+ // const C = 0x10000;
+ // @compute @workgroup_size(0x10000, C)
+ // fn main() {}
+ GlobalConst("C", ty.u32(), Expr(0x10000_a));
+ Func("main", utils::Empty, ty.void_(), utils::Empty,
+ utils::Vector{
+ Stage(ast::PipelineStage::kCompute),
+ WorkgroupSize(0x10000_a, Expr(Source{{12, 34}}, "C")),
+ });
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: total workgroup grid size cannot exceed 0xffffffff");
+}
+
+TEST_F(ResolverFunctionValidationTest, WorkgroupSize_OverflowsU32_0x10000_O_0x10000) {
+ // override O = 0;
+ // @compute @workgroup_size(0x10000, O, 0x10000)
+ // fn main() {}
+ Override("O", ty.u32(), Expr(0_a));
+ Func("main", utils::Empty, ty.void_(), utils::Empty,
+ utils::Vector{
+ Stage(ast::PipelineStage::kCompute),
+ WorkgroupSize(0x10000_a, "O", Expr(Source{{12, 34}}, 0x10000_a)),
+ });
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: total workgroup grid size cannot exceed 0xffffffff");
+}
+
TEST_F(ResolverFunctionValidationTest, WorkgroupSize_NonConst) {
// var<private> x = 64i;
// @compute @workgroup_size(x)
@@ -780,7 +851,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- "12:34 error: workgroup_size argument must be a constant or override expression of "
+ "12:34 error: workgroup_size argument must be a constant or override-expression of "
"type abstract-integer, i32 or u32");
}
@@ -795,7 +866,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- "12:34 error: workgroup_size argument must be a constant or override expression of "
+ "12:34 error: workgroup_size argument must be a constant or override-expression of "
"type abstract-integer, i32 or u32");
}
@@ -810,7 +881,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- "12:34 error: workgroup_size argument must be a constant or override expression of "
+ "12:34 error: workgroup_size argument must be a constant or override-expression of "
"type abstract-integer, i32 or u32");
}
@@ -825,7 +896,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- "12:34 error: workgroup_size argument must be a constant or override expression of "
+ "12:34 error: workgroup_size argument must be a constant or override-expression of "
"type abstract-integer, i32 or u32");
}
diff --git a/src/tint/resolver/host_shareable_validation_test.cc b/src/tint/resolver/host_shareable_validation_test.cc
index bd8f3ca..97519cd 100644
--- a/src/tint/resolver/host_shareable_validation_test.cc
+++ b/src/tint/resolver/host_shareable_validation_test.cc
@@ -36,7 +36,7 @@
EXPECT_EQ(
r()->error(),
R"(56:78 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
-12:34 note: while analysing structure member S.x
+12:34 note: while analyzing structure member S.x
56:78 note: while instantiating 'var' g)");
}
@@ -51,7 +51,7 @@
EXPECT_EQ(
r()->error(),
R"(56:78 error: Type 'vec3<bool>' cannot be used in address space 'storage' as it is non-host-shareable
-12:34 note: while analysing structure member S.x
+12:34 note: while analyzing structure member S.x
56:78 note: while instantiating 'var' g)");
}
@@ -67,7 +67,7 @@
EXPECT_EQ(
r()->error(),
R"(56:78 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
-12:34 note: while analysing structure member S.x
+12:34 note: while analyzing structure member S.x
56:78 note: while instantiating 'var' g)");
}
@@ -86,10 +86,10 @@
EXPECT_EQ(
r()->error(),
R"(9:10 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
-1:2 note: while analysing structure member I1.x
-3:4 note: while analysing structure member I2.y
-5:6 note: while analysing structure member I3.z
-7:8 note: while analysing structure member S.m
+1:2 note: while analyzing structure member I1.x
+3:4 note: while analyzing structure member I2.y
+5:6 note: while analyzing structure member I3.z
+7:8 note: while analyzing structure member S.m
9:10 note: while instantiating 'var' g)");
}
diff --git a/src/tint/resolver/intrinsic_table.cc b/src/tint/resolver/intrinsic_table.cc
index a82f9e8..6a190de 100644
--- a/src/tint/resolver/intrinsic_table.cc
+++ b/src/tint/resolver/intrinsic_table.cc
@@ -1186,7 +1186,7 @@
for (auto& p : match.parameters) {
params.Push(builder.create<sem::Parameter>(
nullptr, static_cast<uint32_t>(params.Length()), p.type, ast::AddressSpace::kNone,
- ast::Access::kInvalid, p.usage));
+ ast::Access::kUndefined, p.usage));
}
sem::PipelineStageSet supported_stages;
if (match.overload->flags.Contains(OverloadFlag::kSupportsVertexPipeline)) {
@@ -1384,7 +1384,7 @@
for (auto& p : match.parameters) {
params.Push(builder.create<sem::Parameter>(
nullptr, static_cast<uint32_t>(params.Length()), p.type, ast::AddressSpace::kNone,
- ast::Access::kInvalid, p.usage));
+ ast::Access::kUndefined, p.usage));
}
auto eval_stage = match.overload->const_eval_fn ? sem::EvaluationStage::kConstant
: sem::EvaluationStage::kRuntime;
@@ -1397,9 +1397,9 @@
// Conversion.
auto* target = utils::GetOrCreate(converters, match, [&]() {
- auto param = builder.create<sem::Parameter>(nullptr, 0u, match.parameters[0].type,
- ast::AddressSpace::kNone, ast::Access::kInvalid,
- match.parameters[0].usage);
+ auto param = builder.create<sem::Parameter>(
+ nullptr, 0u, match.parameters[0].type, ast::AddressSpace::kNone,
+ ast::Access::kUndefined, match.parameters[0].usage);
auto eval_stage = match.overload->const_eval_fn ? sem::EvaluationStage::kConstant
: sem::EvaluationStage::kRuntime;
return builder.create<sem::TypeConversion>(match.return_type, param, eval_stage);
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 0fe6691..b8ba0fe 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -251,7 +251,7 @@
[&](const ast::Pointer* t) -> sem::Pointer* {
if (auto* el = Type(t->type)) {
auto access = t->access;
- if (access == ast::Access::kInvalid) {
+ if (access == ast::Access::kUndefined) {
access = DefaultAccessForAddressSpace(t->address_space);
}
return builder_->create<sem::Pointer>(el, t->address_space, access);
@@ -386,12 +386,13 @@
sem::Variable* sem = nullptr;
if (is_global) {
sem = builder_->create<sem::GlobalVariable>(
- v, ty, sem::EvaluationStage::kRuntime, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ v, ty, sem::EvaluationStage::kRuntime, ast::AddressSpace::kNone,
+ ast::Access::kUndefined,
/* constant_value */ nullptr, sem::BindingPoint{}, std::nullopt);
} else {
sem = builder_->create<sem::LocalVariable>(v, ty, sem::EvaluationStage::kRuntime,
- ast::AddressSpace::kNone, ast::Access::kInvalid,
- current_statement_,
+ ast::AddressSpace::kNone,
+ ast::Access::kUndefined, current_statement_,
/* constant_value */ nullptr);
}
@@ -415,6 +416,8 @@
// Does the variable have a constructor?
if (v->constructor) {
+ ExprEvalStageConstraint constraint{sem::EvaluationStage::kOverride, "override initializer"};
+ TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
rhs = Materialize(Expression(v->constructor), ty);
if (!rhs) {
return nullptr;
@@ -441,7 +444,7 @@
}
auto* sem = builder_->create<sem::GlobalVariable>(
- v, ty, sem::EvaluationStage::kOverride, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ v, ty, sem::EvaluationStage::kOverride, ast::AddressSpace::kNone, ast::Access::kUndefined,
/* constant_value */ nullptr, sem::BindingPoint{}, std::nullopt);
sem->SetConstructor(rhs);
@@ -452,8 +455,8 @@
}
auto* c = materialize->ConstantValue();
if (!c) {
- // TODO(crbug.com/tint/1633): Handle invalid materialization when expressions
- // are supported.
+ // TODO(crbug.com/tint/1633): Handle invalid materialization when expressions are
+ // supported.
return nullptr;
}
@@ -492,9 +495,14 @@
return nullptr;
}
- const auto* rhs = Expression(c->constructor);
- if (!rhs) {
- return nullptr;
+ const sem::Expression* rhs = nullptr;
+ {
+ ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "const initializer"};
+ TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
+ rhs = Expression(c->constructor);
+ if (!rhs) {
+ return nullptr;
+ }
}
if (ty) {
@@ -508,12 +516,6 @@
ty = rhs->Type();
}
- const auto value = rhs->ConstantValue();
- if (!value) {
- AddError("'const' initializer must be constant expression", c->constructor->source);
- return nullptr;
- }
-
if (!validator_.VariableInitializer(c, ast::AddressSpace::kNone, ty, rhs)) {
return nullptr;
}
@@ -524,12 +526,13 @@
return nullptr;
}
+ const auto value = rhs->ConstantValue();
auto* sem = is_global ? static_cast<sem::Variable*>(builder_->create<sem::GlobalVariable>(
c, ty, sem::EvaluationStage::kConstant, ast::AddressSpace::kNone,
- ast::Access::kInvalid, value, sem::BindingPoint{}, std::nullopt))
+ ast::Access::kUndefined, value, sem::BindingPoint{}, std::nullopt))
: static_cast<sem::Variable*>(builder_->create<sem::LocalVariable>(
c, ty, sem::EvaluationStage::kConstant, ast::AddressSpace::kNone,
- ast::Access::kInvalid, current_statement_, value));
+ ast::Access::kUndefined, current_statement_, value));
sem->SetConstructor(rhs);
builder_->Sem().Add(c, sem);
@@ -551,6 +554,12 @@
// Does the variable have a constructor?
if (var->constructor) {
+ ExprEvalStageConstraint constraint{
+ is_global ? sem::EvaluationStage::kOverride : sem::EvaluationStage::kRuntime,
+ "var initializer",
+ };
+ TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
+
rhs = Materialize(Expression(var->constructor), storage_ty);
if (!rhs) {
return nullptr;
@@ -588,7 +597,7 @@
}
auto access = var->declared_access;
- if (access == ast::Access::kInvalid) {
+ if (access == ast::Access::kUndefined) {
access = DefaultAccessForAddressSpace(address_space);
}
@@ -752,9 +761,9 @@
location = c->As<uint32_t>();
}
- auto* sem = builder_->create<sem::Parameter>(param, index, ty, ast::AddressSpace::kNone,
- ast::Access::kInvalid, sem::ParameterUsage::kNone,
- binding_point, location);
+ auto* sem = builder_->create<sem::Parameter>(
+ param, index, ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
+ sem::ParameterUsage::kNone, binding_point, location);
builder_->Sem().Add(param, sem);
return sem;
}
@@ -857,16 +866,13 @@
}
sem::Statement* Resolver::StaticAssert(const ast::StaticAssert* assertion) {
+ ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "static assertion"};
+ TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
auto* expr = Expression(assertion->condition);
if (!expr) {
return nullptr;
}
auto* cond = expr->ConstantValue();
- if (!cond) {
- AddError("static assertion condition must be a constant expression",
- assertion->condition->source);
- return nullptr;
- }
if (auto* ty = cond->Type(); !ty->Is<sem::Bool>()) {
AddError(
"static assertion condition must be a bool, got '" + builder_->FriendlyName(ty) + "'",
@@ -1067,12 +1073,12 @@
utils::Vector<const sem::Type*, 3> arg_tys;
constexpr const char* kErrBadExpr =
- "workgroup_size argument must be a constant or override expression of type "
+ "workgroup_size argument must be a constant or override-expression of type "
"abstract-integer, i32 or u32";
for (size_t i = 0; i < 3; i++) {
// Each argument to this attribute can either be a literal, an identifier for a module-scope
- // constants, a constant expression, or nullptr if not specified.
+ // constants, a const-expression, or nullptr if not specified.
auto* value = values[i];
if (!value) {
break;
@@ -1125,6 +1131,15 @@
}
}
+ uint64_t total_size = static_cast<uint64_t>(ws[0].value_or(1));
+ for (size_t i = 1; i < 3; i++) {
+ total_size *= static_cast<uint64_t>(ws[i].value_or(1));
+ if (total_size > 0xffffffff) {
+ AddError("total workgroup grid size cannot exceed 0xffffffff", values[i]->source);
+ return false;
+ }
+ }
+
current_function_->SetWorkgroupSize(std::move(ws));
return true;
}
@@ -1448,6 +1463,13 @@
return nullptr;
}
+ if (auto* constraint = expr_eval_stage_constraint_.constraint) {
+ if (!validator_.EvaluationStage(sem_expr, expr_eval_stage_constraint_.stage,
+ constraint)) {
+ return nullptr;
+ }
+ }
+
builder_->Sem().Add(expr, sem_expr);
if (expr == root) {
return sem_expr;
@@ -1788,7 +1810,7 @@
static_cast<uint32_t>(i), // index
arr->ElemType(), // type
ast::AddressSpace::kNone, // address_space
- ast::Access::kInvalid);
+ ast::Access::kUndefined);
});
return builder_->create<sem::TypeConstructor>(arr, std::move(params),
args_stage);
@@ -1817,7 +1839,7 @@
static_cast<uint32_t>(i), // index
str->Members()[i]->Type(), // type
ast::AddressSpace::kNone, // address_space
- ast::Access::kInvalid); // access
+ ast::Access::kUndefined); // access
}
return builder_->create<sem::TypeConstructor>(str, std::move(params),
args_stage);
@@ -2081,14 +2103,16 @@
if (texture_index == -1) {
TINT_ICE(Resolver, diagnostics_) << "texture builtin without texture parameter";
}
- auto* texture = args[static_cast<size_t>(texture_index)]->As<sem::VariableUser>()->Variable();
- if (!texture->Type()->UnwrapRef()->Is<sem::StorageTexture>()) {
- int sampler_index = signature.IndexOf(sem::ParameterUsage::kSampler);
- const sem::Variable* sampler =
- sampler_index != -1
- ? args[static_cast<size_t>(sampler_index)]->As<sem::VariableUser>()->Variable()
- : nullptr;
- current_function_->AddTextureSamplerPair(texture, sampler);
+ if (auto* user = args[static_cast<size_t>(texture_index)]->As<sem::VariableUser>()) {
+ auto* texture = user->Variable();
+ if (!texture->Type()->UnwrapRef()->Is<sem::StorageTexture>()) {
+ int sampler_index = signature.IndexOf(sem::ParameterUsage::kSampler);
+ const sem::Variable* sampler =
+ sampler_index != -1
+ ? args[static_cast<size_t>(sampler_index)]->As<sem::VariableUser>()->Variable()
+ : nullptr;
+ current_function_->AddTextureSamplerPair(texture, sampler);
+ }
}
}
@@ -2758,7 +2782,7 @@
// For alignment, use the alignment attribute if provided, otherwise use the
// default alignment for the member type.
// Diagnostic errors are raised if a basic rule is violated.
- // Validation of storage-class rules requires analysing the actual variable
+ // Validation of storage-class rules requires analyzing the actual variable
// usage of the structure, and so is performed as part of the variable
// validation.
uint64_t struct_size = 0;
@@ -2806,6 +2830,10 @@
// Offset attributes are not part of the WGSL spec, but are emitted
// by the SPIR-V reader.
+ ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant,
+ "@offset value"};
+ TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
+
auto* materialized = Materialize(Expression(o->expr));
if (!materialized) {
return nullptr;
@@ -2824,6 +2852,9 @@
align = 1;
has_offset_attr = true;
} else if (auto* a = attr->As<ast::StructMemberAlignAttribute>()) {
+ ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "@align"};
+ TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
+
auto* materialized = Materialize(Expression(a->expr));
if (!materialized) {
return nullptr;
@@ -2844,9 +2875,12 @@
AddError("'align' value must be a positive, power-of-two integer", a->source);
return nullptr;
}
- align = const_value->As<u32>();
+ align = u32(value);
has_align_attr = true;
} else if (auto* s = attr->As<ast::StructMemberSizeAttribute>()) {
+ ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "@size"};
+ TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
+
auto* materialized = Materialize(Expression(s->expr));
if (!materialized) {
return nullptr;
@@ -2864,9 +2898,12 @@
s->source);
return nullptr;
}
- size = const_value->As<u32>();
+ size = u32(value);
has_size_attr = true;
} else if (auto* l = attr->As<ast::LocationAttribute>()) {
+ ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "@location"};
+ TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
+
auto* materialize = Materialize(Expression(l->expr));
if (!materialize) {
return nullptr;
@@ -3228,7 +3265,7 @@
if (!ApplyAddressSpaceUsageToType(address_space, const_cast<sem::Type*>(member->Type()),
usage)) {
std::stringstream err;
- err << "while analysing structure member " << sem_.TypeNameOf(str) << "."
+ err << "while analyzing structure member " << sem_.TypeNameOf(str) << "."
<< builder_->Symbols().NameFor(member->Declaration()->symbol);
AddNote(err.str(), member->Declaration()->source);
return false;
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index 5c413f9..f699996 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -414,6 +414,15 @@
using StructConstructorSig =
utils::UnorderedKeyWrapper<std::tuple<const sem::Struct*, size_t, sem::EvaluationStage>>;
+ /// ExprEvalStageConstraint describes a constraint on when expressions can be evaluated.
+ struct ExprEvalStageConstraint {
+ /// The latest stage that the expression can be evaluated
+ sem::EvaluationStage stage = sem::EvaluationStage::kRuntime;
+ /// The 'thing' that is imposing the contraint. e.g. "var declaration"
+ /// If nullptr, then there is no constraint
+ const char* constraint = nullptr;
+ };
+
ProgramBuilder* const builder_;
diag::List& diagnostics_;
ConstEval const_eval_;
@@ -425,10 +434,10 @@
std::vector<sem::Function*> entry_points_;
std::unordered_map<const sem::Type*, const Source&> atomic_composite_info_;
utils::Bitset<0> marked_;
+ ExprEvalStageConstraint expr_eval_stage_constraint_;
std::unordered_map<OverrideId, const sem::Variable*> override_ids_;
std::unordered_map<ArrayConstructorSig, sem::CallTarget*> array_ctors_;
std::unordered_map<StructConstructorSig, sem::CallTarget*> struct_ctors_;
-
sem::Function* current_function_ = nullptr;
sem::Statement* current_statement_ = nullptr;
sem::CompoundStatement* current_compound_statement_ = nullptr;
diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc
index 631725a..5e4494c 100644
--- a/src/tint/resolver/resolver_test.cc
+++ b/src/tint/resolver/resolver_test.cc
@@ -2212,6 +2212,51 @@
EXPECT_TRUE(pairs[0].second == nullptr);
}
+TEST_F(ResolverTest, TextureSampler_Bug1715) { // crbug.com/tint/1715
+ // @binding(0) @group(0) var s: sampler;
+ // @binding(1) @group(0) var t: texture_2d<f32>;
+ // @binding(2) @group(0) var<uniform> c: vec2<f32>;
+ //
+ // @fragment
+ // fn main() -> @location(0) vec4<f32> {
+ // return helper(&s, &t);
+ // }
+ //
+ // fn helper(sl: ptr<function, sampler>, tl: ptr<function, texture_2d<f32>>) -> vec4<f32> {
+ // return textureSampleLevel(*tl, *sl, c, 0.0);
+ // }
+ GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), Group(0_a), Binding(0_a));
+ GlobalVar("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), Group(0_a),
+ Binding(1_a));
+ GlobalVar("c", ty.vec2<f32>(), ast::AddressSpace::kUniform, Group(0_a), Binding(2_a));
+
+ Func("main", utils::Empty, ty.vec4<f32>(),
+ utils::Vector{
+ Return(Call("helper", AddressOf("s"), AddressOf("t"))),
+ },
+ utils::Vector{
+ Stage(ast::PipelineStage::kFragment),
+ },
+ utils::Vector{
+ Location(0_u),
+ });
+
+ Func("helper",
+ utils::Vector{
+ Param("sl", ty.pointer(ty.sampler(ast::SamplerKind::kSampler),
+ ast::AddressSpace::kFunction)),
+ Param("tl", ty.pointer(ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+ ast::AddressSpace::kFunction)),
+ },
+ ty.vec4<f32>(),
+ utils::Vector{
+ Return(Call("textureSampleLevel", Deref("tl"), Deref("sl"), "c", 0.0_a)),
+ });
+
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "error: cannot take the address of expression in handle address space");
+}
+
TEST_F(ResolverTest, ModuleDependencyOrderedDeclarations) {
auto* f0 = Func("f0", utils::Empty, ty.void_(), utils::Empty);
auto* v0 = GlobalVar("v0", ty.i32(), ast::AddressSpace::kPrivate);
diff --git a/src/tint/resolver/static_assert_test.cc b/src/tint/resolver/static_assert_test.cc
index ea7396d..6e8ecb7 100644
--- a/src/tint/resolver/static_assert_test.cc
+++ b/src/tint/resolver/static_assert_test.cc
@@ -88,7 +88,8 @@
WrapInFunction(StaticAssert(Expr(Source{{12, 34}}, "V")));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- "12:34 error: static assertion condition must be a constant expression");
+ "12:34 error: static assertion requires a const-expression, but expression is a "
+ "runtime-expression");
}
TEST_F(ResolverStaticAssertTest, Local_LessThan_Pass) {
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index 1271f06..7cc5cef 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -1165,7 +1165,7 @@
// var a : texture_storage_1d<ru32int>;
auto* st = ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d,
- ast::TexelFormat::kR32Uint, ast::Access::kInvalid);
+ ast::TexelFormat::kR32Uint, ast::Access::kUndefined);
GlobalVar("a", st, Group(0_a), Binding(0_a));
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 35ba7e9..3066315 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -263,7 +263,7 @@
switch (t->access) {
case ast::Access::kWrite:
break;
- case ast::Access::kInvalid:
+ case ast::Access::kUndefined:
AddError("storage texture missing access control", t->source);
return false;
default:
@@ -593,7 +593,7 @@
[&](const ast::Var* var) {
if (auto* init = global->Constructor();
init && init->Stage() > sem::EvaluationStage::kOverride) {
- AddError("module-scope 'var' initializer must be a constant or override expression",
+ AddError("module-scope 'var' initializer must be a constant or override-expression",
init->Declaration()->source);
return false;
}
@@ -621,7 +621,7 @@
// https://gpuweb.github.io/gpuweb/wgsl/#variable-declaration
// The access mode always has a default, and except for variables in the storage address
// space, must not be written.
- if (var->declared_access != ast::Access::kInvalid) {
+ if (var->declared_access != ast::Access::kUndefined) {
if (global->AddressSpace() == ast::AddressSpace::kStorage) {
// The access mode for the storage address space can only be 'read' or
// 'read_write'.
@@ -795,7 +795,7 @@
auto* storage_ty = v->Type()->UnwrapRef();
if (auto* init = v->Constructor(); init && init->Stage() > sem::EvaluationStage::kOverride) {
- AddError("'override' initializer must be an override expression",
+ AddError("'override' initializer must be an override-expression",
init->Declaration()->source);
return false;
}
@@ -1021,7 +1021,7 @@
}
if (attr->type == ast::InterpolationType::kFlat &&
- attr->sampling != ast::InterpolationSampling::kInvalid) {
+ attr->sampling != ast::InterpolationSampling::kUndefined) {
AddError("flat interpolation attribute must not have a sampling parameter", attr->source);
return false;
}
@@ -1299,7 +1299,7 @@
member->Declaration()->attributes, member->Type(),
member->Declaration()->source, param_or_ret,
/*is_struct_member*/ true, member->Location())) {
- AddNote("while analysing entry point '" + symbols_.NameFor(decl->symbol) + "'",
+ AddNote("while analyzing entry point '" + symbols_.NameFor(decl->symbol) + "'",
decl->source);
return false;
}
@@ -1391,6 +1391,30 @@
return true;
}
+bool Validator::EvaluationStage(const sem::Expression* expr,
+ sem::EvaluationStage latest_stage,
+ std::string_view constraint) const {
+ if (expr->Stage() > latest_stage) {
+ auto stage_name = [](sem::EvaluationStage stage) -> std::string {
+ switch (stage) {
+ case sem::EvaluationStage::kRuntime:
+ return "a runtime-expression";
+ case sem::EvaluationStage::kOverride:
+ return "an override-expression";
+ case sem::EvaluationStage::kConstant:
+ return "a const-expression";
+ }
+ return "<unknown>";
+ };
+
+ AddError(std::string(constraint) + " requires " + stage_name(latest_stage) +
+ ", but expression is " + stage_name(expr->Stage()),
+ expr->Declaration()->source);
+ return false;
+ }
+ return true;
+}
+
bool Validator::Statements(utils::VectorRef<const ast::Statement*> stmts) const {
for (auto* stmt : stmts) {
if (!sem_.Get(stmt)->IsReachable()) {
@@ -1663,52 +1687,31 @@
std::string name = sem::str(usage);
auto* arg = call->Arguments()[index];
if (auto values = arg->ConstantValue()) {
- // Assert that the constant values are of the expected type.
- if (!values->Type()->is_integer_scalar_or_vector()) {
- TINT_ICE(Resolver, diagnostics_)
- << "failed to resolve '" + func_name + "' " << name << " parameter type";
- return false;
- }
-
- // Currently const_expr is restricted to literals and type constructors.
- // Check that that's all we have for the parameter.
- bool is_const_expr = true;
- ast::TraverseExpressions(
- arg->Declaration(), diagnostics_, [&](const ast::Expression* e) {
- if (e->IsAnyOf<ast::LiteralExpression, ast::CallExpression>()) {
- return ast::TraverseAction::Descend;
- }
- is_const_expr = false;
- return ast::TraverseAction::Stop;
- });
- if (is_const_expr) {
- if (auto* vector = builtin->Parameters()[index]->Type()->As<sem::Vector>()) {
- for (size_t i = 0; i < vector->Width(); i++) {
- auto value = values->Index(i)->As<AInt>();
- if (value < min || value > max) {
- AddError("each component of the " + name +
- " argument must be at least " + std::to_string(min) +
- " and at most " + std::to_string(max) + ". " + name +
- " component " + std::to_string(i) + " is " +
- std::to_string(value),
- arg->Declaration()->source);
- return false;
- }
- }
- } else {
- auto value = values->As<AInt>();
+ if (auto* vector = values->Type()->As<sem::Vector>()) {
+ for (size_t i = 0; i < vector->Width(); i++) {
+ auto value = values->Index(i)->As<AInt>();
if (value < min || value > max) {
- AddError("the " + name + " argument must be at least " +
+ AddError("each component of the " + name + " argument must be at least " +
std::to_string(min) + " and at most " + std::to_string(max) +
- ". " + name + " is " + std::to_string(value),
+ ". " + name + " component " + std::to_string(i) + " is " +
+ std::to_string(value),
arg->Declaration()->source);
return false;
}
}
- return true;
+ } else {
+ auto value = values->As<AInt>();
+ if (value < min || value > max) {
+ AddError("the " + name + " argument must be at least " + std::to_string(min) +
+ " and at most " + std::to_string(max) + ". " + name + " is " +
+ std::to_string(value),
+ arg->Declaration()->source);
+ return false;
+ }
}
+ return true;
}
- AddError("the " + name + " argument must be a const_expression",
+ AddError("the " + name + " argument must be a const-expression",
arg->Declaration()->source);
return false;
};
@@ -1726,7 +1729,7 @@
}
const auto extension = builtin->RequiredExtension();
- if (extension == ast::Extension::kInvalid) {
+ if (extension == ast::Extension::kUndefined) {
return true;
}
@@ -1902,7 +1905,7 @@
}
if (array_type->IsOverrideSized()) {
- AddError("cannot construct an array that has an override expression count", ctor->source);
+ AddError("cannot construct an array that has an override-expression count", ctor->source);
return false;
}
diff --git a/src/tint/resolver/validator.h b/src/tint/resolver/validator.h
index 2b84ad1..1297a9a 100644
--- a/src/tint/resolver/validator.h
+++ b/src/tint/resolver/validator.h
@@ -25,6 +25,7 @@
#include "src/tint/ast/pipeline_stage.h"
#include "src/tint/program_builder.h"
#include "src/tint/resolver/sem_helper.h"
+#include "src/tint/sem/evaluation_stage.h"
#include "src/tint/source.h"
// Forward declarations
@@ -209,6 +210,15 @@
/// @returns true on success, false otherwise
bool EntryPoint(const sem::Function* func, ast::PipelineStage stage) const;
+ /// Validates that the expression must not be evaluated any later than @p latest_stage
+ /// @param expr the expression to check
+ /// @param latest_stage the latest evaluation stage that the expression can be evaluated
+ /// @param constraint the 'thing' that is imposing the contraint. e.g. "var declaration"
+ /// @returns true if @p expr is evaluated in or before @p latest_stage, false otherwise
+ bool EvaluationStage(const sem::Expression* expr,
+ sem::EvaluationStage latest_stage,
+ std::string_view constraint) const;
+
/// Validates a for loop
/// @param stmt the for loop statement to validate
/// @returns true on success, false otherwise
diff --git a/src/tint/resolver/variable_validation_test.cc b/src/tint/resolver/variable_validation_test.cc
index 28f8dc3..65cfd51 100644
--- a/src/tint/resolver/variable_validation_test.cc
+++ b/src/tint/resolver/variable_validation_test.cc
@@ -329,7 +329,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
-56:78 note: while analysing structure member S.m
+56:78 note: while analyzing structure member S.m
12:34 note: while instantiating 'var' v)");
}
@@ -423,7 +423,9 @@
WrapInFunction(v, c);
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(12:34 error: 'const' initializer must be constant expression)");
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: const initializer requires a const-expression, but expression is a runtime-expression)");
}
TEST_F(ResolverVariableValidationTest, ConstInitWithOverride) {
@@ -432,7 +434,9 @@
WrapInFunction(c);
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(12:34 error: 'const' initializer must be constant expression)");
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: const initializer requires a const-expression, but expression is an override-expression)");
}
TEST_F(ResolverVariableValidationTest, ConstInitWithLet) {
@@ -441,7 +445,30 @@
WrapInFunction(l, c);
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(12:34 error: 'const' initializer must be constant expression)");
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: const initializer requires a const-expression, but expression is a runtime-expression)");
+}
+
+TEST_F(ResolverVariableValidationTest, ConstInitWithRuntimeExpr) {
+ // const c = clamp(2, dpdx(0.5), 3);
+ WrapInFunction(Const("c", Call("clamp", 2_a, Call(Source{{12, 34}}, "dpdx", 0.5_a), 3_a)));
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: const initializer requires a const-expression, but expression is a runtime-expression)");
+}
+
+TEST_F(ResolverVariableValidationTest, ConstInitWithOverrideExpr) {
+ auto* o = Override("v", Expr(1_i));
+ auto* c = Const("c", Add(10_a, Expr(Source{{12, 34}}, o)));
+ WrapInFunction(c);
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: const initializer requires a const-expression, but expression is an override-expression)");
}
} // namespace
diff --git a/src/tint/sem/array.h b/src/tint/sem/array.h
index d41cd98..693549d 100644
--- a/src/tint/sem/array.h
+++ b/src/tint/sem/array.h
@@ -31,7 +31,7 @@
namespace tint::sem {
-/// The variant of an ArrayCount when the array is a constant expression.
+/// The variant of an ArrayCount when the array is a const-expression.
/// Example:
/// ```
/// const N = 123;
@@ -144,7 +144,7 @@
/// @returns the number of elements in the array.
const ArrayCount& Count() const { return count_; }
- /// @returns the array count if the count is a constant expression, otherwise returns nullopt.
+ /// @returns the array count if the count is a const-expression, otherwise returns nullopt.
inline std::optional<uint32_t> ConstantCount() const {
if (auto* count = std::get_if<ConstantArrayCount>(&count_)) {
return count->value;
@@ -175,7 +175,7 @@
/// natural stride
bool IsStrideImplicit() const { return stride_ == implicit_stride_; }
- /// @returns true if this array is sized using an constant expression
+ /// @returns true if this array is sized using an const-expression
bool IsConstantSized() const { return std::holds_alternative<ConstantArrayCount>(count_); }
/// @returns true if this array is sized using an override variable
diff --git a/src/tint/sem/builtin.cc b/src/tint/sem/builtin.cc
index 5017352..68d8fc8 100644
--- a/src/tint/sem/builtin.cc
+++ b/src/tint/sem/builtin.cc
@@ -182,7 +182,7 @@
if (IsDP4a()) {
return ast::Extension::kChromiumExperimentalDp4A;
}
- return ast::Extension::kInvalid;
+ return ast::Extension::kUndefined;
}
} // namespace tint::sem
diff --git a/src/tint/sem/function.h b/src/tint/sem/function.h
index 50d853c..9ef99ab 100644
--- a/src/tint/sem/function.h
+++ b/src/tint/sem/function.h
@@ -40,8 +40,9 @@
namespace tint::sem {
/// WorkgroupSize is a three-dimensional array of WorkgroupDimensions.
-/// Each dimension is a std::optional as a workgroup size can be a constant or override expression.
-/// Override expressions are not known at compilation time, so these will be std::nullopt.
+/// Each dimension is a std::optional as a workgroup size can be a const-expression or
+/// override-expression. Override expressions are not known at compilation time, so these will be
+/// std::nullopt.
using WorkgroupSize = std::array<std::optional<uint32_t>, 3>;
/// Function holds the semantic information for function nodes.
diff --git a/src/tint/sem/pointer.cc b/src/tint/sem/pointer.cc
index cb73724..3918233 100644
--- a/src/tint/sem/pointer.cc
+++ b/src/tint/sem/pointer.cc
@@ -25,7 +25,7 @@
Pointer::Pointer(const Type* subtype, ast::AddressSpace address_space, ast::Access access)
: subtype_(subtype), address_space_(address_space), access_(access) {
TINT_ASSERT(Semantic, !subtype->Is<Reference>());
- TINT_ASSERT(Semantic, access != ast::Access::kInvalid);
+ TINT_ASSERT(Semantic, access != ast::Access::kUndefined);
}
size_t Pointer::Hash() const {
diff --git a/src/tint/sem/reference.cc b/src/tint/sem/reference.cc
index 297b779..a3a9f24 100644
--- a/src/tint/sem/reference.cc
+++ b/src/tint/sem/reference.cc
@@ -24,7 +24,7 @@
Reference::Reference(const Type* subtype, ast::AddressSpace address_space, ast::Access access)
: subtype_(subtype), address_space_(address_space), access_(access) {
TINT_ASSERT(Semantic, !subtype->Is<Reference>());
- TINT_ASSERT(Semantic, access != ast::Access::kInvalid);
+ TINT_ASSERT(Semantic, access != ast::Access::kUndefined);
}
size_t Reference::Hash() const {
diff --git a/src/tint/sem/storage_texture.cc b/src/tint/sem/storage_texture.cc
index caa8a79..763eb21 100644
--- a/src/tint/sem/storage_texture.cc
+++ b/src/tint/sem/storage_texture.cc
@@ -75,7 +75,7 @@
return type_mgr.Get<sem::F32>();
}
- case ast::TexelFormat::kInvalid:
+ case ast::TexelFormat::kUndefined:
break;
}
diff --git a/src/tint/symbol_table.h b/src/tint/symbol_table.h
index e07d4df..617266f 100644
--- a/src/tint/symbol_table.h
+++ b/src/tint/symbol_table.h
@@ -52,7 +52,7 @@
/// Returns the symbol for the given `name`
/// @param name the name to lookup
- /// @returns the symbol for the name or symbol::kInvalid if not found.
+ /// @returns the symbol for the name or symbol::kUndefined if not found.
Symbol Get(const std::string& name) const;
/// Returns the name for the given symbol
diff --git a/src/tint/templates/enums.tmpl.inc b/src/tint/templates/enums.tmpl.inc
index 4726d80..d04140b 100644
--- a/src/tint/templates/enums.tmpl.inc
+++ b/src/tint/templates/enums.tmpl.inc
@@ -12,7 +12,7 @@
{{- /* ------------------------------------------------------------------ */ -}}
{{- $enum := PascalCase $.Name -}}
enum class {{$enum}} {
- kInvalid,
+ kUndefined,
{{- range $entry := $.Entries }}
k{{PascalCase $entry.Name}},{{if $entry.IsInternal}} // Tint-internal enum entry - not parsed{{end}}
{{- end }}
@@ -25,7 +25,7 @@
/// Parse{{$enum}} parses a {{$enum}} from a string.
/// @param str the string to parse
-/// @returns the parsed enum, or {{$enum}}::kInvalid if the string could not be parsed.
+/// @returns the parsed enum, or {{$enum}}::kUndefined if the string could not be parsed.
{{$enum}} Parse{{$enum}}(std::string_view str);
constexpr const char* k{{$enum}}Strings[] = {
@@ -47,14 +47,14 @@
{{- $enum := PascalCase $.Name -}}
/// Parse{{$enum}} parses a {{$enum}} from a string.
/// @param str the string to parse
-/// @returns the parsed enum, or {{$enum}}::kInvalid if the string could not be parsed.
+/// @returns the parsed enum, or {{$enum}}::kUndefined if the string could not be parsed.
{{$enum}} Parse{{$enum}}(std::string_view str) {
{{- range $entry := $.PublicEntries }}
if (str == "{{$entry.Name}}") {
return {{template "EnumCase" $entry}};
}
{{- end }}
- return {{$enum}}::kInvalid;
+ return {{$enum}}::kUndefined;
}
{{- end -}}
@@ -67,8 +67,8 @@
{{- $enum := PascalCase $.Name -}}
std::ostream& operator<<(std::ostream& out, {{$enum}} value) {
switch (value) {
- case {{$enum}}::kInvalid:
- return out << "invalid";
+ case {{$enum}}::kUndefined:
+ return out << "undefined";
{{- range $entry := $.Entries }}
case {{template "EnumCase" $entry}}:
return out << "{{$entry.Name}}";
@@ -105,9 +105,9 @@
static constexpr Case kInvalidCases[] = {
{{- $exclude := $.NameSet -}}
{{- range $entry := $.PublicEntries }}
- {"{{Scramble $entry.Name $exclude}}", {{$enum}}::kInvalid},
- {"{{Scramble $entry.Name $exclude}}", {{$enum}}::kInvalid},
- {"{{Scramble $entry.Name $exclude}}", {{$enum}}::kInvalid},
+ {"{{Scramble $entry.Name $exclude}}", {{$enum}}::kUndefined},
+ {"{{Scramble $entry.Name $exclude}}", {{$enum}}::kUndefined},
+ {"{{Scramble $entry.Name $exclude}}", {{$enum}}::kUndefined},
{{- end }}
};
diff --git a/src/tint/tint.natvis b/src/tint/tint.natvis
index 45ecf93..aedd9be 100644
--- a/src/tint/tint.natvis
+++ b/src/tint/tint.natvis
@@ -16,6 +16,11 @@
-->
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
+
+ <!--=================================================================-->
+ <!-- utils -->
+ <!--=================================================================-->
+
<Type Name="tint::utils::Slice<*>">
<DisplayString>{{ length={len}, capacity={cap} }}</DisplayString>
<Expand>
@@ -29,10 +34,15 @@
</Type>
<Type Name="tint::utils::Vector<*,*>">
+ <Intrinsic Name="Length" Expression="(size_t)(impl_.slice.len)" />
<Expand>
<Item Name="[heap]">impl_.slice.cap > (int)$T2</Item>
- <ExpandedItem>impl_.slice</ExpandedItem>
+ <!--<ExpandedItem>impl_.slice</ExpandedItem>-->
<!--<Item Name="[slice]">impl_.slice</Item>-->
+ <ArrayItems>
+ <Size>Length()</Size>
+ <ValuePointer>impl_.slice.data</ValuePointer>
+ </ArrayItems>
</Expand>
</Type>
@@ -49,6 +59,10 @@
<DisplayString Optional="true">{debug_name_,sb}</DisplayString>
</Type>
+ <!--=================================================================-->
+ <!-- ast -->
+ <!--=================================================================-->
+
<Type Name="tint::ast::AssignmentStatement">
<DisplayString>{*lhs} = {*rhs};</DisplayString>
<Expand>
@@ -58,11 +72,8 @@
</Type>
<Type Name="tint::ast::IfStatement">
- <DisplayString Condition="else_statements.size() == 0">if ({*condition}) {*body}</DisplayString>
- <DisplayString Condition="else_statements.size() == 1">if ({*condition}) {*body} {*else_statements[0]} </DisplayString>
- <DisplayString Condition="else_statements.size() == 2">if ({*condition}) {*body} {*else_statements[0]} {*else_statements[1]} </DisplayString>
- <DisplayString Condition="else_statements.size() == 3">if ({*condition}) {*body} {*else_statements[0]} {*else_statements[1]} {*else_statements[2]}</DisplayString>
- <DisplayString Condition="else_statements.size() > 3">if ({*condition}) {*body} {*else_statements[0]} {*else_statements[1]} {*else_statements[2]} else {else_statements.size()-3} more...</DisplayString>
+ <DisplayString Condition="!else_statement">if ({*condition}) {*body}</DisplayString>
+ <DisplayString Condition="else_statement">if ({*condition}) {*body} else { *else_statement }</DisplayString>
</Type>
<Type Name="tint::ast::ElseStatement">
@@ -71,10 +82,10 @@
</Type>
<Type Name="tint::ast::BlockStatement">
- <DisplayString Condition="statements.size() == 1">{{ {*statements[0]} }} </DisplayString>
- <DisplayString Condition="statements.size() == 2">{{ {*statements[0]} {*statements[1]} }} </DisplayString>
- <DisplayString Condition="statements.size() == 3">{{ {*statements[0]} {*statements[1]} {*statements[2]} }} </DisplayString>
- <DisplayString Condition="statements.size() > 3">{{ {*statements[0]} {*statements[1]} {*statements[2]} {statements.size()-3} more... }} </DisplayString>
+ <DisplayString Condition="statements.Length() == 1">{{ {*statements[0]} }} </DisplayString>
+ <DisplayString Condition="statements.Length() == 2">{{ {*statements[0]} {*statements[1]} }} </DisplayString>
+ <DisplayString Condition="statements.Length() == 3">{{ {*statements[0]} {*statements[1]} {*statements[2]} }} </DisplayString>
+ <DisplayString Condition="statements.Length() > 3">{{ {*statements[0]} {*statements[1]} {*statements[2]} {statements.Length()-3} more... }} </DisplayString>
</Type>
<Type Name="tint::ast::ReturnStatement">
@@ -114,15 +125,16 @@
</Expand>
</Type>
- <Type Name="tint::ast::Variable">
- <!-- Hack: we can deduce that this variable is a parameter if it's const with a type but no constructor, which is illegal for a let -->
- <DisplayString Condition=" is_const & !!type & !constructor">{symbol} : {*type}</DisplayString>
+ <Type Name="tint::ast::Let">
+ <DisplayString Condition="!!type">let {symbol} : {*type} = {*constructor}</DisplayString>
+ <DisplayString Condition=" !type">let {symbol} = {*constructor}</DisplayString>
+ </Type>
- <DisplayString Condition=" is_const & !!type & !!constructor">let {symbol} : {*type} = {*constructor}</DisplayString>
- <DisplayString Condition=" is_const & !type & !constructor">let {symbol} = {*constructor}</DisplayString>
- <DisplayString Condition="!is_const & !!type & !!constructor">var {symbol} : {*type} = {*constructor}</DisplayString>
- <DisplayString Condition="!is_const & !type & !!constructor">var {symbol} = {*constructor}</DisplayString>
- <DisplayString Condition="!is_const & !!type & !constructor">var {symbol}</DisplayString>
+ <Type Name="tint::ast::Variable">
+ <DisplayString Condition="!!type & !!constructor">var {symbol} : {*type} = {*constructor}</DisplayString>
+ <DisplayString Condition="!!type & !constructor">var {symbol} : {*type}</DisplayString>
+ <DisplayString Condition=" !type & !!constructor">var {symbol} = {*constructor}</DisplayString>
+ <DisplayString Condition=" !type & !constructor">var {symbol}</DisplayString>
</Type>
<Type Name="tint::ast::VariableDeclStatement">
@@ -142,11 +154,11 @@
</Type>
<Type Name="tint::ast::UnaryOpExpression">
- <DisplayString Condition="op==tint::ast::UnaryOp::kAddressOf">&({expr})</DisplayString>
- <DisplayString Condition="op==tint::ast::UnaryOp::kComplement">~({expr})</DisplayString>
- <DisplayString Condition="op==tint::ast::UnaryOp::kIndirection">*({expr})</DisplayString>
- <DisplayString Condition="op==tint::ast::UnaryOp::kNegation">-({expr})</DisplayString>
- <DisplayString Condition="op==tint::ast::UnaryOp::kNot">!({expr})</DisplayString>
+ <DisplayString Condition="op==tint::ast::UnaryOp::kAddressOf">&({*expr})</DisplayString>
+ <DisplayString Condition="op==tint::ast::UnaryOp::kComplement">~({*expr})</DisplayString>
+ <DisplayString Condition="op==tint::ast::UnaryOp::kIndirection">*({*expr})</DisplayString>
+ <DisplayString Condition="op==tint::ast::UnaryOp::kNegation">-({*expr})</DisplayString>
+ <DisplayString Condition="op==tint::ast::UnaryOp::kNot">!({*expr})</DisplayString>
</Type>
<Type Name="tint::ast::BinaryExpression">
@@ -172,11 +184,12 @@
</Type>
<Type Name="tint::ast::CallExpression">
- <DisplayString Condition="args.size() == 0">{target}()</DisplayString>
- <DisplayString Condition="args.size() == 1">{target}({*args[0]})</DisplayString>
- <DisplayString Condition="args.size() == 2">{target}({*args[0]}, {*args[1]})</DisplayString>
- <DisplayString Condition="args.size() == 3">{target}({*args[0]}, {*args[1]}, {*args[2]})</DisplayString>
- <DisplayString Condition="args.size() > 3">{target}({*args[0]}, {*args[1]}, {*args[2]}, {args.size()-3} more...)</DisplayString>
+ <DisplayString Condition="args.Length() == 0">{target}()</DisplayString>
+ <DisplayString Condition="args.Length() == 1">{target}({*args[0]})</DisplayString>
+ <DisplayString Condition="args.Length() == 2">{target}({*args[0]}, {*args[1]})</DisplayString>
+ <DisplayString Condition="args.Length() == 3">{target}({*args[0]}, {*args[1]}, {*args[2]})</DisplayString>
+ <DisplayString Condition="args.Length() == 4">{target}({*args[0]}, {*args[1]}, {*args[2]}, {*args[3]})</DisplayString>
+ <DisplayString Condition="args.Length() > 4">{target}({*args[0]}, {*args[1]}, {*args[2]}, {args.Length()-3} more...)</DisplayString>
<!-- TODO: add more overloads -->
</Type>
@@ -190,6 +203,15 @@
<DisplayString Condition="count == nullptr">array<{*type}></DisplayString>
</Type>
+ <Type Name="tint::ast::Vector">
+ <DisplayString Condition="width==2 & !type">vec2</DisplayString>
+ <DisplayString Condition="width==3 & !type">vec3</DisplayString>
+ <DisplayString Condition="width==4 & !type">vec4</DisplayString>
+ <DisplayString Condition="width==2 & !!type">vec2<{*type}></DisplayString>
+ <DisplayString Condition="width==3 & !!type">vec3<{*type}></DisplayString>
+ <DisplayString Condition="width==4 & !!type">vec4<{*type}></DisplayString>
+ </Type>
+
<Type Name="tint::ast::TypeName">
<DisplayString>{name}</DisplayString>
</Type>
@@ -210,4 +232,36 @@
<DisplayString>f32</DisplayString>
</Type>
+ <!--=================================================================-->
+ <!-- sem -->
+ <!--=================================================================-->
+
+ <Type Name="tint::sem::AbstractInt">
+ <DisplayString>AbstractInt</DisplayString>
+ </Type>
+
+ <Type Name="tint::sem::AbstractFloat">
+ <DisplayString>AbstractFloat</DisplayString>
+ </Type>
+
+ <Type Name="tint::sem::Bool">
+ <DisplayString>bool</DisplayString>
+ </Type>
+
+ <Type Name="tint::sem::F32">
+ <DisplayString>f32</DisplayString>
+ </Type>
+
+ <Type Name="tint::sem::Vector">
+ <DisplayString>vec{width_}<{*subtype_}></DisplayString>
+ </Type>
+
+ <Type Name="tint::sem::Constant">
+ <DisplayString>Type={*Type()} Value={Value()}</DisplayString>
+ </Type>
+
+ <Type Name="tint::sem::Expression">
+ <DisplayString>Decl={*declaration_}</DisplayString>
+ </Type>
+
</AutoVisualizer>
diff --git a/src/tint/transform/binding_remapper.cc b/src/tint/transform/binding_remapper.cc
index 66d8e99..102bb1e 100644
--- a/src/tint/transform/binding_remapper.cc
+++ b/src/tint/transform/binding_remapper.cc
@@ -121,7 +121,7 @@
auto ac_it = remappings->access_controls.find(from);
if (ac_it != remappings->access_controls.end()) {
ast::Access ac = ac_it->second;
- if (ac == ast::Access::kInvalid) {
+ if (ac == ast::Access::kUndefined) {
ctx.dst->Diagnostics().add_error(
diag::System::Transform,
"invalid access mode (" + std::to_string(static_cast<uint32_t>(ac)) + ")");
diff --git a/src/tint/transform/canonicalize_entry_point_io.cc b/src/tint/transform/canonicalize_entry_point_io.cc
index 38a1b28..b990965 100644
--- a/src/tint/transform/canonicalize_entry_point_io.cc
+++ b/src/tint/transform/canonicalize_entry_point_io.cc
@@ -223,7 +223,7 @@
(ast::HasAttribute<ast::LocationAttribute>(attributes) ||
cfg.shader_style == ShaderStyle::kSpirv)) {
attributes.Push(ctx.dst->Interpolate(ast::InterpolationType::kFlat,
- ast::InterpolationSampling::kInvalid));
+ ast::InterpolationSampling::kUndefined));
}
// Disable validation for use of the `input` address space.
@@ -292,7 +292,7 @@
ast::HasAttribute<ast::LocationAttribute>(attributes) &&
!ast::HasAttribute<ast::InterpolateAttribute>(attributes)) {
attributes.Push(ctx.dst->Interpolate(ast::InterpolationType::kFlat,
- ast::InterpolationSampling::kInvalid));
+ ast::InterpolationSampling::kUndefined));
}
// In GLSL, if it's a builtin, override the name with the
diff --git a/src/tint/transform/robustness_test.cc b/src/tint/transform/robustness_test.cc
index fa04e47..fe2f343 100644
--- a/src/tint/transform/robustness_test.cc
+++ b/src/tint/transform/robustness_test.cc
@@ -1311,7 +1311,8 @@
}
)";
- auto* expect = R"(error: array size is an override-expression, when expected a constant-expression.
+ auto* expect =
+ R"(error: array size is an override-expression, when expected a constant-expression.
Was the SubstituteOverride transform run?)";
auto got = Run<Robustness>(src);
diff --git a/src/tint/transform/substitute_override.cc b/src/tint/transform/substitute_override.cc
index de597c2..bdaa45b 100644
--- a/src/tint/transform/substitute_override.cc
+++ b/src/tint/transform/substitute_override.cc
@@ -17,6 +17,8 @@
#include <functional>
#include "src/tint/program_builder.h"
+#include "src/tint/sem/builtin.h"
+#include "src/tint/sem/index_accessor_expression.h"
#include "src/tint/sem/variable.h"
TINT_INSTANTIATE_TYPEINFO(tint::transform::SubstituteOverride);
@@ -75,13 +77,32 @@
if (!ctor) {
ctx.dst->Diagnostics().add_error(diag::System::Transform,
- "Failed to create override expression");
+ "Failed to create override-expression");
return nullptr;
}
return ctx.dst->Const(src, sym, ty, ctor);
});
+ // Ensure that objects that are indexed with an override-expression are materialized.
+ // If the object is not materialized, and the 'override' variable is turned to a 'const', the
+ // resulting type of the index may change. See: crbug.com/tint/1697.
+ ctx.ReplaceAll(
+ [&](const ast::IndexAccessorExpression* expr) -> const ast::IndexAccessorExpression* {
+ if (auto* sem = ctx.src->Sem().Get(expr)) {
+ if (auto* access = sem->UnwrapMaterialize()->As<sem::IndexAccessorExpression>()) {
+ if (access->Object()->UnwrapMaterialize()->Type()->HoldsAbstract() &&
+ access->Index()->Stage() == sem::EvaluationStage::kOverride) {
+ auto& b = *ctx.dst;
+ auto* obj = b.Call(sem::str(sem::BuiltinType::kTintMaterialize),
+ ctx.Clone(expr->object));
+ return b.IndexAccessor(obj, ctx.Clone(expr->index));
+ }
+ }
+ }
+ return nullptr;
+ });
+
ctx.Clone();
}
diff --git a/src/tint/transform/substitute_override_test.cc b/src/tint/transform/substitute_override_test.cc
index f84c32c..abc67f5 100644
--- a/src/tint/transform/substitute_override_test.cc
+++ b/src/tint/transform/substitute_override_test.cc
@@ -238,5 +238,46 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(SubstituteOverrideTest, IndexMaterialization) {
+ auto* src = R"(
+override O = 0; // Try switching to 'const'
+
+fn f() {
+ const smaller_than_any_f32 = 1e-50;
+ const large_float = 1e27;
+ // When O is an override, the outer index value is not constant, so the
+ // value is not calculated at shader-creation time, and does not error.
+ //
+ // When O is a const, and 'smaller_than_any_f32' *is not* materialized, the
+ // outer index value will evaluate to 10000, resulting in an out-of-bounds
+ // error.
+ //
+ // When O is a const, and 'smaller_than_any_f32' *is* materialized, the
+ // materialization of 'smaller_than_any_f32' to f32 will evaluate to zero,
+ // and so the outer index value will be zero, and we get no error.
+ _ = vec2(0)[i32(vec2(smaller_than_any_f32)[O]*large_float*large_float)];
+}
+)";
+
+ auto* expect = R"(
+const O = 0i;
+
+fn f() {
+ const smaller_than_any_f32 = 1e-50;
+ const large_float = 1000000000000000013287555072.0;
+ _ = _tint_materialize(vec2(0))[i32(((_tint_materialize(vec2(smaller_than_any_f32))[O] * large_float) * large_float))];
+}
+)";
+
+ SubstituteOverride::Config cfg;
+ cfg.map.insert({OverrideId{0}, 0.0});
+
+ DataMap data;
+ data.Add<SubstituteOverride::Config>(cfg);
+ auto got = Run<SubstituteOverride>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
} // namespace
} // namespace tint::transform
diff --git a/src/tint/writer/append_vector.cc b/src/tint/writer/append_vector.cc
index 98e9efc..edf9132 100644
--- a/src/tint/writer/append_vector.cc
+++ b/src/tint/writer/append_vector.cc
@@ -137,7 +137,7 @@
auto* scalar_cast_target = b->create<sem::TypeConversion>(
packed_el_sem_ty,
b->create<sem::Parameter>(nullptr, 0u, scalar_sem->Type()->UnwrapRef(),
- ast::AddressSpace::kNone, ast::Access::kInvalid),
+ ast::AddressSpace::kNone, ast::Access::kUndefined),
sem::EvaluationStage::kRuntime);
auto* scalar_cast_sem = b->create<sem::Call>(
scalar_cast_ast, scalar_cast_target, sem::EvaluationStage::kRuntime,
@@ -158,7 +158,7 @@
[&](const tint::sem::Expression* arg, size_t i) -> const sem::Parameter* {
return b->create<sem::Parameter>(
nullptr, static_cast<uint32_t>(i), arg->Type()->UnwrapRef(),
- ast::AddressSpace::kNone, ast::Access::kInvalid);
+ ast::AddressSpace::kNone, ast::Access::kUndefined);
}),
sem::EvaluationStage::kRuntime);
auto* constructor_sem =
diff --git a/src/tint/writer/float_to_string.cc b/src/tint/writer/float_to_string.cc
index 7494ace..3b4260e 100644
--- a/src/tint/writer/float_to_string.cc
+++ b/src/tint/writer/float_to_string.cc
@@ -25,9 +25,34 @@
namespace tint::writer {
-std::string FloatToString(float f) {
- // Try printing the float in fixed point, with a smallish limit on the
- // precision
+namespace {
+
+template <typename T>
+struct Traits;
+
+template <>
+struct Traits<float> {
+ using uint_t = uint32_t;
+ static constexpr int kExponentBias = 127;
+ static constexpr uint_t kExponentMask = 0x7f800000;
+ static constexpr uint_t kMantissaMask = 0x007fffff;
+ static constexpr uint_t kSignMask = 0x80000000;
+ static constexpr int kMantissaBits = 23;
+};
+
+template <>
+struct Traits<double> {
+ using uint_t = uint64_t;
+ static constexpr int kExponentBias = 1023;
+ static constexpr uint_t kExponentMask = 0x7ff0000000000000;
+ static constexpr uint_t kMantissaMask = 0x000fffffffffffff;
+ static constexpr uint_t kSignMask = 0x8000000000000000;
+ static constexpr int kMantissaBits = 52;
+};
+
+template <typename F>
+std::string ToString(F f) {
+ // Try printing the float in fixed point, with a smallish limit on the precision
std::stringstream fixed;
fixed.flags(fixed.flags() | std::ios_base::showpoint | std::ios_base::fixed);
fixed.imbue(std::locale::classic());
@@ -36,13 +61,13 @@
std::string str = fixed.str();
// If this string can be parsed without loss of information, use it.
- // (Use double here to dodge a bug in older libc++ versions which
- // would incorrectly read back FLT_MAX as INF.)
+ // (Use double here to dodge a bug in older libc++ versions which would incorrectly read back
+ // FLT_MAX as INF.)
double roundtripped;
fixed >> roundtripped;
- auto float_equal_no_warning = std::equal_to<float>();
- if (float_equal_no_warning(f, static_cast<float>(roundtripped))) {
+ auto float_equal_no_warning = std::equal_to<F>();
+ if (float_equal_no_warning(f, static_cast<F>(roundtripped))) {
while (str.length() >= 2 && str[str.size() - 1] == '0' && str[str.size() - 2] != '.') {
str.pop_back();
}
@@ -50,38 +75,41 @@
return str;
}
- // Resort to scientific, with the minimum precision needed to preserve the
- // whole float
+ // Resort to scientific, with the minimum precision needed to preserve the whole float
std::stringstream sci;
sci.imbue(std::locale::classic());
- sci.precision(std::numeric_limits<float>::max_digits10);
+ sci.precision(std::numeric_limits<F>::max_digits10);
sci << f;
return sci.str();
}
-std::string FloatToBitPreservingString(float f) {
+template <typename F>
+std::string ToBitPreservingString(F f) {
+ using T = Traits<F>;
+ using uint_t = typename T::uint_t;
+
// For the NaN case, avoid handling the number as a floating point value.
// Some machines will modify the top bit in the mantissa of a NaN.
std::stringstream ss;
- uint32_t float_bits = 0u;
+ typename T::uint_t float_bits = 0u;
+ static_assert(sizeof(float_bits) == sizeof(f));
std::memcpy(&float_bits, &f, sizeof(float_bits));
// Handle the sign.
- const uint32_t kSignMask = 1u << 31;
- if (float_bits & kSignMask) {
+ if (float_bits & T::kSignMask) {
// If `f` is -0.0 print -0.0.
ss << '-';
// Strip sign bit.
- float_bits = float_bits & (~kSignMask);
+ float_bits = float_bits & (~T::kSignMask);
}
switch (std::fpclassify(f)) {
case FP_ZERO:
case FP_NORMAL:
std::memcpy(&f, &float_bits, sizeof(float_bits));
- ss << FloatToString(f);
+ ss << ToString(f);
break;
default: {
@@ -89,46 +117,39 @@
// TODO(dneto): It's unclear how Infinity and NaN should be handled.
// See https://github.com/gpuweb/gpuweb/issues/1769
- // std::hexfloat prints 'nan' and 'inf' instead of an
- // explicit representation like we want. Split it out
- // manually.
- const int kExponentBias = 127;
- const int kExponentMask = 0x7f800000;
- const int kMantissaMask = 0x007fffff;
- const int kMantissaBits = 23;
-
- int mantissaNibbles = (kMantissaBits + 3) / 4;
+ // std::hexfloat prints 'nan' and 'inf' instead of an explicit representation like we
+ // want. Split it out manually.
+ int mantissa_nibbles = (T::kMantissaBits + 3) / 4;
const int biased_exponent =
- static_cast<int>((float_bits & kExponentMask) >> kMantissaBits);
- int exponent = biased_exponent - kExponentBias;
- uint32_t mantissa = float_bits & kMantissaMask;
+ static_cast<int>((float_bits & T::kExponentMask) >> T::kMantissaBits);
+ int exponent = biased_exponent - T::kExponentBias;
+ uint_t mantissa = float_bits & T::kMantissaMask;
ss << "0x";
- if (exponent == 128) {
+ if (exponent == T::kExponentBias + 1) {
if (mantissa == 0) {
// Infinity case.
- ss << "1p+128";
+ ss << "1p+" << exponent;
} else {
- // NaN case.
- // Emit the mantissa bits as if they are left-justified after the
- // binary point. This is what SPIRV-Tools hex float emitter does,
- // and it's a justifiable choice independent of the bit width
- // of the mantissa.
- mantissa <<= (4 - (kMantissaBits % 4));
- // Remove trailing zeroes, for tidyness.
+ // NaN case.
+ // Emit the mantissa bits as if they are left-justified after the binary point.
+ // This is what SPIRV-Tools hex float emitter does, and it's a justifiable
+ // choice independent of the bit width of the mantissa.
+ mantissa <<= (4 - (T::kMantissaBits % 4));
+ // Remove trailing zeroes, for tidiness.
while (0 == (0xf & mantissa)) {
mantissa >>= 4;
- mantissaNibbles--;
+ mantissa_nibbles--;
}
- ss << "1." << std::hex << std::setfill('0') << std::setw(mantissaNibbles)
- << mantissa << "p+128";
+ ss << "1." << std::hex << std::setfill('0') << std::setw(mantissa_nibbles)
+ << mantissa << "p+" << std::dec << exponent;
}
} else {
// Subnormal, and not zero.
TINT_ASSERT(Writer, mantissa != 0);
- const int kTopBit = (1 << kMantissaBits);
+ const auto kTopBit = static_cast<uint_t>(1u) << T::kMantissaBits;
// Shift left until we get 1.x
while (0 == (kTopBit & mantissa)) {
@@ -138,17 +159,19 @@
// Emit the leading 1, and remove it from the mantissa.
ss << "1";
mantissa = mantissa ^ kTopBit;
- mantissa <<= 1;
exponent++;
+ // Left-justify mantissa to whole nibble.
+ mantissa <<= (4 - (T::kMantissaBits % 4));
+
// Emit the fractional part.
if (mantissa) {
- // Remove trailing zeroes, for tidyness
+ // Remove trailing zeroes, for tidiness
while (0 == (0xf & mantissa)) {
mantissa >>= 4;
- mantissaNibbles--;
+ mantissa_nibbles--;
}
- ss << "." << std::hex << std::setfill('0') << std::setw(mantissaNibbles)
+ ss << "." << std::hex << std::setfill('0') << std::setw(mantissa_nibbles)
<< mantissa;
}
// Emit the exponent
@@ -159,4 +182,22 @@
return ss.str();
}
+} // namespace
+
+std::string FloatToString(float f) {
+ return ToString(f);
+}
+
+std::string FloatToBitPreservingString(float f) {
+ return ToBitPreservingString(f);
+}
+
+std::string DoubleToString(double f) {
+ return ToString(f);
+}
+
+std::string DoubleToBitPreservingString(double f) {
+ return ToBitPreservingString(f);
+}
+
} // namespace tint::writer
diff --git a/src/tint/writer/float_to_string.h b/src/tint/writer/float_to_string.h
index 4a5afd4..387c0a8 100644
--- a/src/tint/writer/float_to_string.h
+++ b/src/tint/writer/float_to_string.h
@@ -27,11 +27,24 @@
/// @return the float f formatted to a string
std::string FloatToString(float f);
+/// Converts the double `f` to a string using fixed-point notation (not
+/// scientific). The double will be printed with the full precision required to
+/// describe the double. All trailing `0`s will be omitted after the last
+/// non-zero fractional number, unless the fractional is zero, in which case the
+/// number will end with `.0`.
+/// @return the double f formatted to a string
+std::string DoubleToString(double f);
+
/// Converts the float `f` to a string, using hex float notation for infinities,
/// NaNs, or subnormal numbers. Otherwise behaves as FloatToString.
/// @return the float f formatted to a string
std::string FloatToBitPreservingString(float f);
+/// Converts the double `f` to a string, using hex double notation for infinities,
+/// NaNs, or subnormal numbers. Otherwise behaves as FloatToString.
+/// @return the double f formatted to a string
+std::string DoubleToBitPreservingString(double f);
+
} // namespace tint::writer
#endif // SRC_TINT_WRITER_FLOAT_TO_STRING_H_
diff --git a/src/tint/writer/float_to_string_test.cc b/src/tint/writer/float_to_string_test.cc
index 8e5f244..901334e 100644
--- a/src/tint/writer/float_to_string_test.cc
+++ b/src/tint/writer/float_to_string_test.cc
@@ -14,33 +14,19 @@
#include "src/tint/writer/float_to_string.h"
-#include <cmath>
+#include <math.h>
#include <cstring>
#include <limits>
#include "gtest/gtest.h"
+#include "src/tint/utils/bitcast.h"
namespace tint::writer {
namespace {
-// Makes an IEEE 754 binary32 floating point number with
-// - 0 sign if sign is 0, 1 otherwise
-// - 'exponent_bits' is placed in the exponent space.
-// So, the exponent bias must already be included.
-float MakeFloat(uint32_t sign, uint32_t biased_exponent, uint32_t mantissa) {
- const uint32_t sign_bit = sign ? 0x80000000u : 0u;
- // The binary32 exponent is 8 bits, just below the sign.
- const uint32_t exponent_bits = (biased_exponent & 0xffu) << 23;
- // The mantissa is the bottom 23 bits.
- const uint32_t mantissa_bits = (mantissa & 0x7fffffu);
-
- uint32_t bits = sign_bit | exponent_bits | mantissa_bits;
- float result = 0.0f;
- static_assert(sizeof(result) == sizeof(bits),
- "expected float and uint32_t to be the same size");
- std::memcpy(&result, &bits, sizeof(bits));
- return result;
-}
+////////////////////////////////////////////////////////////////////////////////
+// FloatToString //
+////////////////////////////////////////////////////////////////////////////////
TEST(FloatToStringTest, Zero) {
EXPECT_EQ(FloatToString(0.0f), "0.0");
@@ -93,14 +79,18 @@
EXPECT_EQ(FloatToString(1e-20f), "9.99999968e-21");
}
-// FloatToBitPreservingString
-//
-// First replicate the tests for FloatToString
+////////////////////////////////////////////////////////////////////////////////
+// FloatToBitPreservingString //
+////////////////////////////////////////////////////////////////////////////////
TEST(FloatToBitPreservingStringTest, Zero) {
EXPECT_EQ(FloatToBitPreservingString(0.0f), "0.0");
}
+TEST(FloatToBitPreservingStringTest, NegativeZero) {
+ EXPECT_EQ(FloatToBitPreservingString(-0.0f), "-0.0");
+}
+
TEST(FloatToBitPreservingStringTest, One) {
EXPECT_EQ(FloatToBitPreservingString(1.0f), "1.0");
}
@@ -141,49 +131,204 @@
"-340282346638528859811704183484516925440.0");
}
-// Special cases for bit-preserving output.
-
-TEST(FloatToBitPreservingStringTest, NegativeZero) {
- EXPECT_EQ(FloatToBitPreservingString(std::copysign(0.0f, -5.0f)), "-0.0");
-}
-
-TEST(FloatToBitPreservingStringTest, ZeroAsBits) {
- EXPECT_EQ(FloatToBitPreservingString(MakeFloat(0, 0, 0)), "0.0");
- EXPECT_EQ(FloatToBitPreservingString(MakeFloat(1, 0, 0)), "-0.0");
-}
-
-TEST(FloatToBitPreservingStringTest, OneBits) {
- EXPECT_EQ(FloatToBitPreservingString(MakeFloat(0, 127, 0)), "1.0");
- EXPECT_EQ(FloatToBitPreservingString(MakeFloat(1, 127, 0)), "-1.0");
-}
-
TEST(FloatToBitPreservingStringTest, SmallestDenormal) {
- EXPECT_EQ(FloatToBitPreservingString(MakeFloat(0, 0, 1)), "0x1p-149");
- EXPECT_EQ(FloatToBitPreservingString(MakeFloat(1, 0, 1)), "-0x1p-149");
+ EXPECT_EQ(FloatToBitPreservingString(0x1p-149f), "0x1p-149");
+ EXPECT_EQ(FloatToBitPreservingString(-0x1p-149f), "-0x1p-149");
}
TEST(FloatToBitPreservingStringTest, BiggerDenormal) {
- EXPECT_EQ(FloatToBitPreservingString(MakeFloat(0, 0, 2)), "0x1p-148");
- EXPECT_EQ(FloatToBitPreservingString(MakeFloat(1, 0, 2)), "-0x1p-148");
+ EXPECT_EQ(FloatToBitPreservingString(0x1p-148f), "0x1p-148");
+ EXPECT_EQ(FloatToBitPreservingString(-0x1p-148f), "-0x1p-148");
}
TEST(FloatToBitPreservingStringTest, LargestDenormal) {
- EXPECT_EQ(FloatToBitPreservingString(MakeFloat(0, 0, 0x7fffff)), "0x1.fffffcp-127");
+ static_assert(0x0.fffffep-126f == 0x1.fffffcp-127f);
+ EXPECT_EQ(FloatToBitPreservingString(0x0.fffffep-126f), "0x1.fffffcp-127");
}
TEST(FloatToBitPreservingStringTest, Subnormal_cafebe) {
- EXPECT_EQ(FloatToBitPreservingString(MakeFloat(0, 0, 0xcafebe)), "0x1.2bfaf8p-127");
- EXPECT_EQ(FloatToBitPreservingString(MakeFloat(1, 0, 0xcafebe)), "-0x1.2bfaf8p-127");
+ EXPECT_EQ(FloatToBitPreservingString(0x1.2bfaf8p-127f), "0x1.2bfaf8p-127");
+ EXPECT_EQ(FloatToBitPreservingString(-0x1.2bfaf8p-127f), "-0x1.2bfaf8p-127");
}
TEST(FloatToBitPreservingStringTest, Subnormal_aaaaa) {
- EXPECT_EQ(FloatToBitPreservingString(MakeFloat(0, 0, 0xaaaaa)), "0x1.55554p-130");
- EXPECT_EQ(FloatToBitPreservingString(MakeFloat(1, 0, 0xaaaaa)), "-0x1.55554p-130");
+ EXPECT_EQ(FloatToBitPreservingString(0x1.55554p-130f), "0x1.55554p-130");
+ EXPECT_EQ(FloatToBitPreservingString(-0x1.55554p-130f), "-0x1.55554p-130");
}
TEST(FloatToBitPreservingStringTest, Infinity) {
- EXPECT_EQ(FloatToBitPreservingString(MakeFloat(0, 255, 0)), "0x1p+128");
- EXPECT_EQ(FloatToBitPreservingString(MakeFloat(1, 255, 0)), "-0x1p+128");
+ EXPECT_EQ(FloatToBitPreservingString(INFINITY), "0x1p+128");
+ EXPECT_EQ(FloatToBitPreservingString(-INFINITY), "-0x1p+128");
+}
+
+TEST(FloatToBitPreservingStringTest, NaN) {
+ // TODO(crbug.com/tint/1714): On x86, this bitcast will set bit 22 (the highest mantissa bit) to
+ // 1, regardless of the bit value in the integer. This is likely due to IEEE 754's
+ // recommendation that that the highest mantissa bit differentiates quiet NaNs from signalling
+ // NaNs. On x86, float return values usually go via the FPU which can transform the signalling
+ // NaN bit (0) to quiet NaN (1). As NaN floating point numbers can be silently modified by the
+ // architecture, and the signalling bit is architecture defined, this test may fail on other
+ // architectures.
+ auto nan = utils::Bitcast<float>(0x7fc0beef);
+ EXPECT_EQ(FloatToBitPreservingString(nan), "0x1.817ddep+128");
+ EXPECT_EQ(FloatToBitPreservingString(-nan), "-0x1.817ddep+128");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DoubleToString //
+////////////////////////////////////////////////////////////////////////////////
+
+TEST(DoubleToStringTest, Zero) {
+ EXPECT_EQ(DoubleToString(0.0), "0.0");
+}
+
+TEST(DoubleToStringTest, One) {
+ EXPECT_EQ(DoubleToString(1.0), "1.0");
+}
+
+TEST(DoubleToStringTest, MinusOne) {
+ EXPECT_EQ(DoubleToString(-1.0), "-1.0");
+}
+
+TEST(DoubleToStringTest, Billion) {
+ EXPECT_EQ(DoubleToString(1e9), "1000000000.0");
+}
+
+TEST(DoubleToStringTest, Small) {
+ EXPECT_NE(DoubleToString(std::numeric_limits<double>::epsilon()), "0.0");
+}
+
+TEST(DoubleToStringTest, Highest) {
+ const auto highest = std::numeric_limits<double>::max();
+ const auto expected_highest = 1.797693134862315708e+308;
+ if (highest < expected_highest || highest > expected_highest) {
+ GTEST_SKIP() << "std::numeric_limits<double>::max() is not as expected for "
+ "this target";
+ }
+ EXPECT_EQ(DoubleToString(std::numeric_limits<double>::max()),
+ "179769313486231570814527423731704356798070567525844996598917476803157260780028538760"
+ "589558632766878171540458953514382464234321326889464182768467546703537516986049910576"
+ "551282076245490090389328944075868508455133942304583236903222948165808559332123348274"
+ "797826204144723168738177180919299881250404026184124858368.0");
+}
+
+TEST(DoubleToStringTest, Lowest) {
+ // Some compilers complain if you test floating point numbers for equality.
+ // So say it via two inequalities.
+ const auto lowest = std::numeric_limits<double>::lowest();
+ const auto expected_lowest = -1.797693134862315708e+308;
+ if (lowest < expected_lowest || lowest > expected_lowest) {
+ GTEST_SKIP() << "std::numeric_limits<double>::lowest() is not as expected for "
+ "this target";
+ }
+ EXPECT_EQ(DoubleToString(std::numeric_limits<double>::lowest()),
+ "-17976931348623157081452742373170435679807056752584499659891747680315726078002853876"
+ "058955863276687817154045895351438246423432132688946418276846754670353751698604991057"
+ "655128207624549009038932894407586850845513394230458323690322294816580855933212334827"
+ "4797826204144723168738177180919299881250404026184124858368.0");
+}
+
+TEST(DoubleToStringTest, Precision) {
+ EXPECT_EQ(DoubleToString(1e-8), "0.00000001");
+ EXPECT_EQ(DoubleToString(1e-9), "0.000000001");
+ EXPECT_EQ(DoubleToString(1e-10), "1e-10");
+ EXPECT_EQ(DoubleToString(1e-15), "1.0000000000000001e-15");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DoubleToBitPreservingString //
+////////////////////////////////////////////////////////////////////////////////
+
+TEST(DoubleToBitPreservingStringTest, Zero) {
+ EXPECT_EQ(DoubleToBitPreservingString(0.0), "0.0");
+}
+
+TEST(DoubleToBitPreservingStringTest, NegativeZero) {
+ EXPECT_EQ(DoubleToBitPreservingString(-0.0), "-0.0");
+}
+
+TEST(DoubleToBitPreservingStringTest, One) {
+ EXPECT_EQ(DoubleToBitPreservingString(1.0), "1.0");
+}
+
+TEST(DoubleToBitPreservingStringTest, MinusOne) {
+ EXPECT_EQ(DoubleToBitPreservingString(-1.0), "-1.0");
+}
+
+TEST(DoubleToBitPreservingStringTest, Billion) {
+ EXPECT_EQ(DoubleToBitPreservingString(1e9), "1000000000.0");
+}
+
+TEST(DoubleToBitPreservingStringTest, Small) {
+ EXPECT_NE(DoubleToBitPreservingString(std::numeric_limits<double>::epsilon()), "0.0");
+}
+
+TEST(DoubleToBitPreservingStringTest, Highest) {
+ const auto highest = std::numeric_limits<double>::max();
+ const auto expected_highest = 1.797693134862315708e+308;
+ if (highest < expected_highest || highest > expected_highest) {
+ GTEST_SKIP() << "std::numeric_limits<float>::max() is not as expected for "
+ "this target";
+ }
+ EXPECT_EQ(DoubleToBitPreservingString(std::numeric_limits<double>::max()),
+ "179769313486231570814527423731704356798070567525844996598917476803157260780028538760"
+ "589558632766878171540458953514382464234321326889464182768467546703537516986049910576"
+ "551282076245490090389328944075868508455133942304583236903222948165808559332123348274"
+ "797826204144723168738177180919299881250404026184124858368.0");
+}
+
+TEST(DoubleToBitPreservingStringTest, Lowest) {
+ // Some compilers complain if you test floating point numbers for equality.
+ // So say it via two inequalities.
+ const auto lowest = std::numeric_limits<double>::lowest();
+ const auto expected_lowest = -1.797693134862315708e+308;
+ if (lowest < expected_lowest || lowest > expected_lowest) {
+ GTEST_SKIP() << "std::numeric_limits<float>::lowest() is not as expected for "
+ "this target";
+ }
+ EXPECT_EQ(DoubleToBitPreservingString(std::numeric_limits<double>::lowest()),
+ "-17976931348623157081452742373170435679807056752584499659891747680315726078002853876"
+ "058955863276687817154045895351438246423432132688946418276846754670353751698604991057"
+ "655128207624549009038932894407586850845513394230458323690322294816580855933212334827"
+ "4797826204144723168738177180919299881250404026184124858368.0");
+}
+
+TEST(DoubleToBitPreservingStringTest, SmallestDenormal) {
+ EXPECT_EQ(DoubleToBitPreservingString(0x1p-1074), "0x1p-1074");
+ EXPECT_EQ(DoubleToBitPreservingString(-0x1p-1074), "-0x1p-1074");
+}
+
+TEST(DoubleToBitPreservingStringTest, BiggerDenormal) {
+ EXPECT_EQ(DoubleToBitPreservingString(0x1p-1073), "0x1p-1073");
+ EXPECT_EQ(DoubleToBitPreservingString(-0x1p-1073), "-0x1p-1073");
+}
+
+TEST(DoubleToBitPreservingStringTest, LargestDenormal) {
+ static_assert(0x0.fffffffffffffp-1022 == 0x1.ffffffffffffep-1023);
+ EXPECT_EQ(DoubleToBitPreservingString(0x0.fffffffffffffp-1022), "0x1.ffffffffffffep-1023");
+ EXPECT_EQ(DoubleToBitPreservingString(-0x0.fffffffffffffp-1022), "-0x1.ffffffffffffep-1023");
+}
+
+TEST(DoubleToBitPreservingStringTest, Subnormal_cafef00dbeef) {
+ EXPECT_EQ(DoubleToBitPreservingString(0x1.cafef00dbeefp-1023), "0x1.cafef00dbeefp-1023");
+ EXPECT_EQ(DoubleToBitPreservingString(-0x1.cafef00dbeefp-1023), "-0x1.cafef00dbeefp-1023");
+}
+
+TEST(DoubleToBitPreservingStringTest, Subnormal_aaaaaaaaaaaaap) {
+ static_assert(0x0.aaaaaaaaaaaaap-1023 == 0x1.5555555555554p-1024);
+ EXPECT_EQ(DoubleToBitPreservingString(0x0.aaaaaaaaaaaaap-1023), "0x1.5555555555554p-1024");
+ EXPECT_EQ(DoubleToBitPreservingString(-0x0.aaaaaaaaaaaaap-1023), "-0x1.5555555555554p-1024");
+}
+
+TEST(DoubleToBitPreservingStringTest, Infinity) {
+ EXPECT_EQ(DoubleToBitPreservingString(static_cast<double>(INFINITY)), "0x1p+1024");
+ EXPECT_EQ(DoubleToBitPreservingString(static_cast<double>(-INFINITY)), "-0x1p+1024");
+}
+
+TEST(DoubleToBitPreservingStringTest, NaN) {
+ auto nan = utils::Bitcast<double>(0x7ff8cafef00dbeefull);
+ EXPECT_EQ(DoubleToBitPreservingString(static_cast<double>(nan)), "0x1.8cafef00dbeefp+1024");
+ EXPECT_EQ(DoubleToBitPreservingString(static_cast<double>(-nan)), "-0x1.8cafef00dbeefp+1024");
}
} // namespace
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index da67b3d..49da7d6 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -141,7 +141,7 @@
return "rgba32i";
case ast::TexelFormat::kRgba32Float:
return "rgba32f";
- case ast::TexelFormat::kInvalid:
+ case ast::TexelFormat::kUndefined:
return "unknown";
}
return "unknown";
@@ -509,20 +509,20 @@
{
auto decl = line(&b);
if (!EmitTypeAndName(decl, ret_ty, ast::AddressSpace::kNone,
- ast::Access::kInvalid, fn_name)) {
+ ast::Access::kUndefined, fn_name)) {
return "";
}
{
ScopedParen sp(decl);
const auto* ty = TypeOf(expr->lhs)->UnwrapRef();
if (!EmitTypeAndName(decl, ty, ast::AddressSpace::kNone,
- ast::Access::kInvalid, "lhs")) {
+ ast::Access::kUndefined, "lhs")) {
return "";
}
decl << ", ";
ty = TypeOf(expr->rhs)->UnwrapRef();
if (!EmitTypeAndName(decl, ty, ast::AddressSpace::kNone,
- ast::Access::kInvalid, "rhs")) {
+ ast::Access::kUndefined, "rhs")) {
return "";
}
}
@@ -935,7 +935,7 @@
{
auto pre = line();
if (!EmitTypeAndName(pre, builtin->ReturnType(), ast::AddressSpace::kNone,
- ast::Access::kInvalid, result)) {
+ ast::Access::kUndefined, result)) {
return false;
}
pre << ";";
@@ -1213,7 +1213,7 @@
{
auto l = line(b);
if (!EmitType(l, builtin->ReturnType(), ast::AddressSpace::kNone,
- ast::Access::kInvalid, "")) {
+ ast::Access::kUndefined, "")) {
return false;
}
l << " result;";
@@ -1239,7 +1239,7 @@
{
auto l = line(b);
if (!EmitType(l, builtin->ReturnType(), ast::AddressSpace::kNone,
- ast::Access::kInvalid, "")) {
+ ast::Access::kUndefined, "")) {
return false;
}
l << " result;";
@@ -1887,7 +1887,7 @@
[&](const ast::Override*) {
// Override is removed with SubstituteOverride
diagnostics_.add_error(diag::System::Writer,
- "override expressions should have been removed with the "
+ "override-expressions should have been removed with the "
"SubstituteOverride transform");
return false;
},
@@ -2049,7 +2049,7 @@
switch (interpolate->type) {
case ast::InterpolationType::kPerspective:
case ast::InterpolationType::kLinear:
- case ast::InterpolationType::kInvalid:
+ case ast::InterpolationType::kUndefined:
break;
case ast::InterpolationType::kFlat:
out << "flat ";
@@ -2061,7 +2061,7 @@
break;
case ast::InterpolationSampling::kSample:
case ast::InterpolationSampling::kCenter:
- case ast::InterpolationSampling::kInvalid:
+ case ast::InterpolationSampling::kUndefined:
break;
}
}
@@ -2109,7 +2109,7 @@
if (!wgsize[i].has_value()) {
diagnostics_.add_error(
diag::System::Writer,
- "override expressions should have been removed with the SubstituteOverride "
+ "override-expressions should have been removed with the SubstituteOverride "
"transform");
return false;
}
@@ -2198,7 +2198,7 @@
return true;
},
[&](const sem::Vector* v) {
- if (!EmitType(out, v, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
+ if (!EmitType(out, v, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
return false;
}
@@ -2219,7 +2219,7 @@
return true;
},
[&](const sem::Matrix* m) {
- if (!EmitType(out, m, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
+ if (!EmitType(out, m, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
return false;
}
@@ -2236,7 +2236,7 @@
return true;
},
[&](const sem::Array* a) {
- if (!EmitType(out, a, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
+ if (!EmitType(out, a, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
return false;
}
@@ -2260,7 +2260,7 @@
return true;
},
[&](const sem::Struct* s) {
- if (!EmitType(out, s, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
+ if (!EmitType(out, s, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
return false;
}
@@ -2351,7 +2351,7 @@
}
}
} else if (auto* str = type->As<sem::Struct>()) {
- if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
+ if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
return false;
}
bool first = true;
@@ -2365,7 +2365,7 @@
EmitZeroValue(out, member->Type());
}
} else if (auto* arr = type->As<sem::Array>()) {
- if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
+ if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
return false;
}
ScopedParen sp(out);
@@ -2972,7 +2972,7 @@
auto out = line();
// TODO(senorblanco): handle const
- if (!EmitTypeAndName(out, type, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ if (!EmitTypeAndName(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined,
builder_.Symbols().NameFor(let->symbol))) {
return false;
}
@@ -2994,7 +2994,7 @@
auto out = line();
out << "const ";
- if (!EmitTypeAndName(out, type, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ if (!EmitTypeAndName(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined,
builder_.Symbols().NameFor(var->symbol))) {
return false;
}
@@ -3022,7 +3022,7 @@
{
auto decl = line(&b);
if (!EmitTypeAndName(decl, builtin->ReturnType(), ast::AddressSpace::kNone,
- ast::Access::kInvalid, fn_name)) {
+ ast::Access::kUndefined, fn_name)) {
return "";
}
{
@@ -3037,8 +3037,8 @@
decl << "inout ";
ty = ptr->StoreType();
}
- if (!EmitTypeAndName(decl, ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
- param_name)) {
+ if (!EmitTypeAndName(decl, ty, ast::AddressSpace::kNone,
+ ast::Access::kUndefined, param_name)) {
return "";
}
parameter_names.emplace_back(std::move(param_name));
diff --git a/src/tint/writer/glsl/generator_impl_function_test.cc b/src/tint/writer/glsl/generator_impl_function_test.cc
index 1357985..56beefc 100644
--- a/src/tint/writer/glsl/generator_impl_function_test.cc
+++ b/src/tint/writer/glsl/generator_impl_function_test.cc
@@ -799,7 +799,7 @@
EXPECT_FALSE(gen.Generate()) << gen.error();
EXPECT_EQ(
gen.error(),
- R"(error: override expressions should have been removed with the SubstituteOverride transform)");
+ R"(error: override-expressions should have been removed with the SubstituteOverride transform)");
}
TEST_F(GlslGeneratorImplTest_Function, Emit_Function_WithArrayParams) {
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index 94e80e8..a51b954 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -325,7 +325,8 @@
std::string fn;
{
std::ostringstream ss;
- if (!EmitType(ss, vec, tint::ast::AddressSpace::kInvalid, ast::Access::kInvalid, "")) {
+ if (!EmitType(ss, vec, tint::ast::AddressSpace::kUndefined, ast::Access::kUndefined,
+ "")) {
return "";
}
fn = UniqueIdentifier("set_" + ss.str());
@@ -333,13 +334,13 @@
{
auto out = line(&helpers_);
out << "void " << fn << "(inout ";
- if (!EmitTypeAndName(out, vec, ast::AddressSpace::kInvalid, ast::Access::kInvalid,
+ if (!EmitTypeAndName(out, vec, ast::AddressSpace::kUndefined, ast::Access::kUndefined,
"vec")) {
return "";
}
out << ", int idx, ";
- if (!EmitTypeAndName(out, vec->type(), ast::AddressSpace::kInvalid,
- ast::Access::kInvalid, "val")) {
+ if (!EmitTypeAndName(out, vec->type(), ast::AddressSpace::kUndefined,
+ ast::Access::kUndefined, "val")) {
return "";
}
out << ") {";
@@ -398,7 +399,8 @@
std::string fn;
{
std::ostringstream ss;
- if (!EmitType(ss, mat, tint::ast::AddressSpace::kInvalid, ast::Access::kInvalid, "")) {
+ if (!EmitType(ss, mat, tint::ast::AddressSpace::kUndefined, ast::Access::kUndefined,
+ "")) {
return "";
}
fn = UniqueIdentifier("set_vector_" + ss.str());
@@ -406,13 +408,13 @@
{
auto out = line(&helpers_);
out << "void " << fn << "(inout ";
- if (!EmitTypeAndName(out, mat, ast::AddressSpace::kInvalid, ast::Access::kInvalid,
+ if (!EmitTypeAndName(out, mat, ast::AddressSpace::kUndefined, ast::Access::kUndefined,
"mat")) {
return "";
}
out << ", int col, ";
- if (!EmitTypeAndName(out, mat->ColumnType(), ast::AddressSpace::kInvalid,
- ast::Access::kInvalid, "val")) {
+ if (!EmitTypeAndName(out, mat->ColumnType(), ast::AddressSpace::kUndefined,
+ ast::Access::kUndefined, "val")) {
return "";
}
out << ") {";
@@ -466,7 +468,8 @@
std::string fn;
{
std::ostringstream ss;
- if (!EmitType(ss, mat, tint::ast::AddressSpace::kInvalid, ast::Access::kInvalid, "")) {
+ if (!EmitType(ss, mat, tint::ast::AddressSpace::kUndefined, ast::Access::kUndefined,
+ "")) {
return "";
}
fn = UniqueIdentifier("set_scalar_" + ss.str());
@@ -474,13 +477,13 @@
{
auto out = line(&helpers_);
out << "void " << fn << "(inout ";
- if (!EmitTypeAndName(out, mat, ast::AddressSpace::kInvalid, ast::Access::kInvalid,
+ if (!EmitTypeAndName(out, mat, ast::AddressSpace::kUndefined, ast::Access::kUndefined,
"mat")) {
return "";
}
out << ", int col, int row, ";
- if (!EmitTypeAndName(out, mat->type(), ast::AddressSpace::kInvalid,
- ast::Access::kInvalid, "val")) {
+ if (!EmitTypeAndName(out, mat->type(), ast::AddressSpace::kUndefined,
+ ast::Access::kUndefined, "val")) {
return "";
}
out << ") {";
@@ -655,7 +658,7 @@
if (auto* vec = ty->As<sem::Vector>()) {
auto* elem_ty = vec->type();
- if (!EmitType(out, ty, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
+ if (!EmitType(out, ty, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
return false;
}
@@ -720,7 +723,8 @@
std::string ty_name;
{
std::ostringstream ss;
- if (!EmitType(ss, ty, tint::ast::AddressSpace::kInvalid, ast::Access::kInvalid, "")) {
+ if (!EmitType(ss, ty, tint::ast::AddressSpace::kUndefined, ast::Access::kUndefined,
+ "")) {
return "";
}
ty_name = ss.str();
@@ -1428,12 +1432,12 @@
auto rmw = [&](const char* hlsl) -> bool {
{
auto fn = line(&buf);
- if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
name)) {
return false;
}
fn << "(RWByteAddressBuffer buffer, uint offset, ";
- if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
"value")) {
return false;
}
@@ -1449,7 +1453,7 @@
{
auto l = line(&buf);
- if (!EmitTypeAndName(l, result_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ if (!EmitTypeAndName(l, result_ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
"original_value")) {
return false;
}
@@ -1498,8 +1502,8 @@
// InterlockedOr using 0 as the OR value
{
auto fn = line(&buf);
- if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
- name)) {
+ if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone,
+ ast::Access::kUndefined, name)) {
return false;
}
fn << "(RWByteAddressBuffer buffer, uint offset) {";
@@ -1514,8 +1518,8 @@
{
auto l = line(&buf);
- if (!EmitTypeAndName(l, result_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
- "value")) {
+ if (!EmitTypeAndName(l, result_ty, ast::AddressSpace::kNone,
+ ast::Access::kUndefined, "value")) {
return false;
}
l << " = 0;";
@@ -1532,8 +1536,8 @@
{
auto fn = line(&buf);
fn << "void " << name << "(RWByteAddressBuffer buffer, uint offset, ";
- if (!EmitTypeAndName(fn, value_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
- "value")) {
+ if (!EmitTypeAndName(fn, value_ty, ast::AddressSpace::kNone,
+ ast::Access::kUndefined, "value")) {
return false;
}
fn << ") {";
@@ -1548,7 +1552,7 @@
{
auto l = line(&buf);
- if (!EmitTypeAndName(l, value_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ if (!EmitTypeAndName(l, value_ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
"ignored")) {
return false;
}
@@ -1563,18 +1567,18 @@
auto* value_ty = params[2]->Type()->UnwrapRef();
{
auto fn = line(&buf);
- if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
- name)) {
+ if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone,
+ ast::Access::kUndefined, name)) {
return false;
}
fn << "(RWByteAddressBuffer buffer, uint offset, ";
- if (!EmitTypeAndName(fn, value_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
- "compare")) {
+ if (!EmitTypeAndName(fn, value_ty, ast::AddressSpace::kNone,
+ ast::Access::kUndefined, "compare")) {
return false;
}
fn << ", ";
- if (!EmitTypeAndName(fn, value_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
- "value")) {
+ if (!EmitTypeAndName(fn, value_ty, ast::AddressSpace::kNone,
+ ast::Access::kUndefined, "value")) {
return false;
}
fn << ") {";
@@ -1589,8 +1593,8 @@
{ // T result = {0};
auto l = line(&buf);
- if (!EmitTypeAndName(l, result_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
- "result")) {
+ if (!EmitTypeAndName(l, result_ty, ast::AddressSpace::kNone,
+ ast::Access::kUndefined, "result")) {
return false;
}
l << "=";
@@ -1625,7 +1629,7 @@
if (!builtin->ReturnType()->Is<sem::Void>()) {
auto pre = line();
if (!EmitTypeAndName(pre, builtin->ReturnType(), ast::AddressSpace::kNone,
- ast::Access::kInvalid, result)) {
+ ast::Access::kUndefined, result)) {
return false;
}
pre << " = ";
@@ -1688,8 +1692,8 @@
{ // T result = 0;
auto pre = line();
auto* value_ty = builtin->Parameters()[1]->Type()->UnwrapRef();
- if (!EmitTypeAndName(pre, value_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
- result)) {
+ if (!EmitTypeAndName(pre, value_ty, ast::AddressSpace::kNone,
+ ast::Access::kUndefined, result)) {
return false;
}
pre << " = ";
@@ -1729,7 +1733,7 @@
{ // T compare_value = <compare_value>;
auto pre = line();
if (!EmitTypeAndName(pre, TypeOf(compare_value)->UnwrapRef(),
- ast::AddressSpace::kNone, ast::Access::kInvalid, compare)) {
+ ast::AddressSpace::kNone, ast::Access::kUndefined, compare)) {
return false;
}
pre << " = ";
@@ -1839,7 +1843,7 @@
{
auto l = line(b);
if (!EmitType(l, builtin->ReturnType(), ast::AddressSpace::kNone,
- ast::Access::kInvalid, "")) {
+ ast::Access::kUndefined, "")) {
return false;
}
l << " result;";
@@ -1881,7 +1885,7 @@
{
auto l = line(b);
if (!EmitType(l, builtin->ReturnType(), ast::AddressSpace::kNone,
- ast::Access::kInvalid, "")) {
+ ast::Access::kUndefined, "")) {
return false;
}
l << " result = {sig, int" << width << "(exp)};";
@@ -2718,7 +2722,7 @@
auto const* type = v->Type();
auto address_space = ast::AddressSpace::kNone;
- auto access = ast::Access::kInvalid;
+ auto access = ast::Access::kUndefined;
if (auto* ptr = type->As<sem::Pointer>()) {
type = ptr->StoreType();
@@ -2833,7 +2837,7 @@
[&](const ast::Override*) {
// Override is removed with SubstituteOverride
diagnostics_.add_error(diag::System::Writer,
- "override expressions should have been removed with the "
+ "override-expressions should have been removed with the "
"SubstituteOverride transform");
return false;
},
@@ -3007,7 +3011,7 @@
case ast::InterpolationType::kFlat:
modifiers += "nointerpolation ";
break;
- case ast::InterpolationType::kInvalid:
+ case ast::InterpolationType::kUndefined:
break;
}
switch (sampling) {
@@ -3018,7 +3022,7 @@
modifiers += "sample ";
break;
case ast::InterpolationSampling::kCenter:
- case ast::InterpolationSampling::kInvalid:
+ case ast::InterpolationSampling::kUndefined:
break;
}
return modifiers;
@@ -3040,7 +3044,7 @@
if (!wgsize[i].has_value()) {
diagnostics_.add_error(
diag::System::Writer,
- "override expressions should have been removed with the SubstituteOverride "
+ "override-expressions should have been removed with the SubstituteOverride "
"transform");
return false;
}
@@ -3140,7 +3144,7 @@
return true;
}
- if (!EmitType(out, v, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
+ if (!EmitType(out, v, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
return false;
}
@@ -3157,7 +3161,7 @@
return true;
},
[&](const sem::Matrix* m) {
- if (!EmitType(out, m, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
+ if (!EmitType(out, m, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
return false;
}
@@ -3176,7 +3180,7 @@
[&](const sem::Array* a) {
if (constant->AllZero()) {
out << "(";
- if (!EmitType(out, a, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
+ if (!EmitType(out, a, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
return false;
}
out << ")0";
@@ -3206,7 +3210,7 @@
[&](const sem::Struct* s) {
if (constant->AllZero()) {
out << "(";
- if (!EmitType(out, s, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
+ if (!EmitType(out, s, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
return false;
}
out << ")0";
@@ -3327,12 +3331,12 @@
[&](const sem::Struct*) {
out << "(";
TINT_DEFER(out << ")" << value);
- return EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kInvalid, "");
+ return EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined, "");
},
[&](const sem::Array*) {
out << "(";
TINT_DEFER(out << ")" << value);
- return EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kInvalid, "");
+ return EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined, "");
},
[&](Default) {
diagnostics_.add_error(
@@ -4080,7 +4084,7 @@
auto out = line();
out << "const ";
- if (!EmitTypeAndName(out, type, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ if (!EmitTypeAndName(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined,
builder_.Symbols().NameFor(let->symbol))) {
return false;
}
@@ -4108,7 +4112,7 @@
{
auto decl = line(&b);
if (!EmitTypeAndName(decl, builtin->ReturnType(), ast::AddressSpace::kNone,
- ast::Access::kInvalid, fn_name)) {
+ ast::Access::kUndefined, fn_name)) {
return "";
}
{
@@ -4123,8 +4127,8 @@
decl << "inout ";
ty = ptr->StoreType();
}
- if (!EmitTypeAndName(decl, ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
- param_name)) {
+ if (!EmitTypeAndName(decl, ty, ast::AddressSpace::kNone,
+ ast::Access::kUndefined, param_name)) {
return "";
}
parameter_names.emplace_back(std::move(param_name));
diff --git a/src/tint/writer/hlsl/generator_impl_function_test.cc b/src/tint/writer/hlsl/generator_impl_function_test.cc
index 1adb852..56465d3 100644
--- a/src/tint/writer/hlsl/generator_impl_function_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_function_test.cc
@@ -728,7 +728,7 @@
EXPECT_FALSE(gen.Generate()) << gen.error();
EXPECT_EQ(
gen.error(),
- R"(error: override expressions should have been removed with the SubstituteOverride transform)");
+ R"(error: override-expressions should have been removed with the SubstituteOverride transform)");
}
TEST_F(HlslGeneratorImplTest_Function, Emit_Function_WithArrayParams) {
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index 5db1d8f..27a5191 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -301,7 +301,7 @@
[&](const ast::Override*) {
// Override is removed with SubstituteOverride
diagnostics_.add_error(diag::System::Writer,
- "override expressions should have been removed with the "
+ "override-expressions should have been removed with the "
"SubstituteOverride transform.");
return false;
},
@@ -590,8 +590,18 @@
ScopedParen sp(out);
{
ScopedBitCast lhs_uint_cast(this, out, lhs_type, unsigned_type_of(lhs_type));
- if (!EmitExpression(out, expr->lhs)) {
- return false;
+
+ // In case the type is packed, cast to our own type in order to remove the packing.
+ // Otherwise, this just casts to itself.
+ if (lhs_type->is_signed_integer_vector()) {
+ ScopedCast lhs_self_cast(this, out, lhs_type, lhs_type);
+ if (!EmitExpression(out, expr->lhs)) {
+ return false;
+ }
+ } else {
+ if (!EmitExpression(out, expr->lhs)) {
+ return false;
+ }
}
}
if (!emit_op()) {
@@ -1947,7 +1957,7 @@
case ast::InterpolationSampling::kSample:
attr = "sample_";
break;
- case ast::InterpolationSampling::kInvalid:
+ case ast::InterpolationSampling::kUndefined:
break;
}
switch (type) {
@@ -1960,7 +1970,7 @@
case ast::InterpolationType::kFlat:
attr += "flat";
break;
- case ast::InterpolationType::kInvalid:
+ case ast::InterpolationType::kUndefined:
break;
}
return attr;
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index 57a363d..83587f1 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -509,7 +509,7 @@
// Check if the workgroup_size uses pipeline-overridable constants.
if (!wgsize[0].has_value() || !wgsize[1].has_value() || !wgsize[2].has_value()) {
error_ =
- "override expressions should have been removed with the SubstituteOverride "
+ "override-expressions should have been removed with the SubstituteOverride "
"transform";
return false;
}
@@ -766,7 +766,7 @@
push_annot(spv::Op::OpDecorate,
{Operand(var_id), U32Operand(SpvDecorationNonWritable)});
break;
- case ast::Access::kInvalid:
+ case ast::Access::kUndefined:
case ast::Access::kReadWrite:
break;
}
@@ -4009,7 +4009,7 @@
SpvStorageClass Builder::ConvertAddressSpace(ast::AddressSpace klass) const {
switch (klass) {
- case ast::AddressSpace::kInvalid:
+ case ast::AddressSpace::kUndefined:
return SpvStorageClassMax;
case ast::AddressSpace::kIn:
return SpvStorageClassInput;
@@ -4071,7 +4071,7 @@
return SpvBuiltInSampleId;
case ast::BuiltinValue::kSampleMask:
return SpvBuiltInSampleMask;
- case ast::BuiltinValue::kInvalid:
+ case ast::BuiltinValue::kUndefined:
break;
}
return SpvBuiltInMax;
@@ -4088,7 +4088,7 @@
push_annot(spv::Op::OpDecorate, {Operand(id), U32Operand(SpvDecorationFlat)});
break;
case ast::InterpolationType::kPerspective:
- case ast::InterpolationType::kInvalid:
+ case ast::InterpolationType::kUndefined:
break;
}
switch (sampling) {
@@ -4100,7 +4100,7 @@
push_annot(spv::Op::OpDecorate, {Operand(id), U32Operand(SpvDecorationSample)});
break;
case ast::InterpolationSampling::kCenter:
- case ast::InterpolationSampling::kInvalid:
+ case ast::InterpolationSampling::kUndefined:
break;
}
}
@@ -4142,7 +4142,7 @@
return SpvImageFormatRgba32i;
case ast::TexelFormat::kRgba32Float:
return SpvImageFormatRgba32f;
- case ast::TexelFormat::kInvalid:
+ case ast::TexelFormat::kUndefined:
return SpvImageFormatUnknown;
}
return SpvImageFormatUnknown;
diff --git a/src/tint/writer/spirv/builder_function_attribute_test.cc b/src/tint/writer/spirv/builder_function_attribute_test.cc
index 60bf062..b905d8c 100644
--- a/src/tint/writer/spirv/builder_function_attribute_test.cc
+++ b/src/tint/writer/spirv/builder_function_attribute_test.cc
@@ -164,7 +164,7 @@
EXPECT_FALSE(b.GenerateExecutionModes(func, 3)) << b.error();
EXPECT_EQ(
b.error(),
- R"(override expressions should have been removed with the SubstituteOverride transform)");
+ R"(override-expressions should have been removed with the SubstituteOverride transform)");
}
TEST_F(BuilderTest, Decoration_ExecutionMode_WorkgroupSize_LiteralAndConst) {
@@ -181,7 +181,7 @@
EXPECT_FALSE(b.GenerateExecutionModes(func, 3)) << b.error();
EXPECT_EQ(
b.error(),
- R"(override expressions should have been removed with the SubstituteOverride transform)");
+ R"(override-expressions should have been removed with the SubstituteOverride transform)");
}
TEST_F(BuilderTest, Decoration_ExecutionMode_MultipleFragment) {
diff --git a/src/tint/writer/spirv/builder_global_variable_test.cc b/src/tint/writer/spirv/builder_global_variable_test.cc
index b0bbf3f..0d2ec9c 100644
--- a/src/tint/writer/spirv/builder_global_variable_test.cc
+++ b/src/tint/writer/spirv/builder_global_variable_test.cc
@@ -270,7 +270,7 @@
BuilderTest_Type,
BuiltinDataTest,
testing::Values(
- BuiltinData{ast::BuiltinValue::kInvalid, ast::AddressSpace::kNone, SpvBuiltInMax},
+ BuiltinData{ast::BuiltinValue::kUndefined, ast::AddressSpace::kNone, SpvBuiltInMax},
BuiltinData{ast::BuiltinValue::kPosition, ast::AddressSpace::kIn, SpvBuiltInFragCoord},
BuiltinData{ast::BuiltinValue::kPosition, ast::AddressSpace::kOut, SpvBuiltInPosition},
BuiltinData{
diff --git a/src/tint/writer/wgsl/generator_impl.cc b/src/tint/writer/wgsl/generator_impl.cc
index cf43f23..7795455 100644
--- a/src/tint/writer/wgsl/generator_impl.cc
+++ b/src/tint/writer/wgsl/generator_impl.cc
@@ -263,7 +263,11 @@
// Note that all normal and subnormal f16 values are normal f32 values, and since NaN
// and Inf are not allowed to be spelled in literal, it should be fine to emit f16
// literals in this way.
- out << FloatToBitPreservingString(static_cast<float>(l->value)) << l->suffix;
+ if (l->suffix == ast::FloatLiteralExpression::Suffix::kNone) {
+ out << DoubleToBitPreservingString(l->value);
+ } else {
+ out << FloatToBitPreservingString(static_cast<float>(l->value)) << l->suffix;
+ }
return true;
},
[&](const ast::IntLiteralExpression* l) { //
@@ -346,7 +350,7 @@
bool GeneratorImpl::EmitImageFormat(std::ostream& out, const ast::TexelFormat fmt) {
switch (fmt) {
- case ast::TexelFormat::kInvalid:
+ case ast::TexelFormat::kUndefined:
diagnostics_.add_error(diag::System::Writer, "unknown image format");
return false;
default:
@@ -433,7 +437,7 @@
if (!EmitType(out, ptr->type)) {
return false;
}
- if (ptr->access != ast::Access::kInvalid) {
+ if (ptr->access != ast::Access::kUndefined) {
out << ", ";
if (!EmitAccess(out, ptr->access)) {
return false;
@@ -656,9 +660,9 @@
out << "var";
auto address_space = var->declared_address_space;
auto ac = var->declared_access;
- if (address_space != ast::AddressSpace::kNone || ac != ast::Access::kInvalid) {
+ if (address_space != ast::AddressSpace::kNone || ac != ast::Access::kUndefined) {
out << "<" << address_space;
- if (ac != ast::Access::kInvalid) {
+ if (ac != ast::Access::kUndefined) {
out << ", ";
if (!EmitAccess(out, ac)) {
return false;
@@ -769,7 +773,7 @@
},
[&](const ast::InterpolateAttribute* interpolate) {
out << "interpolate(" << interpolate->type;
- if (interpolate->sampling != ast::InterpolationSampling::kInvalid) {
+ if (interpolate->sampling != ast::InterpolationSampling::kUndefined) {
out << ", " << interpolate->sampling;
}
out << ")";