sem: Add CompoundStatement

This change introduces sem::CompoundStatement, a new base class for
statements that can hold other statements.

sem::BlockStatements now derives from sem::CompoundStatement, and
this change introduces the following new CompoundStatements:
* `sem::IfStatement`
* `sem::ElseStatement`
* `sem::ForLoopStatement`
* `sem::LoopStatement`
* `sem::SwitchStatement`.
These new CompoundStatements are now inserted into the semantic
tree as now documented in `docs/compound_statements.md`.

The `sem::BlockStatement::FindFirstParent()` methods have been
moved down to `sem::Statement`.

The `Resolver::BlockScope()` method has been replaced with
`Resolver::Scope()` which now maintains the `current_statement_`,
`current_compound_statement_ ` and `current_block_`. This
simplifies statement nesting.

The most significant change in behavior is that statements now
always have a parent, so calling Block() on the initializer or
continuing of a for-loop statement will now return the
BlockStatement that holds the for-loop. Before this would
return nullptr.

Fixed: tint:979
Change-Id: I90e38fd719da2a281ed9210e975ab96171cb6842
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/57707
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: James Price <jrprice@google.com>
This commit is contained in:
Ben Clayton
2021-07-14 09:44:41 +00:00
committed by Tint LUCI CQ
parent 3e27a20f31
commit 6e459fecb7
20 changed files with 1229 additions and 462 deletions

View File

