Rework Resolver so that we construct semantic types in a single pass.

The semantic nodes cannot be fully immutable, as they contain cyclic
references. Remove Resolver::CreateSemanticNodes(), and instead
construct and mutate the semantic nodes in the single traversal pass.

Give up on trying to maintain the 'authored' type names (aliased names).
These are a nightmare to maintain, and provided limited use.

Significantly simplfies the Resolver, and allows us to generate more
semantic to semantic references, reducing sem -> ast -> sem hops.

Note: This change introduces constant value propagation across constant
variables. This is unlocked by the earlier construction of the
sem::Variable.

Change-Id: I592092fdc47fe24d30e512952511c9ab7c16d7a1
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/68406
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
This commit is contained in:
Ben Clayton
2021-11-05 16:51:38 +00:00
committed by Tint LUCI CQ
parent 2423df3e04
commit a9156ff091
43 changed files with 1448 additions and 1550 deletions

View File

@@ -289,8 +289,9 @@ TEST_F(ResolverArrayAccessorTest, EXpr_Deref_FuncBadParent) {
Func("func", {p}, ty.f32(), {Decl(idx), Decl(x), Return(x)});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: cannot index type 'ptr<function, vec4<f32>>'");
EXPECT_EQ(
r()->error(),
"12:34 error: cannot index type 'ptr<function, vec4<f32>, read_write>'");
}
TEST_F(ResolverArrayAccessorTest, Exr_Deref_BadParent) {

View File

@@ -102,7 +102,7 @@ TEST_F(ResolverAssignmentValidationTest,
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: cannot assign 'array<f32, len>' to 'array<f32, 4>'");
"12:34 error: cannot assign 'array<f32, 5>' to 'array<f32, 4>'");
}
TEST_F(ResolverAssignmentValidationTest,
@@ -332,7 +332,7 @@ TEST_F(ResolverAssignmentValidationTest, AssignToPhony_DynamicArray_Fail) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
"12:34 error: cannot assign 'ref<storage, array<i32>, read>' to '_'. "
"12:34 error: cannot assign 'array<i32>' to '_'. "
"'_' can only be assigned a constructible, pointer, texture or sampler "
"type");
}

View File

@@ -43,7 +43,7 @@ TEST_F(ResolverCompoundStatementTest, FunctionBlock) {
ASSERT_TRUE(s->Block()->Is<sem::FunctionBlockStatement>());
EXPECT_EQ(s->Block(), s->FindFirstParent<sem::BlockStatement>());
EXPECT_EQ(s->Block(), s->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_EQ(s->Block()->As<sem::FunctionBlockStatement>()->Function(), f);
EXPECT_EQ(s->Function()->Declaration(), f);
EXPECT_EQ(s->Block()->Parent(), nullptr);
}
@@ -74,8 +74,7 @@ TEST_F(ResolverCompoundStatementTest, Block) {
EXPECT_EQ(s->Block()->Parent(),
s->FindFirstParent<sem::FunctionBlockStatement>());
ASSERT_TRUE(s->Block()->Parent()->Is<sem::FunctionBlockStatement>());
EXPECT_EQ(
s->Block()->Parent()->As<sem::FunctionBlockStatement>()->Function(), f);
EXPECT_EQ(s->Function()->Declaration(), f);
EXPECT_EQ(s->Block()->Parent()->Parent(), nullptr);
}
}
@@ -118,7 +117,7 @@ TEST_F(ResolverCompoundStatementTest, Loop) {
EXPECT_TRUE(
Is<sem::FunctionBlockStatement>(s->Parent()->Parent()->Parent()));
EXPECT_EQ(s->FindFirstParent<sem::FunctionBlockStatement>()->Function(), f);
EXPECT_EQ(s->Function()->Declaration(), f);
EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(), nullptr);
}
@@ -144,7 +143,7 @@ TEST_F(ResolverCompoundStatementTest, Loop) {
s->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_TRUE(Is<sem::FunctionBlockStatement>(
s->Parent()->Parent()->Parent()->Parent()));
EXPECT_EQ(s->FindFirstParent<sem::FunctionBlockStatement>()->Function(), f);
EXPECT_EQ(s->Function()->Declaration(), f);
EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent()->Parent(), nullptr);
}
@@ -213,12 +212,7 @@ TEST_F(ResolverCompoundStatementTest, ForLoop) {
Is<sem::FunctionBlockStatement>(s->Block()->Parent()->Parent()));
EXPECT_EQ(s->Block()->Parent()->Parent(),
s->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_EQ(s->Block()
->Parent()
->Parent()
->As<sem::FunctionBlockStatement>()
->Function(),
f);
EXPECT_EQ(s->Function()->Declaration(), f);
EXPECT_EQ(s->Block()->Parent()->Parent()->Parent(), nullptr);
}
}

