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>
This commit is contained in:
parent
4e4cada291
commit
0ddddb00cb
|
@ -386,6 +386,7 @@ libtint_source_set("libtint_syntax_tree_src") {
|
|||
"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 @@ libtint_source_set("libtint_sem_src") {
|
|||
"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 @@ if (tint_build_unittests) {
|
|||
"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",
|
||||
|
|
|
@ -359,6 +359,8 @@ list(APPEND TINT_LIB_SRCS
|
|||
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 @@ if(TINT_BUILD_TESTS)
|
|||
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
|
||||
|
|
|
@ -118,27 +118,6 @@ TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsFunctionUsedAsVariable)
|
|||
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 @@ TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalConstUsedAsVariab
|
|||
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 @@ TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalVarUsedAsVariable
|
|||
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, BuiltinRedeclaredAsAliasUsedAsFunction) {
|
|||
)");
|
||||
}
|
||||
|
||||
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 @@ TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsStructUsedAsFunction) {
|
|||
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()),
|
||||
|
|
|
@ -457,39 +457,5 @@ TEST_F(ResolverCallValidationTest, ComplexPointerChain_NotWholeVar_WithFullPtrPa
|
|||
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
|
||||
|
|
|
@ -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 @@ bool DependencyGraph::Build(const ast::Module& module,
|
|||
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
|
||||
|
|
|
@ -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 @@ class ResolvedIdentifier {
|
|||
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 @@ class ResolvedIdentifier {
|
|||
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 {
|
||||
|
|
|
@ -1174,7 +1174,7 @@ TEST_P(ResolverDependencyGraphResolveToBuiltinFunc, Resolve) {
|
|||
|
||||
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 @@ TEST_P(ResolverDependencyGraphResolveToBuiltinFunc, ShadowedByGlobalVar) {
|
|||
|
||||
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 @@ TEST_P(ResolverDependencyGraphResolveToBuiltinFunc, ShadowedByStruct) {
|
|||
|
||||
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 @@ TEST_P(ResolverDependencyGraphResolveToBuiltinFunc, ShadowedByFunc) {
|
|||
|
||||
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 @@ TEST_P(ResolverDependencyGraphResolveToBuiltinType, Resolve) {
|
|||
|
||||
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 @@ TEST_P(ResolverDependencyGraphResolveToBuiltinType, ShadowedByGlobalVar) {
|
|||
|
||||
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 @@ TEST_P(ResolverDependencyGraphResolveToBuiltinType, ShadowedByStruct) {
|
|||
|
||||
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 @@ TEST_P(ResolverDependencyGraphResolveToBuiltinType, ShadowedByFunc) {
|
|||
|
||||
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,
|
||||
|
|
|
@ -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
|
|
@ -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(ProgramBuilder* builder)
|
|||
Resolver::~Resolver() = default;
|
||||
|
||||
bool Resolver::Resolve() {
|
||||
if (builder_->Diagnostics().contains_errors()) {
|
||||
if (diagnostics_.contains_errors()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -124,7 +125,7 @@ bool Resolver::Resolve() {
|
|||
// 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 @@ type::Type* Resolver::Type(const ast::Type* ty) {
|
|||
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 @@ sem::Variable* Resolver::Let(const ast::Let* v, bool is_global) {
|
|||
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 @@ sem::Variable* Resolver::Override(const ast::Override* v) {
|
|||
|
||||
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 @@ sem::Variable* Resolver::Override(const ast::Override* v) {
|
|||
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 @@ sem::Variable* Resolver::Const(const ast::Const* c, bool is_global) {
|
|||
{
|
||||
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 @@ sem::Variable* Resolver::Var(const ast::Var* var, bool is_global) {
|
|||
};
|
||||
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 @@ sem::Variable* Resolver::Var(const ast::Var* var, bool is_global) {
|
|||
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 @@ sem::Variable* Resolver::Var(const ast::Var* var, bool is_global) {
|
|||
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 @@ sem::Parameter* Resolver::Parameter(const ast::Parameter* param, uint32_t index)
|
|||
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 @@ sem::Parameter* Resolver::Parameter(const ast::Parameter* param, uint32_t index)
|
|||
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 @@ utils::Result<uint32_t> Resolver::LocationAttribute(const ast::LocationAttribute
|
|||
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 @@ void Resolver::SetShadows() {
|
|||
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::GlobalVariable* Resolver::GlobalVariable(const ast::Variable* v) {
|
|||
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 @@ bool Resolver::WorkgroupSize(const ast::Function* func) {
|
|||
if (!value) {
|
||||
break;
|
||||
}
|
||||
const auto* expr = Expression(value);
|
||||
const auto* expr = ValueExpression(value);
|
||||
if (!expr) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1368,7 +1350,7 @@ sem::IfStatement* Resolver::IfStatement(const ast::IfStatement* stmt) {
|
|||
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 @@ sem::ForLoopStatement* Resolver::ForLoopStatement(const ast::ForLoopStatement* s
|
|||
}
|
||||
|
||||
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 @@ sem::WhileStatement* Resolver::WhileStatement(const ast::WhileStatement* stmt) {
|
|||
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::WhileStatement* Resolver::WhileStatement(const ast::WhileStatement* stmt) {
|
|||
});
|
||||
}
|
||||
|
||||
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 @@ sem::ValueExpression* Resolver::Expression(const ast::Expression* root) {
|
|||
|
||||
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 @@ sem::ValueExpression* Resolver::Expression(const ast::Expression* root) {
|
|||
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 @@ sem::ValueExpression* Resolver::Expression(const ast::Expression* root) {
|
|||
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 @@ sem::ValueExpression* Resolver::Expression(const ast::Expression* root) {
|
|||
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 type::Type* Resolver::ConcreteType(const type::Type* ty,
|
|||
|
||||
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::Load(const sem::ValueExpression* expr) {
|
|||
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 @@ const sem::ValueExpression* Resolver::Materialize(const sem::ValueExpression* ex
|
|||
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 @@ const sem::ValueExpression* Resolver::Materialize(const sem::ValueExpression* ex
|
|||
}
|
||||
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 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) {
|
|||
|
||||
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 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) {
|
|||
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 @@ sem::ValueExpression* Resolver::Literal(const ast::LiteralExpression* literal) {
|
|||
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 @@ sem::ValueExpression* Resolver::Literal(const ast::LiteralExpression* literal) {
|
|||
? 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 @@ sem::ValueExpression* Resolver::Literal(const ast::LiteralExpression* literal) {
|
|||
/* 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 @@ sem::ValueExpression* Resolver::Identifier(const ast::IdentifierExpression* expr
|
|||
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 @@ sem::ValueExpression* Resolver::Identifier(const ast::IdentifierExpression* expr
|
|||
});
|
||||
}
|
||||
|
||||
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 @@ type::Array* Resolver::Array(const ast::Array* arr) {
|
|||
|
||||
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 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
|||
"@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 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
|||
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 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
|||
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 @@ sem::Statement* Resolver::ReturnStatement(const ast::ReturnStatement* stmt) {
|
|||
|
||||
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 @@ sem::SwitchStatement* Resolver::SwitchStatement(const ast::SwitchStatement* stmt
|
|||
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 @@ sem::SwitchStatement* Resolver::SwitchStatement(const ast::SwitchStatement* stmt
|
|||
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 @@ sem::Statement* Resolver::AssignmentStatement(const ast::AssignmentStatement* st
|
|||
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 @@ sem::Statement* Resolver::BreakIfStatement(const ast::BreakIfStatement* stmt) {
|
|||
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 @@ sem::Statement* Resolver::CallStatement(const ast::CallStatement* stmt) {
|
|||
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 @@ sem::Statement* Resolver::CompoundAssignmentStatement(
|
|||
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 @@ sem::Statement* Resolver::IncrementDecrementStatement(
|
|||
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::ApplyDiagnosticSeverities(NODE* node) {
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -122,11 +122,15 @@ class Resolver {
|
|||
/// 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 @@ class Resolver {
|
|||
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 @@ class Resolver {
|
|||
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;
|
||||
|
||||
|
|
|
@ -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 @@ type::Type* SemHelper::TypeOf(const ast::Expression* expr) const {
|
|||
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
|
||||
|
|
|
@ -57,39 +57,58 @@ class SemHelper {
|
|||
/// @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_;
|
||||
};
|
||||
|
||||
|
|
|
@ -883,38 +883,6 @@ TEST_F(ResolverTypeValidationTest, ArrayOfNonStorableTypeWithStride) {
|
|||
"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;
|
||||
|
|
|
@ -134,7 +134,7 @@ TEST_F(ResolverValidationTest, Stmt_If_NonBool) {
|
|||
|
||||
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 @@ TEST_F(ResolverValidationTest, Stmt_ElseIf_NonBool) {
|
|||
|
||||
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 @@ TEST_F(ResolverValidationTest, Expr_ErrUnknownExprType) {
|
|||
"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 @@ TEST_F(ResolverValidationTest, UsingUndefinedVariable_Fail) {
|
|||
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 @@ TEST_F(ResolverValidationTest, UsingUndefinedVariableInBlockStatement_Fail) {
|
|||
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 @@ TEST_F(ResolverValidationTest, UsingUndefinedVariableInnerScope_Fail) {
|
|||
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 @@ TEST_F(ResolverValidationTest, UsingUndefinedVariableDifferentScope_Fail) {
|
|||
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 @@ TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadChar) {
|
|||
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 @@ TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadLength) {
|
|||
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 @@ TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadIndex) {
|
|||
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 @@ TEST_F(ResolverTest, Stmt_Loop_ContinueInContinuing_Direct) {
|
|||
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 @@ TEST_F(ResolverTest, Stmt_ForLoop_ContinueInContinuing_Direct) {
|
|||
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 @@ TEST_F(ResolverTest, Stmt_ForLoop_CondIsNotBool) {
|
|||
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 @@ TEST_F(ResolverTest, Stmt_While_CondIsNotBool) {
|
|||
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_ContinueInLoop) {
|
|||
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_BreakInIfInContinuingNotLast) {
|
|||
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 @@ TEST_F(ResolverValidationTest, NegativeStructMemberAlignAttribute) {
|
|||
});
|
||||
|
||||
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 @@ TEST_F(ResolverValidationTest, NonPOTStructMemberAlignAttribute) {
|
|||
});
|
||||
|
||||
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 @@ TEST_F(ResolverValidationTest, ZeroStructMemberAlignAttribute) {
|
|||
});
|
||||
|
||||
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 @@ TEST_F(ResolverValidationTest, ZeroStructMemberSizeAttribute) {
|
|||
});
|
||||
|
||||
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 @@ TEST_F(ResolverValidationTest, OffsetAndSizeAttribute) {
|
|||
});
|
||||
|
||||
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 @@ TEST_F(ResolverValidationTest, OffsetAndAlignAttribute) {
|
|||
});
|
||||
|
||||
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 @@ TEST_F(ResolverValidationTest, OffsetAndAlignAndSizeAttribute) {
|
|||
});
|
||||
|
||||
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 @@ TEST_F(ResolverTest, Expr_Initializer_Cast_Pointer) {
|
|||
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 @@ TEST_F(ResolverTest, PointerIndexing_Fail) {
|
|||
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
|
||||
|
|
|
@ -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
|
|
@ -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_
|
Loading…
Reference in New Issue