mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-14 23:56:16 +00:00
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:
committed by
Tint LUCI CQ
parent
2423df3e04
commit
a9156ff091
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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)");
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user