mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-05-15 03:41:34 +00:00
Create a semantic class for block statements
Semantic information about block statements the resolver would temporarily create while resolving is now exposed in a sem::BlockStatement class. In the process, semantic information about statements in general is overhauled so that a statement has a reference to its parent statement, regardless of whether this is a block. Bug: tint:799 Bug: tint:800 Change-Id: I8771511c5274ea74741b8c86f0f55cbc39810888 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/50904 Commit-Queue: Alastair Donaldson <allydonaldson@googlemail.com> Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
43f50eaf86
commit
ac90829e1c
@ -552,6 +552,7 @@ libtint_source_set("libtint_core_all_src") {
|
|||||||
libtint_source_set("libtint_sem_src") {
|
libtint_source_set("libtint_sem_src") {
|
||||||
sources = [
|
sources = [
|
||||||
"sem/array.cc",
|
"sem/array.cc",
|
||||||
|
"sem/block_statement.cc",
|
||||||
"sem/call.cc",
|
"sem/call.cc",
|
||||||
"sem/call_target.cc",
|
"sem/call_target.cc",
|
||||||
"sem/expression.cc",
|
"sem/expression.cc",
|
||||||
|
@ -235,6 +235,8 @@ set(TINT_LIB_SRCS
|
|||||||
sem/array.cc
|
sem/array.cc
|
||||||
sem/array.h
|
sem/array.h
|
||||||
sem/binding_point.h
|
sem/binding_point.h
|
||||||
|
sem/block_statement.cc
|
||||||
|
sem/block_statement.h
|
||||||
sem/call_target.cc
|
sem/call_target.cc
|
||||||
sem/call_target.h
|
sem/call_target.h
|
||||||
sem/call.cc
|
sem/call.cc
|
||||||
|
@ -145,13 +145,6 @@ Resolver::Resolver(ProgramBuilder* builder)
|
|||||||
|
|
||||||
Resolver::~Resolver() = default;
|
Resolver::~Resolver() = default;
|
||||||
|
|
||||||
Resolver::BlockInfo::BlockInfo(const ast::BlockStatement* b,
|
|
||||||
Resolver::BlockInfo::Type ty,
|
|
||||||
Resolver::BlockInfo* p)
|
|
||||||
: block(b), type(ty), parent(p) {}
|
|
||||||
|
|
||||||
Resolver::BlockInfo::~BlockInfo() = default;
|
|
||||||
|
|
||||||
void Resolver::set_referenced_from_function_if_needed(VariableInfo* var,
|
void Resolver::set_referenced_from_function_if_needed(VariableInfo* var,
|
||||||
bool local) {
|
bool local) {
|
||||||
if (current_function_ == nullptr) {
|
if (current_function_ == nullptr) {
|
||||||
@ -1230,7 +1223,18 @@ bool Resolver::Function(ast::Function* func) {
|
|||||||
|
|
||||||
if (func->body()) {
|
if (func->body()) {
|
||||||
Mark(func->body());
|
Mark(func->body());
|
||||||
if (!BlockStatement(func->body())) {
|
if (current_statement_) {
|
||||||
|
TINT_ICE(diagnostics_)
|
||||||
|
<< "Resolver::Function() called with a current statement";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sem::BlockStatement* sem_block = builder_->create<sem::BlockStatement>(
|
||||||
|
func->body(), nullptr, sem::BlockStatement::Type::kGeneric);
|
||||||
|
builder_->Sem().Add(func->body(), sem_block);
|
||||||
|
ScopedAssignment<sem::Statement*> sa_function_body(current_statement_,
|
||||||
|
sem_block);
|
||||||
|
if (!BlockScope(func->body(),
|
||||||
|
[&] { return Statements(func->body()->list()); })) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1256,11 +1260,6 @@ bool Resolver::Function(ast::Function* func) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resolver::BlockStatement(const ast::BlockStatement* stmt) {
|
|
||||||
return BlockScope(stmt, BlockInfo::Type::kGeneric,
|
|
||||||
[&] { return Statements(stmt->list()); });
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Resolver::Statements(const ast::StatementList& stmts) {
|
bool Resolver::Statements(const ast::StatementList& stmts) {
|
||||||
for (auto* stmt : stmts) {
|
for (auto* stmt : stmts) {
|
||||||
Mark(stmt);
|
Mark(stmt);
|
||||||
@ -1272,21 +1271,36 @@ bool Resolver::Statements(const ast::StatementList& stmts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Resolver::Statement(ast::Statement* stmt) {
|
bool Resolver::Statement(ast::Statement* stmt) {
|
||||||
auto* sem_statement =
|
sem::Statement* sem_statement;
|
||||||
builder_->create<sem::Statement>(stmt, current_block_->block);
|
if (stmt->As<ast::BlockStatement>()) {
|
||||||
|
sem_statement = builder_->create<sem::BlockStatement>(
|
||||||
|
stmt->As<ast::BlockStatement>(), current_statement_,
|
||||||
|
sem::BlockStatement::Type::kGeneric);
|
||||||
|
} else {
|
||||||
|
sem_statement = builder_->create<sem::Statement>(stmt, current_statement_);
|
||||||
|
}
|
||||||
builder_->Sem().Add(stmt, sem_statement);
|
builder_->Sem().Add(stmt, sem_statement);
|
||||||
|
|
||||||
ScopedAssignment<sem::Statement*> sa(current_statement_, sem_statement);
|
ScopedAssignment<sem::Statement*> sa(current_statement_, sem_statement);
|
||||||
|
|
||||||
|
if (stmt->Is<ast::ElseStatement>()) {
|
||||||
|
TINT_ICE(diagnostics_)
|
||||||
|
<< "Resolver::Statement() encountered an Else statement. Else "
|
||||||
|
"statements are embedded in If statements, so should never be "
|
||||||
|
"encountered as top-level statements";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (auto* a = stmt->As<ast::AssignmentStatement>()) {
|
if (auto* a = stmt->As<ast::AssignmentStatement>()) {
|
||||||
return Assignment(a);
|
return Assignment(a);
|
||||||
}
|
}
|
||||||
if (auto* b = stmt->As<ast::BlockStatement>()) {
|
if (auto* b = stmt->As<ast::BlockStatement>()) {
|
||||||
return BlockStatement(b);
|
return BlockScope(b, [&] { return Statements(b->list()); });
|
||||||
}
|
}
|
||||||
if (stmt->Is<ast::BreakStatement>()) {
|
if (stmt->Is<ast::BreakStatement>()) {
|
||||||
if (!current_block_->FindFirstParent(BlockInfo::Type::kLoop) &&
|
if (!current_block_->FindFirstParent(sem::BlockStatement::Type::kLoop) &&
|
||||||
!current_block_->FindFirstParent(BlockInfo::Type::kSwitchCase)) {
|
!current_block_->FindFirstParent(
|
||||||
|
sem::BlockStatement::Type::kSwitchCase)) {
|
||||||
diagnostics_.add_error("break statement must be in a loop or switch case",
|
diagnostics_.add_error("break statement must be in a loop or switch case",
|
||||||
stmt->source());
|
stmt->source());
|
||||||
return false;
|
return false;
|
||||||
@ -1303,9 +1317,10 @@ bool Resolver::Statement(ast::Statement* stmt) {
|
|||||||
if (stmt->Is<ast::ContinueStatement>()) {
|
if (stmt->Is<ast::ContinueStatement>()) {
|
||||||
// Set if we've hit the first continue statement in our parent loop
|
// Set if we've hit the first continue statement in our parent loop
|
||||||
if (auto* loop_block =
|
if (auto* loop_block =
|
||||||
current_block_->FindFirstParent(BlockInfo::Type::kLoop)) {
|
current_block_->FindFirstParent(sem::BlockStatement::Type::kLoop)) {
|
||||||
if (loop_block->first_continue == size_t(~0)) {
|
if (loop_block->FirstContinue() == size_t(~0)) {
|
||||||
loop_block->first_continue = loop_block->decls.size();
|
const_cast<sem::BlockStatement*>(loop_block)
|
||||||
|
->SetFirstContinue(loop_block->Decls().size());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
diagnostics_.add_error("continue statement must be in a loop",
|
diagnostics_.add_error("continue statement must be in a loop",
|
||||||
@ -1325,28 +1340,7 @@ bool Resolver::Statement(ast::Statement* stmt) {
|
|||||||
return IfStatement(i);
|
return IfStatement(i);
|
||||||
}
|
}
|
||||||
if (auto* l = stmt->As<ast::LoopStatement>()) {
|
if (auto* l = stmt->As<ast::LoopStatement>()) {
|
||||||
// We don't call DetermineBlockStatement on the body and continuing block as
|
return LoopStatement(l);
|
||||||
// these would make their BlockInfo siblings as in the AST, but we want the
|
|
||||||
// body BlockInfo to parent the continuing BlockInfo for semantics and
|
|
||||||
// validation. Also, we need to set their types differently.
|
|
||||||
Mark(l->body());
|
|
||||||
return BlockScope(l->body(), BlockInfo::Type::kLoop, [&] {
|
|
||||||
if (!Statements(l->body()->list())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (l->continuing()) { // has_continuing() also checks for empty()
|
|
||||||
Mark(l->continuing());
|
|
||||||
}
|
|
||||||
if (l->has_continuing()) {
|
|
||||||
if (!BlockScope(l->continuing(), BlockInfo::Type::kLoopContinuing,
|
|
||||||
[&] { return Statements(l->continuing()->list()); })) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if (auto* r = stmt->As<ast::ReturnStatement>()) {
|
if (auto* r = stmt->As<ast::ReturnStatement>()) {
|
||||||
return Return(r);
|
return Return(r);
|
||||||
@ -1369,7 +1363,11 @@ bool Resolver::CaseStatement(ast::CaseStatement* stmt) {
|
|||||||
for (auto* sel : stmt->selectors()) {
|
for (auto* sel : stmt->selectors()) {
|
||||||
Mark(sel);
|
Mark(sel);
|
||||||
}
|
}
|
||||||
return BlockScope(stmt->body(), BlockInfo::Type::kSwitchCase,
|
sem::BlockStatement* sem_block = builder_->create<sem::BlockStatement>(
|
||||||
|
stmt->body(), current_statement_, sem::BlockStatement::Type::kSwitchCase);
|
||||||
|
builder_->Sem().Add(stmt->body(), sem_block);
|
||||||
|
ScopedAssignment<sem::Statement*> sa(current_statement_, sem_block);
|
||||||
|
return BlockScope(stmt->body(),
|
||||||
[&] { return Statements(stmt->body()->list()); });
|
[&] { return Statements(stmt->body()->list()); });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1388,17 +1386,21 @@ bool Resolver::IfStatement(ast::IfStatement* stmt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Mark(stmt->body());
|
Mark(stmt->body());
|
||||||
if (!BlockStatement(stmt->body())) {
|
{
|
||||||
|
sem::BlockStatement* sem_block = builder_->create<sem::BlockStatement>(
|
||||||
|
stmt->body(), current_statement_, sem::BlockStatement::Type::kGeneric);
|
||||||
|
builder_->Sem().Add(stmt->body(), sem_block);
|
||||||
|
ScopedAssignment<sem::Statement*> sa(current_statement_, sem_block);
|
||||||
|
if (!BlockScope(stmt->body(),
|
||||||
|
[&] { return Statements(stmt->body()->list()); })) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (auto* else_stmt : stmt->else_statements()) {
|
for (auto* else_stmt : stmt->else_statements()) {
|
||||||
Mark(else_stmt);
|
Mark(else_stmt);
|
||||||
// Else statements are a bit unusual - they're owned by the if-statement,
|
|
||||||
// not a BlockStatement.
|
|
||||||
constexpr ast::BlockStatement* no_block_statement = nullptr;
|
|
||||||
auto* sem_else_stmt =
|
auto* sem_else_stmt =
|
||||||
builder_->create<sem::Statement>(else_stmt, no_block_statement);
|
builder_->create<sem::Statement>(else_stmt, current_statement_);
|
||||||
builder_->Sem().Add(else_stmt, sem_else_stmt);
|
builder_->Sem().Add(else_stmt, sem_else_stmt);
|
||||||
ScopedAssignment<sem::Statement*> sa(current_statement_, sem_else_stmt);
|
ScopedAssignment<sem::Statement*> sa(current_statement_, sem_else_stmt);
|
||||||
if (auto* cond = else_stmt->condition()) {
|
if (auto* cond = else_stmt->condition()) {
|
||||||
@ -1417,11 +1419,54 @@ bool Resolver::IfStatement(ast::IfStatement* stmt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Mark(else_stmt->body());
|
Mark(else_stmt->body());
|
||||||
if (!BlockStatement(else_stmt->body())) {
|
{
|
||||||
|
sem::BlockStatement* sem_block = builder_->create<sem::BlockStatement>(
|
||||||
|
else_stmt->body(), current_statement_,
|
||||||
|
sem::BlockStatement::Type::kGeneric);
|
||||||
|
builder_->Sem().Add(else_stmt->body(), sem_block);
|
||||||
|
ScopedAssignment<sem::Statement*> sa_else_body(current_statement_,
|
||||||
|
sem_block);
|
||||||
|
if (!BlockScope(else_stmt->body(),
|
||||||
|
[&] { return Statements(else_stmt->body()->list()); })) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Resolver::LoopStatement(ast::LoopStatement* stmt) {
|
||||||
|
// We don't call DetermineBlockStatement on the body and continuing block as
|
||||||
|
// these would make their BlockInfo siblings as in the AST, but we want the
|
||||||
|
// body BlockInfo to parent the continuing BlockInfo for semantics and
|
||||||
|
// validation. Also, we need to set their types differently.
|
||||||
|
Mark(stmt->body());
|
||||||
|
|
||||||
|
auto* sem_block_body = builder_->create<sem::BlockStatement>(
|
||||||
|
stmt->body(), current_statement_, sem::BlockStatement::Type::kLoop);
|
||||||
|
builder_->Sem().Add(stmt->body(), sem_block_body);
|
||||||
|
ScopedAssignment<sem::Statement*> body_sa(current_statement_, sem_block_body);
|
||||||
|
return BlockScope(stmt->body(), [&] {
|
||||||
|
if (!Statements(stmt->body()->list())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (stmt->continuing()) { // has_continuing() also checks for empty()
|
||||||
|
Mark(stmt->continuing());
|
||||||
|
}
|
||||||
|
if (stmt->has_continuing()) {
|
||||||
|
auto* sem_block_continuing = builder_->create<sem::BlockStatement>(
|
||||||
|
stmt->continuing(), current_statement_,
|
||||||
|
sem::BlockStatement::Type::kLoopContinuing);
|
||||||
|
builder_->Sem().Add(stmt->continuing(), sem_block_continuing);
|
||||||
|
ScopedAssignment<sem::Statement*> continuing_sa(current_statement_,
|
||||||
|
sem_block_continuing);
|
||||||
|
if (!BlockScope(stmt->continuing(),
|
||||||
|
[&] { return Statements(stmt->continuing()->list()); })) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resolver::Expressions(const ast::ExpressionList& list) {
|
bool Resolver::Expressions(const ast::ExpressionList& list) {
|
||||||
@ -1778,11 +1823,11 @@ bool Resolver::Identifier(ast::IdentifierExpression* expr) {
|
|||||||
// refer to a variable that is bypassed by a continue statement in the
|
// refer to a variable that is bypassed by a continue statement in the
|
||||||
// loop's body block.
|
// loop's body block.
|
||||||
if (auto* continuing_block = current_block_->FindFirstParent(
|
if (auto* continuing_block = current_block_->FindFirstParent(
|
||||||
BlockInfo::Type::kLoopContinuing)) {
|
sem::BlockStatement::Type::kLoopContinuing)) {
|
||||||
auto* loop_block =
|
auto* loop_block =
|
||||||
continuing_block->FindFirstParent(BlockInfo::Type::kLoop);
|
continuing_block->FindFirstParent(sem::BlockStatement::Type::kLoop);
|
||||||
if (loop_block->first_continue != size_t(~0)) {
|
if (loop_block->FirstContinue() != size_t(~0)) {
|
||||||
auto& decls = loop_block->decls;
|
auto& decls = loop_block->Decls();
|
||||||
// If our identifier is in loop_block->decls, make sure its index is
|
// If our identifier is in loop_block->decls, make sure its index is
|
||||||
// less than first_continue
|
// less than first_continue
|
||||||
auto iter = std::find_if(
|
auto iter = std::find_if(
|
||||||
@ -1791,7 +1836,7 @@ bool Resolver::Identifier(ast::IdentifierExpression* expr) {
|
|||||||
if (iter != decls.end()) {
|
if (iter != decls.end()) {
|
||||||
auto var_decl_index =
|
auto var_decl_index =
|
||||||
static_cast<size_t>(std::distance(decls.begin(), iter));
|
static_cast<size_t>(std::distance(decls.begin(), iter));
|
||||||
if (var_decl_index >= loop_block->first_continue) {
|
if (var_decl_index >= loop_block->FirstContinue()) {
|
||||||
diagnostics_.add_error(
|
diagnostics_.add_error(
|
||||||
"continue statement bypasses declaration of '" +
|
"continue statement bypasses declaration of '" +
|
||||||
builder_->Symbols().NameFor(symbol) +
|
builder_->Symbols().NameFor(symbol) +
|
||||||
@ -2227,7 +2272,7 @@ bool Resolver::VariableDeclStatement(const ast::VariableDeclStatement* stmt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
variable_stack_.set(var->symbol(), info);
|
variable_stack_.set(var->symbol(), info);
|
||||||
current_block_->decls.push_back(var);
|
current_block_->AddDecl(var);
|
||||||
|
|
||||||
if (!ValidateVariable(info)) {
|
if (!ValidateVariable(info)) {
|
||||||
return false;
|
return false;
|
||||||
@ -2868,6 +2913,11 @@ bool Resolver::Switch(ast::SwitchStatement* s) {
|
|||||||
}
|
}
|
||||||
for (auto* case_stmt : s->body()) {
|
for (auto* case_stmt : s->body()) {
|
||||||
Mark(case_stmt);
|
Mark(case_stmt);
|
||||||
|
|
||||||
|
sem::Statement* sem_statement =
|
||||||
|
builder_->create<sem::Statement>(case_stmt, current_statement_);
|
||||||
|
builder_->Sem().Add(case_stmt, sem_statement);
|
||||||
|
ScopedAssignment<sem::Statement*> sa(current_statement_, sem_statement);
|
||||||
if (!CaseStatement(case_stmt)) {
|
if (!CaseStatement(case_stmt)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2992,11 +3042,15 @@ bool Resolver::ApplyStorageClassUsageToType(ast::StorageClass sc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
bool Resolver::BlockScope(const ast::BlockStatement* block,
|
bool Resolver::BlockScope(const ast::BlockStatement* block, F&& callback) {
|
||||||
BlockInfo::Type type,
|
auto* sem_block = builder_->Sem().Get<sem::BlockStatement>(block);
|
||||||
F&& callback) {
|
if (!sem_block) {
|
||||||
BlockInfo block_info(block, type, current_block_);
|
TINT_ICE(diagnostics_) << "Resolver::BlockScope() called on a block for "
|
||||||
ScopedAssignment<BlockInfo*> sa(current_block_, &block_info);
|
"which semantic information is not available";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ScopedAssignment<sem::BlockStatement*> sa(
|
||||||
|
current_block_, const_cast<sem::BlockStatement*>(sem_block));
|
||||||
variable_stack_.push_scope();
|
variable_stack_.push_scope();
|
||||||
bool result = callback();
|
bool result = callback();
|
||||||
variable_stack_.pop_scope();
|
variable_stack_.pop_scope();
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "src/program_builder.h"
|
#include "src/program_builder.h"
|
||||||
#include "src/scope_stack.h"
|
#include "src/scope_stack.h"
|
||||||
#include "src/sem/binding_point.h"
|
#include "src/sem/binding_point.h"
|
||||||
|
#include "src/sem/block_statement.h"
|
||||||
#include "src/sem/struct.h"
|
#include "src/sem/struct.h"
|
||||||
#include "src/utils/unique_vector.h"
|
#include "src/utils/unique_vector.h"
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ class CaseStatement;
|
|||||||
class ConstructorExpression;
|
class ConstructorExpression;
|
||||||
class Function;
|
class Function;
|
||||||
class IdentifierExpression;
|
class IdentifierExpression;
|
||||||
|
class LoopStatement;
|
||||||
class MemberAccessorExpression;
|
class MemberAccessorExpression;
|
||||||
class ReturnStatement;
|
class ReturnStatement;
|
||||||
class SwitchStatement;
|
class SwitchStatement;
|
||||||
@ -137,40 +139,6 @@ class Resolver {
|
|||||||
sem::Statement* statement;
|
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.
|
|
||||||
struct BlockInfo {
|
|
||||||
enum class Type { kGeneric, kLoop, kLoopContinuing, kSwitchCase };
|
|
||||||
|
|
||||||
BlockInfo(const ast::BlockStatement* block, Type type, BlockInfo* parent);
|
|
||||||
~BlockInfo();
|
|
||||||
|
|
||||||
template <typename Pred>
|
|
||||||
BlockInfo* FindFirstParent(Pred&& pred) {
|
|
||||||
BlockInfo* curr = this;
|
|
||||||
while (curr && !pred(curr)) {
|
|
||||||
curr = curr->parent;
|
|
||||||
}
|
|
||||||
return curr;
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockInfo* FindFirstParent(BlockInfo::Type ty) {
|
|
||||||
return FindFirstParent(
|
|
||||||
[ty](auto* block_info) { return block_info->type == ty; });
|
|
||||||
}
|
|
||||||
|
|
||||||
ast::BlockStatement const* const block;
|
|
||||||
Type const type;
|
|
||||||
BlockInfo* const parent;
|
|
||||||
std::vector<const ast::Variable*> decls;
|
|
||||||
|
|
||||||
// first_continue is set to the index of the first variable in decls
|
|
||||||
// declared after the first continue statement in a loop block, if any.
|
|
||||||
constexpr static size_t kNoContinue = size_t(~0);
|
|
||||||
size_t first_continue = kNoContinue;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Resolves the program, without creating final the semantic nodes.
|
/// Resolves the program, without creating final the semantic nodes.
|
||||||
/// @returns true on success, false on error
|
/// @returns true on success, false on error
|
||||||
bool ResolveInternal();
|
bool ResolveInternal();
|
||||||
@ -200,7 +168,6 @@ class Resolver {
|
|||||||
bool Assignment(ast::AssignmentStatement* a);
|
bool Assignment(ast::AssignmentStatement* a);
|
||||||
bool Binary(ast::BinaryExpression*);
|
bool Binary(ast::BinaryExpression*);
|
||||||
bool Bitcast(ast::BitcastExpression*);
|
bool Bitcast(ast::BitcastExpression*);
|
||||||
bool BlockStatement(const ast::BlockStatement*);
|
|
||||||
bool Call(ast::CallExpression*);
|
bool Call(ast::CallExpression*);
|
||||||
bool CaseStatement(ast::CaseStatement*);
|
bool CaseStatement(ast::CaseStatement*);
|
||||||
bool Constructor(ast::ConstructorExpression*);
|
bool Constructor(ast::ConstructorExpression*);
|
||||||
@ -211,6 +178,7 @@ class Resolver {
|
|||||||
bool Identifier(ast::IdentifierExpression*);
|
bool Identifier(ast::IdentifierExpression*);
|
||||||
bool IfStatement(ast::IfStatement*);
|
bool IfStatement(ast::IfStatement*);
|
||||||
bool IntrinsicCall(ast::CallExpression*, sem::IntrinsicType);
|
bool IntrinsicCall(ast::CallExpression*, sem::IntrinsicType);
|
||||||
|
bool LoopStatement(ast::LoopStatement*);
|
||||||
bool MemberAccessor(ast::MemberAccessorExpression*);
|
bool MemberAccessor(ast::MemberAccessorExpression*);
|
||||||
bool Parameter(ast::Variable* param);
|
bool Parameter(ast::Variable* param);
|
||||||
bool Return(ast::ReturnStatement* ret);
|
bool Return(ast::ReturnStatement* ret);
|
||||||
@ -317,13 +285,11 @@ class Resolver {
|
|||||||
typ::Type type,
|
typ::Type type,
|
||||||
const std::string& type_name);
|
const std::string& type_name);
|
||||||
|
|
||||||
/// Constructs a new BlockInfo with the given type and with #current_block_ as
|
/// Constructs a new semantic BlockStatement with the given type and with
|
||||||
/// its parent, assigns this to #current_block_, and then calls `callback`.
|
/// #current_block_ as its parent, assigns this to #current_block_, and then
|
||||||
/// The original #current_block_ is restored on exit.
|
/// calls `callback`. The original #current_block_ is restored on exit.
|
||||||
template <typename F>
|
template <typename F>
|
||||||
bool BlockScope(const ast::BlockStatement* block,
|
bool BlockScope(const ast::BlockStatement* block, F&& callback);
|
||||||
BlockInfo::Type type,
|
|
||||||
F&& callback);
|
|
||||||
|
|
||||||
/// Returns a human-readable string representation of the vector type name
|
/// Returns a human-readable string representation of the vector type name
|
||||||
/// with the given parameters.
|
/// with the given parameters.
|
||||||
@ -340,7 +306,7 @@ class Resolver {
|
|||||||
ProgramBuilder* const builder_;
|
ProgramBuilder* const builder_;
|
||||||
diag::List& diagnostics_;
|
diag::List& diagnostics_;
|
||||||
std::unique_ptr<IntrinsicTable> const intrinsic_table_;
|
std::unique_ptr<IntrinsicTable> const intrinsic_table_;
|
||||||
BlockInfo* current_block_ = nullptr;
|
sem::BlockStatement* current_block_ = nullptr;
|
||||||
ScopeStack<VariableInfo*> variable_stack_;
|
ScopeStack<VariableInfo*> variable_stack_;
|
||||||
std::unordered_map<Symbol, FunctionInfo*> symbol_to_function_;
|
std::unordered_map<Symbol, FunctionInfo*> symbol_to_function_;
|
||||||
std::unordered_map<const ast::Function*, FunctionInfo*> function_to_info_;
|
std::unordered_map<const ast::Function*, FunctionInfo*> function_to_info_;
|
||||||
|
@ -56,7 +56,7 @@ class TestHelper : public ProgramBuilder {
|
|||||||
/// if the statement is not owned by a BlockStatement.
|
/// if the statement is not owned by a BlockStatement.
|
||||||
const ast::BlockStatement* BlockOf(ast::Statement* stmt) {
|
const ast::BlockStatement* BlockOf(ast::Statement* stmt) {
|
||||||
auto* sem_stmt = Sem().Get(stmt);
|
auto* sem_stmt = Sem().Get(stmt);
|
||||||
return sem_stmt ? sem_stmt->Block() : nullptr;
|
return sem_stmt ? sem_stmt->Block()->Declaration() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the BlockStatement that holds the given expression.
|
/// Returns the BlockStatement that holds the given expression.
|
||||||
@ -65,7 +65,7 @@ class TestHelper : public ProgramBuilder {
|
|||||||
/// expression is not indirectly owned by a BlockStatement.
|
/// expression is not indirectly owned by a BlockStatement.
|
||||||
const ast::BlockStatement* BlockOf(ast::Expression* expr) {
|
const ast::BlockStatement* BlockOf(ast::Expression* expr) {
|
||||||
auto* sem_stmt = Sem().Get(expr)->Stmt();
|
auto* sem_stmt = Sem().Get(expr)->Stmt();
|
||||||
return sem_stmt ? sem_stmt->Block() : nullptr;
|
return sem_stmt ? sem_stmt->Block()->Declaration() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the semantic variable for the given identifier expression.
|
/// Returns the semantic variable for the given identifier expression.
|
||||||
|
51
src/sem/block_statement.cc
Normal file
51
src/sem/block_statement.cc
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// 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/sem/block_statement.h"
|
||||||
|
|
||||||
|
#include "src/ast/block_statement.h"
|
||||||
|
|
||||||
|
TINT_INSTANTIATE_TYPEINFO(tint::sem::BlockStatement);
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace sem {
|
||||||
|
|
||||||
|
BlockStatement::BlockStatement(const ast::BlockStatement* declaration,
|
||||||
|
const Statement* parent,
|
||||||
|
Type type)
|
||||||
|
: Base(declaration, parent), type_(type) {}
|
||||||
|
|
||||||
|
BlockStatement::~BlockStatement() = default;
|
||||||
|
|
||||||
|
const BlockStatement* BlockStatement::FindFirstParent(
|
||||||
|
BlockStatement::Type ty) const {
|
||||||
|
return FindFirstParent(
|
||||||
|
[ty](auto* block_info) { return block_info->type_ == ty; });
|
||||||
|
}
|
||||||
|
|
||||||
|
const ast::BlockStatement* BlockStatement::Declaration() const {
|
||||||
|
return Base::Declaration()->As<ast::BlockStatement>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockStatement::SetFirstContinue(size_t first_continue) {
|
||||||
|
TINT_ASSERT(type_ == Type::kLoop);
|
||||||
|
first_continue_ = first_continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockStatement::AddDecl(ast::Variable* var) {
|
||||||
|
decls_.push_back(var);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sem
|
||||||
|
} // namespace tint
|
105
src/sem/block_statement.h
Normal file
105
src/sem/block_statement.h
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// 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_SEM_BLOCK_STATEMENT_H_
|
||||||
|
#define SRC_SEM_BLOCK_STATEMENT_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "src/debug.h"
|
||||||
|
#include "src/sem/statement.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
namespace ast {
|
||||||
|
class BlockStatement;
|
||||||
|
class Variable;
|
||||||
|
} // namespace ast
|
||||||
|
|
||||||
|
namespace sem {
|
||||||
|
|
||||||
|
/// Holds semantic information about a block, such as parent block and variables
|
||||||
|
/// declared in the block.
|
||||||
|
class BlockStatement : public Castable<BlockStatement, Statement> {
|
||||||
|
public:
|
||||||
|
enum class Type { kGeneric, kLoop, kLoopContinuing, kSwitchCase };
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
/// @param declaration the AST node for this block statement
|
||||||
|
/// @param parent the owning statement
|
||||||
|
/// @param type the type of block this is
|
||||||
|
BlockStatement(const ast::BlockStatement* declaration,
|
||||||
|
const Statement* parent,
|
||||||
|
Type type);
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
~BlockStatement() override;
|
||||||
|
|
||||||
|
/// @returns the AST block statement associated with this semantic block
|
||||||
|
/// statement
|
||||||
|
const ast::BlockStatement* Declaration() const;
|
||||||
|
|
||||||
|
/// @returns the closest enclosing block that satisfies the given predicate,
|
||||||
|
/// which may be the block itself, or nullptr if no match is found
|
||||||
|
/// @param pred a predicate that the resulting block must satisfy
|
||||||
|
template <typename Pred>
|
||||||
|
const BlockStatement* FindFirstParent(Pred&& pred) const {
|
||||||
|
const BlockStatement* curr = this;
|
||||||
|
while (curr && !pred(curr)) {
|
||||||
|
curr = curr->Block();
|
||||||
|
}
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @returns the closest enclosing block that matches the given type, which
|
||||||
|
/// may be the block itself, or nullptr if no match is found
|
||||||
|
/// @param ty the type of block to be searched for
|
||||||
|
const BlockStatement* FindFirstParent(BlockStatement::Type ty) const;
|
||||||
|
|
||||||
|
/// @returns the declarations associated with this block
|
||||||
|
const std::vector<const ast::Variable*>& Decls() const { return decls_; }
|
||||||
|
|
||||||
|
/// Requires that this is a loop block.
|
||||||
|
/// @returns the index of the first variable declared after the first continue
|
||||||
|
/// statement
|
||||||
|
size_t FirstContinue() const {
|
||||||
|
TINT_ASSERT(type_ == Type::kLoop);
|
||||||
|
return first_continue_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requires that this is a loop block.
|
||||||
|
/// Allows the resolver to set the index of the first variable declared after
|
||||||
|
/// the first continue statement.
|
||||||
|
/// @param first_continue index of the relevant variable
|
||||||
|
void SetFirstContinue(size_t first_continue);
|
||||||
|
|
||||||
|
/// Allows the resolver to associate a declaration with this block.
|
||||||
|
/// @param var a variable declaration to be added to the block
|
||||||
|
void AddDecl(ast::Variable* var);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type const type_;
|
||||||
|
std::vector<const ast::Variable*> decls_;
|
||||||
|
|
||||||
|
// first_continue is set to the index of the first variable in decls
|
||||||
|
// declared after the first continue statement in a loop block, if any.
|
||||||
|
constexpr static size_t kNoContinue = size_t(~0);
|
||||||
|
size_t first_continue_ = kNoContinue;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sem
|
||||||
|
} // namespace tint
|
||||||
|
|
||||||
|
#endif // SRC_SEM_BLOCK_STATEMENT_H_
|
@ -15,7 +15,10 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "src/ast/block_statement.h"
|
#include "src/ast/block_statement.h"
|
||||||
|
#include "src/ast/loop_statement.h"
|
||||||
|
#include "src/ast/statement.h"
|
||||||
#include "src/debug.h"
|
#include "src/debug.h"
|
||||||
|
#include "src/sem/block_statement.h"
|
||||||
#include "src/sem/statement.h"
|
#include "src/sem/statement.h"
|
||||||
|
|
||||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::Statement);
|
TINT_INSTANTIATE_TYPEINFO(tint::sem::Statement);
|
||||||
@ -23,17 +26,44 @@ TINT_INSTANTIATE_TYPEINFO(tint::sem::Statement);
|
|||||||
namespace tint {
|
namespace tint {
|
||||||
namespace sem {
|
namespace sem {
|
||||||
|
|
||||||
Statement::Statement(const ast::Statement* declaration,
|
Statement::Statement(const ast::Statement* declaration, const Statement* parent)
|
||||||
const ast::BlockStatement* block)
|
: declaration_(declaration), parent_(parent) {
|
||||||
: declaration_(declaration), block_(block) {
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if (block) {
|
if (parent_) {
|
||||||
auto& stmts = block->statements();
|
auto* block = Block();
|
||||||
TINT_ASSERT(std::find(stmts.begin(), stmts.end(), declaration) !=
|
if (parent_ == block) {
|
||||||
stmts.end());
|
// The parent of this statement is a block. We thus expect the statement
|
||||||
|
// to be an element of the block. There is one exception: a loop's
|
||||||
|
// continuing block has the loop's body as its parent, but the continuing
|
||||||
|
// block is not a statement in the body, so we rule out that case.
|
||||||
|
auto& stmts = block->Declaration()->statements();
|
||||||
|
if (std::find(stmts.begin(), stmts.end(), declaration) == stmts.end()) {
|
||||||
|
bool statement_is_continuing_for_loop = false;
|
||||||
|
if (parent_->parent_ != nullptr) {
|
||||||
|
if (auto* loop =
|
||||||
|
parent_->parent_->Declaration()->As<ast::LoopStatement>()) {
|
||||||
|
if (loop->has_continuing() && Declaration() == loop->continuing()) {
|
||||||
|
statement_is_continuing_for_loop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TINT_ASSERT(statement_is_continuing_for_loop);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif // NDEBUG
|
#endif // NDEBUG
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const BlockStatement* Statement::Block() const {
|
||||||
|
auto* stmt = parent_;
|
||||||
|
while (stmt != nullptr) {
|
||||||
|
if (auto* block_stmt = stmt->As<BlockStatement>()) {
|
||||||
|
return block_stmt;
|
||||||
|
}
|
||||||
|
stmt = stmt->parent_;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sem
|
} // namespace sem
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
|
@ -21,30 +21,33 @@ namespace tint {
|
|||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
namespace ast {
|
namespace ast {
|
||||||
class BlockStatement;
|
|
||||||
class Statement;
|
class Statement;
|
||||||
} // namespace ast
|
} // namespace ast
|
||||||
|
|
||||||
namespace sem {
|
namespace sem {
|
||||||
|
|
||||||
|
class BlockStatement;
|
||||||
|
|
||||||
/// Statement holds the semantic information for a statement.
|
/// Statement holds the semantic information for a statement.
|
||||||
class Statement : public Castable<Statement, Node> {
|
class Statement : public Castable<Statement, Node> {
|
||||||
public:
|
public:
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// @param declaration the AST node for this statement
|
/// @param declaration the AST node for this statement
|
||||||
/// @param block the owning AST block statement
|
/// @param parent the owning statement
|
||||||
Statement(const ast::Statement* declaration,
|
Statement(const ast::Statement* declaration, const Statement* parent);
|
||||||
const ast::BlockStatement* block);
|
|
||||||
|
|
||||||
/// @return the AST node for this statement
|
/// @return the AST node for this statement
|
||||||
const ast::Statement* Declaration() const { return declaration_; }
|
const ast::Statement* Declaration() const { return declaration_; }
|
||||||
|
|
||||||
/// @return the owning AST block statement for this statement
|
/// @return the statement that encloses this statement
|
||||||
const ast::BlockStatement* Block() const { return block_; }
|
const Statement* Parent() const { return parent_; }
|
||||||
|
|
||||||
|
/// @return the closest enclosing block for this statement
|
||||||
|
const BlockStatement* Block() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ast::Statement const* const declaration_;
|
ast::Statement const* const declaration_;
|
||||||
ast::BlockStatement const* const block_;
|
Statement const* const parent_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sem
|
} // namespace sem
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "src/ast/call_statement.h"
|
#include "src/ast/call_statement.h"
|
||||||
#include "src/program_builder.h"
|
#include "src/program_builder.h"
|
||||||
|
#include "src/sem/block_statement.h"
|
||||||
#include "src/sem/call.h"
|
#include "src/sem/call.h"
|
||||||
#include "src/sem/statement.h"
|
#include "src/sem/statement.h"
|
||||||
#include "src/sem/struct.h"
|
#include "src/sem/struct.h"
|
||||||
@ -155,12 +156,7 @@ Output CalculateArrayLength::Run(const Program* in, const DataMap&) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find the current statement block
|
// Find the current statement block
|
||||||
auto* block = call->Stmt()->Block();
|
auto* block = call->Stmt()->Block()->Declaration();
|
||||||
if (!block) {
|
|
||||||
TINT_ICE(ctx.dst->Diagnostics())
|
|
||||||
<< "arrayLength() statement is outside a BlockStatement";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the storage_buffer_expr is resolves to a variable (typically
|
// If the storage_buffer_expr is resolves to a variable (typically
|
||||||
// true) then key the array_length from the variable. If not, key off
|
// true) then key the array_length from the variable. If not, key off
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "src/program_builder.h"
|
#include "src/program_builder.h"
|
||||||
|
#include "src/sem/block_statement.h"
|
||||||
#include "src/sem/function.h"
|
#include "src/sem/function.h"
|
||||||
#include "src/sem/statement.h"
|
#include "src/sem/statement.h"
|
||||||
#include "src/sem/struct.h"
|
#include "src/sem/struct.h"
|
||||||
@ -257,7 +258,8 @@ Output CanonicalizeEntryPointIO::Run(const Program* in, const DataMap&) {
|
|||||||
auto* ty = CreateASTTypeFor(&ctx, ret_type);
|
auto* ty = CreateASTTypeFor(&ctx, ret_type);
|
||||||
auto* temp_var =
|
auto* temp_var =
|
||||||
ctx.dst->Decl(ctx.dst->Const(temp, ty, new_ret_value()));
|
ctx.dst->Decl(ctx.dst->Const(temp, ty, new_ret_value()));
|
||||||
ctx.InsertBefore(ret_sem->Block()->statements(), ret, temp_var);
|
ctx.InsertBefore(ret_sem->Block()->Declaration()->statements(), ret,
|
||||||
|
temp_var);
|
||||||
new_ret_value = [&ctx, temp] { return ctx.dst->Expr(temp); };
|
new_ret_value = [&ctx, temp] { return ctx.dst->Expr(temp); };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "src/ast/stage_decoration.h"
|
#include "src/ast/stage_decoration.h"
|
||||||
#include "src/ast/variable_decl_statement.h"
|
#include "src/ast/variable_decl_statement.h"
|
||||||
#include "src/program_builder.h"
|
#include "src/program_builder.h"
|
||||||
|
#include "src/sem/block_statement.h"
|
||||||
#include "src/sem/expression.h"
|
#include "src/sem/expression.h"
|
||||||
#include "src/sem/statement.h"
|
#include "src/sem/statement.h"
|
||||||
#include "src/sem/variable.h"
|
#include "src/sem/variable.h"
|
||||||
@ -113,8 +114,8 @@ void Hlsl::PromoteInitializersToConstVar(CloneContext& ctx) const {
|
|||||||
auto* dst_ident = ctx.dst->Expr(dst_symbol);
|
auto* dst_ident = ctx.dst->Expr(dst_symbol);
|
||||||
|
|
||||||
// Insert the constant before the usage
|
// Insert the constant before the usage
|
||||||
ctx.InsertBefore(src_sem_stmt->Block()->statements(), src_stmt,
|
ctx.InsertBefore(src_sem_stmt->Block()->Declaration()->statements(),
|
||||||
dst_var_decl);
|
src_stmt, dst_var_decl);
|
||||||
// Replace the inlined initializer with a reference to the constant
|
// Replace the inlined initializer with a reference to the constant
|
||||||
ctx.Replace(src_init, dst_ident);
|
ctx.Replace(src_init, dst_ident);
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "src/ast/return_statement.h"
|
#include "src/ast/return_statement.h"
|
||||||
#include "src/ast/stage_decoration.h"
|
#include "src/ast/stage_decoration.h"
|
||||||
#include "src/program_builder.h"
|
#include "src/program_builder.h"
|
||||||
|
#include "src/sem/block_statement.h"
|
||||||
#include "src/sem/function.h"
|
#include "src/sem/function.h"
|
||||||
#include "src/sem/statement.h"
|
#include "src/sem/statement.h"
|
||||||
#include "src/sem/struct.h"
|
#include "src/sem/struct.h"
|
||||||
@ -188,7 +189,7 @@ void Spirv::HandleEntryPointIOTypes(CloneContext& ctx) const {
|
|||||||
for (auto* ret : func->ReturnStatements()) {
|
for (auto* ret : func->ReturnStatements()) {
|
||||||
auto* ret_sem = ctx.src->Sem().Get(ret);
|
auto* ret_sem = ctx.src->Sem().Get(ret);
|
||||||
auto* call = ctx.dst->Call(return_func_symbol, ctx.Clone(ret->value()));
|
auto* call = ctx.dst->Call(return_func_symbol, ctx.Clone(ret->value()));
|
||||||
ctx.InsertBefore(ret_sem->Block()->statements(), ret,
|
ctx.InsertBefore(ret_sem->Block()->Declaration()->statements(), ret,
|
||||||
ctx.dst->create<ast::CallStatement>(call));
|
ctx.dst->create<ast::CallStatement>(call));
|
||||||
ctx.Replace(ret, ctx.dst->Return());
|
ctx.Replace(ret, ctx.dst->Return());
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user