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:
parent
3e27a20f31
commit
6e459fecb7
|
@ -0,0 +1,115 @@
|
|||
# Compound Statements
|
||||
|
||||
Compound statements are statements that can hold other statements.
|
||||
|
||||
This document maps the WGSL compound statements to their semantic tree representations.
|
||||
|
||||
## if statement
|
||||
|
||||
WGSL:
|
||||
```
|
||||
if (condition_a) {
|
||||
statement_a;
|
||||
} else if (condition_b) {
|
||||
statement_b;
|
||||
} else {
|
||||
statement_c;
|
||||
}
|
||||
```
|
||||
|
||||
Semantic tree:
|
||||
```
|
||||
sem::IfStatement {
|
||||
condition_a
|
||||
sem::BlockStatement {
|
||||
statement_a
|
||||
}
|
||||
sem::ElseStatement {
|
||||
condition_b
|
||||
sem::BlockStatement {
|
||||
statement_b
|
||||
}
|
||||
}
|
||||
sem::ElseStatement {
|
||||
sem::BlockStatement {
|
||||
statement_c
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## for loop
|
||||
|
||||
WGSL:
|
||||
```
|
||||
for (initializer; condition; continuing) {
|
||||
statement;
|
||||
}
|
||||
```
|
||||
|
||||
Semantic tree:
|
||||
```
|
||||
sem::ForLoopStatement {
|
||||
sem::Statement initializer
|
||||
sem::Expression condition
|
||||
sem::Statement continuing
|
||||
|
||||
sem::LoopBlockStatement {
|
||||
sem::Statement statement
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## loop
|
||||
|
||||
WGSL:
|
||||
```
|
||||
loop (condition) {
|
||||
statement_a;
|
||||
continuing {
|
||||
statement_b;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Semantic tree:
|
||||
```
|
||||
sem::LoopStatement {
|
||||
sem::Expression condition
|
||||
|
||||
sem::LoopBlockStatement {
|
||||
sem::Statement statement_a
|
||||
sem::LoopContinuingBlockStatement {
|
||||
sem::Statement statement_b
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## switch statement
|
||||
|
||||
WGSL:
|
||||
```
|
||||
switch (condition) {
|
||||
case literal_a, literal_b: {
|
||||
statement_a;
|
||||
}
|
||||
default {
|
||||
statement_b;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Semantic tree:
|
||||
```
|
||||
sem::SwitchStatement {
|
||||
sem::Expression condition
|
||||
sem::SwitchCaseBlockStatement {
|
||||
sem::Statement statement_a
|
||||
}
|
||||
sem::SwitchCaseBlockStatement {
|
||||
sem::Statement statement_b
|
||||
}
|
||||
}
|
||||
```
|
88
src/BUILD.gn
88
src/BUILD.gn
|
@ -497,58 +497,40 @@ libtint_source_set("libtint_core_all_src") {
|
|||
"resolver/resolver.h",
|
||||
"scope_stack.h",
|
||||
"sem/array.h",
|
||||
"sem/atomic_type.cc",
|
||||
"sem/atomic_type.h",
|
||||
"sem/binding_point.h",
|
||||
"sem/bool_type.cc",
|
||||
"sem/bool_type.h",
|
||||
"sem/call.h",
|
||||
"sem/call_target.h",
|
||||
"sem/constant.cc",
|
||||
"sem/constant.h",
|
||||
"sem/depth_texture_type.cc",
|
||||
"sem/depth_texture_type.h",
|
||||
"sem/expression.h",
|
||||
"sem/external_texture_type.cc",
|
||||
"sem/external_texture_type.h",
|
||||
"sem/f32_type.cc",
|
||||
"sem/f32_type.h",
|
||||
"sem/i32_type.cc",
|
||||
"sem/for_loop_statement.h",
|
||||
"sem/i32_type.h",
|
||||
"sem/if_statement.h",
|
||||
"sem/info.h",
|
||||
"sem/intrinsic.h",
|
||||
"sem/intrinsic_type.cc",
|
||||
"sem/intrinsic_type.h",
|
||||
"sem/matrix_type.cc",
|
||||
"sem/loop_statement.h",
|
||||
"sem/matrix_type.h",
|
||||
"sem/multisampled_texture_type.cc",
|
||||
"sem/multisampled_texture_type.h",
|
||||
"sem/node.h",
|
||||
"sem/parameter_usage.cc",
|
||||
"sem/parameter_usage.h",
|
||||
"sem/pipeline_stage_set.h",
|
||||
"sem/pointer_type.cc",
|
||||
"sem/pointer_type.h",
|
||||
"sem/reference_type.cc",
|
||||
"sem/reference_type.h",
|
||||
"sem/sampled_texture_type.cc",
|
||||
"sem/sampled_texture_type.h",
|
||||
"sem/sampler_type.cc",
|
||||
"sem/sampler_type.h",
|
||||
"sem/storage_texture_type.cc",
|
||||
"sem/storage_texture_type.h",
|
||||
"sem/texture_type.cc",
|
||||
"sem/switch_statement.h",
|
||||
"sem/texture_type.h",
|
||||
"sem/type.cc",
|
||||
"sem/type.h",
|
||||
"sem/type_manager.cc",
|
||||
"sem/type_manager.h",
|
||||
"sem/type_mappings.h",
|
||||
"sem/u32_type.cc",
|
||||
"sem/u32_type.h",
|
||||
"sem/vector_type.cc",
|
||||
"sem/vector_type.h",
|
||||
"sem/void_type.cc",
|
||||
"sem/void_type.h",
|
||||
"source.cc",
|
||||
"source.h",
|
||||
|
@ -633,18 +615,80 @@ libtint_source_set("libtint_core_all_src") {
|
|||
libtint_source_set("libtint_sem_src") {
|
||||
sources = [
|
||||
"sem/array.cc",
|
||||
"sem/array.h",
|
||||
"sem/atomic_type.cc",
|
||||
"sem/atomic_type.h",
|
||||
"sem/binding_point.h",
|
||||
"sem/block_statement.cc",
|
||||
"sem/bool_type.cc",
|
||||
"sem/bool_type.h",
|
||||
"sem/call.cc",
|
||||
"sem/call.h",
|
||||
"sem/call_target.cc",
|
||||
"sem/call_target.h",
|
||||
"sem/constant.cc",
|
||||
"sem/constant.h",
|
||||
"sem/depth_texture_type.cc",
|
||||
"sem/depth_texture_type.h",
|
||||
"sem/expression.cc",
|
||||
"sem/expression.h",
|
||||
"sem/external_texture_type.cc",
|
||||
"sem/external_texture_type.h",
|
||||
"sem/f32_type.cc",
|
||||
"sem/f32_type.h",
|
||||
"sem/for_loop_statement.cc",
|
||||
"sem/for_loop_statement.h",
|
||||
"sem/function.cc",
|
||||
"sem/i32_type.cc",
|
||||
"sem/i32_type.h",
|
||||
"sem/if_statement.cc",
|
||||
"sem/if_statement.h",
|
||||
"sem/info.cc",
|
||||
"sem/info.h",
|
||||
"sem/intrinsic.cc",
|
||||
"sem/intrinsic.h",
|
||||
"sem/intrinsic_type.cc",
|
||||
"sem/intrinsic_type.h",
|
||||
"sem/loop_statement.cc",
|
||||
"sem/loop_statement.h",
|
||||
"sem/matrix_type.cc",
|
||||
"sem/matrix_type.h",
|
||||
"sem/member_accessor_expression.cc",
|
||||
"sem/multisampled_texture_type.cc",
|
||||
"sem/multisampled_texture_type.h",
|
||||
"sem/node.cc",
|
||||
"sem/node.h",
|
||||
"sem/parameter_usage.cc",
|
||||
"sem/parameter_usage.h",
|
||||
"sem/pipeline_stage_set.h",
|
||||
"sem/pointer_type.cc",
|
||||
"sem/pointer_type.h",
|
||||
"sem/reference_type.cc",
|
||||
"sem/reference_type.h",
|
||||
"sem/sampled_texture_type.cc",
|
||||
"sem/sampled_texture_type.h",
|
||||
"sem/sampler_type.cc",
|
||||
"sem/sampler_type.h",
|
||||
"sem/statement.cc",
|
||||
"sem/storage_texture_type.cc",
|
||||
"sem/storage_texture_type.h",
|
||||
"sem/struct.cc",
|
||||
"sem/switch_statement.cc",
|
||||
"sem/switch_statement.h",
|
||||
"sem/texture_type.cc",
|
||||
"sem/texture_type.h",
|
||||
"sem/type.cc",
|
||||
"sem/type.h",
|
||||
"sem/type_manager.cc",
|
||||
"sem/type_manager.h",
|
||||
"sem/type_mappings.h",
|
||||
"sem/u32_type.cc",
|
||||
"sem/u32_type.h",
|
||||
"sem/variable.cc",
|
||||
"sem/vector_type.cc",
|
||||
"sem/vector_type.h",
|
||||
"sem/void_type.cc",
|
||||
"sem/void_type.h",
|
||||
]
|
||||
|
||||
public_deps = [ ":libtint_core_all_src" ]
|
||||
|
|
|
@ -336,8 +336,14 @@ set(TINT_LIB_SRCS
|
|||
sem/external_texture_type.h
|
||||
sem/f32_type.cc
|
||||
sem/f32_type.h
|
||||
sem/for_loop_statement.cc
|
||||
sem/for_loop_statement.h
|
||||
sem/i32_type.cc
|
||||
sem/i32_type.h
|
||||
sem/if_statement.cc
|
||||
sem/if_statement.h
|
||||
sem/loop_statement.cc
|
||||
sem/loop_statement.h
|
||||
sem/matrix_type.cc
|
||||
sem/matrix_type.h
|
||||
sem/multisampled_texture_type.cc
|
||||
|
@ -352,6 +358,8 @@ set(TINT_LIB_SRCS
|
|||
sem/sampler_type.h
|
||||
sem/storage_texture_type.cc
|
||||
sem/storage_texture_type.h
|
||||
sem/switch_statement.cc
|
||||
sem/switch_statement.h
|
||||
sem/texture_type.cc
|
||||
sem/texture_type.h
|
||||
sem/type.cc
|
||||
|
@ -622,10 +630,10 @@ if(${TINT_BUILD_TESTS})
|
|||
resolver/assignment_validation_test.cc
|
||||
resolver/atomics_test.cc
|
||||
resolver/atomics_validation_test.cc
|
||||
resolver/block_test.cc
|
||||
resolver/builtins_validation_test.cc
|
||||
resolver/call_test.cc
|
||||
resolver/call_validation_test.cc
|
||||
resolver/compound_statement_test.cc
|
||||
resolver/control_block_validation_test.cc
|
||||
resolver/decoration_validation_test.cc
|
||||
resolver/entry_point_validation_test.cc
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
|
|
@ -21,14 +21,12 @@
|
|||
TINT_INSTANTIATE_TYPEINFO(tint::sem::BlockStatement);
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::FunctionBlockStatement);
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::LoopBlockStatement);
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::LoopContinuingBlockStatement);
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::SwitchCaseBlockStatement);
|
||||
|
||||
namespace tint {
|
||||
namespace sem {
|
||||
|
||||
BlockStatement::BlockStatement(const ast::BlockStatement* declaration,
|
||||
const Statement* parent)
|
||||
const CompoundStatement* parent)
|
||||
: Base(declaration, parent) {}
|
||||
|
||||
BlockStatement::~BlockStatement() = default;
|
||||
|
@ -47,25 +45,15 @@ FunctionBlockStatement::FunctionBlockStatement(const ast::Function* function)
|
|||
FunctionBlockStatement::~FunctionBlockStatement() = default;
|
||||
|
||||
LoopBlockStatement::LoopBlockStatement(const ast::BlockStatement* declaration,
|
||||
const Statement* parent)
|
||||
: Base(declaration, parent) {}
|
||||
const CompoundStatement* parent)
|
||||
: Base(declaration, parent) {
|
||||
TINT_ASSERT(Semantic, parent);
|
||||
}
|
||||
LoopBlockStatement::~LoopBlockStatement() = default;
|
||||
|
||||
void LoopBlockStatement::SetFirstContinue(size_t first_continue) {
|
||||
first_continue_ = first_continue;
|
||||
}
|
||||
|
||||
LoopContinuingBlockStatement::LoopContinuingBlockStatement(
|
||||
const ast::BlockStatement* declaration,
|
||||
const Statement* parent)
|
||||
: Base(declaration, parent) {}
|
||||
LoopContinuingBlockStatement::~LoopContinuingBlockStatement() = default;
|
||||
|
||||
SwitchCaseBlockStatement::SwitchCaseBlockStatement(
|
||||
const ast::BlockStatement* declaration,
|
||||
const Statement* parent)
|
||||
: Base(declaration, parent) {}
|
||||
SwitchCaseBlockStatement::~SwitchCaseBlockStatement() = default;
|
||||
|
||||
} // namespace sem
|
||||
} // namespace tint
|
||||
|
|
|
@ -34,13 +34,13 @@ namespace sem {
|
|||
|
||||
/// Holds semantic information about a block, such as parent block and variables
|
||||
/// declared in the block.
|
||||
class BlockStatement : public Castable<BlockStatement, Statement> {
|
||||
class BlockStatement : public Castable<BlockStatement, CompoundStatement> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param declaration the AST node for this block statement
|
||||
/// @param parent the owning statement
|
||||
BlockStatement(const ast::BlockStatement* declaration,
|
||||
const Statement* parent);
|
||||
const CompoundStatement* parent);
|
||||
|
||||
/// Destructor
|
||||
~BlockStatement() override;
|
||||
|
@ -49,33 +49,6 @@ class BlockStatement : public Castable<BlockStatement, Statement> {
|
|||
/// 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 statement itself if it matches the template type `T`,
|
||||
/// otherwise the nearest enclosing block that matches `T`, or nullptr if
|
||||
/// there is none.
|
||||
template <typename T>
|
||||
const T* FindFirstParent() const {
|
||||
const BlockStatement* curr = this;
|
||||
while (curr) {
|
||||
if (auto* block = curr->As<T>()) {
|
||||
return block;
|
||||
}
|
||||
curr = curr->Block();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// @returns the declarations associated with this block
|
||||
const std::vector<const ast::Variable*>& Decls() const { return decls_; }
|
||||
|
||||
|
@ -105,14 +78,14 @@ class FunctionBlockStatement
|
|||
ast::Function const* const function_;
|
||||
};
|
||||
|
||||
/// Holds semantic information about a loop block or a for-loop block
|
||||
/// Holds semantic information about a loop body block or for-loop body block
|
||||
class LoopBlockStatement : public Castable<LoopBlockStatement, BlockStatement> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param declaration the AST node for this block statement
|
||||
/// @param parent the owning statement
|
||||
LoopBlockStatement(const ast::BlockStatement* declaration,
|
||||
const Statement* parent);
|
||||
const CompoundStatement* parent);
|
||||
|
||||
/// Destructor
|
||||
~LoopBlockStatement() override;
|
||||
|
@ -134,34 +107,6 @@ class LoopBlockStatement : public Castable<LoopBlockStatement, BlockStatement> {
|
|||
size_t first_continue_ = kNoContinue;
|
||||
};
|
||||
|
||||
/// Holds semantic information about a loop continuing block
|
||||
class LoopContinuingBlockStatement
|
||||
: public Castable<LoopContinuingBlockStatement, BlockStatement> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param declaration the AST node for this block statement
|
||||
/// @param parent the owning statement
|
||||
LoopContinuingBlockStatement(const ast::BlockStatement* declaration,
|
||||
const Statement* parent);
|
||||
|
||||
/// Destructor
|
||||
~LoopContinuingBlockStatement() override;
|
||||
};
|
||||
|
||||
/// Holds semantic information about a switch case block
|
||||
class SwitchCaseBlockStatement
|
||||
: public Castable<SwitchCaseBlockStatement, BlockStatement> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param declaration the AST node for this block statement
|
||||
/// @param parent the owning statement
|
||||
SwitchCaseBlockStatement(const ast::BlockStatement* declaration,
|
||||
const Statement* parent);
|
||||
|
||||
/// Destructor
|
||||
~SwitchCaseBlockStatement() override;
|
||||
};
|
||||
|
||||
} // namespace sem
|
||||
} // namespace tint
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
// 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/for_loop_statement.h"
|
||||
|
||||
#include "src/program_builder.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::ForLoopStatement);
|
||||
|
||||
namespace tint {
|
||||
namespace sem {
|
||||
|
||||
ForLoopStatement::ForLoopStatement(const ast::ForLoopStatement* declaration,
|
||||
CompoundStatement* parent)
|
||||
: Base(declaration, parent) {}
|
||||
|
||||
ForLoopStatement::~ForLoopStatement() = default;
|
||||
|
||||
} // namespace sem
|
||||
} // namespace tint
|
|
@ -0,0 +1,45 @@
|
|||
// 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_FOR_LOOP_STATEMENT_H_
|
||||
#define SRC_SEM_FOR_LOOP_STATEMENT_H_
|
||||
|
||||
#include "src/sem/statement.h"
|
||||
|
||||
namespace tint {
|
||||
namespace ast {
|
||||
class ForLoopStatement;
|
||||
} // namespace ast
|
||||
} // namespace tint
|
||||
|
||||
namespace tint {
|
||||
namespace sem {
|
||||
|
||||
/// Holds semantic information about a for-loop statement
|
||||
class ForLoopStatement : public Castable<ForLoopStatement, CompoundStatement> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param declaration the AST node for this for-loop statement
|
||||
/// @param parent the owning statement
|
||||
ForLoopStatement(const ast::ForLoopStatement* declaration,
|
||||
CompoundStatement* parent);
|
||||
|
||||
/// Destructor
|
||||
~ForLoopStatement() override;
|
||||
};
|
||||
|
||||
} // namespace sem
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_SEM_FOR_LOOP_STATEMENT_H_
|
|
@ -0,0 +1,38 @@
|
|||
// 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/if_statement.h"
|
||||
|
||||
#include "src/program_builder.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::IfStatement);
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::ElseStatement);
|
||||
|
||||
namespace tint {
|
||||
namespace sem {
|
||||
|
||||
IfStatement::IfStatement(const ast::IfStatement* declaration,
|
||||
CompoundStatement* parent)
|
||||
: Base(declaration, parent) {}
|
||||
|
||||
IfStatement::~IfStatement() = default;
|
||||
|
||||
ElseStatement::ElseStatement(const ast::ElseStatement* declaration,
|
||||
CompoundStatement* parent)
|
||||
: Base(declaration, parent) {}
|
||||
|
||||
ElseStatement::~ElseStatement() = default;
|
||||
|
||||
} // namespace sem
|
||||
} // namespace tint
|
|
@ -0,0 +1,59 @@
|
|||
// 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_IF_STATEMENT_H_
|
||||
#define SRC_SEM_IF_STATEMENT_H_
|
||||
|
||||
#include "src/sem/statement.h"
|
||||
|
||||
// Forward declarations
|
||||
namespace tint {
|
||||
namespace ast {
|
||||
class IfStatement;
|
||||
class ElseStatement;
|
||||
} // namespace ast
|
||||
} // namespace tint
|
||||
|
||||
namespace tint {
|
||||
namespace sem {
|
||||
|
||||
/// Holds semantic information about an if statement
|
||||
class IfStatement : public Castable<IfStatement, CompoundStatement> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param declaration the AST node for this if statement
|
||||
/// @param parent the owning statement
|
||||
IfStatement(const ast::IfStatement* declaration, CompoundStatement* parent);
|
||||
|
||||
/// Destructor
|
||||
~IfStatement() override;
|
||||
};
|
||||
|
||||
/// Holds semantic information about an else statement
|
||||
class ElseStatement : public Castable<ElseStatement, CompoundStatement> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param declaration the AST node for this else statement
|
||||
/// @param parent the owning statement
|
||||
ElseStatement(const ast::ElseStatement* declaration,
|
||||
CompoundStatement* parent);
|
||||
|
||||
/// Destructor
|
||||
~ElseStatement() override;
|
||||
};
|
||||
|
||||
} // namespace sem
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_SEM_IF_STATEMENT_H_
|
|
@ -0,0 +1,40 @@
|
|||
// 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/loop_statement.h"
|
||||
|
||||
#include "src/program_builder.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::LoopStatement);
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::LoopContinuingBlockStatement);
|
||||
|
||||
namespace tint {
|
||||
namespace sem {
|
||||
|
||||
LoopStatement::LoopStatement(const ast::LoopStatement* declaration,
|
||||
CompoundStatement* parent)
|
||||
: Base(declaration, parent) {}
|
||||
|
||||
LoopStatement::~LoopStatement() = default;
|
||||
|
||||
LoopContinuingBlockStatement::LoopContinuingBlockStatement(
|
||||
const ast::BlockStatement* declaration,
|
||||
const CompoundStatement* parent)
|
||||
: Base(declaration, parent) {
|
||||
TINT_ASSERT(Semantic, parent);
|
||||
}
|
||||
LoopContinuingBlockStatement::~LoopContinuingBlockStatement() = default;
|
||||
|
||||
} // namespace sem
|
||||
} // namespace tint
|
|
@ -0,0 +1,60 @@
|
|||
// 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_LOOP_STATEMENT_H_
|
||||
#define SRC_SEM_LOOP_STATEMENT_H_
|
||||
|
||||
#include "src/sem/block_statement.h"
|
||||
|
||||
// Forward declarations
|
||||
namespace tint {
|
||||
namespace ast {
|
||||
class LoopStatement;
|
||||
} // namespace ast
|
||||
} // namespace tint
|
||||
|
||||
namespace tint {
|
||||
namespace sem {
|
||||
|
||||
/// Holds semantic information about a loop statement
|
||||
class LoopStatement : public Castable<LoopStatement, CompoundStatement> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param declaration the AST node for this loop statement
|
||||
/// @param parent the owning statement
|
||||
LoopStatement(const ast::LoopStatement* declaration,
|
||||
CompoundStatement* parent);
|
||||
|
||||
/// Destructor
|
||||
~LoopStatement() override;
|
||||
};
|
||||
|
||||
/// Holds semantic information about a loop continuing block
|
||||
class LoopContinuingBlockStatement
|
||||
: public Castable<LoopContinuingBlockStatement, BlockStatement> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param declaration the AST node for this block statement
|
||||
/// @param parent the owning statement
|
||||
LoopContinuingBlockStatement(const ast::BlockStatement* declaration,
|
||||
const CompoundStatement* parent);
|
||||
|
||||
/// Destructor
|
||||
~LoopContinuingBlockStatement() override;
|
||||
};
|
||||
|
||||
} // namespace sem
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_SEM_LOOP_STATEMENT_H_
|
|
@ -21,32 +21,31 @@
|
|||
#include "src/sem/statement.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::Statement);
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::CompoundStatement);
|
||||
|
||||
namespace tint {
|
||||
namespace sem {
|
||||
|
||||
Statement::Statement(const ast::Statement* declaration, const Statement* parent)
|
||||
Statement::Statement(const ast::Statement* declaration,
|
||||
const CompoundStatement* parent)
|
||||
: declaration_(declaration), parent_(parent) {}
|
||||
|
||||
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;
|
||||
return FindFirstParent<BlockStatement>();
|
||||
}
|
||||
|
||||
const ast::Function* Statement::Function() const {
|
||||
if (auto* block = Block()) {
|
||||
if (auto* fbs = block->FindFirstParent<FunctionBlockStatement>()) {
|
||||
return fbs->Function();
|
||||
}
|
||||
if (auto* fbs = FindFirstParent<FunctionBlockStatement>()) {
|
||||
return fbs->Function();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CompoundStatement::CompoundStatement(const ast::Statement* declaration,
|
||||
const CompoundStatement* parent)
|
||||
: Base(declaration, parent) {}
|
||||
|
||||
CompoundStatement::~CompoundStatement() = default;
|
||||
|
||||
} // namespace sem
|
||||
} // namespace tint
|
||||
|
|
|
@ -31,19 +31,34 @@ class BlockStatement;
|
|||
namespace tint {
|
||||
namespace sem {
|
||||
|
||||
/// Forward declaration
|
||||
class CompoundStatement;
|
||||
|
||||
/// Statement holds the semantic information for a statement.
|
||||
class Statement : public Castable<Statement, Node> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param declaration the AST node for this statement
|
||||
/// @param parent the owning statement
|
||||
Statement(const ast::Statement* declaration, const Statement* parent);
|
||||
Statement(const ast::Statement* declaration, const CompoundStatement* parent);
|
||||
|
||||
/// @return the AST node for this statement
|
||||
const ast::Statement* Declaration() const { return declaration_; }
|
||||
|
||||
/// @return the statement that encloses this statement
|
||||
const Statement* Parent() const { return parent_; }
|
||||
const CompoundStatement* Parent() const { return parent_; }
|
||||
|
||||
/// @returns the closest enclosing parent that satisfies the given predicate,
|
||||
/// which may be the statement itself, or nullptr if no match is found
|
||||
/// @param pred a predicate that the resulting block must satisfy
|
||||
template <typename Pred>
|
||||
const CompoundStatement* FindFirstParent(Pred&& pred) const;
|
||||
|
||||
/// @returns the statement itself if it matches the template type `T`,
|
||||
/// otherwise the nearest enclosing statement that matches `T`, or nullptr if
|
||||
/// there is none.
|
||||
template <typename T>
|
||||
const T* FindFirstParent() const;
|
||||
|
||||
/// @return the closest enclosing block for this statement
|
||||
const BlockStatement* Block() const;
|
||||
|
@ -53,9 +68,52 @@ class Statement : public Castable<Statement, Node> {
|
|||
|
||||
private:
|
||||
ast::Statement const* const declaration_;
|
||||
Statement const* const parent_;
|
||||
CompoundStatement const* const parent_;
|
||||
};
|
||||
|
||||
/// CompoundStatement is the base class of statements that can hold other
|
||||
/// statements.
|
||||
class CompoundStatement : public Castable<Statement, Statement> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param declaration the AST node for this statement
|
||||
/// @param parent the owning statement
|
||||
CompoundStatement(const ast::Statement* declaration,
|
||||
const CompoundStatement* parent);
|
||||
|
||||
/// Destructor
|
||||
~CompoundStatement() override;
|
||||
};
|
||||
|
||||
template <typename Pred>
|
||||
const CompoundStatement* Statement::FindFirstParent(Pred&& pred) const {
|
||||
if (auto* self = As<CompoundStatement>()) {
|
||||
if (pred(self)) {
|
||||
return self;
|
||||
}
|
||||
}
|
||||
const auto* curr = parent_;
|
||||
while (curr && !pred(curr)) {
|
||||
curr = curr->Parent();
|
||||
}
|
||||
return curr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T* Statement::FindFirstParent() const {
|
||||
if (auto* p = As<T>()) {
|
||||
return p;
|
||||
}
|
||||
const auto* curr = parent_;
|
||||
while (curr) {
|
||||
if (auto* p = curr->As<T>()) {
|
||||
return p;
|
||||
}
|
||||
curr = curr->Parent();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace sem
|
||||
} // namespace tint
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
// 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/switch_statement.h"
|
||||
|
||||
#include "src/program_builder.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::SwitchStatement);
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::SwitchCaseBlockStatement);
|
||||
|
||||
namespace tint {
|
||||
namespace sem {
|
||||
|
||||
SwitchStatement::SwitchStatement(const ast::SwitchStatement* declaration,
|
||||
CompoundStatement* parent)
|
||||
: Base(declaration, parent) {}
|
||||
|
||||
SwitchStatement::~SwitchStatement() = default;
|
||||
|
||||
SwitchCaseBlockStatement::SwitchCaseBlockStatement(
|
||||
const ast::BlockStatement* declaration,
|
||||
const CompoundStatement* parent)
|
||||
: Base(declaration, parent) {
|
||||
TINT_ASSERT(Semantic, parent);
|
||||
}
|
||||
SwitchCaseBlockStatement::~SwitchCaseBlockStatement() = default;
|
||||
|
||||
} // namespace sem
|
||||
} // namespace tint
|
|
@ -0,0 +1,60 @@
|
|||
// 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_SWITCH_STATEMENT_H_
|
||||
#define SRC_SEM_SWITCH_STATEMENT_H_
|
||||
|
||||
#include "src/sem/block_statement.h"
|
||||
|
||||
// Forward declarations
|
||||
namespace tint {
|
||||
namespace ast {
|
||||
class SwitchStatement;
|
||||
} // namespace ast
|
||||
} // namespace tint
|
||||
|
||||
namespace tint {
|
||||
namespace sem {
|
||||
|
||||
/// Holds semantic information about an switch statement
|
||||
class SwitchStatement : public Castable<SwitchStatement, CompoundStatement> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param declaration the AST node for this switch statement
|
||||
/// @param parent the owning statement
|
||||
SwitchStatement(const ast::SwitchStatement* declaration,
|
||||
CompoundStatement* parent);
|
||||
|
||||
/// Destructor
|
||||
~SwitchStatement() override;
|
||||
};
|
||||
|
||||
/// Holds semantic information about a switch case block
|
||||
class SwitchCaseBlockStatement
|
||||
: public Castable<SwitchCaseBlockStatement, BlockStatement> {
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param declaration the AST node for this block statement
|
||||
/// @param parent the owning statement
|
||||
SwitchCaseBlockStatement(const ast::BlockStatement* declaration,
|
||||
const CompoundStatement* parent);
|
||||
|
||||
/// Destructor
|
||||
~SwitchCaseBlockStatement() override;
|
||||
};
|
||||
|
||||
} // namespace sem
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_SEM_SWITCH_STATEMENT_H_
|
|
@ -229,10 +229,10 @@ tint_unittests_source_set("tint_unittests_core_src") {
|
|||
"../src/resolver/assignment_validation_test.cc",
|
||||
"../src/resolver/atomics_test.cc",
|
||||
"../src/resolver/atomics_validation_test.cc",
|
||||
"../src/resolver/block_test.cc",
|
||||
"../src/resolver/builtins_validation_test.cc",
|
||||
"../src/resolver/call_test.cc",
|
||||
"../src/resolver/call_validation_test.cc",
|
||||
"../src/resolver/compound_statement_test.cc",
|
||||
"../src/resolver/control_block_validation_test.cc",
|
||||
"../src/resolver/decoration_validation_test.cc",
|
||||
"../src/resolver/entry_point_validation_test.cc",
|
||||
|
|
Loading…
Reference in New Issue