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>
diff --git a/BUILD.gn b/BUILD.gn
index 4d79363..03c2274 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -391,6 +391,7 @@
"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",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e4b81d4..a42d8e3 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -205,6 +205,7 @@
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
diff --git a/src/semantic/call.h b/src/semantic/call.h
index 0ef874f..a4931fb 100644
--- a/src/semantic/call.h
+++ b/src/semantic/call.h
@@ -27,7 +27,8 @@
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;
diff --git a/src/semantic/expression.h b/src/semantic/expression.h
index 76052be..57ed61c 100644
--- a/src/semantic/expression.h
+++ b/src/semantic/expression.h
@@ -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 @@
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
diff --git a/src/semantic/member_accessor_expression.h b/src/semantic/member_accessor_expression.h
index 7b21f97..f46fe6e 100644
--- a/src/semantic/member_accessor_expression.h
+++ b/src/semantic/member_accessor_expression.h
@@ -27,8 +27,11 @@
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_; }
diff --git a/src/semantic/sem_call.cc b/src/semantic/sem_call.cc
index 632f3a0..8f0a41f 100644
--- a/src/semantic/sem_call.cc
+++ b/src/semantic/sem_call.cc
@@ -19,8 +19,8 @@
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;
diff --git a/src/semantic/sem_expression.cc b/src/semantic/sem_expression.cc
index 52d13f7..36cc8c4 100644
--- a/src/semantic/sem_expression.cc
+++ b/src/semantic/sem_expression.cc
@@ -21,7 +21,8 @@
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
diff --git a/src/semantic/sem_member_accessor_expression.cc b/src/semantic/sem_member_accessor_expression.cc
index 1145a23..e85c7bf 100644
--- a/src/semantic/sem_member_accessor_expression.cc
+++ b/src/semantic/sem_member_accessor_expression.cc
@@ -20,8 +20,9 @@
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
diff --git a/src/semantic/sem_statement.cc b/src/semantic/sem_statement.cc
new file mode 100644
index 0000000..af25872
--- /dev/null
+++ b/src/semantic/sem_statement.cc
@@ -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
diff --git a/src/semantic/statement.h b/src/semantic/statement.h
new file mode 100644
index 0000000..0782bcb
--- /dev/null
+++ b/src/semantic/statement.h
@@ -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_
diff --git a/src/type_determiner.cc b/src/type_determiner.cc
index a127caa..1bb2d4c 100644
--- a/src/type_determiner.cc
+++ b/src/type_determiner.cc
@@ -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 @@
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::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 @@
}
variable_stack_.pop_scope();
- current_function_ = nullptr;
-
return true;
}
@@ -234,6 +252,10 @@
}
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 @@
}
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 @@
}
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 @@
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 @@
}
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 @@
// 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));
}
}
diff --git a/src/type_determiner.h b/src/type_determiner.h
index bd30a91..7f8ecc2 100644
--- a/src/type_determiner.h
+++ b/src/type_determiner.h
@@ -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 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 @@
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 @@
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 @@
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_;
diff --git a/src/type_determiner_test.cc b/src/type_determiner_test.cc
index 8dc1575..9f871ba 100644
--- a/src/type_determiner_test.cc
+++ b/src/type_determiner_test.cc
@@ -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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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_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 @@
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 @@
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_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) {
diff --git a/src/writer/append_vector.cc b/src/writer/append_vector.cc
index 5b62095..3e3d841 100644
--- a/src/writer/append_vector.cc
+++ b/src/writer/append_vector.cc
@@ -52,9 +52,12 @@
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 @@
}
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;
}
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index 6ee2728..4d9c78b 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -984,7 +984,8 @@
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);
};