@@ -1,166 +0,0 @@
// 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/resolver/resolver.h"
#include "gmock/gmock.h"
#include "src/resolver/resolver_test_helper.h"
#include "src/sem/block_statement.h"
namespace tint {
namespace resolver {
namespace {
using ResolverBlockTest = ResolverTest;
TEST_F(ResolverBlockTest, FunctionBlock) {
// fn F() {
// var x : 32;
// }
auto* stmt = Decl(Var("x", ty.i32()));
auto* f = Func("F", {}, ty.void_(), {stmt});
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* s = Sem().Get(stmt);
ASSERT_NE(s, nullptr);
ASSERT_NE(s->Block(), nullptr);
ASSERT_TRUE(s->Block()->Is<sem::FunctionBlockStatement>());
EXPECT_EQ(s->Block(), s->Block()->FindFirstParent<sem::BlockStatement>());
EXPECT_EQ(s->Block(),
s->Block()->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_EQ(s->Block()->As<sem::FunctionBlockStatement>()->Function(), f);
EXPECT_EQ(s->Block()->Parent(), nullptr);
}
TEST_F(ResolverBlockTest, Block) {
// fn F() {
// {
// var x : 32;
// }
// }
auto* stmt = Decl(Var("x", ty.i32()));
auto* block = Block(stmt);
auto* f = Func("F", {}, ty.void_(), {block});
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* s = Sem().Get(stmt);
ASSERT_NE(s, nullptr);
ASSERT_NE(s->Block(), nullptr);
EXPECT_EQ(s->Block(), s->Block()->FindFirstParent<sem::BlockStatement>());
EXPECT_EQ(s->Block()->Parent(),
s->Block()->FindFirstParent<sem::FunctionBlockStatement>());
ASSERT_TRUE(s->Block()->Parent()->Is<sem::FunctionBlockStatement>());
EXPECT_EQ(s->Block()->Parent()->As<sem::FunctionBlockStatement>()->Function(),
f);
EXPECT_EQ(s->Block()->Parent()->Parent(), nullptr);
}
TEST_F(ResolverBlockTest, LoopBlock) {
// fn F() {
// loop {
// var x : 32;
// }
// }
auto* stmt = Decl(Var("x", ty.i32()));
auto* loop = Loop(Block(stmt));
auto* f = Func("F", {}, ty.void_(), {loop});
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* s = Sem().Get(stmt);
ASSERT_NE(s, nullptr);
ASSERT_NE(s->Block(), nullptr);
EXPECT_EQ(s->Block(), s->Block()->FindFirstParent<sem::LoopBlockStatement>());
ASSERT_TRUE(Is<sem::FunctionBlockStatement>(s->Block()->Parent()->Parent()));
EXPECT_EQ(s->Block()->Parent()->Parent(),
s->Block()->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_EQ(s->Block()
->Parent()
->Parent()
->As<sem::FunctionBlockStatement>()
->Function(),
f);
EXPECT_EQ(s->Block()->Parent()->Parent()->Parent(), nullptr);
}
TEST_F(ResolverBlockTest, ForLoopBlock) {
// fn F() {
// for (var i : u32; true; i = i + 1u) {
// return;
// }
// }
auto* init = Decl(Var("i", ty.u32()));
auto* cond = Expr(true);
auto* cont = Assign("i", Add("i", 1u));
auto* stmt = Return();
auto* body = Block(stmt);
auto* for_ = For(init, cond, cont, body);
auto* f = Func("F", {}, ty.void_(), {for_});
ASSERT_TRUE(r()->Resolve()) << r()->error();
{
auto* s = Sem().Get(init);
ASSERT_NE(s, nullptr);
ASSERT_NE(s->Block(), nullptr);
EXPECT_EQ(s->Block(),
s->Block()->FindFirstParent<sem::LoopBlockStatement>());
ASSERT_TRUE(
Is<sem::FunctionBlockStatement>(s->Block()->Parent()->Parent()));
}
{ // Condition expression's statement is the for-loop itself
auto* s = Sem().Get(cond);
ASSERT_NE(s, nullptr);
ASSERT_NE(s->Stmt()->Block(), nullptr);
EXPECT_EQ(
s->Stmt()->Block(),
s->Stmt()->Block()->FindFirstParent<sem::FunctionBlockStatement>());
ASSERT_TRUE(Is<sem::FunctionBlockStatement>(s->Stmt()->Block()));
}
{
auto* s = Sem().Get(cont);
ASSERT_NE(s, nullptr);
ASSERT_NE(s->Block(), nullptr);
EXPECT_EQ(s->Block(),
s->Block()->FindFirstParent<sem::LoopBlockStatement>());
ASSERT_TRUE(
Is<sem::FunctionBlockStatement>(s->Block()->Parent()->Parent()));
}
{
auto* s = Sem().Get(stmt);
ASSERT_NE(s, nullptr);
ASSERT_NE(s->Block(), nullptr);
EXPECT_EQ(s->Block(),
s->Block()->FindFirstParent<sem::LoopBlockStatement>());
ASSERT_TRUE(
Is<sem::FunctionBlockStatement>(s->Block()->Parent()->Parent()));
EXPECT_EQ(s->Block()->Parent()->Parent(),
s->Block()->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_EQ(s->Block()
->Parent()
->Parent()
->As<sem::FunctionBlockStatement>()
->Function(),
f);
EXPECT_EQ(s->Block()->Parent()->Parent()->Parent(), nullptr);
}
}
// TODO(bclayton): Add tests for other block types
// (LoopContinuingBlockStatement, SwitchCaseBlockStatement)
} // namespace
} // namespace resolver
} // namespace tint

View File

@@ -0,0 +1,384 @@
// 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/resolver/resolver.h"
#include "gmock/gmock.h"
#include "src/resolver/resolver_test_helper.h"
#include "src/sem/block_statement.h"
#include "src/sem/for_loop_statement.h"
#include "src/sem/if_statement.h"
#include "src/sem/loop_statement.h"
#include "src/sem/switch_statement.h"
namespace tint {
namespace resolver {
namespace {
using ResolverCompoundStatementTest = ResolverTest;
TEST_F(ResolverCompoundStatementTest, FunctionBlock) {
// fn F() {
// var x : 32;
// }
auto* stmt = Decl(Var("x", ty.i32()));
auto* f = Func("F", {}, ty.void_(), {stmt});
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* s = Sem().Get(stmt);
ASSERT_NE(s, nullptr);
ASSERT_NE(s->Block(), nullptr);
ASSERT_TRUE(s->Block()->Is<sem::FunctionBlockStatement>());
EXPECT_EQ(s->Block(), s->FindFirstParent<sem::BlockStatement>());
EXPECT_EQ(s->Block(), s->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_EQ(s->Block()->As<sem::FunctionBlockStatement>()->Function(), f);
EXPECT_EQ(s->Block()->Parent(), nullptr);
}
TEST_F(ResolverCompoundStatementTest, Block) {
// fn F() {
// {
// var x : 32;
// }
// }
auto* stmt = Decl(Var("x", ty.i32()));
auto* block = Block(stmt);
auto* f = Func("F", {}, ty.void_(), {block});
ASSERT_TRUE(r()->Resolve()) << r()->error();
{
auto* s = Sem().Get(block);
ASSERT_NE(s, nullptr);
EXPECT_TRUE(s->Is<sem::BlockStatement>());
EXPECT_EQ(s, s->Block());
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
}
{
auto* s = Sem().Get(stmt);
ASSERT_NE(s, nullptr);
ASSERT_NE(s->Block(), nullptr);
EXPECT_EQ(s->Block(), s->FindFirstParent<sem::BlockStatement>());
EXPECT_EQ(s->Block()->Parent(),
s->FindFirstParent<sem::FunctionBlockStatement>());
ASSERT_TRUE(s->Block()->Parent()->Is<sem::FunctionBlockStatement>());
EXPECT_EQ(
s->Block()->Parent()->As<sem::FunctionBlockStatement>()->Function(), f);
EXPECT_EQ(s->Block()->Parent()->Parent(), nullptr);
}
}
TEST_F(ResolverCompoundStatementTest, Loop) {
// fn F() {
// loop {
// stmt_a;
// continuing {
// stmt_b;
// }
// }
// }
auto* stmt_a = Ignore(1);
auto* stmt_b = Ignore(1);
auto* loop = Loop(Block(stmt_a), Block(stmt_b));
auto* f = Func("F", {}, ty.void_(), {loop});
ASSERT_TRUE(r()->Resolve()) << r()->error();
{
auto* s = Sem().Get(loop);
ASSERT_NE(s, nullptr);
EXPECT_TRUE(s->Is<sem::LoopStatement>());
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_EQ(s->Parent(), s->Block());
}
{
auto* s = Sem().Get(stmt_a);
ASSERT_NE(s, nullptr);
ASSERT_NE(s->Block(), nullptr);
EXPECT_EQ(s->Parent(), s->Block());
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::LoopBlockStatement>());
EXPECT_EQ(s->Parent()->Parent(), s->FindFirstParent<sem::LoopStatement>());
EXPECT_TRUE(Is<sem::LoopStatement>(s->Parent()->Parent()));
EXPECT_EQ(s->Parent()->Parent()->Parent(),
s->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_TRUE(
Is<sem::FunctionBlockStatement>(s->Parent()->Parent()->Parent()));
EXPECT_EQ(s->FindFirstParent<sem::FunctionBlockStatement>()->Function(), f);
EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(), nullptr);
}
{
auto* s = Sem().Get(stmt_b);
ASSERT_NE(s, nullptr);
ASSERT_NE(s->Block(), nullptr);
EXPECT_EQ(s->Parent(), s->Block());
EXPECT_EQ(s->Parent(),
s->FindFirstParent<sem::LoopContinuingBlockStatement>());
EXPECT_TRUE(Is<sem::LoopContinuingBlockStatement>(s->Parent()));
EXPECT_EQ(s->Parent()->Parent(),
s->FindFirstParent<sem::LoopBlockStatement>());
EXPECT_TRUE(Is<sem::LoopBlockStatement>(s->Parent()->Parent()));
EXPECT_EQ(s->Parent()->Parent()->Parent(),
s->FindFirstParent<sem::LoopStatement>());
EXPECT_TRUE(Is<sem::LoopStatement>(s->Parent()->Parent()->Parent()));
EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(),
s->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_TRUE(Is<sem::FunctionBlockStatement>(
s->Parent()->Parent()->Parent()->Parent()));
EXPECT_EQ(s->FindFirstParent<sem::FunctionBlockStatement>()->Function(), f);
EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent()->Parent(), nullptr);
}
}
TEST_F(ResolverCompoundStatementTest, ForLoop) {
// fn F() {
// for (var i : u32; true; i = i + 1u) {
// return;
// }
// }
auto* init = Decl(Var("i", ty.u32()));
auto* cond = Expr(true);
auto* cont = Assign("i", Add("i", 1u));
auto* stmt = Return();
auto* body = Block(stmt);
auto* for_ = For(init, cond, cont, body);
auto* f = Func("F", {}, ty.void_(), {for_});
ASSERT_TRUE(r()->Resolve()) << r()->error();
{
auto* s = Sem().Get(for_);
ASSERT_NE(s, nullptr);
EXPECT_TRUE(s->Is<sem::ForLoopStatement>());
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_EQ(s->Parent(), s->Block());
}
{
auto* s = Sem().Get(init);
ASSERT_NE(s, nullptr);
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::ForLoopStatement>());
EXPECT_TRUE(Is<sem::ForLoopStatement>(s->Parent()));
EXPECT_EQ(s->Block(), s->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_TRUE(Is<sem::FunctionBlockStatement>(s->Parent()->Parent()));
}
{ // Condition expression's statement is the for-loop itself
auto* e = Sem().Get(cond);
ASSERT_NE(e, nullptr);
auto* s = e->Stmt();
ASSERT_NE(s, nullptr);
ASSERT_TRUE(Is<sem::ForLoopStatement>(s));
ASSERT_NE(s->Parent(), nullptr);
EXPECT_EQ(s->Parent(), s->Block());
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_TRUE(Is<sem::FunctionBlockStatement>(s->Block()));
}
{
auto* s = Sem().Get(cont);
ASSERT_NE(s, nullptr);
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::ForLoopStatement>());
EXPECT_TRUE(Is<sem::ForLoopStatement>(s->Parent()));
EXPECT_EQ(s->Block(), s->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_TRUE(Is<sem::FunctionBlockStatement>(s->Parent()->Parent()));
}
{
auto* s = Sem().Get(stmt);
ASSERT_NE(s, nullptr);
ASSERT_NE(s->Block(), nullptr);
EXPECT_EQ(s->Parent(), s->Block());
EXPECT_EQ(s->Block(), s->FindFirstParent<sem::LoopBlockStatement>());
EXPECT_TRUE(Is<sem::ForLoopStatement>(s->Parent()->Parent()));
EXPECT_EQ(s->Block()->Parent(),
s->FindFirstParent<sem::ForLoopStatement>());
ASSERT_TRUE(
Is<sem::FunctionBlockStatement>(s->Block()->Parent()->Parent()));
EXPECT_EQ(s->Block()->Parent()->Parent(),
s->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_EQ(s->Block()
->Parent()
->Parent()
->As<sem::FunctionBlockStatement>()
->Function(),
f);
EXPECT_EQ(s->Block()->Parent()->Parent()->Parent(), nullptr);
}
}
TEST_F(ResolverCompoundStatementTest, If) {
// fn F() {
// if (cond_a) {
// stat_a;
// } elseif (cond_b) {
// stat_b;
// } else {
// stat_c;
// }
// }
auto* cond_a = Expr(true);
auto* stmt_a = Ignore(1);
auto* cond_b = Expr(true);
auto* stmt_b = Ignore(1);
auto* stmt_c = Ignore(1);
auto* if_stmt = If(cond_a, Block(stmt_a), Else(cond_b, Block(stmt_b)),
Else(nullptr, Block(stmt_c)));
WrapInFunction(if_stmt);
ASSERT_TRUE(r()->Resolve()) << r()->error();
{
auto* s = Sem().Get(if_stmt);
ASSERT_NE(s, nullptr);
EXPECT_TRUE(s->Is<sem::IfStatement>());
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_EQ(s->Parent(), s->Block());
}
{
auto* e = Sem().Get(cond_a);
ASSERT_NE(e, nullptr);
auto* s = e->Stmt();
ASSERT_NE(s, nullptr);
EXPECT_TRUE(s->Is<sem::IfStatement>());
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_EQ(s->Parent(), s->Block());
}
{
auto* s = Sem().Get(stmt_a);
ASSERT_NE(s, nullptr);
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::BlockStatement>());
EXPECT_EQ(s->Parent(), s->Block());
EXPECT_EQ(s->Parent()->Parent(), s->FindFirstParent<sem::IfStatement>());
EXPECT_EQ(s->Parent()->Parent()->Parent(),
s->FindFirstParent<sem::FunctionBlockStatement>());
}
{
auto* e = Sem().Get(cond_b);
ASSERT_NE(e, nullptr);
auto* s = e->Stmt();
ASSERT_NE(s, nullptr);
EXPECT_TRUE(s->Is<sem::ElseStatement>());
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::IfStatement>());
EXPECT_EQ(s->Parent()->Parent(),
s->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_EQ(s->Parent()->Parent(), s->Block());
}
{
auto* s = Sem().Get(stmt_b);
ASSERT_NE(s, nullptr);
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::BlockStatement>());
EXPECT_EQ(s->Parent(), s->Block());
EXPECT_EQ(s->Parent()->Parent(), s->FindFirstParent<sem::ElseStatement>());
EXPECT_EQ(s->Parent()->Parent()->Parent(),
s->FindFirstParent<sem::IfStatement>());
EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(),
s->FindFirstParent<sem::FunctionBlockStatement>());
}
{
auto* s = Sem().Get(stmt_c);
ASSERT_NE(s, nullptr);
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::BlockStatement>());
EXPECT_EQ(s->Parent(), s->Block());
EXPECT_EQ(s->Parent()->Parent(), s->FindFirstParent<sem::ElseStatement>());
EXPECT_EQ(s->Parent()->Parent()->Parent(),
s->FindFirstParent<sem::IfStatement>());
EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(),
s->FindFirstParent<sem::FunctionBlockStatement>());
}
}
TEST_F(ResolverCompoundStatementTest, Switch) {
// fn F() {
// switch (expr) {
// case 1: {
// stmt_a;
// }
// case 2: {
// stmt_b;
// }
// default: {
// stmt_c;
// }
// }
// }
auto* expr = Expr(5);
auto* stmt_a = Ignore(1);
auto* stmt_b = Ignore(1);
auto* stmt_c = Ignore(1);
auto* swi =
Switch(expr, Case(Literal(1), Block(stmt_a)),
Case(Literal(2), Block(stmt_b)), DefaultCase(Block(stmt_c)));
WrapInFunction(swi);
ASSERT_TRUE(r()->Resolve()) << r()->error();
{
auto* s = Sem().Get(swi);
ASSERT_NE(s, nullptr);
EXPECT_TRUE(s->Is<sem::SwitchStatement>());
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_EQ(s->Parent(), s->Block());
}
{
auto* e = Sem().Get(expr);
ASSERT_NE(e, nullptr);
auto* s = e->Stmt();
ASSERT_NE(s, nullptr);
EXPECT_TRUE(s->Is<sem::SwitchStatement>());
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
EXPECT_EQ(s->Parent(), s->Block());
}
{
auto* s = Sem().Get(stmt_a);
ASSERT_NE(s, nullptr);
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::SwitchCaseBlockStatement>());
EXPECT_EQ(s->Parent(), s->Block());
EXPECT_EQ(s->Parent()->Parent(),
s->FindFirstParent<sem::SwitchStatement>());
EXPECT_EQ(s->Parent()->Parent()->Parent(),
s->FindFirstParent<sem::FunctionBlockStatement>());
}
{
auto* s = Sem().Get(stmt_b);
ASSERT_NE(s, nullptr);
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::SwitchCaseBlockStatement>());
EXPECT_EQ(s->Parent(), s->Block());
EXPECT_EQ(s->Parent()->Parent(),
s->FindFirstParent<sem::SwitchStatement>());
EXPECT_EQ(s->Parent()->Parent()->Parent(),
s->FindFirstParent<sem::FunctionBlockStatement>());
}
{
auto* s = Sem().Get(stmt_c);
ASSERT_NE(s, nullptr);
EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::SwitchCaseBlockStatement>());
EXPECT_EQ(s->Parent(), s->Block());
EXPECT_EQ(s->Parent()->Parent(),
s->FindFirstParent<sem::SwitchStatement>());
EXPECT_EQ(s->Parent()->Parent()->Parent(),
s->FindFirstParent<sem::FunctionBlockStatement>());
}
}
} // namespace
} // namespace resolver
} // namespace tint