View File

@@ -388,7 +388,7 @@ TEST_F(ResolverFunctionValidationTest,
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: return statement type must match its function return "
"type, returned 'u32', expected 'myf32'");
"type, returned 'u32', expected 'f32'");
}
TEST_F(ResolverFunctionValidationTest, CannotCallEntryPoint) {

View File

@@ -98,11 +98,16 @@ TEST_F(ResolverPtrRefTest, DefaultPtrStorageClass) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_TRUE(TypeOf(function_ptr)->Is<sem::Pointer>());
ASSERT_TRUE(TypeOf(private_ptr)->Is<sem::Pointer>());
ASSERT_TRUE(TypeOf(workgroup_ptr)->Is<sem::Pointer>());
ASSERT_TRUE(TypeOf(uniform_ptr)->Is<sem::Pointer>());
ASSERT_TRUE(TypeOf(storage_ptr)->Is<sem::Pointer>());
ASSERT_TRUE(TypeOf(function_ptr)->Is<sem::Pointer>())
<< "function_ptr is " << TypeOf(function_ptr)->TypeInfo().name;
ASSERT_TRUE(TypeOf(private_ptr)->Is<sem::Pointer>())
<< "private_ptr is " << TypeOf(private_ptr)->TypeInfo().name;
ASSERT_TRUE(TypeOf(workgroup_ptr)->Is<sem::Pointer>())
<< "workgroup_ptr is " << TypeOf(workgroup_ptr)->TypeInfo().name;
ASSERT_TRUE(TypeOf(uniform_ptr)->Is<sem::Pointer>())
<< "uniform_ptr is " << TypeOf(uniform_ptr)->TypeInfo().name;
ASSERT_TRUE(TypeOf(storage_ptr)->Is<sem::Pointer>())
<< "storage_ptr is " << TypeOf(storage_ptr)->TypeInfo().name;
EXPECT_EQ(TypeOf(function_ptr)->As<sem::Pointer>()->Access(),
ast::Access::kReadWrite);

View File

@@ -167,7 +167,7 @@ TEST_F(ResolverPtrRefValidationTest, InferredPtrAccessMismatch) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: cannot initialize let of type "
"'ptr<storage, i32>' with value of type "
"'ptr<storage, i32, read>' with value of type "
"'ptr<storage, i32, read_write>'");
}

File diff suppressed because it is too large Load Diff

View File

