tint/resolver: Start handling sem::Expression
Change Resolver::Expression() and Resolver::Identifier() to return a
sem::Expression instead of a sem::ValueExpression.
This is required as IdentifierExpressions may resolve to non-values.
Handle expressions that resolve to types, when they are expected to
resolve to a value.
Bug: tint:1810
Change-Id: I1cb069e3e736fc85af7bc395b388abe153b1f65a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/118500
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 60f4fb7..f08b4ee 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -386,6 +386,7 @@
"sem/struct.h",
"sem/switch_statement.h",
"sem/type_conversion.h",
+ "sem/type_expression.h",
"sem/type_initializer.h",
"sem/type_mappings.h",
"sem/value_expression.h",
@@ -797,6 +798,8 @@
"sem/switch_statement.h",
"sem/type_conversion.cc",
"sem/type_conversion.h",
+ "sem/type_expression.cc",
+ "sem/type_expression.h",
"sem/type_initializer.cc",
"sem/type_initializer.h",
"sem/type_mappings.h",
@@ -1448,6 +1451,7 @@
"resolver/diagnostic_control_test.cc",
"resolver/entry_point_validation_test.cc",
"resolver/evaluation_stage_test.cc",
+ "resolver/expression_kind_test.cc",
"resolver/f16_extension_test.cc",
"resolver/function_validation_test.cc",
"resolver/host_shareable_validation_test.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index de1e543..46aa1c4 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -359,6 +359,8 @@
sem/struct.h
sem/switch_statement.cc
sem/switch_statement.h
+ sem/type_expression.cc
+ sem/type_expression.h
sem/type_initializer.cc
sem/type_initializer.h
sem/type_conversion.cc
@@ -943,6 +945,7 @@
resolver/diagnostic_control_test.cc
resolver/entry_point_validation_test.cc
resolver/evaluation_stage_test.cc
+ resolver/expression_kind_test.cc
resolver/f16_extension_test.cc
resolver/function_validation_test.cc
resolver/host_shareable_validation_test.cc
diff --git a/src/tint/resolver/builtin_validation_test.cc b/src/tint/resolver/builtin_validation_test.cc
index 51741d0..2750d97 100644
--- a/src/tint/resolver/builtin_validation_test.cc
+++ b/src/tint/resolver/builtin_validation_test.cc
@@ -118,27 +118,6 @@
EXPECT_EQ(r()->error(), R"(56:78 error: missing '(' for function call)");
}
-TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsFunctionUsedAsType) {
- Func(Source{{12, 34}}, "mix", utils::Empty, ty.i32(),
- utils::Vector{
- Return(1_i),
- });
- WrapInFunction(Call(ty(Source{{56, 78}}, "mix")));
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(56:78 error: cannot use function 'mix' as type
-12:34 note: 'mix' declared here)");
-}
-
-TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalConstUsedAsFunction) {
- GlobalConst(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i));
- WrapInFunction(Call(Ident(Source{{56, 78}}, "mix"), 1_f, 2_f, 3_f));
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(56:78 error: cannot call variable 'mix'
-12:34 note: 'mix' declared here)");
-}
-
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalConstUsedAsVariable) {
auto* mix = GlobalConst(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i));
auto* use = Expr("mix");
@@ -150,24 +129,6 @@
EXPECT_EQ(sem->Variable(), Sem().Get(mix));
}
-TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalConstUsedAsType) {
- GlobalConst(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i));
- WrapInFunction(Call(ty(Source{{56, 78}}, "mix")));
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(56:78 error: cannot use variable 'mix' as type
-12:34 note: 'mix' declared here)");
-}
-
-TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalVarUsedAsFunction) {
- GlobalVar(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i), type::AddressSpace::kPrivate);
- WrapInFunction(Call(Ident(Source{{56, 78}}, "mix"), 1_f, 2_f, 3_f));
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(56:78 error: cannot call variable 'mix'
-12:34 note: 'mix' declared here)");
-}
-
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalVarUsedAsVariable) {
auto* mix =
GlobalVar(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i), type::AddressSpace::kPrivate);
@@ -180,15 +141,6 @@
EXPECT_EQ(sem->Variable(), Sem().Get(mix));
}
-TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalVarUsedAsType) {
- GlobalVar(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i), type::AddressSpace::kPrivate);
- WrapInFunction(Call(ty(Source{{56, 78}}, "mix")));
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(56:78 error: cannot use variable 'mix' as type
-12:34 note: 'mix' declared here)");
-}
-
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsAliasUsedAsFunction) {
Alias(Source{{12, 34}}, "mix", ty.i32());
WrapInFunction(Call(Source{{56, 78}}, "mix", 1_f, 2_f, 3_f));
@@ -205,14 +157,6 @@
)");
}
-TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsAliasUsedAsVariable) {
- Alias(Source{{12, 34}}, "mix", ty.i32());
- WrapInFunction(Decl(Var("v", Expr(Source{{56, 78}}, "mix"))));
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(56:78 error: missing '(' for type initializer or cast)");
-}
-
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsAliasUsedAsType) {
auto* mix = Alias(Source{{12, 34}}, "mix", ty.i32());
auto* use = Call(ty("mix"));
@@ -235,16 +179,6 @@
R"(12:34 error: struct initializer has too many inputs: expected 1, found 3)");
}
-TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsStructUsedAsVariable) {
- Structure("mix", utils::Vector{
- Member("m", ty.i32()),
- });
- WrapInFunction(Decl(Var("v", Expr(Source{{12, 34}}, "mix"))));
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(12:34 error: missing '(' for type initializer or cast)");
-}
-
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsStructUsedAsType) {
auto* mix = Structure("mix", utils::Vector{
Member("m", ty.i32()),
diff --git a/src/tint/resolver/call_validation_test.cc b/src/tint/resolver/call_validation_test.cc
index e811377..07e2271 100644
--- a/src/tint/resolver/call_validation_test.cc
+++ b/src/tint/resolver/call_validation_test.cc
@@ -457,39 +457,5 @@
EXPECT_TRUE(r()->Resolve());
}
-TEST_F(ResolverCallValidationTest, CallVariable) {
- // var v : i32;
- // fn f() {
- // v();
- // }
- GlobalVar("v", ty.i32(), type::AddressSpace::kPrivate);
- Func("f", utils::Empty, ty.void_(),
- utils::Vector{
- CallStmt(Call(Source{{12, 34}}, "v")),
- });
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(error: cannot call variable 'v'
-note: 'v' declared here)");
-}
-
-TEST_F(ResolverCallValidationTest, CallVariableShadowsFunction) {
- // fn x() {}
- // fn f() {
- // var x : i32;
- // x();
- // }
- Func("x", utils::Empty, ty.void_(), utils::Empty);
- Func("f", utils::Empty, ty.void_(),
- utils::Vector{
- Decl(Var(Source{{56, 78}}, "x", ty.i32())),
- CallStmt(Call(Source{{12, 34}}, "x")),
- });
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(error: cannot call variable 'x'
-56:78 note: 'x' declared here)");
-}
-
} // namespace
} // namespace tint::resolver
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index 1c207f6..b77c812 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -28,6 +28,7 @@
#include "src/tint/ast/break_statement.h"
#include "src/tint/ast/call_statement.h"
#include "src/tint/ast/compound_assignment_statement.h"
+#include "src/tint/ast/const.h"
#include "src/tint/ast/continue_statement.h"
#include "src/tint/ast/depth_multisampled_texture.h"
#include "src/tint/ast/depth_texture.h"
@@ -49,6 +50,7 @@
#include "src/tint/ast/loop_statement.h"
#include "src/tint/ast/matrix.h"
#include "src/tint/ast/multisampled_texture.h"
+#include "src/tint/ast/override.h"
#include "src/tint/ast/pointer.h"
#include "src/tint/ast/return_statement.h"
#include "src/tint/ast/sampled_texture.h"
@@ -65,6 +67,7 @@
#include "src/tint/ast/traverse_expressions.h"
#include "src/tint/ast/type_name.h"
#include "src/tint/ast/u32.h"
+#include "src/tint/ast/var.h"
#include "src/tint/ast/variable_decl_statement.h"
#include "src/tint/ast/vector.h"
#include "src/tint/ast/while_statement.h"
@@ -822,20 +825,42 @@
return da.Run(module);
}
-std::ostream& operator<<(std::ostream& out, const ResolvedIdentifier& ri) {
- if (!ri) {
- return out << "<unresolved symbol>";
+std::string ResolvedIdentifier::String(const SymbolTable& symbols, diag::List& diagnostics) const {
+ if (!Resolved()) {
+ return "<unresolved symbol>";
}
- if (auto* node = ri.Node()) {
- return out << "'" << node->TypeInfo().name << "' at " << node->source;
+ if (auto* node = Node()) {
+ return Switch(
+ node,
+ [&](const ast::TypeDecl* n) { //
+ return "type '" + symbols.NameFor(n->name->symbol) + "'";
+ },
+ [&](const ast::Var* n) { //
+ return "var '" + symbols.NameFor(n->name->symbol) + "'";
+ },
+ [&](const ast::Const* n) { //
+ return "const '" + symbols.NameFor(n->name->symbol) + "'";
+ },
+ [&](const ast::Override* n) { //
+ return "override '" + symbols.NameFor(n->name->symbol) + "'";
+ },
+ [&](const ast::Function* n) { //
+ return "function '" + symbols.NameFor(n->name->symbol) + "'";
+ },
+ [&](Default) {
+ TINT_UNREACHABLE(Resolver, diagnostics)
+ << "unhandled ast::Node: " << node->TypeInfo().name;
+ return "<unknown>";
+ });
}
- if (auto builtin_fn = ri.BuiltinFunction(); builtin_fn != sem::BuiltinType::kNone) {
- return out << "builtin function '" << builtin_fn << "'";
+ if (auto builtin_fn = BuiltinFunction(); builtin_fn != sem::BuiltinType::kNone) {
+ return "builtin function '" + utils::ToString(builtin_fn) + "'";
}
- if (auto builtin_ty = ri.BuiltinType(); builtin_ty != type::Builtin::kUndefined) {
- return out << "builtin function '" << builtin_ty << "'";
+ if (auto builtin_ty = BuiltinType(); builtin_ty != type::Builtin::kUndefined) {
+ return "builtin type '" + utils::ToString(builtin_ty) + "'";
}
- return out << "<unhandled ResolvedIdentifier value>";
+ TINT_UNREACHABLE(Resolver, diagnostics) << "unhandled ResolvedIdentifier";
+ return "<unknown>";
}
} // namespace tint::resolver
diff --git a/src/tint/resolver/dependency_graph.h b/src/tint/resolver/dependency_graph.h
index 5bbc7a4..dc26289 100644
--- a/src/tint/resolver/dependency_graph.h
+++ b/src/tint/resolver/dependency_graph.h
@@ -15,6 +15,7 @@
#ifndef SRC_TINT_RESOLVER_DEPENDENCY_GRAPH_H_
#define SRC_TINT_RESOLVER_DEPENDENCY_GRAPH_H_
+#include <string>
#include <vector>
#include "src/tint/ast/module.h"
@@ -42,7 +43,7 @@
ResolvedIdentifier(T value) : value_(value) {} // NOLINT(runtime/explicit)
/// @return true if the ResolvedIdentifier holds a value (successfully resolved)
- operator bool() const { return !std::holds_alternative<std::monostate>(value_); }
+ bool Resolved() const { return !std::holds_alternative<std::monostate>(value_); }
/// @return the node pointer if the ResolvedIdentifier holds an AST node, otherwise nullptr
const ast::Node* Node() const {
@@ -87,15 +88,15 @@
return !(*this == other);
}
+ /// @param symbols the program's symbol table
+ /// @param diagnostics diagnostics used to report ICEs
+ /// @return a description of the resolved symbol
+ std::string String(const SymbolTable& symbols, diag::List& diagnostics) const;
+
private:
std::variant<std::monostate, const ast::Node*, sem::BuiltinType, type::Builtin> value_;
};
-/// @param out the std::ostream to write to
-/// @param ri the ResolvedIdentifier
-/// @returns `out` so calls can be chained
-std::ostream& operator<<(std::ostream& out, const ResolvedIdentifier& ri);
-
/// DependencyGraph holds information about module-scope declaration dependency
/// analysis and symbol resolutions.
struct DependencyGraph {
diff --git a/src/tint/resolver/dependency_graph_test.cc b/src/tint/resolver/dependency_graph_test.cc
index e6c460e..57e84f1 100644
--- a/src/tint/resolver/dependency_graph_test.cc
+++ b/src/tint/resolver/dependency_graph_test.cc
@@ -1174,7 +1174,7 @@
auto resolved = Build().resolved_identifiers.Get(ident);
ASSERT_TRUE(resolved);
- EXPECT_EQ(resolved->BuiltinFunction(), builtin) << *resolved;
+ EXPECT_EQ(resolved->BuiltinFunction(), builtin) << resolved->String(Symbols(), Diagnostics());
}
TEST_P(ResolverDependencyGraphResolveToBuiltinFunc, ShadowedByGlobalVar) {
@@ -1189,7 +1189,7 @@
auto resolved = Build().resolved_identifiers.Get(ident);
ASSERT_TRUE(resolved);
- EXPECT_EQ(resolved->Node(), decl) << *resolved;
+ EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
}
TEST_P(ResolverDependencyGraphResolveToBuiltinFunc, ShadowedByStruct) {
@@ -1204,7 +1204,7 @@
auto resolved = Build().resolved_identifiers.Get(ident);
ASSERT_TRUE(resolved);
- EXPECT_EQ(resolved->Node(), decl) << *resolved;
+ EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
}
TEST_P(ResolverDependencyGraphResolveToBuiltinFunc, ShadowedByFunc) {
@@ -1219,7 +1219,7 @@
auto resolved = Build().resolved_identifiers.Get(ident);
ASSERT_TRUE(resolved);
- EXPECT_EQ(resolved->Node(), decl) << *resolved;
+ EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
}
INSTANTIATE_TEST_SUITE_P(Types,
@@ -1258,7 +1258,8 @@
auto resolved = Build().resolved_identifiers.Get(ident);
ASSERT_TRUE(resolved);
- EXPECT_EQ(resolved->BuiltinType(), type::ParseBuiltin(name)) << *resolved;
+ EXPECT_EQ(resolved->BuiltinType(), type::ParseBuiltin(name))
+ << resolved->String(Symbols(), Diagnostics());
}
TEST_P(ResolverDependencyGraphResolveToBuiltinType, ShadowedByGlobalVar) {
@@ -1273,7 +1274,7 @@
auto resolved = Build().resolved_identifiers.Get(ident);
ASSERT_TRUE(resolved);
- EXPECT_EQ(resolved->Node(), decl) << *resolved;
+ EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
}
TEST_P(ResolverDependencyGraphResolveToBuiltinType, ShadowedByStruct) {
@@ -1288,7 +1289,7 @@
auto resolved = Build().resolved_identifiers.Get(ident);
ASSERT_TRUE(resolved);
- EXPECT_EQ(resolved->Node(), decl) << *resolved;
+ EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
}
TEST_P(ResolverDependencyGraphResolveToBuiltinType, ShadowedByFunc) {
@@ -1303,7 +1304,7 @@
auto resolved = Build().resolved_identifiers.Get(ident);
ASSERT_TRUE(resolved);
- EXPECT_EQ(resolved->Node(), decl) << *resolved;
+ EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
}
INSTANTIATE_TEST_SUITE_P(Types,
diff --git a/src/tint/resolver/expression_kind_test.cc b/src/tint/resolver/expression_kind_test.cc
new file mode 100644
index 0000000..e662570
--- /dev/null
+++ b/src/tint/resolver/expression_kind_test.cc
@@ -0,0 +1,227 @@
+// Copyright 2023 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/resolver.h"
+
+#include "gmock/gmock.h"
+#include "src/tint/resolver/resolver_test_helper.h"
+
+using namespace tint::number_suffixes; // NOLINT
+
+namespace tint::resolver {
+namespace {
+
+enum class Def {
+ kBuiltinFunction,
+ kBuiltinType,
+ kFunction,
+ kStruct,
+ kTypeAlias,
+ kVariable,
+};
+
+std::ostream& operator<<(std::ostream& out, Def def) {
+ switch (def) {
+ case Def::kBuiltinFunction:
+ return out << "Def::kBuiltinFunction";
+ case Def::kBuiltinType:
+ return out << "Def::kBuiltinType";
+ case Def::kFunction:
+ return out << "Def::kFunction";
+ case Def::kStruct:
+ return out << "Def::kStruct";
+ case Def::kTypeAlias:
+ return out << "Def::kTypeAlias";
+ case Def::kVariable:
+ return out << "Def::kVariable";
+ }
+ return out << "<unknown>";
+}
+
+enum class Use {
+ kCallExpr,
+ kCallStmt,
+ kFunctionReturnType,
+ kMemberType,
+ kValueExpression,
+ kVariableType,
+};
+
+std::ostream& operator<<(std::ostream& out, Use use) {
+ switch (use) {
+ case Use::kCallExpr:
+ return out << "Use::kCallExpr";
+ case Use::kCallStmt:
+ return out << "Use::kCallStmt";
+ case Use::kFunctionReturnType:
+ return out << "Use::kFunctionReturnType";
+ case Use::kMemberType:
+ return out << "Use::kMemberType";
+ case Use::kValueExpression:
+ return out << "Use::kValueExpression";
+ case Use::kVariableType:
+ return out << "Use::kVariableType";
+ }
+ return out << "<unknown>";
+}
+
+struct Case {
+ Def def;
+ Use use;
+ const char* error;
+};
+
+std::ostream& operator<<(std::ostream& out, Case c) {
+ return out << "{" << c.def << ", " << c.use << "}";
+}
+
+static const char* kPass = "<pass>";
+
+static const Source kDefSource{Source::Range{{1, 2}, {3, 4}}};
+static const Source kUseSource{Source::Range{{5, 6}, {7, 8}}};
+
+using ResolverExpressionKindTest = ResolverTestWithParam<Case>;
+
+TEST_P(ResolverExpressionKindTest, Test) {
+ Symbol sym;
+ switch (GetParam().def) {
+ case Def::kBuiltinFunction:
+ sym = Sym("workgroupBarrier");
+ break;
+ case Def::kBuiltinType:
+ sym = Sym("vec4f");
+ break;
+ case Def::kFunction:
+ Func(kDefSource, "FUNCTION", utils::Empty, ty.i32(), Return(1_i));
+ sym = Sym("FUNCTION");
+ break;
+ case Def::kStruct:
+ Structure(kDefSource, "STRUCT", utils::Vector{Member("m", ty.i32())});
+ sym = Sym("STRUCT");
+ break;
+ case Def::kTypeAlias:
+ Alias(kDefSource, "ALIAS", ty.i32());
+ sym = Sym("ALIAS");
+ break;
+ case Def::kVariable:
+ GlobalConst(kDefSource, "VARIABLE", Expr(1_i));
+ sym = Sym("VARIABLE");
+ break;
+ }
+
+ switch (GetParam().use) {
+ case Use::kCallExpr:
+ Func("f", utils::Empty, ty.void_(), Decl(Var("v", Call(Ident(kUseSource, sym)))));
+ break;
+ case Use::kCallStmt:
+ Func("f", utils::Empty, ty.void_(), CallStmt(Call(Ident(kUseSource, sym))));
+ break;
+ case Use::kFunctionReturnType:
+ Func("f", utils::Empty, ty(kUseSource, sym), Return(Call(sym)));
+ break;
+ case Use::kMemberType:
+ Structure("s", utils::Vector{Member("m", ty(kUseSource, sym))});
+ break;
+ case Use::kValueExpression:
+ GlobalVar("v", type::AddressSpace::kPrivate, Expr(kUseSource, sym));
+ break;
+ case Use::kVariableType:
+ GlobalVar("v", type::AddressSpace::kPrivate, ty(kUseSource, sym));
+ break;
+ }
+
+ if (GetParam().error == kPass) {
+ EXPECT_TRUE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "");
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), GetParam().error);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ,
+ ResolverExpressionKindTest,
+ testing::ValuesIn(std::vector<Case>{
+ {Def::kBuiltinFunction, Use::kCallStmt, kPass},
+ {Def::kBuiltinFunction, Use::kFunctionReturnType,
+ R"(5:6 error: cannot use builtin function 'workgroupBarrier' as type)"},
+ {Def::kBuiltinFunction, Use::kMemberType,
+ R"(5:6 error: cannot use builtin function 'workgroupBarrier' as type)"},
+ {Def::kBuiltinFunction, Use::kValueExpression,
+ R"(7:8 error: missing '(' for builtin function call)"},
+ {Def::kBuiltinFunction, Use::kVariableType,
+ R"(5:6 error: cannot use builtin function 'workgroupBarrier' as type)"},
+
+ {Def::kBuiltinType, Use::kCallExpr, kPass},
+ {Def::kBuiltinType, Use::kCallStmt, kPass},
+ {Def::kBuiltinType, Use::kFunctionReturnType, kPass},
+ {Def::kBuiltinType, Use::kMemberType, kPass},
+ {Def::kBuiltinType, Use::kValueExpression,
+ R"(5:6 error: cannot use type 'vec4<f32>' as value
+7:8 note: are you missing '()' for type initializer?)"},
+ {Def::kBuiltinType, Use::kVariableType, kPass},
+
+ {Def::kFunction, Use::kCallExpr, kPass},
+ {Def::kFunction, Use::kCallStmt, kPass},
+ {Def::kFunction, Use::kFunctionReturnType,
+ R"(5:6 error: cannot use function 'FUNCTION' as type
+1:2 note: 'FUNCTION' declared here)"},
+ {Def::kFunction, Use::kMemberType,
+ R"(5:6 error: cannot use function 'FUNCTION' as type
+1:2 note: 'FUNCTION' declared here)"},
+ {Def::kFunction, Use::kValueExpression, R"(7:8 error: missing '(' for function call)"},
+ {Def::kFunction, Use::kVariableType,
+ R"(5:6 error: cannot use function 'FUNCTION' as type
+1:2 note: 'FUNCTION' declared here)"},
+
+ {Def::kStruct, Use::kCallExpr, kPass},
+ {Def::kStruct, Use::kCallStmt, kPass},
+ {Def::kStruct, Use::kFunctionReturnType, kPass},
+ {Def::kStruct, Use::kMemberType, kPass},
+ {Def::kStruct, Use::kValueExpression,
+ R"(5:6 error: cannot use type 'STRUCT' as value
+7:8 note: are you missing '()' for type initializer?
+1:2 note: 'STRUCT' declared here)"},
+ {Def::kStruct, Use::kVariableType, kPass},
+
+ {Def::kTypeAlias, Use::kCallExpr, kPass},
+ {Def::kTypeAlias, Use::kCallStmt, kPass},
+ {Def::kTypeAlias, Use::kFunctionReturnType, kPass},
+ {Def::kTypeAlias, Use::kMemberType, kPass},
+ {Def::kTypeAlias, Use::kValueExpression,
+ R"(5:6 error: cannot use type 'i32' as value
+7:8 note: are you missing '()' for type initializer?)"},
+ {Def::kTypeAlias, Use::kVariableType, kPass},
+
+ {Def::kVariable, Use::kCallStmt,
+ R"(5:6 error: cannot use const 'VARIABLE' as call target
+1:2 note: 'VARIABLE' declared here)"},
+ {Def::kVariable, Use::kCallExpr,
+ R"(5:6 error: cannot use const 'VARIABLE' as call target
+1:2 note: 'VARIABLE' declared here)"},
+ {Def::kVariable, Use::kFunctionReturnType,
+ R"(5:6 error: cannot use const 'VARIABLE' as type
+1:2 note: 'VARIABLE' declared here)"},
+ {Def::kVariable, Use::kMemberType,
+ R"(5:6 error: cannot use const 'VARIABLE' as type
+1:2 note: 'VARIABLE' declared here)"},
+ {Def::kVariable, Use::kValueExpression, kPass},
+ {Def::kVariable, Use::kVariableType,
+ R"(5:6 error: cannot use const 'VARIABLE' as type
+1:2 note: 'VARIABLE' declared here)"},
+ }));
+
+} // namespace
+} // namespace tint::resolver
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 01bad34..47338a3 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -67,6 +67,7 @@
#include "src/tint/sem/struct.h"
#include "src/tint/sem/switch_statement.h"
#include "src/tint/sem/type_conversion.h"
+#include "src/tint/sem/type_expression.h"
#include "src/tint/sem/type_initializer.h"
#include "src/tint/sem/variable.h"
#include "src/tint/sem/while_statement.h"
@@ -115,7 +116,7 @@
Resolver::~Resolver() = default;
bool Resolver::Resolve() {
- if (builder_->Diagnostics().contains_errors()) {
+ if (diagnostics_.contains_errors()) {
return false;
}
@@ -124,7 +125,7 @@
// Pre-allocate the marked bitset with the total number of AST nodes.
marked_.Resize(builder_->ASTNodes().Count());
- if (!DependencyGraph::Build(builder_->AST(), builder_->Symbols(), builder_->Diagnostics(),
+ if (!DependencyGraph::Build(builder_->AST(), builder_->Symbols(), diagnostics_,
dependencies_)) {
return false;
}
@@ -329,49 +330,30 @@
Mark(t->name);
if (t->name->Is<ast::TemplatedIdentifier>()) {
- TINT_UNREACHABLE(Resolver, builder_->Diagnostics()) << "TODO(crbug.com/tint/1810)";
+ TINT_UNREACHABLE(Resolver, diagnostics_) << "TODO(crbug.com/tint/1810)";
}
auto resolved = dependencies_.resolved_identifiers.Get(t->name);
if (!resolved) {
- TINT_ICE(Resolver, builder_->Diagnostics()) << "identifier was not resolved";
+ TINT_ICE(Resolver, diagnostics_)
+ << "identifier '" << builder_->Symbols().NameFor(t->name->symbol)
+ << "' was not resolved";
return nullptr;
}
if (auto* ast_node = resolved->Node()) {
- auto* resolved_node = sem_.Get(ast_node);
- return Switch(
- resolved_node, //
- [&](type::Type* type) { return type; },
- [&](sem::Variable* variable) {
- auto name =
- builder_->Symbols().NameFor(variable->Declaration()->name->symbol);
- AddError("cannot use variable '" + name + "' as type", ty->source);
- AddNote("'" + name + "' declared here", variable->Declaration()->source);
- return nullptr;
- },
- [&](sem::Function* func) {
- auto name = builder_->Symbols().NameFor(func->Declaration()->name->symbol);
- AddError("cannot use function '" + name + "' as type", ty->source);
- AddNote("'" + name + "' declared here", func->Declaration()->source);
- return nullptr;
- });
+ auto* type = sem_.Get<type::Type>(ast_node);
+ if (TINT_UNLIKELY(!type)) {
+ ErrorMismatchedResolvedIdentifier(t->source, *resolved, "type");
+ return nullptr;
+ }
+ return type;
+ }
+ if (auto b = resolved->BuiltinType(); b != type::Builtin::kUndefined) {
+ return BuiltinType(b, t->name);
}
- if (auto builtin_ty = resolved->BuiltinType();
- builtin_ty != type::Builtin::kUndefined) {
- return BuiltinType(builtin_ty, t->name);
- }
-
- if (auto builtin_fn = resolved->BuiltinFunction();
- builtin_fn != sem::BuiltinType::kNone) {
- auto name = builder_->Symbols().NameFor(t->name->symbol);
- AddError("cannot use builtin '" + name + "' as type", ty->source);
- return nullptr;
- }
-
- TINT_UNREACHABLE(Resolver, diagnostics_)
- << "unhandled resolved identifier: " << *resolved;
+ ErrorMismatchedResolvedIdentifier(t->source, *resolved, "type");
return nullptr;
});
@@ -414,7 +396,7 @@
return nullptr;
}
- auto* rhs = Load(Materialize(Expression(v->initializer), ty));
+ auto* rhs = Load(Materialize(ValueExpression(v->initializer), ty));
if (!rhs) {
return nullptr;
}
@@ -473,7 +455,7 @@
ExprEvalStageConstraint constraint{sem::EvaluationStage::kOverride, "override initializer"};
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
- rhs = Materialize(Expression(v->initializer), ty);
+ rhs = Materialize(ValueExpression(v->initializer), ty);
if (!rhs) {
return nullptr;
}
@@ -507,7 +489,7 @@
ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "@id"};
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
- auto* materialized = Materialize(Expression(id_attr->expr));
+ auto* materialized = Materialize(ValueExpression(id_attr->expr));
if (!materialized) {
return nullptr;
}
@@ -560,7 +542,7 @@
{
ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "const initializer"};
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
- rhs = Expression(c->initializer);
+ rhs = ValueExpression(c->initializer);
if (!rhs) {
return nullptr;
}
@@ -625,7 +607,7 @@
};
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
- rhs = Load(Materialize(Expression(var->initializer), storage_ty));
+ rhs = Load(Materialize(ValueExpression(var->initializer), storage_ty));
if (!rhs) {
return nullptr;
}
@@ -689,7 +671,7 @@
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
auto* attr = ast::GetAttribute<ast::BindingAttribute>(var->attributes);
- auto* materialized = Materialize(Expression(attr->expr));
+ auto* materialized = Materialize(ValueExpression(attr->expr));
if (!materialized) {
return nullptr;
}
@@ -713,7 +695,7 @@
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
auto* attr = ast::GetAttribute<ast::GroupAttribute>(var->attributes);
- auto* materialized = Materialize(Expression(attr->expr));
+ auto* materialized = Materialize(ValueExpression(attr->expr));
if (!materialized) {
return nullptr;
}
@@ -799,7 +781,7 @@
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
auto* attr = ast::GetAttribute<ast::BindingAttribute>(param->attributes);
- auto* materialized = Materialize(Expression(attr->expr));
+ auto* materialized = Materialize(ValueExpression(attr->expr));
if (!materialized) {
return nullptr;
}
@@ -810,7 +792,7 @@
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
auto* attr = ast::GetAttribute<ast::GroupAttribute>(param->attributes);
- auto* materialized = Materialize(Expression(attr->expr));
+ auto* materialized = Materialize(ValueExpression(attr->expr));
if (!materialized) {
return nullptr;
}
@@ -838,7 +820,7 @@
ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "@location value"};
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
- auto* materialized = Materialize(Expression(attr->expr));
+ auto* materialized = Materialize(ValueExpression(attr->expr));
if (!materialized) {
return utils::Failure;
}
@@ -924,7 +906,7 @@
for (auto it : dependencies_.shadows) {
CastableBase* b = sem_.Get(it.value);
if (TINT_UNLIKELY(!b)) {
- TINT_ICE(Resolver, builder_->Diagnostics())
+ TINT_ICE(Resolver, diagnostics_)
<< "AST node '" << it.value->TypeInfo().name << "' had no semantic info\n"
<< "At: " << it.value->source << "\n"
<< "Pointer: " << it.value;
@@ -978,7 +960,7 @@
sem::Statement* Resolver::ConstAssert(const ast::ConstAssert* assertion) {
ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "const assertion"};
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
- auto* expr = Expression(assertion->condition);
+ auto* expr = ValueExpression(assertion->condition);
if (!expr) {
return nullptr;
}
@@ -1196,7 +1178,7 @@
if (!value) {
break;
}
- const auto* expr = Expression(value);
+ const auto* expr = ValueExpression(value);
if (!expr) {
return false;
}
@@ -1368,7 +1350,7 @@
auto* sem =
builder_->create<sem::IfStatement>(stmt, current_compound_statement_, current_function_);
return StatementScope(stmt, sem, [&] {
- auto* cond = Load(Expression(stmt->condition));
+ auto* cond = Load(ValueExpression(stmt->condition));
if (!cond) {
return false;
}
@@ -1463,7 +1445,7 @@
}
if (auto* cond_expr = stmt->condition) {
- auto* cond = Load(Expression(cond_expr));
+ auto* cond = Load(ValueExpression(cond_expr));
if (!cond) {
return false;
}
@@ -1506,7 +1488,7 @@
return StatementScope(stmt, sem, [&] {
auto& behaviors = sem->Behaviors();
- auto* cond = Load(Expression(stmt->condition));
+ auto* cond = Load(ValueExpression(stmt->condition));
if (!cond) {
return false;
}
@@ -1533,7 +1515,7 @@
});
}
-sem::ValueExpression* Resolver::Expression(const ast::Expression* root) {
+sem::Expression* Resolver::Expression(const ast::Expression* root) {
utils::Vector<const ast::Expression*, 64> sorted;
constexpr size_t kMaxExpressionDepth = 512U;
bool failed = false;
@@ -1567,30 +1549,15 @@
for (auto* expr : utils::Reverse(sorted)) {
auto* sem_expr = Switch(
- expr,
- [&](const ast::IndexAccessorExpression* array) -> sem::ValueExpression* {
- return IndexAccessor(array);
- },
- [&](const ast::BinaryExpression* bin_op) -> sem::ValueExpression* {
- return Binary(bin_op);
- },
- [&](const ast::BitcastExpression* bitcast) -> sem::ValueExpression* {
- return Bitcast(bitcast);
- },
- [&](const ast::CallExpression* call) -> sem::ValueExpression* { return Call(call); },
- [&](const ast::IdentifierExpression* ident) -> sem::ValueExpression* {
- return Identifier(ident);
- },
- [&](const ast::LiteralExpression* literal) -> sem::ValueExpression* {
- return Literal(literal);
- },
- [&](const ast::MemberAccessorExpression* member) -> sem::ValueExpression* {
- return MemberAccessor(member);
- },
- [&](const ast::UnaryOpExpression* unary) -> sem::ValueExpression* {
- return UnaryOp(unary);
- },
- [&](const ast::PhonyExpression*) -> sem::ValueExpression* {
+ expr, [&](const ast::IndexAccessorExpression* array) { return IndexAccessor(array); },
+ [&](const ast::BinaryExpression* bin_op) { return Binary(bin_op); },
+ [&](const ast::BitcastExpression* bitcast) { return Bitcast(bitcast); },
+ [&](const ast::CallExpression* call) { return Call(call); },
+ [&](const ast::IdentifierExpression* ident) { return Identifier(ident); },
+ [&](const ast::LiteralExpression* literal) { return Literal(literal); },
+ [&](const ast::MemberAccessorExpression* member) { return MemberAccessor(member); },
+ [&](const ast::UnaryOpExpression* unary) { return UnaryOp(unary); },
+ [&](const ast::PhonyExpression*) {
return builder_->create<sem::ValueExpression>(expr, builder_->create<type::Void>(),
sem::EvaluationStage::kRuntime,
current_statement_,
@@ -1606,10 +1573,14 @@
return nullptr;
}
- if (auto* constraint = expr_eval_stage_constraint_.constraint) {
- if (!validator_.EvaluationStage(sem_expr, expr_eval_stage_constraint_.stage,
- constraint)) {
- return nullptr;
+ auto* val = sem_expr->As<sem::ValueExpression>();
+
+ if (val) {
+ if (auto* constraint = expr_eval_stage_constraint_.constraint) {
+ if (!validator_.EvaluationStage(val, expr_eval_stage_constraint_.stage,
+ constraint)) {
+ return nullptr;
+ }
}
}
@@ -1618,11 +1589,11 @@
return sem_expr;
}
- // If we just processed the lhs of a constexpr logical binary expression, mark the rhs
- // for short-circuiting.
- if (sem_expr->ConstantValue()) {
+ // If we just processed the lhs of a constexpr logical binary expression, mark the rhs for
+ // short-circuiting.
+ if (val && val->ConstantValue()) {
if (auto binary = logical_binary_lhs_to_parent_.Find(expr)) {
- const bool lhs_is_true = sem_expr->ConstantValue()->ValueAs<bool>();
+ const bool lhs_is_true = val->ConstantValue()->ValueAs<bool>();
if (((*binary)->IsLogicalAnd() && !lhs_is_true) ||
((*binary)->IsLogicalOr() && lhs_is_true)) {
// Mark entire expression tree to not const-evaluate
@@ -1643,6 +1614,10 @@
return nullptr;
}
+sem::ValueExpression* Resolver::ValueExpression(const ast::Expression* expr) {
+ return sem_.AsValue(Expression(expr));
+}
+
void Resolver::RegisterStore(const sem::ValueExpression* expr) {
auto& info = alias_analysis_infos_[current_function_];
Switch(
@@ -1811,7 +1786,7 @@
const sem::ValueExpression* Resolver::Load(const sem::ValueExpression* expr) {
if (!expr) {
- // Allow for Load(Expression(blah)), where failures pass through Load()
+ // Allow for Load(ValueExpression(blah)), where failures pass through Load()
return nullptr;
}
@@ -1839,7 +1814,7 @@
const sem::ValueExpression* Resolver::Materialize(const sem::ValueExpression* expr,
const type::Type* target_type /* = nullptr */) {
if (!expr) {
- // Allow for Materialize(Expression(blah)), where failures pass through Materialize()
+ // Allow for Materialize(ValueExpression(blah)), where failures pass through Materialize()
return nullptr;
}
@@ -1859,7 +1834,7 @@
if (!skip_const_eval_.Contains(decl)) {
auto expr_val = expr->ConstantValue();
if (TINT_UNLIKELY(!expr_val)) {
- TINT_ICE(Resolver, builder_->Diagnostics())
+ TINT_ICE(Resolver, diagnostics_)
<< decl->source << "Materialize(" << decl->TypeInfo().name
<< ") called on expression with no constant value";
return nullptr;
@@ -1872,7 +1847,7 @@
}
materialized_val = val.Get();
if (TINT_UNLIKELY(!materialized_val)) {
- TINT_ICE(Resolver, builder_->Diagnostics())
+ TINT_ICE(Resolver, diagnostics_)
<< decl->source << "ConvertValue(" << builder_->FriendlyName(expr_val->Type())
<< " -> " << builder_->FriendlyName(concrete_ty) << ") returned invalid value";
return nullptr;
@@ -2328,14 +2303,15 @@
auto resolved = dependencies_.resolved_identifiers.Get(ident);
if (!resolved) {
- TINT_ICE(Resolver, builder_->Diagnostics()) << "identifier was not resolved";
+ TINT_ICE(Resolver, diagnostics_)
+ << "identifier '" << builder_->Symbols().NameFor(ident->symbol)
+ << "' was not resolved";
return nullptr;
}
if (auto* ast_node = resolved->Node()) {
- auto* resolved_node = sem_.Get(ast_node);
return Switch(
- resolved_node, //
+ sem_.Get(ast_node), //
[&](const type::Type* ty) {
// A type initializer or conversions.
// Note: Unlike the code path where we're resolving the call target from an
@@ -2344,24 +2320,22 @@
return ty_init_or_conv(ty);
},
[&](sem::Function* func) { return FunctionCall(expr, func, args, arg_behaviors); },
- [&](sem::Variable* var) {
- auto name = builder_->Symbols().NameFor(var->Declaration()->name->symbol);
- AddError("cannot call variable '" + name + "'", ident->source);
- AddNote("'" + name + "' declared here", var->Declaration()->source);
+ [&](Default) {
+ ErrorMismatchedResolvedIdentifier(ident->source, *resolved, "call target");
return nullptr;
});
}
- if (auto builtin_fn = resolved->BuiltinFunction(); builtin_fn != sem::BuiltinType::kNone) {
- return BuiltinCall(expr, builtin_fn, args);
+ if (auto f = resolved->BuiltinFunction(); f != sem::BuiltinType::kNone) {
+ return BuiltinCall(expr, f, args);
}
- if (auto builtin_ty = resolved->BuiltinType(); builtin_ty != type::Builtin::kUndefined) {
- auto* ty = BuiltinType(builtin_ty, expr->target.name);
+ if (auto b = resolved->BuiltinType(); b != type::Builtin::kUndefined) {
+ auto* ty = BuiltinType(b, expr->target.name);
return ty ? ty_init_or_conv(ty) : nullptr;
}
- TINT_UNREACHABLE(Resolver, diagnostics_) << "unhandled resolved identifier: " << *resolved;
+ ErrorMismatchedResolvedIdentifier(ident->source, *resolved, "call target");
return nullptr;
}
@@ -2669,7 +2643,7 @@
case ast::IntLiteralExpression::Suffix::kU:
return builder_->create<type::U32>();
}
- TINT_UNREACHABLE(Resolver, builder_->Diagnostics())
+ TINT_UNREACHABLE(Resolver, diagnostics_)
<< "Unhandled integer literal suffix: " << i->suffix;
return nullptr;
},
@@ -2684,13 +2658,13 @@
? builder_->create<type::F16>()
: nullptr;
}
- TINT_UNREACHABLE(Resolver, builder_->Diagnostics())
+ TINT_UNREACHABLE(Resolver, diagnostics_)
<< "Unhandled float literal suffix: " << f->suffix;
return nullptr;
},
[&](const ast::BoolLiteralExpression*) { return builder_->create<type::Bool>(); },
[&](Default) {
- TINT_UNREACHABLE(Resolver, builder_->Diagnostics())
+ TINT_UNREACHABLE(Resolver, diagnostics_)
<< "Unhandled literal type: " << literal->TypeInfo().name;
return nullptr;
});
@@ -2716,12 +2690,14 @@
/* has_side_effects */ false);
}
-sem::ValueExpression* Resolver::Identifier(const ast::IdentifierExpression* expr) {
+sem::Expression* Resolver::Identifier(const ast::IdentifierExpression* expr) {
Mark(expr->identifier);
auto resolved = dependencies_.resolved_identifiers.Get(expr->identifier);
if (!resolved) {
- TINT_ICE(Resolver, builder_->Diagnostics()) << "identifier was not resolved";
+ TINT_ICE(Resolver, diagnostics_)
+ << "identifier '" << builder_->Symbols().NameFor(expr->identifier->symbol)
+ << "' was not resolved";
return nullptr;
}
@@ -2801,9 +2777,8 @@
variable->AddUser(user);
return user;
},
- [&](const type::Type*) {
- AddError("missing '(' for type initializer or cast", expr->source.End());
- return nullptr;
+ [&](const type::Type* ty) {
+ return builder_->create<sem::TypeExpression>(expr, current_statement_, ty);
},
[&](const sem::Function*) {
AddError("missing '(' for function call", expr->source.End());
@@ -2811,17 +2786,21 @@
});
}
- if (resolved->BuiltinType() != type::Builtin::kUndefined) {
- AddError("missing '(' for type initializer or cast", expr->source.End());
- return nullptr;
+ if (auto builtin_ty = resolved->BuiltinType(); builtin_ty != type::Builtin::kUndefined) {
+ auto* ty = BuiltinType(builtin_ty, expr->identifier);
+ if (!ty) {
+ return nullptr;
+ }
+ return builder_->create<sem::TypeExpression>(expr, current_statement_, ty);
}
if (resolved->BuiltinFunction() != sem::BuiltinType::kNone) {
- AddError("missing '(' for builtin call", expr->source.End());
+ AddError("missing '(' for builtin function call", expr->source.End());
return nullptr;
}
- TINT_UNREACHABLE(Resolver, diagnostics_) << "unhandled resolved identifier: " << *resolved;
+ TINT_UNREACHABLE(Resolver, diagnostics_)
+ << "unhandled resolved identifier: " << resolved->String(builder_->Symbols(), diagnostics_);
return nullptr;
}
@@ -3231,7 +3210,7 @@
const type::ArrayCount* Resolver::ArrayCount(const ast::Expression* count_expr) {
// Evaluate the constant array count expression.
- const auto* count_sem = Materialize(Expression(count_expr));
+ const auto* count_sem = Materialize(ValueExpression(count_expr));
if (!count_sem) {
return nullptr;
}
@@ -3413,7 +3392,7 @@
"@offset value"};
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
- auto* materialized = Materialize(Expression(o->expr));
+ auto* materialized = Materialize(ValueExpression(o->expr));
if (!materialized) {
return false;
}
@@ -3435,7 +3414,7 @@
ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "@align"};
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
- auto* materialized = Materialize(Expression(a->expr));
+ auto* materialized = Materialize(ValueExpression(a->expr));
if (!materialized) {
return false;
}
@@ -3464,7 +3443,7 @@
ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "@size"};
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
- auto* materialized = Materialize(Expression(s->expr));
+ auto* materialized = Materialize(ValueExpression(s->expr));
if (!materialized) {
return false;
}
@@ -3590,7 +3569,7 @@
const type::Type* value_ty = nullptr;
if (auto* value = stmt->value) {
- const auto* expr = Load(Expression(value));
+ const auto* expr = Load(ValueExpression(value));
if (!expr) {
return false;
}
@@ -3620,7 +3599,7 @@
return StatementScope(stmt, sem, [&] {
auto& behaviors = sem->Behaviors();
- const auto* cond = Load(Expression(stmt->condition));
+ const auto* cond = Load(ValueExpression(stmt->condition));
if (!cond) {
return false;
}
@@ -3637,7 +3616,7 @@
if (sel->IsDefault()) {
continue;
}
- auto* sem_expr = Expression(sel->expr);
+ auto* sem_expr = ValueExpression(sel->expr);
if (!sem_expr) {
return false;
}
@@ -3710,14 +3689,14 @@
auto* sem =
builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
return StatementScope(stmt, sem, [&] {
- auto* lhs = Expression(stmt->lhs);
+ auto* lhs = ValueExpression(stmt->lhs);
if (!lhs) {
return false;
}
const bool is_phony_assignment = stmt->lhs->Is<ast::PhonyExpression>();
- const auto* rhs = Expression(stmt->rhs);
+ const auto* rhs = ValueExpression(stmt->rhs);
if (!rhs) {
return false;
}
@@ -3762,7 +3741,7 @@
auto* sem = builder_->create<sem::BreakIfStatement>(stmt, current_compound_statement_,
current_function_);
return StatementScope(stmt, sem, [&] {
- auto* cond = Load(Expression(stmt->condition));
+ auto* cond = Load(ValueExpression(stmt->condition));
if (!cond) {
return false;
}
@@ -3778,7 +3757,7 @@
auto* sem =
builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
return StatementScope(stmt, sem, [&] {
- if (auto* expr = Expression(stmt->expr)) {
+ if (auto* expr = ValueExpression(stmt->expr)) {
sem->Behaviors() = expr->Behaviors();
return true;
}
@@ -3791,12 +3770,12 @@
auto* sem =
builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
return StatementScope(stmt, sem, [&] {
- auto* lhs = Expression(stmt->lhs);
+ auto* lhs = ValueExpression(stmt->lhs);
if (!lhs) {
return false;
}
- auto* rhs = Load(Expression(stmt->rhs));
+ auto* rhs = Load(ValueExpression(stmt->rhs));
if (!rhs) {
return false;
}
@@ -3849,7 +3828,7 @@
auto* sem =
builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
return StatementScope(stmt, sem, [&] {
- auto* lhs = Expression(stmt->lhs);
+ auto* lhs = ValueExpression(stmt->lhs);
if (!lhs) {
return false;
}
@@ -4004,6 +3983,29 @@
}
}
+void Resolver::ErrorMismatchedResolvedIdentifier(const Source& source,
+ const ResolvedIdentifier& resolved,
+ std::string_view wanted) {
+ AddError("cannot use " + resolved.String(builder_->Symbols(), diagnostics_) + " as " +
+ std::string(wanted),
+ source);
+
+ Switch(
+ resolved.Node(),
+ [&](const ast::TypeDecl* n) {
+ AddNote("'" + builder_->Symbols().NameFor(n->name->symbol) + "' declared here",
+ n->source);
+ },
+ [&](const ast::Variable* n) {
+ AddNote("'" + builder_->Symbols().NameFor(n->name->symbol) + "' declared here",
+ n->source);
+ },
+ [&](const ast::Function* n) {
+ AddNote("'" + builder_->Symbols().NameFor(n->name->symbol) + "' declared here",
+ n->source);
+ });
+}
+
void Resolver::AddError(const std::string& msg, const Source& source) const {
diagnostics_.add_error(diag::System::Resolver, msg, source);
}
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index ac6ac5d..0d3c879 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -122,11 +122,15 @@
/// ProgramBuilder.
void CreateSemanticNodes() const;
+ /// @returns the call of Expression() cast to a sem::ValueExpression. If the sem::Expression is
+ /// not a sem::ValueExpression, then an error diagnostic is raised and nullptr is returned.
+ sem::ValueExpression* ValueExpression(const ast::Expression* expr);
+
/// Expression traverses the graph of expressions starting at `expr`, building a postordered
/// list (leaf-first) of all the expression nodes. Each of the expressions are then resolved by
/// dispatching to the appropriate expression handlers below.
/// @returns the resolved semantic node for the expression `expr`, or nullptr on failure.
- sem::ValueExpression* Expression(const ast::Expression* expr);
+ sem::Expression* Expression(const ast::Expression* expr);
////////////////////////////////////////////////////////////////////////////////////////////////
// Expression resolving methods
@@ -147,7 +151,7 @@
sem::Function* target,
utils::Vector<const sem::ValueExpression*, N>& args,
sem::Behaviors arg_behaviors);
- sem::ValueExpression* Identifier(const ast::IdentifierExpression*);
+ sem::Expression* Identifier(const ast::IdentifierExpression*);
template <size_t N>
sem::Call* BuiltinCall(const ast::CallExpression*,
sem::BuiltinType,
@@ -419,6 +423,15 @@
template <typename NODE>
void ApplyDiagnosticSeverities(NODE* node);
+ /// Raises an error diagnostic that the resolved identifier @p resolved was not of the expected
+ /// kind.
+ /// @param source the source of the error diagnostic
+ /// @param resolved the resolved identifier
+ /// @param wanted the expected kind
+ void ErrorMismatchedResolvedIdentifier(const Source& source,
+ const ResolvedIdentifier& resolved,
+ std::string_view wanted);
+
/// Adds the given error message to the diagnostics
void AddError(const std::string& msg, const Source& source) const;
diff --git a/src/tint/resolver/sem_helper.cc b/src/tint/resolver/sem_helper.cc
index 836fb97..2c11659 100644
--- a/src/tint/resolver/sem_helper.cc
+++ b/src/tint/resolver/sem_helper.cc
@@ -14,6 +14,7 @@
#include "src/tint/resolver/sem_helper.h"
+#include "src/tint/sem/type_expression.h"
#include "src/tint/sem/value_expression.h"
namespace tint::resolver {
@@ -35,4 +36,35 @@
return sem ? const_cast<type::Type*>(sem->Type()) : nullptr;
}
+void SemHelper::ErrorExpectedValueExpr(const sem::Expression* expr) const {
+ Switch(
+ expr, //
+ [&](const sem::TypeExpression* ty_expr) {
+ auto name = ty_expr->Type()->FriendlyName(builder_->Symbols());
+ AddError("cannot use type '" + name + "' as value", ty_expr->Declaration()->source);
+ if (auto* ident = ty_expr->Declaration()->As<ast::IdentifierExpression>()) {
+ AddNote("are you missing '()' for type initializer?",
+ Source{{ident->source.range.end}});
+ }
+ if (auto* str = ty_expr->Type()->As<type::Struct>()) {
+ AddNote("'" + name + "' declared here", str->Source());
+ }
+ },
+ [&](Default) {
+ TINT_ICE(Resolver, builder_->Diagnostics())
+ << "unhandled sem::Expression type: " << (expr ? expr->TypeInfo().name : "<null>");
+ });
+}
+
+void SemHelper::AddError(const std::string& msg, const Source& source) const {
+ builder_->Diagnostics().add_error(diag::System::Resolver, msg, source);
+}
+
+void SemHelper::AddWarning(const std::string& msg, const Source& source) const {
+ builder_->Diagnostics().add_warning(diag::System::Resolver, msg, source);
+}
+
+void SemHelper::AddNote(const std::string& msg, const Source& source) const {
+ builder_->Diagnostics().add_note(diag::System::Resolver, msg, source);
+}
} // namespace tint::resolver
diff --git a/src/tint/resolver/sem_helper.h b/src/tint/resolver/sem_helper.h
index 2a7241b..9c918cd 100644
--- a/src/tint/resolver/sem_helper.h
+++ b/src/tint/resolver/sem_helper.h
@@ -57,39 +57,58 @@
/// @returns the sem node for @p ast
template <typename AST = ast::Node>
auto* GetVal(const AST* ast) const {
- if constexpr (traits::IsTypeOrDerived<sem::SemanticNodeTypeFor<AST>,
- sem::ValueExpression>) {
- return Get(ast);
+ return AsValue(Get(ast));
+ }
+
+ /// @param expr the semantic node
+ /// @returns one of:
+ /// * nullptr if @p expr is nullptr
+ /// * @p expr if the static pointer type already derives from sem::ValueExpression
+ /// * @p expr cast to sem::ValueExpression if the cast is successful
+ /// * nullptr if @p expr is not a sem::ValueExpression. In this case an error diagnostic is
+ /// raised.
+ template <typename EXPR>
+ auto* AsValue(EXPR* expr) const {
+ if constexpr (traits::IsTypeOrDerived<EXPR, sem::ValueExpression>) {
+ return expr;
} else {
- if (auto* sem = Get(ast); TINT_LIKELY(sem)) {
- auto* val = sem->template As<sem::ValueExpression>();
- if (TINT_LIKELY(val)) {
+ if (TINT_LIKELY(expr)) {
+ if (auto* val = expr->template As<sem::ValueExpression>(); TINT_LIKELY(val)) {
return val;
}
- // TODO(crbug.com/tint/1810): Improve error
- builder_->Diagnostics().add_error(diag::System::Resolver,
- "required value expression, got something else",
- ast->source);
+ ErrorExpectedValueExpr(expr);
}
return static_cast<sem::ValueExpression*>(nullptr);
}
}
- /// @returns the resolved type of the ast::Expression `expr`
+ /// @returns the resolved type of the ast::Expression @p expr
/// @param expr the expression
type::Type* TypeOf(const ast::Expression* expr) const;
- /// @returns the type name of the given semantic type, unwrapping
- /// references.
+ /// @returns the type name of the given semantic type, unwrapping references.
/// @param ty the type to look up
std::string TypeNameOf(const type::Type* ty) const;
- /// @returns the type name of the given semantic type, without unwrapping
- /// references.
+ /// @returns the type name of the given semantic type, without unwrapping references.
/// @param ty the type to look up
std::string RawTypeNameOf(const type::Type* ty) const;
+ /// Raises an error diagnostic that the expression @p got was expected to be a
+ /// sem::ValueExpression, but the expression evaluated to something different.
+ /// @param expr the expression
+ void ErrorExpectedValueExpr(const sem::Expression* expr) const;
+
private:
+ /// Adds the given error message to the diagnostics
+ void AddError(const std::string& msg, const Source& source) const;
+
+ /// Adds the given warning message to the diagnostics
+ void AddWarning(const std::string& msg, const Source& source) const;
+
+ /// Adds the given note message to the diagnostics
+ void AddNote(const std::string& msg, const Source& source) const;
+
ProgramBuilder* builder_;
};
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index b4e83c3..b28cecf 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -883,38 +883,6 @@
"12:34 error: ptr<uniform, u32, read> cannot be used as an element type of an array");
}
-TEST_F(ResolverTypeValidationTest, VariableAsType) {
- // var<private> a : i32;
- // var<private> b : a;
- GlobalVar("a", ty.i32(), type::AddressSpace::kPrivate);
- GlobalVar("b", ty("a"), type::AddressSpace::kPrivate);
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(error: cannot use variable 'a' as type
-note: 'a' declared here)");
-}
-
-TEST_F(ResolverTypeValidationTest, FunctionAsType) {
- // fn f() {}
- // var<private> v : f;
- Func("f", utils::Empty, ty.void_(), {});
- GlobalVar("v", ty("f"), type::AddressSpace::kPrivate);
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(error: cannot use function 'f' as type
-note: 'f' declared here)");
-}
-
-TEST_F(ResolverTypeValidationTest, BuiltinAsType) {
- // var<private> v : max;
- GlobalVar("v", ty("max"), type::AddressSpace::kPrivate);
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "error: cannot use builtin 'max' as type");
-}
-
namespace GetCanonicalTests {
struct Params {
builder::ast_type_func_ptr create_ast_type;
diff --git a/src/tint/resolver/validation_test.cc b/src/tint/resolver/validation_test.cc
index 128feb5..bbfd4aa 100644
--- a/src/tint/resolver/validation_test.cc
+++ b/src/tint/resolver/validation_test.cc
@@ -134,7 +134,7 @@
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: if statement condition must be bool, got f32");
+ EXPECT_EQ(r()->error(), R"(12:34 error: if statement condition must be bool, got f32)");
}
TEST_F(ResolverValidationTest, Stmt_ElseIf_NonBool) {
@@ -144,7 +144,7 @@
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: if statement condition must be bool, got f32");
+ EXPECT_EQ(r()->error(), R"(12:34 error: if statement condition must be bool, got f32)");
}
TEST_F(ResolverValidationTest, Expr_ErrUnknownExprType) {
@@ -158,48 +158,6 @@
"tint::resolver::FakeExpr");
}
-TEST_F(ResolverValidationTest, Expr_DontCall_Function) {
- Func("func", utils::Empty, ty.void_(), utils::Empty, {});
- WrapInFunction(Expr(Source{{12, 34}}, "func"));
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: missing '(' for function call");
-}
-
-TEST_F(ResolverValidationTest, Expr_DontCall_Builtin) {
- WrapInFunction(Expr(Source{{12, 34}}, "round"));
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: missing '(' for builtin call");
-}
-
-TEST_F(ResolverValidationTest, Expr_DontCall_Type) {
- Alias("T", ty.u32());
- WrapInFunction(Expr(Source{{12, 34}}, "T"));
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: missing '(' for type initializer or cast");
-}
-
-TEST_F(ResolverValidationTest, Expr_DontCall_BuiltinType) {
- WrapInFunction(Expr(Source{{12, 34}}, "vec3f"));
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: missing '(' for type initializer or cast");
-}
-
-TEST_F(ResolverValidationTest, AssignmentStmt_InvalidLHS_BuiltinFunctionName) {
- // normalize = 2;
-
- auto* lhs = Expr(Source{{12, 34}}, "normalize");
- auto* rhs = Expr(2_i);
- auto* assign = Assign(lhs, rhs);
- WrapInFunction(assign);
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: missing '(' for builtin call");
-}
-
TEST_F(ResolverValidationTest, UsingUndefinedVariable_Fail) {
// b = 2;
@@ -209,7 +167,7 @@
WrapInFunction(assign);
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: unknown identifier: 'b'");
+ EXPECT_EQ(r()->error(), R"(12:34 error: unknown identifier: 'b')");
}
TEST_F(ResolverValidationTest, UsingUndefinedVariableInBlockStatement_Fail) {
@@ -224,7 +182,7 @@
WrapInFunction(body);
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: unknown identifier: 'b'");
+ EXPECT_EQ(r()->error(), R"(12:34 error: unknown identifier: 'b')");
}
TEST_F(ResolverValidationTest, UsingUndefinedVariableGlobalVariable_Pass) {
@@ -264,7 +222,7 @@
WrapInFunction(outer_body);
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: unknown identifier: 'a'");
+ EXPECT_EQ(r()->error(), R"(12:34 error: unknown identifier: 'a')");
}
TEST_F(ResolverValidationTest, UsingUndefinedVariableOuterScope_Pass) {
@@ -304,7 +262,7 @@
WrapInFunction(outer_body);
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: unknown identifier: 'a'");
+ EXPECT_EQ(r()->error(), R"(12:34 error: unknown identifier: 'a')");
}
TEST_F(ResolverValidationTest, AddressSpace_FunctionVariableWorkgroupClass) {
@@ -362,7 +320,7 @@
WrapInFunction(mem);
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "3:5 error: invalid vector swizzle character");
+ EXPECT_EQ(r()->error(), R"(3:5 error: invalid vector swizzle character)");
}
TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_MixedChars) {
@@ -383,7 +341,7 @@
WrapInFunction(mem);
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "3:3 error: invalid vector swizzle size");
+ EXPECT_EQ(r()->error(), R"(3:3 error: invalid vector swizzle size)");
}
TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadIndex) {
@@ -393,7 +351,7 @@
WrapInFunction(mem);
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "3:3 error: invalid vector swizzle member");
+ EXPECT_EQ(r()->error(), R"(3:3 error: invalid vector swizzle member)");
}
TEST_F(ResolverValidationTest, Expr_MemberAccessor_BadParent) {
@@ -791,7 +749,8 @@
Continue(Source{{12, 34}}))));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: continuing blocks must not contain a continue statement");
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: continuing blocks must not contain a continue statement)");
}
TEST_F(ResolverTest, Stmt_Loop_ContinueInContinuing_Indirect) {
@@ -938,7 +897,8 @@
Block(Break())));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: continuing blocks must not contain a continue statement");
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: continuing blocks must not contain a continue statement)");
}
TEST_F(ResolverTest, Stmt_ForLoop_ContinueInContinuing_Indirect) {
@@ -972,7 +932,7 @@
WrapInFunction(For(nullptr, Expr(Source{{12, 34}}, 1_f), nullptr, Block()));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: for-loop condition must be bool, got f32");
+ EXPECT_EQ(r()->error(), R"(12:34 error: for-loop condition must be bool, got f32)");
}
TEST_F(ResolverTest, Stmt_While_CondIsBoolRef) {
@@ -992,7 +952,7 @@
WrapInFunction(While(Expr(Source{{12, 34}}, 1_f), Block()));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: while condition must be bool, got f32");
+ EXPECT_EQ(r()->error(), R"(12:34 error: while condition must be bool, got f32)");
}
TEST_F(ResolverValidationTest, Stmt_ContinueInLoop) {
@@ -1004,7 +964,7 @@
TEST_F(ResolverValidationTest, Stmt_ContinueNotInLoop) {
WrapInFunction(Continue(Source{{12, 34}}));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: continue statement must be in a loop");
+ EXPECT_EQ(r()->error(), R"(12:34 error: continue statement must be in a loop)");
}
TEST_F(ResolverValidationTest, Stmt_BreakInLoop) {
@@ -1164,7 +1124,7 @@
TEST_F(ResolverValidationTest, Stmt_BreakNotInLoopOrSwitch) {
WrapInFunction(Break(Source{{12, 34}}));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: break statement must be in a loop or switch case");
+ EXPECT_EQ(r()->error(), R"(12:34 error: break statement must be in a loop or switch case)");
}
TEST_F(ResolverValidationTest, StructMemberDuplicateName) {
@@ -1205,7 +1165,8 @@
});
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: @align value must be a positive, power-of-two integer");
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: @align value must be a positive, power-of-two integer)");
}
TEST_F(ResolverValidationTest, NonPOTStructMemberAlignAttribute) {
@@ -1214,7 +1175,8 @@
});
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: @align value must be a positive, power-of-two integer");
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: @align value must be a positive, power-of-two integer)");
}
TEST_F(ResolverValidationTest, ZeroStructMemberAlignAttribute) {
@@ -1223,7 +1185,8 @@
});
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: @align value must be a positive, power-of-two integer");
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: @align value must be a positive, power-of-two integer)");
}
TEST_F(ResolverValidationTest, ZeroStructMemberSizeAttribute) {
@@ -1232,7 +1195,7 @@
});
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: @size must be at least as big as the type's size (4)");
+ EXPECT_EQ(r()->error(), R"(12:34 error: @size must be at least as big as the type's size (4))");
}
TEST_F(ResolverValidationTest, OffsetAndSizeAttribute) {
@@ -1242,7 +1205,7 @@
});
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: @offset cannot be used with @align or @size");
+ EXPECT_EQ(r()->error(), R"(12:34 error: @offset cannot be used with @align or @size)");
}
TEST_F(ResolverValidationTest, OffsetAndAlignAttribute) {
@@ -1252,7 +1215,7 @@
});
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: @offset cannot be used with @align or @size");
+ EXPECT_EQ(r()->error(), R"(12:34 error: @offset cannot be used with @align or @size)");
}
TEST_F(ResolverValidationTest, OffsetAndAlignAndSizeAttribute) {
@@ -1262,7 +1225,7 @@
});
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: @offset cannot be used with @align or @size");
+ EXPECT_EQ(r()->error(), R"(12:34 error: @offset cannot be used with @align or @size)");
}
TEST_F(ResolverTest, Expr_Initializer_Cast_Pointer) {
@@ -1272,28 +1235,28 @@
WrapInFunction(Decl(vf), Decl(ip));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: type is not constructible");
+ EXPECT_EQ(r()->error(), R"(12:34 error: type is not constructible)");
}
TEST_F(ResolverTest, I32_Overflow) {
GlobalVar("v", ty.i32(), type::AddressSpace::kPrivate, Expr(Source{{12, 24}}, 2147483648_a));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:24 error: value 2147483648 cannot be represented as 'i32'");
+ EXPECT_EQ(r()->error(), R"(12:24 error: value 2147483648 cannot be represented as 'i32')");
}
TEST_F(ResolverTest, I32_Underflow) {
GlobalVar("v", ty.i32(), type::AddressSpace::kPrivate, Expr(Source{{12, 24}}, -2147483649_a));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:24 error: value -2147483649 cannot be represented as 'i32'");
+ EXPECT_EQ(r()->error(), R"(12:24 error: value -2147483649 cannot be represented as 'i32')");
}
TEST_F(ResolverTest, U32_Overflow) {
GlobalVar("v", ty.u32(), type::AddressSpace::kPrivate, Expr(Source{{12, 24}}, 4294967296_a));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:24 error: value 4294967296 cannot be represented as 'u32'");
+ EXPECT_EQ(r()->error(), R"(12:24 error: value 4294967296 cannot be represented as 'u32')");
}
// var a: array<i32,2>;
@@ -1310,7 +1273,8 @@
WrapInFunction(a, idx);
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "error: cannot index type 'ptr<function, array<i32, 2>, read_write>'");
+ EXPECT_EQ(r()->error(),
+ R"(error: cannot index type 'ptr<function, array<i32, 2>, read_write>')");
}
} // namespace
diff --git a/src/tint/sem/type_expression.cc b/src/tint/sem/type_expression.cc
new file mode 100644
index 0000000..5129def
--- /dev/null
+++ b/src/tint/sem/type_expression.cc
@@ -0,0 +1,28 @@
+// Copyright 2023 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/sem/type_expression.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::sem::TypeExpression);
+
+namespace tint::sem {
+
+TypeExpression::TypeExpression(const ast::Expression* declaration,
+ const Statement* statement,
+ const type::Type* type)
+ : Base(declaration, statement), type_(type) {}
+
+TypeExpression::~TypeExpression() = default;
+
+} // namespace tint::sem
diff --git a/src/tint/sem/type_expression.h b/src/tint/sem/type_expression.h
new file mode 100644
index 0000000..0d4e43b
--- /dev/null
+++ b/src/tint/sem/type_expression.h
@@ -0,0 +1,50 @@
+// Copyright 2023 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_SEM_TYPE_EXPRESSION_H_
+#define SRC_TINT_SEM_TYPE_EXPRESSION_H_
+
+#include "src/tint/sem/expression.h"
+
+// Forward declarations
+namespace tint::type {
+class Type;
+} // namespace tint::type
+
+namespace tint::sem {
+
+/// TypeExpression holds the semantic information for expression nodes that resolve to types.
+class TypeExpression : public Castable<TypeExpression, Expression> {
+ public:
+ /// Constructor
+ /// @param declaration the AST node
+ /// @param statement the statement that owns this expression
+ /// @param type the type that this expression resolved to
+ TypeExpression(const ast::Expression* declaration,
+ const Statement* statement,
+ const type::Type* type);
+
+ /// Destructor
+ ~TypeExpression() override;
+
+ /// @return the type that the expression resolved to
+ const type::Type* Type() const { return type_; }
+
+ private:
+ type::Type const* const type_;
+};
+
+} // namespace tint::sem
+
+#endif // SRC_TINT_SEM_TYPE_EXPRESSION_H_