View File

@@ -51,7 +51,10 @@
#include "src/sem/atomic_type.h"
#include "src/sem/call.h"
#include "src/sem/depth_texture_type.h"
#include "src/sem/for_loop_statement.h"
#include "src/sem/function.h"
#include "src/sem/if_statement.h"
#include "src/sem/loop_statement.h"
#include "src/sem/member_accessor_expression.h"
#include "src/sem/multisampled_texture_type.h"
#include "src/sem/pointer_type.h"
@@ -61,6 +64,7 @@
#include "src/sem/statement.h"
#include "src/sem/storage_texture_type.h"
#include "src/sem/struct.h"
#include "src/sem/switch_statement.h"
#include "src/sem/variable.h"
#include "src/utils/defer.h"
#include "src/utils/get_or_create.h"
@@ -1568,16 +1572,14 @@ bool Resolver::Function(ast::Function* func) {
if (func->body()) {
Mark(func->body());
if (current_statement_) {
if (current_compound_statement_) {
TINT_ICE(Resolver, diagnostics_)
<< "Resolver::Function() called with a current statement";
<< "Resolver::Function() called with a current compound statement";
return false;
}
auto* sem_block = builder_->create<sem::FunctionBlockStatement>(func);
builder_->Sem().Add(func->body(), sem_block);
TINT_SCOPED_ASSIGNMENT(current_statement_, sem_block);
if (!BlockScope(func->body(),
[&] { return Statements(func->body()->list()); })) {
if (!Scope(sem_block, [&] { return Statements(func->body()->list()); })) {
return false;
}
}
@@ -1725,17 +1727,11 @@ bool Resolver::ValidateStatements(const ast::StatementList& stmts) {
}
bool Resolver::Statement(ast::Statement* stmt) {
sem::Statement* sem_statement;
if (stmt->As<ast::BlockStatement>()) {
sem_statement = builder_->create<sem::BlockStatement>(
stmt->As<ast::BlockStatement>(), current_statement_);
} else {
sem_statement = builder_->create<sem::Statement>(stmt, current_statement_);
if (stmt->Is<ast::CaseStatement>()) {
AddError("case statement can only be used inside a switch statement",
stmt->source());
return false;
}
builder_->Sem().Add(stmt, sem_statement);
TINT_SCOPED_ASSIGNMENT(current_statement_, sem_statement);
if (stmt->Is<ast::ElseStatement>()) {
TINT_ICE(Resolver, diagnostics_)
<< "Resolver::Statement() encountered an Else statement. Else "
@@ -1744,15 +1740,35 @@ bool Resolver::Statement(ast::Statement* stmt) {
return false;
}
// Compound statements. These create their own sem::CompoundStatement
// bindings.
if (auto* b = stmt->As<ast::BlockStatement>()) {
return BlockStatement(b);
}
if (auto* l = stmt->As<ast::ForLoopStatement>()) {
return ForLoopStatement(l);
}
if (auto* l = stmt->As<ast::LoopStatement>()) {
return LoopStatement(l);
}
if (auto* i = stmt->As<ast::IfStatement>()) {
return IfStatement(i);
}
if (auto* s = stmt->As<ast::SwitchStatement>()) {
return SwitchStatement(s);
}
// Non-Compound statements
sem::Statement* sem_statement =
builder_->create<sem::Statement>(stmt, current_compound_statement_);
builder_->Sem().Add(stmt, sem_statement);
TINT_SCOPED_ASSIGNMENT(current_statement_, sem_statement);
if (auto* a = stmt->As<ast::AssignmentStatement>()) {
return Assignment(a);
}
if (auto* b = stmt->As<ast::BlockStatement>()) {
return BlockScope(b, [&] { return Statements(b->list()); });
}
if (stmt->Is<ast::BreakStatement>()) {
if (!current_block_->FindFirstParent<sem::LoopBlockStatement>() &&
!current_block_->FindFirstParent<sem::SwitchCaseBlockStatement>()) {
if (!sem_statement->FindFirstParent<sem::LoopBlockStatement>() &&
!sem_statement->FindFirstParent<sem::SwitchCaseBlockStatement>()) {
AddError("break statement must be in a loop or switch case",
stmt->source());
return false;
@@ -1769,9 +1785,6 @@ bool Resolver::Statement(ast::Statement* stmt) {
}
return true;
}
if (auto* c = stmt->As<ast::CaseStatement>()) {
return CaseStatement(c);
}
if (stmt->Is<ast::ContinueStatement>()) {
// Set if we've hit the first continue statement in our parent loop
if (auto* loop_block =
@@ -1793,21 +1806,9 @@ bool Resolver::Statement(ast::Statement* stmt) {
if (stmt->Is<ast::FallthroughStatement>()) {
return true;
}
if (auto* i = stmt->As<ast::IfStatement>()) {
return IfStatement(i);
}
if (auto* l = stmt->As<ast::LoopStatement>()) {
return LoopStatement(l);
}
if (auto* l = stmt->As<ast::ForLoopStatement>()) {
return ForLoopStatement(l);
}
if (auto* r = stmt->As<ast::ReturnStatement>()) {
return Return(r);
}
if (auto* s = stmt->As<ast::SwitchStatement>()) {
return Switch(s);
}
if (auto* v = stmt->As<ast::VariableDeclStatement>()) {
return VariableDeclStatement(v);
}
@@ -1819,51 +1820,59 @@ bool Resolver::Statement(ast::Statement* stmt) {
}
bool Resolver::CaseStatement(ast::CaseStatement* stmt) {
auto* sem = builder_->create<sem::SwitchCaseBlockStatement>(
stmt->body(), current_compound_statement_);
builder_->Sem().Add(stmt, sem);
builder_->Sem().Add(stmt->body(), sem);
Mark(stmt->body());
for (auto* sel : stmt->selectors()) {
Mark(sel);
}
auto* sem_block = builder_->create<sem::SwitchCaseBlockStatement>(
stmt->body(), current_statement_);
builder_->Sem().Add(stmt->body(), sem_block);
TINT_SCOPED_ASSIGNMENT(current_statement_, sem_block);
return BlockScope(stmt->body(),
[&] { return Statements(stmt->body()->list()); });
return Scope(sem, [&] { return Statements(stmt->body()->list()); });
}
bool Resolver::IfStatement(ast::IfStatement* stmt) {
Mark(stmt->condition());
if (!Expression(stmt->condition())) {
return false;
}
auto* cond_type = TypeOf(stmt->condition())->UnwrapRef();
if (!cond_type->Is<sem::Bool>()) {
AddError("if statement condition must be bool, got " +
cond_type->FriendlyName(builder_->Symbols()),
stmt->condition()->source());
return false;
}
Mark(stmt->body());
{
auto* sem_block =
builder_->create<sem::BlockStatement>(stmt->body(), current_statement_);
builder_->Sem().Add(stmt->body(), sem_block);
TINT_SCOPED_ASSIGNMENT(current_statement_, sem_block);
if (!BlockScope(stmt->body(),
[&] { return Statements(stmt->body()->list()); })) {
auto* sem =
builder_->create<sem::IfStatement>(stmt, current_compound_statement_);
builder_->Sem().Add(stmt, sem);
return Scope(sem, [&] {
Mark(stmt->condition());
if (!Expression(stmt->condition())) {
return false;
}
}
for (auto* else_stmt : stmt->else_statements()) {
Mark(else_stmt);
auto* sem_else_stmt =
builder_->create<sem::Statement>(else_stmt, current_statement_);
builder_->Sem().Add(else_stmt, sem_else_stmt);
TINT_SCOPED_ASSIGNMENT(current_statement_, sem_else_stmt);
if (auto* cond = else_stmt->condition()) {
auto* cond_type = TypeOf(stmt->condition())->UnwrapRef();
if (!cond_type->Is<sem::Bool>()) {
AddError("if statement condition must be bool, got " +
cond_type->FriendlyName(builder_->Symbols()),
stmt->condition()->source());
return false;
}
Mark(stmt->body());
auto* body = builder_->create<sem::BlockStatement>(
stmt->body(), current_compound_statement_);
builder_->Sem().Add(stmt->body(), body);
if (!Scope(body, [&] { return Statements(stmt->body()->list()); })) {
return false;
}
for (auto* else_stmt : stmt->else_statements()) {
Mark(else_stmt);
if (!ElseStatement(else_stmt)) {
return false;
}
}
return true;
});
}
bool Resolver::ElseStatement(ast::ElseStatement* stmt) {
auto* sem =
builder_->create<sem::ElseStatement>(stmt, current_compound_statement_);
builder_->Sem().Add(stmt, sem);
return Scope(sem, [&] {
if (auto* cond = stmt->condition()) {
Mark(cond);
if (!Expression(cond)) {
return false;
@@ -1877,95 +1886,93 @@ bool Resolver::IfStatement(ast::IfStatement* stmt) {
return false;
}
}
Mark(else_stmt->body());
{
auto* sem_block = builder_->create<sem::BlockStatement>(
else_stmt->body(), current_statement_);
builder_->Sem().Add(else_stmt->body(), sem_block);
TINT_SCOPED_ASSIGNMENT(current_statement_, sem_block);
if (!BlockScope(else_stmt->body(),
[&] { return Statements(else_stmt->body()->list()); })) {
return false;
}
}
}
return true;
Mark(stmt->body());
auto* body = builder_->create<sem::BlockStatement>(
stmt->body(), current_compound_statement_);
builder_->Sem().Add(stmt->body(), body);
return Scope(body, [&] { return Statements(stmt->body()->list()); });
});
}
bool Resolver::BlockStatement(ast::BlockStatement* stmt) {
auto* sem = builder_->create<sem::BlockStatement>(
stmt->As<ast::BlockStatement>(), current_compound_statement_);
builder_->Sem().Add(stmt, sem);
return Scope(sem, [&] { return Statements(stmt->list()); });
}
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 =
builder_->create<sem::LoopStatement>(stmt, current_compound_statement_);
builder_->Sem().Add(stmt, sem);
return Scope(sem, [&] {
Mark(stmt->body());
auto* sem_block_body = builder_->create<sem::LoopBlockStatement>(
stmt->body(), current_statement_);
builder_->Sem().Add(stmt->body(), sem_block_body);
TINT_SCOPED_ASSIGNMENT(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::LoopContinuingBlockStatement>(
stmt->continuing(), current_statement_);
builder_->Sem().Add(stmt->continuing(), sem_block_continuing);
TINT_SCOPED_ASSIGNMENT(current_statement_, sem_block_continuing);
if (!BlockScope(stmt->continuing(),
[&] { return Statements(stmt->continuing()->list()); })) {
auto* body = builder_->create<sem::LoopBlockStatement>(
stmt->body(), current_compound_statement_);
builder_->Sem().Add(stmt->body(), body);
return Scope(body, [&] {
if (!Statements(stmt->body()->list())) {
return false;
}
}
return true;
if (stmt->continuing()) { // has_continuing() also checks for empty()
Mark(stmt->continuing());
}
if (stmt->has_continuing()) {
auto* continuing = builder_->create<sem::LoopContinuingBlockStatement>(
stmt->continuing(), current_compound_statement_);
builder_->Sem().Add(stmt->continuing(), continuing);
if (!Scope(continuing,
[&] { return Statements(stmt->continuing()->list()); })) {
return false;
}
}
return true;
});
});
}
bool Resolver::ForLoopStatement(ast::ForLoopStatement* stmt) {
Mark(stmt->body());
auto* sem_block_body = builder_->create<sem::LoopBlockStatement>(
stmt->body(), current_statement_);
builder_->Sem().Add(stmt->body(), sem_block_body);
TINT_SCOPED_ASSIGNMENT(current_statement_, sem_block_body);
TINT_SCOPED_ASSIGNMENT(current_block_, sem_block_body);
variable_stack_.push_scope();
TINT_DEFER(variable_stack_.pop_scope());
if (auto* initializer = stmt->initializer()) {
Mark(initializer);
if (!Statement(initializer)) {
return false;
}
}
if (auto* condition = stmt->condition()) {
Mark(condition);
if (!Expression(condition)) {
return false;
auto* sem = builder_->create<sem::ForLoopStatement>(
stmt, current_compound_statement_);
builder_->Sem().Add(stmt, sem);
return Scope(sem, [&] {
if (auto* initializer = stmt->initializer()) {
Mark(initializer);
if (!Statement(initializer)) {
return false;
}
}
if (!TypeOf(condition)->Is<sem::Bool>()) {
AddError("for-loop condition must be bool, got " + TypeNameOf(condition),
condition->source());
return false;
}
}
if (auto* condition = stmt->condition()) {
Mark(condition);
if (!Expression(condition)) {
return false;
}
if (auto* continuing = stmt->continuing()) {
Mark(continuing);
if (!Statement(continuing)) {
return false;
if (!TypeOf(condition)->Is<sem::Bool>()) {
AddError(
"for-loop condition must be bool, got " + TypeNameOf(condition),
condition->source());
return false;
}
}
}
return BlockScope(stmt->body(),
[&] { return Statements(stmt->body()->list()); });
if (auto* continuing = stmt->continuing()) {
Mark(continuing);
if (!Statement(continuing)) {
return false;
}
}
Mark(stmt->body());
auto* body = builder_->create<sem::LoopBlockStatement>(
stmt->body(), current_compound_statement_);
builder_->Sem().Add(stmt->body(), body);
return Scope(body, [&] { return Statements(stmt->body()->statements()); });
});
}
bool Resolver::Expressions(const ast::ExpressionList& list) {
@@ -3036,7 +3043,9 @@ bool Resolver::VariableDeclStatement(const ast::VariableDeclStatement* stmt) {
}
variable_stack_.set(var->symbol(), info);
current_block_->AddDecl(var);
if (current_block_) { // Not all statements are inside a block
current_block_->AddDecl(var);
}
if (!ValidateVariable(info)) {
return false;
@@ -3858,26 +3867,26 @@ bool Resolver::ValidateSwitch(const ast::SwitchStatement* s) {
return true;
}
bool Resolver::Switch(ast::SwitchStatement* s) {
Mark(s->condition());
if (!Expression(s->condition())) {
return false;
}
for (auto* case_stmt : s->body()) {
Mark(case_stmt);
sem::Statement* sem_statement =
builder_->create<sem::Statement>(case_stmt, current_statement_);
builder_->Sem().Add(case_stmt, sem_statement);
TINT_SCOPED_ASSIGNMENT(current_statement_, sem_statement);
if (!CaseStatement(case_stmt)) {
bool Resolver::SwitchStatement(ast::SwitchStatement* stmt) {
auto* sem =
builder_->create<sem::SwitchStatement>(stmt, current_compound_statement_);
builder_->Sem().Add(stmt, sem);
return Scope(sem, [&] {
Mark(stmt->condition());
if (!Expression(stmt->condition())) {
return false;
}
}
if (!ValidateSwitch(s)) {
return false;
}
return true;
for (auto* case_stmt : stmt->body()) {
Mark(case_stmt);
if (!CaseStatement(case_stmt)) {
return false;
}
}
if (!ValidateSwitch(stmt)) {
return false;
}
return true;
});
}
bool Resolver::Assignment(ast::AssignmentStatement* a) {
@@ -4032,18 +4041,22 @@ bool Resolver::ApplyStorageClassUsageToType(ast::StorageClass sc,
}
template <typename F>
bool Resolver::BlockScope(const ast::BlockStatement* block, F&& callback) {
auto* sem_block = builder_->Sem().Get<sem::BlockStatement>(block);
if (!sem_block) {
TINT_ICE(Resolver, diagnostics_)
<< "Resolver::BlockScope() called on a block for "
"which semantic information is not available";
return false;
}
TINT_SCOPED_ASSIGNMENT(current_block_,
const_cast<sem::BlockStatement*>(sem_block));
bool Resolver::Scope(sem::CompoundStatement* stmt, F&& callback) {
auto* prev_current_statement = current_statement_;
auto* prev_current_compound_statement = current_compound_statement_;
auto* prev_current_block = current_block_;
current_statement_ = stmt;
current_compound_statement_ = stmt;
current_block_ = stmt->As<sem::BlockStatement>();
variable_stack_.push_scope();
TINT_DEFER(variable_stack_.pop_scope());
TINT_DEFER({
TINT_DEFER(variable_stack_.pop_scope());
current_block_ = prev_current_block;
current_compound_statement_ = prev_current_compound_statement;
current_statement_ = prev_current_statement;
});
return callback();
}

View File

@@ -242,9 +242,11 @@ class Resolver {
bool Assignment(ast::AssignmentStatement* a);
bool Binary(ast::BinaryExpression*);
bool Bitcast(ast::BitcastExpression*);
bool BlockStatement(ast::BlockStatement*);
bool Call(ast::CallExpression*);
bool CaseStatement(ast::CaseStatement*);
bool Constructor(ast::ConstructorExpression*);
bool ElseStatement(ast::ElseStatement*);
bool Expression(ast::Expression*);
bool Expressions(const ast::ExpressionList&);
bool ForLoopStatement(ast::ForLoopStatement*);
@@ -260,7 +262,7 @@ class Resolver {
bool Return(ast::ReturnStatement* ret);
bool Statement(ast::Statement*);
bool Statements(const ast::StatementList&);
bool Switch(ast::SwitchStatement* s);
bool SwitchStatement(ast::SwitchStatement* s);
bool UnaryOp(ast::UnaryOpExpression*);
bool VariableDeclStatement(const ast::VariableDeclStatement*);
@@ -394,11 +396,14 @@ class Resolver {
const sem::Type* type,
std::string type_name = "");
/// Constructs a new semantic BlockStatement with the given type and with
/// #current_block_ as its parent, assigns this to #current_block_, and then
/// calls `callback`. The original #current_block_ is restored on exit.
/// Assigns `stmt` to #current_statement_, #current_compound_statement_, and
/// possibly #current_block_, pushes the variable scope, then calls
/// `callback`. Before returning #current_statement_,
/// #current_compound_statement_, and #current_block_ are restored to their
/// original values, and the variable scope is popped.
/// @returns the value returned by callback
template <typename F>
bool BlockScope(const ast::BlockStatement* block, F&& callback);
bool Scope(sem::CompoundStatement* stmt, F&& callback);
/// Returns a human-readable string representation of the vector type name
/// with the given parameters.
@@ -449,7 +454,6 @@ class Resolver {
ProgramBuilder* const builder_;
diag::List& diagnostics_;
std::unique_ptr<IntrinsicTable> const intrinsic_table_;
sem::BlockStatement* current_block_ = nullptr;
ScopeStack<VariableInfo*> variable_stack_;
std::unordered_map<Symbol, FunctionInfo*> symbol_to_function_;
std::vector<FunctionInfo*> entry_points_;
@@ -466,6 +470,8 @@ class Resolver {
FunctionInfo* current_function_ = nullptr;
sem::Statement* current_statement_ = nullptr;
sem::CompoundStatement* current_compound_statement_ = nullptr;
sem::BlockStatement* current_block_ = nullptr;
BlockAllocator<VariableInfo> variable_infos_;
BlockAllocator<FunctionInfo> function_infos_;
};