@@ -95,79 +95,9 @@ class Resolver {
/// Describes the context in which a variable is declared
enum class VariableKind { kParameter, kLocal, kGlobal };
/// Structure holding semantic information about a variable.
/// Used to build the sem::Variable nodes at the end of resolving.
struct VariableInfo {
VariableInfo(const ast::Variable* decl,
sem::Type* type,
const std::string& type_name,
ast::StorageClass storage_class,
ast::Access ac,
VariableKind k,
uint32_t idx);
~VariableInfo();
ast::Variable const* const declaration;
sem::Type* type;
std::string const type_name;
ast::StorageClass storage_class;
ast::Access const access;
std::vector<const ast::IdentifierExpression*> users;
sem::BindingPoint binding_point;
VariableKind kind;
uint32_t index = 0; // Parameter index, if kind == kParameter
uint16_t constant_id = 0;
};
struct IntrinsicCallInfo {
const ast::CallExpression* call;
const sem::Intrinsic* intrinsic;
};
std::set<std::pair<const sem::Struct*, ast::StorageClass>>
valid_struct_storage_layouts_;
/// Structure holding semantic information about a function.
/// Used to build the sem::Function nodes at the end of resolving.
struct FunctionInfo {
explicit FunctionInfo(const ast::Function* decl);
~FunctionInfo();
const ast::Function* const declaration;
std::vector<VariableInfo*> parameters;
utils::UniqueVector<VariableInfo*> referenced_module_vars;
utils::UniqueVector<VariableInfo*> local_referenced_module_vars;
std::vector<const ast::ReturnStatement*> return_statements;
std::vector<const ast::CallExpression*> callsites;
sem::Type* return_type = nullptr;
std::string return_type_name;
std::array<sem::WorkgroupDimension, 3> workgroup_size;
std::vector<IntrinsicCallInfo> intrinsic_calls;
// List of transitive calls this function makes
utils::UniqueVector<FunctionInfo*> transitive_calls;
// List of entry point functions that transitively call this function
utils::UniqueVector<FunctionInfo*> ancestor_entry_points;
};
/// Structure holding semantic information about an expression.
/// Used to build the sem::Expression nodes at the end of resolving.
struct ExpressionInfo {
sem::Type const* type;
std::string const type_name; // Declared type name
sem::Statement* statement;
sem::Constant constant_value;
};
/// Structure holding semantic information about a call expression to an
/// ast::Function.
/// Used to build the sem::Call nodes at the end of resolving.
struct FunctionCallInfo {
FunctionInfo* function;
sem::Statement* statement;
};
/// Structure holding semantic information about a block (i.e. scope), such as
/// parent block and variables declared in the block.
/// Used to validate variable scoping rules.
@@ -231,35 +161,40 @@ class Resolver {
const ast::ExpressionList& params,
uint32_t* id);
void set_referenced_from_function_if_needed(VariableInfo* var, bool local);
//////////////////////////////////////////////////////////////////////////////
// AST and Type traversal methods
//////////////////////////////////////////////////////////////////////////////
// Expression resolving methods
// Returns the semantic node pointer on success, nullptr on failure.
sem::Expression* ArrayAccessor(const ast::ArrayAccessorExpression*);
sem::Expression* Binary(const ast::BinaryExpression*);
sem::Expression* Bitcast(const ast::BitcastExpression*);
sem::Expression* Call(const ast::CallExpression*);
sem::Expression* Constructor(const ast::ConstructorExpression*);
sem::Expression* Expression(const ast::Expression*);
sem::Function* Function(const ast::Function*);
sem::Call* FunctionCall(const ast::CallExpression*);
sem::Expression* Identifier(const ast::IdentifierExpression*);
sem::Call* IntrinsicCall(const ast::CallExpression*, sem::IntrinsicType);
sem::Expression* MemberAccessor(const ast::MemberAccessorExpression*);
sem::Expression* UnaryOp(const ast::UnaryOpExpression*);
// Statement resolving methods
// Each return true on success, false on failure.
bool ArrayAccessor(const ast::ArrayAccessorExpression*);
bool Assignment(const ast::AssignmentStatement* a);
bool Binary(const ast::BinaryExpression*);
bool Bitcast(const ast::BitcastExpression*);
bool BlockStatement(const ast::BlockStatement*);
bool Call(const ast::CallExpression*);
bool CaseStatement(const ast::CaseStatement*);
bool Constructor(const ast::ConstructorExpression*);
bool ElseStatement(const ast::ElseStatement*);
bool Expression(const ast::Expression*);
bool ForLoopStatement(const ast::ForLoopStatement*);
bool Function(const ast::Function*);
bool FunctionCall(const ast::CallExpression* call);
bool GlobalVariable(const ast::Variable* var);
bool Identifier(const ast::IdentifierExpression*);
bool IfStatement(const ast::IfStatement*);
bool IntrinsicCall(const ast::CallExpression*, sem::IntrinsicType);
bool LoopStatement(const ast::LoopStatement*);
bool MemberAccessor(const ast::MemberAccessorExpression*);
bool Parameter(const ast::Variable* param);
bool GlobalVariable(const ast::Variable* var);
bool IfStatement(const ast::IfStatement*);
bool LoopStatement(const ast::LoopStatement*);
bool Return(const ast::ReturnStatement* ret);
bool Statement(const ast::Statement*);
bool Statements(const ast::StatementList&);
bool SwitchStatement(const ast::SwitchStatement* s);
bool UnaryOp(const ast::UnaryOpExpression*);
bool VariableDeclStatement(const ast::VariableDeclStatement*);
// AST and Type validation methods
@@ -270,18 +205,16 @@ class Resolver {
uint32_t el_align,
const Source& source);
bool ValidateAtomic(const ast::Atomic* a, const sem::Atomic* s);
bool ValidateAtomicVariable(const VariableInfo* info);
bool ValidateAtomicVariable(const sem::Variable* var);
bool ValidateAssignment(const ast::AssignmentStatement* a);
bool ValidateBuiltinDecoration(const ast::BuiltinDecoration* deco,
const sem::Type* storage_type,
const bool is_input);
bool ValidateCall(const ast::CallExpression* call);
bool ValidateCallStatement(const ast::CallStatement* stmt);
bool ValidateEntryPoint(const ast::Function* func, const FunctionInfo* info);
bool ValidateFunction(const ast::Function* func, const FunctionInfo* info);
bool ValidateFunctionCall(const ast::CallExpression* call,
const FunctionInfo* target);
bool ValidateGlobalVariable(const VariableInfo* var);
bool ValidateCall(const sem::Call* call);
bool ValidateEntryPoint(const sem::Function* func);
bool ValidateFunction(const sem::Function* func);
bool ValidateFunctionCall(const sem::Call* call);
bool ValidateGlobalVariable(const sem::Variable* var);
bool ValidateInterpolateDecoration(const ast::InterpolateDecoration* deco,
const sem::Type* storage_type);
bool ValidateLocationDecoration(const ast::LocationDecoration* location,
@@ -291,11 +224,11 @@ class Resolver {
const bool is_input = false);
bool ValidateMatrix(const sem::Matrix* ty, const Source& source);
bool ValidateFunctionParameter(const ast::Function* func,
const VariableInfo* info);
const sem::Variable* var);
bool ValidateNoDuplicateDefinition(Symbol sym,
const Source& source,
bool check_global_scope_only = false);
bool ValidateParameter(const ast::Function* func, const VariableInfo* info);
bool ValidateParameter(const ast::Function* func, const sem::Variable* var);
bool ValidateReturn(const ast::ReturnStatement* ret);
bool ValidateStatements(const ast::StatementList& stmts);
bool ValidateStorageTexture(const ast::StorageTexture* t);
@@ -303,33 +236,30 @@ class Resolver {
bool ValidateStructureConstructor(const ast::TypeConstructorExpression* ctor,
const sem::Struct* struct_type);
bool ValidateSwitch(const ast::SwitchStatement* s);
bool ValidateVariable(const VariableInfo* info);
bool ValidateVariable(const sem::Variable* var);
bool ValidateVariableConstructor(const ast::Variable* var,
ast::StorageClass storage_class,
const sem::Type* storage_type,
const std::string& type_name,
const sem::Type* rhs_type,
const std::string& rhs_type_name);
const sem::Type* rhs_type);
bool ValidateVector(const sem::Vector* ty, const Source& source);
bool ValidateVectorConstructor(const ast::TypeConstructorExpression* ctor,
const sem::Vector* vec_type,
const std::string& type_name);
const sem::Vector* vec_type);
bool ValidateMatrixConstructor(const ast::TypeConstructorExpression* ctor,
const sem::Matrix* matrix_type,
const std::string& type_name);
const sem::Matrix* matrix_type);
bool ValidateScalarConstructor(const ast::TypeConstructorExpression* ctor,
const sem::Type* type,
const std::string& type_name);
const sem::Type* type);
bool ValidateArrayConstructor(const ast::TypeConstructorExpression* ctor,
const sem::Array* arr_type);
bool ValidateTypeDecl(const ast::TypeDecl* named_type) const;
bool ValidateTextureIntrinsicFunction(const ast::CallExpression* ast_call,
const sem::Call* sem_call);
bool ValidateTextureIntrinsicFunction(const sem::Call* call);
bool ValidateNoDuplicateDecorations(const ast::DecorationList& decorations);
// sem::Struct is assumed to have at least one member
bool ValidateStorageClassLayout(const sem::Struct* type,
ast::StorageClass sc);
bool ValidateStorageClassLayout(const VariableInfo* info);
bool ValidateStorageClassLayout(const sem::Variable* var);
/// Resolves the WorkgroupSize for the given function
bool WorkgroupSizeFor(const ast::Function*, sem::WorkgroupSize& ws);
/// @returns the sem::Type for the ast::Type `ty`, building it if it
/// hasn't been constructed already. If an error is raised, nullptr is
@@ -355,16 +285,16 @@ class Resolver {
/// raised. raised, nullptr is returned.
sem::Struct* Structure(const ast::Struct* str);
/// @returns the VariableInfo for the variable `var`, building it if it hasn't
/// been constructed already. If an error is raised, nullptr is returned.
/// @returns the semantic info for the variable `var`. If an error is raised,
/// nullptr is returned.
/// @note this method does not resolve the decorations as these are
/// context-dependent (global, local, parameter)
/// @param var the variable to create or return the `VariableInfo` for
/// @param kind what kind of variable we are declaring
/// @param index the index of the parameter, if this variable is a parameter
VariableInfo* Variable(const ast::Variable* var,
VariableKind kind,
uint32_t index = 0);
sem::Variable* Variable(const ast::Variable* var,
VariableKind kind,
uint32_t index = 0);
/// Records the storage class usage for the given type, and any transient
/// dependencies of the type. Validates that the type can be used for the
@@ -389,23 +319,17 @@ class Resolver {
/// @param expr the expression
sem::Type* TypeOf(const ast::Expression* expr);
/// @returns the declared type name of the ast::Expression `expr`
/// @param expr the type name
std::string TypeNameOf(const ast::Expression* expr);
/// @returns the type name of the given semantic type, unwrapping references.
std::string TypeNameOf(const sem::Type* ty);
/// @returns the type name of the given semantic type, without unwrapping
/// references.
std::string RawTypeNameOf(const sem::Type* ty);
/// @returns the semantic type of the AST literal `lit`
/// @param lit the literal
sem::Type* TypeOf(const ast::Literal* lit);
/// Records the semantic information for the expression node with the resolved
/// type `type` and optional declared type name `type_name`.
/// @param expr the expression
/// @param type the resolved type
/// @param type_name the declared type name
void SetExprInfo(const ast::Expression* expr,
const sem::Type* type,
std::string type_name = "");
/// Assigns `stmt` to #current_statement_, #current_compound_statement_, and
/// possibly #current_block_, pushes the variable scope, then calls
/// `callback`. Before returning #current_statement_,
@@ -437,16 +361,13 @@ class Resolver {
void AddNote(const std::string& msg, const Source& source) const;
template <typename CALLBACK>
void TraverseCallChain(FunctionInfo* from,
FunctionInfo* to,
void TraverseCallChain(const sem::Function* from,
const sem::Function* to,
CALLBACK&& callback) const;
//////////////////////////////////////////////////////////////////////////////
/// Constant value evaluation methods
//////////////////////////////////////////////////////////////////////////////
/// @return the Constant value of the given Expression
sem::Constant ConstantValueOf(const ast::Expression* expr);
/// Cast `Value` to `target_type`
/// @return the casted value
sem::Constant ConstantCast(const sem::Constant& value,
@@ -461,29 +382,27 @@ class Resolver {
const ast::TypeConstructorExpression* type_ctor,
const sem::Type* type);
/// Sem is a helper for obtaining the semantic node for the given AST node.
template <typename SEM = sem::Info::InferFromAST,
typename AST_OR_TYPE = CastableBase>
const sem::Info::GetResultType<SEM, AST_OR_TYPE>* Sem(const AST_OR_TYPE* ast);
ProgramBuilder* const builder_;
diag::List& diagnostics_;
std::unique_ptr<IntrinsicTable> const intrinsic_table_;
ScopeStack<VariableInfo*> variable_stack_;
std::unordered_map<Symbol, FunctionInfo*> symbol_to_function_;
std::vector<FunctionInfo*> entry_points_;
ScopeStack<sem::Variable*> variable_stack_;
std::unordered_map<Symbol, sem::Function*> symbol_to_function_;
std::vector<sem::Function*> entry_points_;
std::unordered_map<const sem::Type*, const Source&> atomic_composite_info_;
std::unordered_map<const ast::Function*, FunctionInfo*> function_to_info_;
std::unordered_map<const ast::Variable*, VariableInfo*> variable_to_info_;
std::unordered_map<const ast::CallExpression*, FunctionCallInfo>
function_calls_;
std::unordered_map<const ast::Expression*, ExpressionInfo> expr_info_;
std::unordered_map<Symbol, TypeDeclInfo> named_type_info_;
std::unordered_set<const ast::Node*> marked_;
std::unordered_map<uint32_t, const VariableInfo*> constant_ids_;
std::unordered_map<uint32_t, const sem::Variable*> constant_ids_;
FunctionInfo* current_function_ = nullptr;
sem::Function* current_function_ = nullptr;
sem::Statement* current_statement_ = nullptr;
sem::CompoundStatement* current_compound_statement_ = nullptr;
sem::BlockStatement* current_block_ = nullptr;
BlockAllocator<VariableInfo> variable_infos_;
BlockAllocator<FunctionInfo> function_infos_;
};
} // namespace resolver

View File

@@ -15,6 +15,7 @@
#include "src/resolver/resolver.h"
#include "src/sem/constant.h"
#include "src/utils/get_or_create.h"
namespace tint {
namespace resolver {
@@ -26,46 +27,6 @@ using f32 = ProgramBuilder::f32;
} // namespace
sem::Constant Resolver::ConstantCast(const sem::Constant& value,
const sem::Type* target_elem_type) {
if (value.ElementType() == target_elem_type) {
return value;
}
sem::Constant::Scalars elems;
for (size_t i = 0; i < value.Elements().size(); ++i) {
if (target_elem_type->Is<sem::I32>()) {
elems.emplace_back(
value.WithScalarAt(i, [](auto&& s) { return static_cast<i32>(s); }));
} else if (target_elem_type->Is<sem::U32>()) {
elems.emplace_back(
value.WithScalarAt(i, [](auto&& s) { return static_cast<u32>(s); }));
} else if (target_elem_type->Is<sem::F32>()) {
elems.emplace_back(
value.WithScalarAt(i, [](auto&& s) { return static_cast<f32>(s); }));
} else if (target_elem_type->Is<sem::Bool>()) {
elems.emplace_back(
value.WithScalarAt(i, [](auto&& s) { return static_cast<bool>(s); }));
}
}
auto* target_type =
value.Type()->Is<sem::Vector>()
? builder_->create<sem::Vector>(target_elem_type,
static_cast<uint32_t>(elems.size()))
: target_elem_type;
return sem::Constant(target_type, elems);
}
sem::Constant Resolver::ConstantValueOf(const ast::Expression* expr) {
auto it = expr_info_.find(expr);
if (it != expr_info_.end()) {
return it->second.constant_value;
}
return {};
}
sem::Constant Resolver::EvaluateConstantValue(const ast::Expression* expr,
const sem::Type* type) {
if (auto* e = expr->As<ast::ScalarConstructorExpression>()) {
@@ -131,11 +92,11 @@ sem::Constant Resolver::EvaluateConstantValue(
// type_ctor's type.
sem::Constant::Scalars elems;
for (auto* cv : ctor_values) {
auto value = ConstantValueOf(cv);
if (!value.IsValid()) {
auto* expr = builder_->Sem().Get(cv);
if (!expr || !expr->ConstantValue()) {
return {};
}
auto cast = ConstantCast(value, elem_type);
auto cast = ConstantCast(expr->ConstantValue(), elem_type);
elems.insert(elems.end(), cast.Elements().begin(), cast.Elements().end());
}
@@ -149,5 +110,37 @@ sem::Constant Resolver::EvaluateConstantValue(
return sem::Constant(type, std::move(elems));
}
sem::Constant Resolver::ConstantCast(const sem::Constant& value,
const sem::Type* target_elem_type) {
if (value.ElementType() == target_elem_type) {
return value;
}
sem::Constant::Scalars elems;
for (size_t i = 0; i < value.Elements().size(); ++i) {
if (target_elem_type->Is<sem::I32>()) {
elems.emplace_back(
value.WithScalarAt(i, [](auto&& s) { return static_cast<i32>(s); }));
} else if (target_elem_type->Is<sem::U32>()) {
elems.emplace_back(
value.WithScalarAt(i, [](auto&& s) { return static_cast<u32>(s); }));
} else if (target_elem_type->Is<sem::F32>()) {
elems.emplace_back(
value.WithScalarAt(i, [](auto&& s) { return static_cast<f32>(s); }));
} else if (target_elem_type->Is<sem::Bool>()) {
elems.emplace_back(
value.WithScalarAt(i, [](auto&& s) { return static_cast<bool>(s); }));
}
}
auto* target_type =
value.Type()->Is<sem::Vector>()
? builder_->create<sem::Vector>(target_elem_type,
static_cast<uint32_t>(elems.size()))
: target_elem_type;
return sem::Constant(target_type, elems);
}
} // namespace resolver
} // namespace tint

View File

@@ -922,8 +922,8 @@ TEST_F(ResolverTest, Function_CallSites) {
auto* foo_sem = Sem().Get(foo);
ASSERT_NE(foo_sem, nullptr);
ASSERT_EQ(foo_sem->CallSites().size(), 2u);
EXPECT_EQ(foo_sem->CallSites()[0], call_1);
EXPECT_EQ(foo_sem->CallSites()[1], call_2);
EXPECT_EQ(foo_sem->CallSites()[0]->Declaration(), call_1);
EXPECT_EQ(foo_sem->CallSites()[1]->Declaration(), call_2);
auto* bar_sem = Sem().Get(bar);
ASSERT_NE(bar_sem, nullptr);
@@ -1908,17 +1908,17 @@ TEST_F(ResolverTest, Function_EntryPoints_StageDecoration) {
const auto& b_eps = func_b_sem->AncestorEntryPoints();
ASSERT_EQ(2u, b_eps.size());
EXPECT_EQ(Symbols().Register("ep_1"), b_eps[0]);
EXPECT_EQ(Symbols().Register("ep_2"), b_eps[1]);
EXPECT_EQ(Symbols().Register("ep_1"), b_eps[0]->Declaration()->symbol);
EXPECT_EQ(Symbols().Register("ep_2"), b_eps[1]->Declaration()->symbol);
const auto& a_eps = func_a_sem->AncestorEntryPoints();
ASSERT_EQ(1u, a_eps.size());
EXPECT_EQ(Symbols().Register("ep_1"), a_eps[0]);
EXPECT_EQ(Symbols().Register("ep_1"), a_eps[0]->Declaration()->symbol);
const auto& c_eps = func_c_sem->AncestorEntryPoints();
ASSERT_EQ(2u, c_eps.size());
EXPECT_EQ(Symbols().Register("ep_1"), c_eps[0]);
EXPECT_EQ(Symbols().Register("ep_2"), c_eps[1]);
EXPECT_EQ(Symbols().Register("ep_1"), c_eps[0]->Declaration()->symbol);
EXPECT_EQ(Symbols().Register("ep_2"), c_eps[1]->Declaration()->symbol);
EXPECT_TRUE(ep_1_sem->AncestorEntryPoints().empty());
EXPECT_TRUE(ep_2_sem->AncestorEntryPoints().empty());

View File

@@ -179,11 +179,11 @@ TEST_F(ResolverStorageClassLayoutValidationTest,
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(56:78 error: the offset of a struct member of type 'Inner' in storage class 'uniform' must be a multiple of 16 bytes, but 'inner' is currently at offset 4. Consider setting [[align(16)]] on this member
R"(56:78 error: the offset of a struct member of type '[[stride(16)]] array<f32, 10>' in storage class 'uniform' must be a multiple of 16 bytes, but 'inner' is currently at offset 4. Consider setting [[align(16)]] on this member
12:34 note: see layout of struct:
/* align(4) size(164) */ struct Outer {
/* offset( 0) align(4) size( 4) */ scalar : f32;
/* offset( 4) align(4) size(160) */ inner : Inner;
/* offset( 4) align(4) size(160) */ inner : [[stride(16)]] array<f32, 10>;
/* */ };
78:90 note: see declaration of variable)");
}
@@ -351,7 +351,7 @@ TEST_F(ResolverStorageClassLayoutValidationTest,
R"(34:56 error: uniform storage requires that array elements be aligned to 16 bytes, but array stride of 'inner' is currently 8. Consider setting [[stride(16)]] on the array type
12:34 note: see layout of struct:
/* align(4) size(84) */ struct Outer {
/* offset( 0) align(4) size(80) */ inner : Inner;
/* offset( 0) align(4) size(80) */ inner : [[stride(8)]] array<f32, 10>;
/* offset(80) align(4) size( 4) */ scalar : i32;
/* */ };
78:90 note: see declaration of variable)");

View File

@@ -96,7 +96,8 @@ TEST_F(ResolverStorageClassValidationTest, StorageBufferBoolAlias) {
EXPECT_EQ(
r()->error(),
R"(56:78 error: variables declared in the <storage> storage class must be of a structure type)");
R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
56:78 note: while instantiating variable g)");
}
TEST_F(ResolverStorageClassValidationTest, NotStorage_AccessMode) {
@@ -194,7 +195,8 @@ TEST_F(ResolverStorageClassValidationTest, UniformBufferBool) {
EXPECT_EQ(
r()->error(),
R"(56:78 error: variables declared in the <uniform> storage class must be of a structure type)");
R"(56:78 error: Type 'bool' cannot be used in storage class 'uniform' as it is non-host-shareable
56:78 note: while instantiating variable g)");
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferPointer) {
@@ -243,7 +245,8 @@ TEST_F(ResolverStorageClassValidationTest, UniformBufferBoolAlias) {
EXPECT_EQ(
r()->error(),
R"(56:78 error: variables declared in the <uniform> storage class must be of a structure type)");
R"(56:78 error: Type 'bool' cannot be used in storage class 'uniform' as it is non-host-shareable
56:78 note: while instantiating variable g)");
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferNoBlockDecoration) {

View File

@@ -1553,7 +1553,7 @@ TEST_F(ResolverTypeConstructorValidationTest,
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: type in vector constructor does not match vector "
"type: expected 'f32', found 'UnsignedInt'");
"type: expected 'f32', found 'u32'");
}
TEST_F(ResolverTypeConstructorValidationTest,
@@ -1638,10 +1638,9 @@ struct MatrixDimensions {
uint32_t columns;
};
static std::string MatrixStr(const MatrixDimensions& dimensions,
std::string subtype = "f32") {
static std::string MatrixStr(const MatrixDimensions& dimensions) {
return "mat" + std::to_string(dimensions.columns) + "x" +
std::to_string(dimensions.rows) + "<" + subtype + ">";
std::to_string(dimensions.rows) + "<f32>";
}
using MatrixConstructorTest = ResolverTestWithParam<MatrixDimensions>;
@@ -1919,9 +1918,9 @@ TEST_P(MatrixConstructorTest, Expr_Constructor_ElementTypeAlias_Error) {
WrapInFunction(tc);
EXPECT_FALSE(r()->Resolve());
EXPECT_THAT(r()->error(), HasSubstr("12:1 error: invalid constructor for " +
MatrixStr(param, "Float32") +
"\n\n3 candidates available:"));
EXPECT_THAT(r()->error(),
HasSubstr("12:1 error: invalid constructor for " +
MatrixStr(param) + "\n\n3 candidates available:"));
}
TEST_P(MatrixConstructorTest, Expr_Constructor_ElementTypeAlias_Success) {

View File

@@ -499,9 +499,10 @@ TEST_F(ResolverValidationTest, EXpr_MemberAccessor_FuncBadParent) {
Func("func", {p}, ty.f32(), {Decl(x), Return(x)});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"error: invalid member accessor expression. Expected vector or "
"struct, got 'ptr<function, vec4<f32>>'");
EXPECT_EQ(
r()->error(),
"error: invalid member accessor expression. "
"Expected vector or struct, got 'ptr<function, vec4<f32>, read_write>'");
}
TEST_F(ResolverValidationTest,

View File

@@ -120,7 +120,7 @@ TEST_F(ResolverVarLetValidationTest, LetConstructorWrongTypeViaAlias) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(3:3 error: cannot initialize let of type 'I32' with value of type 'u32')");
R"(3:3 error: cannot initialize let of type 'i32' with value of type 'u32')");
}
TEST_F(ResolverVarLetValidationTest, VarConstructorWrongTypeViaAlias) {
@@ -131,7 +131,7 @@ TEST_F(ResolverVarLetValidationTest, VarConstructorWrongTypeViaAlias) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(3:3 error: cannot initialize var of type 'I32' with value of type 'u32')");
R"(3:3 error: cannot initialize var of type 'i32' with value of type 'u32')");
}
TEST_F(ResolverVarLetValidationTest, LetOfPtrConstructedWithRef) {
@@ -147,7 +147,7 @@ TEST_F(ResolverVarLetValidationTest, LetOfPtrConstructedWithRef) {
EXPECT_EQ(
r()->error(),
R"(12:34 error: cannot initialize let of type 'ptr<function, f32>' with value of type 'f32')");
R"(12:34 error: cannot initialize let of type 'ptr<function, f32, read_write>' with value of type 'f32')");
}
TEST_F(ResolverVarLetValidationTest, LocalVarRedeclared) {