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:
Ben Clayton 2023-02-09 15:35:27 +00:00 committed by Ben Clayton
parent 4e4cada291
commit 0ddddb00cb
16 changed files with 606 additions and 369 deletions

View File

@ -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",

View File

@ -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

View File

@ -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()),

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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,

View File

@ -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

View File

@ -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);
}

View File

@ -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;

View File

@ -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

View File

@ -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_;
};

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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_