Add semantic::Statement
Add Stmt() accessor on all semantic::Expressions so the owning statement can be retrieved. Change-Id: I5d584335a6d137fdeab0b8d74a161fcae9b46080 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/41545 Reviewed-by: James Price <jrprice@google.com> Reviewed-by: dan sinclair <dsinclair@chromium.org> Commit-Queue: dan sinclair <dsinclair@chromium.org> Auto-Submit: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
e7dab3c9ea
commit
f97b9c9310
1
BUILD.gn
1
BUILD.gn
|
@ -391,6 +391,7 @@ source_set("libtint_core_src") {
|
|||
"src/semantic/sem_intrinsic.cc",
|
||||
"src/semantic/sem_member_accessor_expression.cc",
|
||||
"src/semantic/sem_node.cc",
|
||||
"src/semantic/sem_statement.cc",
|
||||
"src/semantic/sem_variable.cc",
|
||||
"src/semantic/type_mappings.h",
|
||||
"src/source.cc",
|
||||
|
|
|
@ -205,6 +205,7 @@ set(TINT_LIB_SRCS
|
|||
semantic/sem_info.cc
|
||||
semantic/sem_intrinsic.cc
|
||||
semantic/sem_node.cc
|
||||
semantic/sem_statement.cc
|
||||
semantic/sem_variable.cc
|
||||
semantic/type_mappings.h
|
||||
source.cc
|
||||
|
|
|
@ -27,7 +27,8 @@ class Call : public Castable<Call, Expression> {
|
|||
public:
|
||||
/// Constructor
|
||||
/// @param target the call target
|
||||
explicit Call(const CallTarget* target);
|
||||
/// @param statement the statement that owns this expression
|
||||
explicit Call(const CallTarget* target, Statement* statement);
|
||||
|
||||
/// Destructor
|
||||
~Call() override;
|
||||
|
|
|
@ -20,10 +20,11 @@
|
|||
namespace tint {
|
||||
|
||||
// Forward declarations
|
||||
namespace semantic {
|
||||
class Statement;
|
||||
} // namespace semantic
|
||||
namespace type {
|
||||
|
||||
class Type;
|
||||
|
||||
} // namespace type
|
||||
|
||||
namespace semantic {
|
||||
|
@ -33,13 +34,18 @@ class Expression : public Castable<Expression, Node> {
|
|||
public:
|
||||
/// Constructor
|
||||
/// @param type the resolved type of the expression
|
||||
explicit Expression(type::Type* type);
|
||||
/// @param statement the statement that owns this expression
|
||||
explicit Expression(type::Type* type, Statement* statement);
|
||||
|
||||
/// @return the resolved type of the expression
|
||||
type::Type* Type() const { return type_; }
|
||||
|
||||
/// @return the statement that owns this expression
|
||||
Statement* Stmt() const { return statement_; }
|
||||
|
||||
private:
|
||||
type::Type* const type_;
|
||||
Statement* const statement_;
|
||||
};
|
||||
|
||||
} // namespace semantic
|
||||
|
|
|
@ -27,8 +27,11 @@ class MemberAccessorExpression
|
|||
public:
|
||||
/// Constructor
|
||||
/// @param type the resolved type of the expression
|
||||
/// @param statement the statement that owns this expression
|
||||
/// @param is_swizzle true if this member access is for a vector swizzle
|
||||
MemberAccessorExpression(type::Type* type, bool is_swizzle);
|
||||
MemberAccessorExpression(type::Type* type,
|
||||
Statement* statement,
|
||||
bool is_swizzle);
|
||||
|
||||
/// @return true if this member access is for a vector swizzle
|
||||
bool IsSwizzle() const { return is_swizzle_; }
|
||||
|
|
|
@ -19,8 +19,8 @@ TINT_INSTANTIATE_CLASS_ID(tint::semantic::Call);
|
|||
namespace tint {
|
||||
namespace semantic {
|
||||
|
||||
Call::Call(const CallTarget* target)
|
||||
: Base(target->ReturnType()), target_(target) {}
|
||||
Call::Call(const CallTarget* target, Statement* statement)
|
||||
: Base(target->ReturnType(), statement), target_(target) {}
|
||||
|
||||
Call::~Call() = default;
|
||||
|
||||
|
|
|
@ -21,7 +21,8 @@ TINT_INSTANTIATE_CLASS_ID(tint::semantic::Expression);
|
|||
namespace tint {
|
||||
namespace semantic {
|
||||
|
||||
Expression::Expression(type::Type* type) : type_(type->UnwrapIfNeeded()) {}
|
||||
Expression::Expression(type::Type* type, Statement* statement)
|
||||
: type_(type->UnwrapIfNeeded()), statement_(statement) {}
|
||||
|
||||
} // namespace semantic
|
||||
} // namespace tint
|
||||
|
|
|
@ -20,8 +20,9 @@ namespace tint {
|
|||
namespace semantic {
|
||||
|
||||
MemberAccessorExpression::MemberAccessorExpression(type::Type* type,
|
||||
Statement* statement,
|
||||
bool is_swizzle)
|
||||
: Base(type), is_swizzle_(is_swizzle) {}
|
||||
: Base(type, statement), is_swizzle_(is_swizzle) {}
|
||||
|
||||
} // namespace semantic
|
||||
} // namespace tint
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2021 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/semantic/statement.h"
|
||||
|
||||
#include "src/type/type.h"
|
||||
|
||||
TINT_INSTANTIATE_CLASS_ID(tint::semantic::Statement);
|
||||
|
||||
namespace tint {
|
||||
namespace semantic {
|
||||
|
||||
Statement::Statement(ast::Statement* declaration) : declaration_(declaration) {}
|
||||
|
||||
} // namespace semantic
|
||||
} // namespace tint
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2021 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_SEMANTIC_STATEMENT_H_
|
||||
#define SRC_SEMANTIC_STATEMENT_H_
|
||||
|
||||
#include "src/semantic/node.h"
|
||||
|
||||
namespace tint {
|
||||
|
||||
// Forward declarations
|
||||
namespace ast {
|
||||
class Statement;
|
||||
} // namespace ast
|
||||
|
||||
namespace semantic {
|
||||
|
||||
/// Statement holds the semantic information for a statement.
|
||||
class Statement : public Castable<Statement, Node> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param declaration the AST node for this statement
|
||||
explicit Statement(ast::Statement* declaration);
|
||||
|
||||
/// @return the AST node for this statement
|
||||
ast::Statement* Declaration() const { return declaration_; }
|
||||
|
||||
private:
|
||||
ast::Statement* const declaration_;
|
||||
};
|
||||
|
||||
} // namespace semantic
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_SEMANTIC_STATEMENT_H_
|
|
@ -47,6 +47,7 @@
|
|||
#include "src/semantic/function.h"
|
||||
#include "src/semantic/intrinsic.h"
|
||||
#include "src/semantic/member_accessor_expression.h"
|
||||
#include "src/semantic/statement.h"
|
||||
#include "src/semantic/variable.h"
|
||||
#include "src/type/array_type.h"
|
||||
#include "src/type/bool_type.h"
|
||||
|
@ -69,6 +70,23 @@ namespace {
|
|||
|
||||
using IntrinsicType = tint::semantic::IntrinsicType;
|
||||
|
||||
// Helper class that temporarily assigns a value to a reference for the scope of
|
||||
// the object. Once the ScopedAssignment is destructed, the original value is
|
||||
// restored.
|
||||
template <typename T>
|
||||
class ScopedAssignment {
|
||||
public:
|
||||
ScopedAssignment(T& ref, T val) : ref_(ref) {
|
||||
old_value_ = ref;
|
||||
ref = val;
|
||||
}
|
||||
~ScopedAssignment() { ref_ = old_value_; }
|
||||
|
||||
private:
|
||||
T& ref_;
|
||||
T old_value_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TypeDeterminer::TypeDeterminer(ProgramBuilder* builder)
|
||||
|
@ -171,9 +189,11 @@ bool TypeDeterminer::DetermineFunctions(const ast::FunctionList& funcs) {
|
|||
}
|
||||
|
||||
bool TypeDeterminer::DetermineFunction(ast::Function* func) {
|
||||
current_function_ = function_infos_.Create<FunctionInfo>(func);
|
||||
symbol_to_function_[func->symbol()] = current_function_;
|
||||
function_to_info_.emplace(func, current_function_);
|
||||
auto* func_info = function_infos_.Create<FunctionInfo>(func);
|
||||
symbol_to_function_[func->symbol()] = func_info;
|
||||
function_to_info_.emplace(func, func_info);
|
||||
|
||||
ScopedAssignment<FunctionInfo*> sa(current_function_, func_info);
|
||||
|
||||
variable_stack_.push_scope();
|
||||
for (auto* param : func->params()) {
|
||||
|
@ -185,8 +205,6 @@ bool TypeDeterminer::DetermineFunction(ast::Function* func) {
|
|||
}
|
||||
variable_stack_.pop_scope();
|
||||
|
||||
current_function_ = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -234,6 +252,10 @@ bool TypeDeterminer::DetermineVariableStorageClass(ast::Statement* stmt) {
|
|||
}
|
||||
|
||||
bool TypeDeterminer::DetermineResultType(ast::Statement* stmt) {
|
||||
auto* sem_statement = builder_->create<semantic::Statement>(stmt);
|
||||
|
||||
ScopedAssignment<semantic::Statement*> sa(current_statement_, sem_statement);
|
||||
|
||||
if (auto* a = stmt->As<ast::AssignmentStatement>()) {
|
||||
return DetermineResultType(a->lhs()) && DetermineResultType(a->rhs());
|
||||
}
|
||||
|
@ -451,7 +473,8 @@ bool TypeDeterminer::DetermineCall(ast::CallExpression* call) {
|
|||
}
|
||||
|
||||
auto* function = iter->second;
|
||||
function_calls_.emplace(call, function);
|
||||
function_calls_.emplace(call,
|
||||
FunctionCallInfo{function, current_statement_});
|
||||
SetType(call, function->declaration->return_type());
|
||||
}
|
||||
|
||||
|
@ -501,12 +524,14 @@ bool TypeDeterminer::DetermineIntrinsicCall(
|
|||
}
|
||||
auto* intrinsic = builder_->create<semantic::Intrinsic>(intrinsic_type,
|
||||
ret_ty, parameters);
|
||||
builder_->Sem().Add(call, builder_->create<semantic::Call>(intrinsic));
|
||||
builder_->Sem().Add(
|
||||
call, builder_->create<semantic::Call>(intrinsic, current_statement_));
|
||||
SetType(call, ret_ty);
|
||||
return false;
|
||||
}
|
||||
|
||||
builder_->Sem().Add(call, builder_->create<semantic::Call>(result.intrinsic));
|
||||
builder_->Sem().Add(call, builder_->create<semantic::Call>(
|
||||
result.intrinsic, current_statement_));
|
||||
SetType(call, result.intrinsic->ReturnType());
|
||||
return true;
|
||||
}
|
||||
|
@ -791,9 +816,9 @@ bool TypeDeterminer::DetermineMemberAccessor(
|
|||
return false;
|
||||
}
|
||||
|
||||
builder_->Sem().Add(
|
||||
expr,
|
||||
builder_->create<semantic::MemberAccessorExpression>(ret, is_swizzle));
|
||||
builder_->Sem().Add(expr,
|
||||
builder_->create<semantic::MemberAccessorExpression>(
|
||||
ret, current_statement_, is_swizzle));
|
||||
SetType(expr, ret);
|
||||
|
||||
return true;
|
||||
|
@ -890,16 +915,16 @@ TypeDeterminer::VariableInfo* TypeDeterminer::CreateVariableInfo(
|
|||
}
|
||||
|
||||
type::Type* TypeDeterminer::TypeOf(ast::Expression* expr) {
|
||||
auto it = expr_types_.find(expr);
|
||||
if (it != expr_types_.end()) {
|
||||
return it->second;
|
||||
auto it = expr_info_.find(expr);
|
||||
if (it != expr_info_.end()) {
|
||||
return it->second.type;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TypeDeterminer::SetType(ast::Expression* expr, type::Type* type) {
|
||||
assert(expr_types_.count(expr) == 0);
|
||||
expr_types_.emplace(expr, type);
|
||||
assert(expr_info_.count(expr) == 0);
|
||||
expr_info_.emplace(expr, ExpressionInfo{type, current_statement_});
|
||||
}
|
||||
|
||||
void TypeDeterminer::CreateSemanticNodes() const {
|
||||
|
@ -938,20 +963,21 @@ void TypeDeterminer::CreateSemanticNodes() const {
|
|||
// Create semantic nodes for all ast::CallExpressions
|
||||
for (auto it : function_calls_) {
|
||||
auto* call = it.first;
|
||||
auto* func_info = it.second;
|
||||
auto* sem_func = func_info_to_sem_func.at(func_info);
|
||||
sem.Add(call, builder_->create<semantic::Call>(sem_func));
|
||||
auto info = it.second;
|
||||
auto* sem_func = func_info_to_sem_func.at(info.function);
|
||||
sem.Add(call, builder_->create<semantic::Call>(sem_func, info.statement));
|
||||
}
|
||||
|
||||
// Create semantic nodes for all remaining expression types
|
||||
for (auto it : expr_types_) {
|
||||
for (auto it : expr_info_) {
|
||||
auto* expr = it.first;
|
||||
auto* type = it.second;
|
||||
auto& info = it.second;
|
||||
if (sem.Get(expr)) {
|
||||
// Expression has already been assigned a semantic node
|
||||
continue;
|
||||
}
|
||||
sem.Add(expr, builder_->create<semantic::Expression>(type));
|
||||
sem.Add(expr,
|
||||
builder_->create<semantic::Expression>(info.type, info.statement));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,8 +30,9 @@
|
|||
#include "src/type/storage_texture_type.h"
|
||||
|
||||
namespace tint {
|
||||
namespace ast {
|
||||
|
||||
// Forward declarations
|
||||
namespace ast {
|
||||
class ArrayAccessorExpression;
|
||||
class BinaryExpression;
|
||||
class BitcastExpression;
|
||||
|
@ -42,8 +43,10 @@ class IdentifierExpression;
|
|||
class MemberAccessorExpression;
|
||||
class UnaryOpExpression;
|
||||
class Variable;
|
||||
|
||||
} // namespace ast
|
||||
namespace semantic {
|
||||
class Statement;
|
||||
} // namespace semantic
|
||||
|
||||
/// Determines types for all items in the given tint program
|
||||
class TypeDeterminer {
|
||||
|
@ -95,7 +98,7 @@ class TypeDeterminer {
|
|||
std::unordered_set<T> set;
|
||||
};
|
||||
|
||||
/// Structure holding semantic information about a function.
|
||||
/// Structure holding semantic information about a variable.
|
||||
/// Used to build the semantic::Function nodes at the end of resolving.
|
||||
struct VariableInfo {
|
||||
explicit VariableInfo(ast::Variable* decl);
|
||||
|
@ -117,6 +120,21 @@ class TypeDeterminer {
|
|||
UniqueVector<Symbol> ancestor_entry_points;
|
||||
};
|
||||
|
||||
/// Structure holding semantic information about an expression.
|
||||
/// Used to build the semantic::Expression nodes at the end of resolving.
|
||||
struct ExpressionInfo {
|
||||
type::Type* type;
|
||||
semantic::Statement* statement;
|
||||
};
|
||||
|
||||
/// Structure holding semantic information about a call expression to an
|
||||
/// ast::Function.
|
||||
/// Used to build the semantic::Call nodes at the end of resolving.
|
||||
struct FunctionCallInfo {
|
||||
FunctionInfo* function;
|
||||
semantic::Statement* statement;
|
||||
};
|
||||
|
||||
/// Determines type information for the program, without creating final the
|
||||
/// semantic nodes.
|
||||
/// @returns true if the determination was successful
|
||||
|
@ -203,9 +221,10 @@ class TypeDeterminer {
|
|||
std::unordered_map<Symbol, FunctionInfo*> symbol_to_function_;
|
||||
std::unordered_map<ast::Function*, FunctionInfo*> function_to_info_;
|
||||
std::unordered_map<ast::Variable*, VariableInfo*> variable_to_info_;
|
||||
std::unordered_map<ast::CallExpression*, FunctionInfo*> function_calls_;
|
||||
std::unordered_map<ast::Expression*, type::Type*> expr_types_;
|
||||
std::unordered_map<ast::CallExpression*, FunctionCallInfo> function_calls_;
|
||||
std::unordered_map<ast::Expression*, ExpressionInfo> expr_info_;
|
||||
FunctionInfo* current_function_ = nullptr;
|
||||
semantic::Statement* current_statement_ = nullptr;
|
||||
BlockAllocator<VariableInfo> variable_infos_;
|
||||
BlockAllocator<FunctionInfo> function_infos_;
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
#include "src/semantic/call.h"
|
||||
#include "src/semantic/expression.h"
|
||||
#include "src/semantic/function.h"
|
||||
#include "src/semantic/statement.h"
|
||||
#include "src/semantic/variable.h"
|
||||
#include "src/type/access_control_type.h"
|
||||
#include "src/type/alias_type.h"
|
||||
|
@ -105,6 +106,11 @@ class TypeDeterminerHelper : public ProgramBuilder {
|
|||
|
||||
TypeDeterminer* td() const { return td_.get(); }
|
||||
|
||||
ast::Statement* StmtOf(ast::Expression* expr) {
|
||||
auto* sem_stmt = Sem().Get(expr)->Stmt();
|
||||
return sem_stmt ? sem_stmt->Declaration() : nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<TypeDeterminer> td_;
|
||||
};
|
||||
|
@ -149,14 +155,17 @@ TEST_F(TypeDeterminerTest, Stmt_Assign) {
|
|||
|
||||
EXPECT_TRUE(TypeOf(lhs)->Is<type::I32>());
|
||||
EXPECT_TRUE(TypeOf(rhs)->Is<type::F32>());
|
||||
EXPECT_EQ(StmtOf(lhs), assign);
|
||||
EXPECT_EQ(StmtOf(rhs), assign);
|
||||
}
|
||||
|
||||
TEST_F(TypeDeterminerTest, Stmt_Case) {
|
||||
auto* lhs = Expr(2);
|
||||
auto* rhs = Expr(2.3f);
|
||||
|
||||
auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
|
||||
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||
create<ast::AssignmentStatement>(lhs, rhs),
|
||||
assign,
|
||||
});
|
||||
ast::CaseSelectorList lit;
|
||||
lit.push_back(create<ast::SintLiteral>(ty.i32(), 3));
|
||||
|
@ -169,14 +178,17 @@ TEST_F(TypeDeterminerTest, Stmt_Case) {
|
|||
ASSERT_NE(TypeOf(rhs), nullptr);
|
||||
EXPECT_TRUE(TypeOf(lhs)->Is<type::I32>());
|
||||
EXPECT_TRUE(TypeOf(rhs)->Is<type::F32>());
|
||||
EXPECT_EQ(StmtOf(lhs), assign);
|
||||
EXPECT_EQ(StmtOf(rhs), assign);
|
||||
}
|
||||
|
||||
TEST_F(TypeDeterminerTest, Stmt_Block) {
|
||||
auto* lhs = Expr(2);
|
||||
auto* rhs = Expr(2.3f);
|
||||
|
||||
auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
|
||||
auto* block = create<ast::BlockStatement>(ast::StatementList{
|
||||
create<ast::AssignmentStatement>(lhs, rhs),
|
||||
assign,
|
||||
});
|
||||
WrapInFunction(block);
|
||||
|
||||
|
@ -186,16 +198,20 @@ TEST_F(TypeDeterminerTest, Stmt_Block) {
|
|||
ASSERT_NE(TypeOf(rhs), nullptr);
|
||||
EXPECT_TRUE(TypeOf(lhs)->Is<type::I32>());
|
||||
EXPECT_TRUE(TypeOf(rhs)->Is<type::F32>());
|
||||
EXPECT_EQ(StmtOf(lhs), assign);
|
||||
EXPECT_EQ(StmtOf(rhs), assign);
|
||||
}
|
||||
|
||||
TEST_F(TypeDeterminerTest, Stmt_Else) {
|
||||
auto* lhs = Expr(2);
|
||||
auto* rhs = Expr(2.3f);
|
||||
|
||||
auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
|
||||
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||
create<ast::AssignmentStatement>(lhs, rhs),
|
||||
assign,
|
||||
});
|
||||
auto* stmt = create<ast::ElseStatement>(Expr(3), body);
|
||||
auto* cond = Expr(3);
|
||||
auto* stmt = create<ast::ElseStatement>(cond, body);
|
||||
WrapInFunction(stmt);
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
|
@ -206,6 +222,9 @@ TEST_F(TypeDeterminerTest, Stmt_Else) {
|
|||
EXPECT_TRUE(TypeOf(stmt->condition())->Is<type::I32>());
|
||||
EXPECT_TRUE(TypeOf(lhs)->Is<type::I32>());
|
||||
EXPECT_TRUE(TypeOf(rhs)->Is<type::F32>());
|
||||
EXPECT_EQ(StmtOf(lhs), assign);
|
||||
EXPECT_EQ(StmtOf(rhs), assign);
|
||||
EXPECT_EQ(StmtOf(cond), stmt);
|
||||
}
|
||||
|
||||
TEST_F(TypeDeterminerTest, Stmt_If) {
|
||||
|
@ -216,16 +235,17 @@ TEST_F(TypeDeterminerTest, Stmt_If) {
|
|||
create<ast::AssignmentStatement>(else_lhs, else_rhs),
|
||||
});
|
||||
|
||||
auto* else_stmt = create<ast::ElseStatement>(Expr(3), else_body);
|
||||
auto* else_cond = Expr(3);
|
||||
auto* else_stmt = create<ast::ElseStatement>(else_cond, else_body);
|
||||
|
||||
auto* lhs = Expr(2);
|
||||
auto* rhs = Expr(2.3f);
|
||||
|
||||
auto* body = create<ast::BlockStatement>(ast::StatementList{
|
||||
create<ast::AssignmentStatement>(lhs, rhs),
|
||||
});
|
||||
auto* stmt = create<ast::IfStatement>(Expr(3), body,
|
||||
ast::ElseStatementList{else_stmt});
|
||||
auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
|
||||
auto* body = create<ast::BlockStatement>(ast::StatementList{assign});
|
||||
auto* cond = Expr(3);
|
||||
auto* stmt =
|
||||
create<ast::IfStatement>(cond, body, ast::ElseStatementList{else_stmt});
|
||||
WrapInFunction(stmt);
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
|
@ -240,6 +260,10 @@ TEST_F(TypeDeterminerTest, Stmt_If) {
|
|||
EXPECT_TRUE(TypeOf(else_rhs)->Is<type::F32>());
|
||||
EXPECT_TRUE(TypeOf(lhs)->Is<type::I32>());
|
||||
EXPECT_TRUE(TypeOf(rhs)->Is<type::F32>());
|
||||
EXPECT_EQ(StmtOf(lhs), assign);
|
||||
EXPECT_EQ(StmtOf(rhs), assign);
|
||||
EXPECT_EQ(StmtOf(cond), stmt);
|
||||
EXPECT_EQ(StmtOf(else_cond), else_stmt);
|
||||
}
|
||||
|
||||
TEST_F(TypeDeterminerTest, Stmt_Loop) {
|
||||
|
@ -332,6 +356,7 @@ TEST_F(TypeDeterminerTest, Stmt_Call) {
|
|||
|
||||
ASSERT_NE(TypeOf(expr), nullptr);
|
||||
EXPECT_TRUE(TypeOf(expr)->Is<type::F32>());
|
||||
EXPECT_EQ(StmtOf(expr), call);
|
||||
}
|
||||
|
||||
TEST_F(TypeDeterminerTest, Stmt_Call_undeclared) {
|
||||
|
@ -376,14 +401,15 @@ TEST_F(TypeDeterminerTest, Stmt_VariableDecl) {
|
|||
}
|
||||
|
||||
TEST_F(TypeDeterminerTest, Stmt_VariableDecl_ModuleScope) {
|
||||
auto* var = Global("my_var", ast::StorageClass::kNone, ty.i32(), Expr(2),
|
||||
ast::VariableDecorationList{});
|
||||
auto* init = var->constructor();
|
||||
auto* init = Expr(2);
|
||||
Global("my_var", ast::StorageClass::kNone, ty.i32(), init,
|
||||
ast::VariableDecorationList{});
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
|
||||
ASSERT_NE(TypeOf(init), nullptr);
|
||||
EXPECT_TRUE(TypeOf(init)->Is<type::I32>());
|
||||
EXPECT_EQ(StmtOf(init), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(TypeDeterminerTest, Stmt_VariableDecl_OuterScopeAfterInnerScope) {
|
||||
|
@ -438,6 +464,10 @@ TEST_F(TypeDeterminerTest, Stmt_VariableDecl_OuterScopeAfterInnerScope) {
|
|||
EXPECT_TRUE(TypeOf(bar_i32_init)->UnwrapAll()->Is<type::I32>());
|
||||
ASSERT_NE(TypeOf(bar_f32_init), nullptr);
|
||||
EXPECT_TRUE(TypeOf(bar_f32_init)->UnwrapAll()->Is<type::F32>());
|
||||
EXPECT_EQ(StmtOf(foo_i32_init), foo_i32_decl);
|
||||
EXPECT_EQ(StmtOf(bar_i32_init), bar_i32_decl);
|
||||
EXPECT_EQ(StmtOf(foo_f32_init), foo_f32_decl);
|
||||
EXPECT_EQ(StmtOf(bar_f32_init), bar_f32_decl);
|
||||
}
|
||||
|
||||
TEST_F(TypeDeterminerTest, Stmt_VariableDecl_ModuleScopeAfterFunctionScope) {
|
||||
|
@ -480,6 +510,9 @@ TEST_F(TypeDeterminerTest, Stmt_VariableDecl_ModuleScopeAfterFunctionScope) {
|
|||
EXPECT_TRUE(TypeOf(fn_i32_init)->Is<type::I32>());
|
||||
ASSERT_NE(TypeOf(fn_f32_init), nullptr);
|
||||
EXPECT_TRUE(TypeOf(fn_f32_init)->UnwrapAll()->Is<type::F32>());
|
||||
EXPECT_EQ(StmtOf(fn_i32_init), fn_i32_decl);
|
||||
EXPECT_EQ(StmtOf(mod_init), nullptr);
|
||||
EXPECT_EQ(StmtOf(fn_f32_init), fn_f32_decl);
|
||||
}
|
||||
|
||||
TEST_F(TypeDeterminerTest, Expr_Error_Unknown) {
|
||||
|
@ -708,59 +741,77 @@ TEST_F(TypeDeterminerTest, Expr_Identifier_GlobalConstant) {
|
|||
}
|
||||
|
||||
TEST_F(TypeDeterminerTest, Expr_Identifier_FunctionVariable_Const) {
|
||||
auto* my_var = Expr("my_var");
|
||||
|
||||
auto* my_var_a = Expr("my_var");
|
||||
auto* my_var_b = Expr("my_var");
|
||||
auto* var = Const("my_var", ast::StorageClass::kNone, ty.f32());
|
||||
auto* assign = create<ast::AssignmentStatement>(my_var_a, my_var_b);
|
||||
|
||||
Func("my_func", ast::VariableList{}, ty.f32(),
|
||||
ast::StatementList{
|
||||
create<ast::VariableDeclStatement>(var),
|
||||
create<ast::AssignmentStatement>(my_var, Expr("my_var")),
|
||||
assign,
|
||||
},
|
||||
ast::FunctionDecorationList{});
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
|
||||
ASSERT_NE(TypeOf(my_var), nullptr);
|
||||
EXPECT_TRUE(TypeOf(my_var)->Is<type::F32>());
|
||||
ASSERT_NE(TypeOf(my_var_a), nullptr);
|
||||
EXPECT_TRUE(TypeOf(my_var_a)->Is<type::F32>());
|
||||
EXPECT_EQ(StmtOf(my_var_a), assign);
|
||||
ASSERT_NE(TypeOf(my_var_b), nullptr);
|
||||
EXPECT_TRUE(TypeOf(my_var_b)->Is<type::F32>());
|
||||
EXPECT_EQ(StmtOf(my_var_b), assign);
|
||||
}
|
||||
|
||||
TEST_F(TypeDeterminerTest, Expr_Identifier_FunctionVariable) {
|
||||
auto* my_var = Expr("my_var");
|
||||
auto* my_var_a = Expr("my_var");
|
||||
auto* my_var_b = Expr("my_var");
|
||||
auto* assign = create<ast::AssignmentStatement>(my_var_a, my_var_b);
|
||||
|
||||
Func("my_func", ast::VariableList{}, ty.f32(),
|
||||
ast::StatementList{
|
||||
create<ast::VariableDeclStatement>(
|
||||
Var("my_var", ast::StorageClass::kNone, ty.f32())),
|
||||
create<ast::AssignmentStatement>(my_var, Expr("my_var")),
|
||||
assign,
|
||||
},
|
||||
ast::FunctionDecorationList{});
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
|
||||
ASSERT_NE(TypeOf(my_var), nullptr);
|
||||
EXPECT_TRUE(TypeOf(my_var)->Is<type::Pointer>());
|
||||
EXPECT_TRUE(TypeOf(my_var)->As<type::Pointer>()->type()->Is<type::F32>());
|
||||
ASSERT_NE(TypeOf(my_var_a), nullptr);
|
||||
EXPECT_TRUE(TypeOf(my_var_a)->Is<type::Pointer>());
|
||||
EXPECT_TRUE(TypeOf(my_var_a)->As<type::Pointer>()->type()->Is<type::F32>());
|
||||
EXPECT_EQ(StmtOf(my_var_a), assign);
|
||||
ASSERT_NE(TypeOf(my_var_b), nullptr);
|
||||
EXPECT_TRUE(TypeOf(my_var_b)->Is<type::Pointer>());
|
||||
EXPECT_TRUE(TypeOf(my_var_b)->As<type::Pointer>()->type()->Is<type::F32>());
|
||||
EXPECT_EQ(StmtOf(my_var_b), assign);
|
||||
}
|
||||
|
||||
TEST_F(TypeDeterminerTest, Expr_Identifier_Function_Ptr) {
|
||||
type::Pointer ptr(ty.f32(), ast::StorageClass::kFunction);
|
||||
|
||||
auto* my_var = Expr("my_var");
|
||||
auto* my_var_a = Expr("my_var");
|
||||
auto* my_var_b = Expr("my_var");
|
||||
auto* assign = create<ast::AssignmentStatement>(my_var_a, my_var_b);
|
||||
|
||||
Func("my_func", ast::VariableList{}, ty.f32(),
|
||||
ast::StatementList{
|
||||
create<ast::VariableDeclStatement>(
|
||||
Var("my_var", ast::StorageClass::kNone, &ptr)),
|
||||
create<ast::AssignmentStatement>(my_var, Expr("my_var")),
|
||||
Var("my_var", ast::StorageClass::kNone,
|
||||
ty.pointer<f32>(ast::StorageClass::kFunction))),
|
||||
assign,
|
||||
},
|
||||
ast::FunctionDecorationList{});
|
||||
|
||||
EXPECT_TRUE(td()->Determine()) << td()->error();
|
||||
|
||||
ASSERT_NE(TypeOf(my_var), nullptr);
|
||||
EXPECT_TRUE(TypeOf(my_var)->Is<type::Pointer>());
|
||||
EXPECT_TRUE(TypeOf(my_var)->As<type::Pointer>()->type()->Is<type::F32>());
|
||||
ASSERT_NE(TypeOf(my_var_a), nullptr);
|
||||
EXPECT_TRUE(TypeOf(my_var_a)->Is<type::Pointer>());
|
||||
EXPECT_TRUE(TypeOf(my_var_a)->As<type::Pointer>()->type()->Is<type::F32>());
|
||||
EXPECT_EQ(StmtOf(my_var_a), assign);
|
||||
ASSERT_NE(TypeOf(my_var_b), nullptr);
|
||||
EXPECT_TRUE(TypeOf(my_var_b)->Is<type::Pointer>());
|
||||
EXPECT_TRUE(TypeOf(my_var_b)->As<type::Pointer>()->type()->Is<type::F32>());
|
||||
EXPECT_EQ(StmtOf(my_var_b), assign);
|
||||
}
|
||||
|
||||
TEST_F(TypeDeterminerTest, Expr_Call_Function) {
|
||||
|
|
|
@ -52,9 +52,12 @@ ast::TypeConstructorExpression* AppendVector(ProgramBuilder* b,
|
|||
packed_el_ty = vector_sem->Type();
|
||||
}
|
||||
|
||||
auto* statement = vector_sem->Stmt();
|
||||
|
||||
// Cast scalar to the vector element type
|
||||
auto* scalar_cast = b->Construct(packed_el_ty, scalar);
|
||||
b->Sem().Add(scalar_cast, b->create<semantic::Expression>(packed_el_ty));
|
||||
b->Sem().Add(scalar_cast,
|
||||
b->create<semantic::Expression>(packed_el_ty, statement));
|
||||
|
||||
auto* packed_ty = b->create<type::Vector>(packed_el_ty, packed_size);
|
||||
|
||||
|
@ -73,7 +76,8 @@ ast::TypeConstructorExpression* AppendVector(ProgramBuilder* b,
|
|||
}
|
||||
|
||||
auto* constructor = b->Construct(packed_ty, std::move(packed));
|
||||
b->Sem().Add(constructor, b->create<semantic::Expression>(packed_ty));
|
||||
b->Sem().Add(constructor,
|
||||
b->create<semantic::Expression>(packed_ty, statement));
|
||||
|
||||
return constructor;
|
||||
}
|
||||
|
|
|
@ -984,7 +984,8 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& pre,
|
|||
auto emit_vector_appended_with_i32_zero = [&](tint::ast::Expression* vector) {
|
||||
auto* i32 = builder_.create<type::I32>();
|
||||
auto* zero = builder_.Expr(0);
|
||||
builder_.Sem().Add(zero, builder_.create<semantic::Expression>(i32));
|
||||
auto* stmt = builder_.Sem().Get(vector)->Stmt();
|
||||
builder_.Sem().Add(zero, builder_.create<semantic::Expression>(i32, stmt));
|
||||
auto* packed = AppendVector(&builder_, vector, zero);
|
||||
return EmitExpression(pre, out, packed);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue