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",
|
"resolver/resolver.h",
|
||||||
"scope_stack.h",
|
"scope_stack.h",
|
||||||
"sem/array.h",
|
"sem/array.h",
|
||||||
"sem/atomic_type.cc",
|
|
||||||
"sem/atomic_type.h",
|
"sem/atomic_type.h",
|
||||||
"sem/binding_point.h",
|
"sem/binding_point.h",
|
||||||
"sem/bool_type.cc",
|
|
||||||
"sem/bool_type.h",
|
"sem/bool_type.h",
|
||||||
"sem/call.h",
|
"sem/call.h",
|
||||||
"sem/call_target.h",
|
"sem/call_target.h",
|
||||||
"sem/constant.cc",
|
|
||||||
"sem/constant.h",
|
"sem/constant.h",
|
||||||
"sem/depth_texture_type.cc",
|
|
||||||
"sem/depth_texture_type.h",
|
"sem/depth_texture_type.h",
|
||||||
"sem/expression.h",
|
"sem/expression.h",
|
||||||
"sem/external_texture_type.cc",
|
|
||||||
"sem/external_texture_type.h",
|
"sem/external_texture_type.h",
|
||||||
"sem/f32_type.cc",
|
|
||||||
"sem/f32_type.h",
|
"sem/f32_type.h",
|
||||||
"sem/i32_type.cc",
|
"sem/for_loop_statement.h",
|
||||||
"sem/i32_type.h",
|
"sem/i32_type.h",
|
||||||
|
"sem/if_statement.h",
|
||||||
"sem/info.h",
|
"sem/info.h",
|
||||||
"sem/intrinsic.h",
|
"sem/intrinsic.h",
|
||||||
"sem/intrinsic_type.cc",
|
|
||||||
"sem/intrinsic_type.h",
|
"sem/intrinsic_type.h",
|
||||||
"sem/matrix_type.cc",
|
"sem/loop_statement.h",
|
||||||
"sem/matrix_type.h",
|
"sem/matrix_type.h",
|
||||||
"sem/multisampled_texture_type.cc",
|
|
||||||
"sem/multisampled_texture_type.h",
|
"sem/multisampled_texture_type.h",
|
||||||
"sem/node.h",
|
"sem/node.h",
|
||||||
"sem/parameter_usage.cc",
|
|
||||||
"sem/parameter_usage.h",
|
"sem/parameter_usage.h",
|
||||||
"sem/pipeline_stage_set.h",
|
"sem/pipeline_stage_set.h",
|
||||||
"sem/pointer_type.cc",
|
|
||||||
"sem/pointer_type.h",
|
"sem/pointer_type.h",
|
||||||
"sem/reference_type.cc",
|
|
||||||
"sem/reference_type.h",
|
"sem/reference_type.h",
|
||||||
"sem/sampled_texture_type.cc",
|
|
||||||
"sem/sampled_texture_type.h",
|
"sem/sampled_texture_type.h",
|
||||||
"sem/sampler_type.cc",
|
|
||||||
"sem/sampler_type.h",
|
"sem/sampler_type.h",
|
||||||
"sem/storage_texture_type.cc",
|
|
||||||
"sem/storage_texture_type.h",
|
"sem/storage_texture_type.h",
|
||||||
"sem/texture_type.cc",
|
"sem/switch_statement.h",
|
||||||
"sem/texture_type.h",
|
"sem/texture_type.h",
|
||||||
"sem/type.cc",
|
|
||||||
"sem/type.h",
|
"sem/type.h",
|
||||||
"sem/type_manager.cc",
|
|
||||||
"sem/type_manager.h",
|
"sem/type_manager.h",
|
||||||
"sem/type_mappings.h",
|
"sem/type_mappings.h",
|
||||||
"sem/u32_type.cc",
|
|
||||||
"sem/u32_type.h",
|
"sem/u32_type.h",
|
||||||
"sem/vector_type.cc",
|
|
||||||
"sem/vector_type.h",
|
"sem/vector_type.h",
|
||||||
"sem/void_type.cc",
|
|
||||||
"sem/void_type.h",
|
"sem/void_type.h",
|
||||||
"source.cc",
|
"source.cc",
|
||||||
"source.h",
|
"source.h",
|
||||||
|
@ -633,18 +615,80 @@ libtint_source_set("libtint_core_all_src") {
|
||||||
libtint_source_set("libtint_sem_src") {
|
libtint_source_set("libtint_sem_src") {
|
||||||
sources = [
|
sources = [
|
||||||
"sem/array.cc",
|
"sem/array.cc",
|
||||||
|
"sem/array.h",
|
||||||
|
"sem/atomic_type.cc",
|
||||||
|
"sem/atomic_type.h",
|
||||||
|
"sem/binding_point.h",
|
||||||
"sem/block_statement.cc",
|
"sem/block_statement.cc",
|
||||||
|
"sem/bool_type.cc",
|
||||||
|
"sem/bool_type.h",
|
||||||
"sem/call.cc",
|
"sem/call.cc",
|
||||||
|
"sem/call.h",
|
||||||
"sem/call_target.cc",
|
"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.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/function.cc",
|
||||||
|
"sem/i32_type.cc",
|
||||||
|
"sem/i32_type.h",
|
||||||
|
"sem/if_statement.cc",
|
||||||
|
"sem/if_statement.h",
|
||||||
"sem/info.cc",
|
"sem/info.cc",
|
||||||
|
"sem/info.h",
|
||||||
"sem/intrinsic.cc",
|
"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/member_accessor_expression.cc",
|
||||||
|
"sem/multisampled_texture_type.cc",
|
||||||
|
"sem/multisampled_texture_type.h",
|
||||||
"sem/node.cc",
|
"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/statement.cc",
|
||||||
|
"sem/storage_texture_type.cc",
|
||||||
|
"sem/storage_texture_type.h",
|
||||||
"sem/struct.cc",
|
"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/variable.cc",
|
||||||
|
"sem/vector_type.cc",
|
||||||
|
"sem/vector_type.h",
|
||||||
|
"sem/void_type.cc",
|
||||||
|
"sem/void_type.h",
|
||||||
]
|
]
|
||||||
|
|
||||||
public_deps = [ ":libtint_core_all_src" ]
|
public_deps = [ ":libtint_core_all_src" ]
|
||||||
|
|
|
@ -336,8 +336,14 @@ set(TINT_LIB_SRCS
|
||||||
sem/external_texture_type.h
|
sem/external_texture_type.h
|
||||||
sem/f32_type.cc
|
sem/f32_type.cc
|
||||||
sem/f32_type.h
|
sem/f32_type.h
|
||||||
|
sem/for_loop_statement.cc
|
||||||
|
sem/for_loop_statement.h
|
||||||
sem/i32_type.cc
|
sem/i32_type.cc
|
||||||
sem/i32_type.h
|
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.cc
|
||||||
sem/matrix_type.h
|
sem/matrix_type.h
|
||||||
sem/multisampled_texture_type.cc
|
sem/multisampled_texture_type.cc
|
||||||
|
@ -352,6 +358,8 @@ set(TINT_LIB_SRCS
|
||||||
sem/sampler_type.h
|
sem/sampler_type.h
|
||||||
sem/storage_texture_type.cc
|
sem/storage_texture_type.cc
|
||||||
sem/storage_texture_type.h
|
sem/storage_texture_type.h
|
||||||
|
sem/switch_statement.cc
|
||||||
|
sem/switch_statement.h
|
||||||
sem/texture_type.cc
|
sem/texture_type.cc
|
||||||
sem/texture_type.h
|
sem/texture_type.h
|
||||||
sem/type.cc
|
sem/type.cc
|
||||||
|
@ -622,10 +630,10 @@ if(${TINT_BUILD_TESTS})
|
||||||
resolver/assignment_validation_test.cc
|
resolver/assignment_validation_test.cc
|
||||||
resolver/atomics_test.cc
|
resolver/atomics_test.cc
|
||||||
resolver/atomics_validation_test.cc
|
resolver/atomics_validation_test.cc
|
||||||
resolver/block_test.cc
|
|
||||||
resolver/builtins_validation_test.cc
|
resolver/builtins_validation_test.cc
|
||||||
resolver/call_test.cc
|
resolver/call_test.cc
|
||||||
resolver/call_validation_test.cc
|
resolver/call_validation_test.cc
|
||||||
|
resolver/compound_statement_test.cc
|
||||||
resolver/control_block_validation_test.cc
|
resolver/control_block_validation_test.cc
|
||||||
resolver/decoration_validation_test.cc
|
resolver/decoration_validation_test.cc
|
||||||
resolver/entry_point_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/atomic_type.h"
|
||||||
#include "src/sem/call.h"
|
#include "src/sem/call.h"
|
||||||
#include "src/sem/depth_texture_type.h"
|
#include "src/sem/depth_texture_type.h"
|
||||||
|
#include "src/sem/for_loop_statement.h"
|
||||||
#include "src/sem/function.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/member_accessor_expression.h"
|
||||||
#include "src/sem/multisampled_texture_type.h"
|
#include "src/sem/multisampled_texture_type.h"
|
||||||
#include "src/sem/pointer_type.h"
|
#include "src/sem/pointer_type.h"
|
||||||
|
@ -61,6 +64,7 @@
|
||||||
#include "src/sem/statement.h"
|
#include "src/sem/statement.h"
|
||||||
#include "src/sem/storage_texture_type.h"
|
#include "src/sem/storage_texture_type.h"
|
||||||
#include "src/sem/struct.h"
|
#include "src/sem/struct.h"
|
||||||
|
#include "src/sem/switch_statement.h"
|
||||||
#include "src/sem/variable.h"
|
#include "src/sem/variable.h"
|
||||||
#include "src/utils/defer.h"
|
#include "src/utils/defer.h"
|
||||||
#include "src/utils/get_or_create.h"
|
#include "src/utils/get_or_create.h"
|
||||||
|
@ -1568,16 +1572,14 @@ bool Resolver::Function(ast::Function* func) {
|
||||||
|
|
||||||
if (func->body()) {
|
if (func->body()) {
|
||||||
Mark(func->body());
|
Mark(func->body());
|
||||||
if (current_statement_) {
|
if (current_compound_statement_) {
|
||||||
TINT_ICE(Resolver, diagnostics_)
|
TINT_ICE(Resolver, diagnostics_)
|
||||||
<< "Resolver::Function() called with a current statement";
|
<< "Resolver::Function() called with a current compound statement";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto* sem_block = builder_->create<sem::FunctionBlockStatement>(func);
|
auto* sem_block = builder_->create<sem::FunctionBlockStatement>(func);
|
||||||
builder_->Sem().Add(func->body(), sem_block);
|
builder_->Sem().Add(func->body(), sem_block);
|
||||||
TINT_SCOPED_ASSIGNMENT(current_statement_, sem_block);
|
if (!Scope(sem_block, [&] { return Statements(func->body()->list()); })) {
|
||||||
if (!BlockScope(func->body(),
|
|
||||||
[&] { return Statements(func->body()->list()); })) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1725,17 +1727,11 @@ bool Resolver::ValidateStatements(const ast::StatementList& stmts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resolver::Statement(ast::Statement* stmt) {
|
bool Resolver::Statement(ast::Statement* stmt) {
|
||||||
sem::Statement* sem_statement;
|
if (stmt->Is<ast::CaseStatement>()) {
|
||||||
if (stmt->As<ast::BlockStatement>()) {
|
AddError("case statement can only be used inside a switch statement",
|
||||||
sem_statement = builder_->create<sem::BlockStatement>(
|
stmt->source());
|
||||||
stmt->As<ast::BlockStatement>(), current_statement_);
|
return false;
|
||||||
} else {
|
|
||||||
sem_statement = builder_->create<sem::Statement>(stmt, current_statement_);
|
|
||||||
}
|
}
|
||||||
builder_->Sem().Add(stmt, sem_statement);
|
|
||||||
|
|
||||||
TINT_SCOPED_ASSIGNMENT(current_statement_, sem_statement);
|
|
||||||
|
|
||||||
if (stmt->Is<ast::ElseStatement>()) {
|
if (stmt->Is<ast::ElseStatement>()) {
|
||||||
TINT_ICE(Resolver, diagnostics_)
|
TINT_ICE(Resolver, diagnostics_)
|
||||||
<< "Resolver::Statement() encountered an Else statement. Else "
|
<< "Resolver::Statement() encountered an Else statement. Else "
|
||||||
|
@ -1744,15 +1740,35 @@ bool Resolver::Statement(ast::Statement* stmt) {
|
||||||
return false;
|
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>()) {
|
if (auto* a = stmt->As<ast::AssignmentStatement>()) {
|
||||||
return Assignment(a);
|
return Assignment(a);
|
||||||
}
|
}
|
||||||
if (auto* b = stmt->As<ast::BlockStatement>()) {
|
|
||||||
return BlockScope(b, [&] { return Statements(b->list()); });
|
|
||||||
}
|
|
||||||
if (stmt->Is<ast::BreakStatement>()) {
|
if (stmt->Is<ast::BreakStatement>()) {
|
||||||
if (!current_block_->FindFirstParent<sem::LoopBlockStatement>() &&
|
if (!sem_statement->FindFirstParent<sem::LoopBlockStatement>() &&
|
||||||
!current_block_->FindFirstParent<sem::SwitchCaseBlockStatement>()) {
|
!sem_statement->FindFirstParent<sem::SwitchCaseBlockStatement>()) {
|
||||||
AddError("break statement must be in a loop or switch case",
|
AddError("break statement must be in a loop or switch case",
|
||||||
stmt->source());
|
stmt->source());
|
||||||
return false;
|
return false;
|
||||||
|
@ -1769,9 +1785,6 @@ bool Resolver::Statement(ast::Statement* stmt) {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (auto* c = stmt->As<ast::CaseStatement>()) {
|
|
||||||
return CaseStatement(c);
|
|
||||||
}
|
|
||||||
if (stmt->Is<ast::ContinueStatement>()) {
|
if (stmt->Is<ast::ContinueStatement>()) {
|
||||||
// Set if we've hit the first continue statement in our parent loop
|
// Set if we've hit the first continue statement in our parent loop
|
||||||
if (auto* loop_block =
|
if (auto* loop_block =
|
||||||
|
@ -1793,21 +1806,9 @@ bool Resolver::Statement(ast::Statement* stmt) {
|
||||||
if (stmt->Is<ast::FallthroughStatement>()) {
|
if (stmt->Is<ast::FallthroughStatement>()) {
|
||||||
return true;
|
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>()) {
|
if (auto* r = stmt->As<ast::ReturnStatement>()) {
|
||||||
return Return(r);
|
return Return(r);
|
||||||
}
|
}
|
||||||
if (auto* s = stmt->As<ast::SwitchStatement>()) {
|
|
||||||
return Switch(s);
|
|
||||||
}
|
|
||||||
if (auto* v = stmt->As<ast::VariableDeclStatement>()) {
|
if (auto* v = stmt->As<ast::VariableDeclStatement>()) {
|
||||||
return VariableDeclStatement(v);
|
return VariableDeclStatement(v);
|
||||||
}
|
}
|
||||||
|
@ -1819,19 +1820,22 @@ bool Resolver::Statement(ast::Statement* stmt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resolver::CaseStatement(ast::CaseStatement* 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());
|
Mark(stmt->body());
|
||||||
for (auto* sel : stmt->selectors()) {
|
for (auto* sel : stmt->selectors()) {
|
||||||
Mark(sel);
|
Mark(sel);
|
||||||
}
|
}
|
||||||
auto* sem_block = builder_->create<sem::SwitchCaseBlockStatement>(
|
return Scope(sem, [&] { return Statements(stmt->body()->list()); });
|
||||||
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()); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resolver::IfStatement(ast::IfStatement* stmt) {
|
bool Resolver::IfStatement(ast::IfStatement* stmt) {
|
||||||
|
auto* sem =
|
||||||
|
builder_->create<sem::IfStatement>(stmt, current_compound_statement_);
|
||||||
|
builder_->Sem().Add(stmt, sem);
|
||||||
|
return Scope(sem, [&] {
|
||||||
Mark(stmt->condition());
|
Mark(stmt->condition());
|
||||||
if (!Expression(stmt->condition())) {
|
if (!Expression(stmt->condition())) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1846,24 +1850,29 @@ bool Resolver::IfStatement(ast::IfStatement* stmt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Mark(stmt->body());
|
Mark(stmt->body());
|
||||||
{
|
auto* body = builder_->create<sem::BlockStatement>(
|
||||||
auto* sem_block =
|
stmt->body(), current_compound_statement_);
|
||||||
builder_->create<sem::BlockStatement>(stmt->body(), current_statement_);
|
builder_->Sem().Add(stmt->body(), body);
|
||||||
builder_->Sem().Add(stmt->body(), sem_block);
|
if (!Scope(body, [&] { return Statements(stmt->body()->list()); })) {
|
||||||
TINT_SCOPED_ASSIGNMENT(current_statement_, sem_block);
|
|
||||||
if (!BlockScope(stmt->body(),
|
|
||||||
[&] { return Statements(stmt->body()->list()); })) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for (auto* else_stmt : stmt->else_statements()) {
|
for (auto* else_stmt : stmt->else_statements()) {
|
||||||
Mark(else_stmt);
|
Mark(else_stmt);
|
||||||
auto* sem_else_stmt =
|
if (!ElseStatement(else_stmt)) {
|
||||||
builder_->create<sem::Statement>(else_stmt, current_statement_);
|
return false;
|
||||||
builder_->Sem().Add(else_stmt, sem_else_stmt);
|
}
|
||||||
TINT_SCOPED_ASSIGNMENT(current_statement_, sem_else_stmt);
|
}
|
||||||
if (auto* cond = else_stmt->condition()) {
|
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);
|
Mark(cond);
|
||||||
if (!Expression(cond)) {
|
if (!Expression(cond)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1877,33 +1886,33 @@ bool Resolver::IfStatement(ast::IfStatement* stmt) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Mark(else_stmt->body());
|
|
||||||
{
|
Mark(stmt->body());
|
||||||
auto* sem_block = builder_->create<sem::BlockStatement>(
|
auto* body = builder_->create<sem::BlockStatement>(
|
||||||
else_stmt->body(), current_statement_);
|
stmt->body(), current_compound_statement_);
|
||||||
builder_->Sem().Add(else_stmt->body(), sem_block);
|
builder_->Sem().Add(stmt->body(), body);
|
||||||
TINT_SCOPED_ASSIGNMENT(current_statement_, sem_block);
|
return Scope(body, [&] { return Statements(stmt->body()->list()); });
|
||||||
if (!BlockScope(else_stmt->body(),
|
});
|
||||||
[&] { return Statements(else_stmt->body()->list()); })) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
bool Resolver::BlockStatement(ast::BlockStatement* stmt) {
|
||||||
return true;
|
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) {
|
bool Resolver::LoopStatement(ast::LoopStatement* stmt) {
|
||||||
// We don't call DetermineBlockStatement on the body and continuing block as
|
auto* sem =
|
||||||
// these would make their BlockInfo siblings as in the AST, but we want the
|
builder_->create<sem::LoopStatement>(stmt, current_compound_statement_);
|
||||||
// body BlockInfo to parent the continuing BlockInfo for semantics and
|
builder_->Sem().Add(stmt, sem);
|
||||||
// validation. Also, we need to set their types differently.
|
return Scope(sem, [&] {
|
||||||
Mark(stmt->body());
|
Mark(stmt->body());
|
||||||
|
|
||||||
auto* sem_block_body = builder_->create<sem::LoopBlockStatement>(
|
auto* body = builder_->create<sem::LoopBlockStatement>(
|
||||||
stmt->body(), current_statement_);
|
stmt->body(), current_compound_statement_);
|
||||||
builder_->Sem().Add(stmt->body(), sem_block_body);
|
builder_->Sem().Add(stmt->body(), body);
|
||||||
TINT_SCOPED_ASSIGNMENT(current_statement_, sem_block_body);
|
return Scope(body, [&] {
|
||||||
return BlockScope(stmt->body(), [&] {
|
|
||||||
if (!Statements(stmt->body()->list())) {
|
if (!Statements(stmt->body()->list())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1911,32 +1920,24 @@ bool Resolver::LoopStatement(ast::LoopStatement* stmt) {
|
||||||
Mark(stmt->continuing());
|
Mark(stmt->continuing());
|
||||||
}
|
}
|
||||||
if (stmt->has_continuing()) {
|
if (stmt->has_continuing()) {
|
||||||
auto* sem_block_continuing =
|
auto* continuing = builder_->create<sem::LoopContinuingBlockStatement>(
|
||||||
builder_->create<sem::LoopContinuingBlockStatement>(
|
stmt->continuing(), current_compound_statement_);
|
||||||
stmt->continuing(), current_statement_);
|
builder_->Sem().Add(stmt->continuing(), continuing);
|
||||||
builder_->Sem().Add(stmt->continuing(), sem_block_continuing);
|
if (!Scope(continuing,
|
||||||
TINT_SCOPED_ASSIGNMENT(current_statement_, sem_block_continuing);
|
|
||||||
if (!BlockScope(stmt->continuing(),
|
|
||||||
[&] { return Statements(stmt->continuing()->list()); })) {
|
[&] { return Statements(stmt->continuing()->list()); })) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resolver::ForLoopStatement(ast::ForLoopStatement* stmt) {
|
bool Resolver::ForLoopStatement(ast::ForLoopStatement* stmt) {
|
||||||
Mark(stmt->body());
|
auto* sem = builder_->create<sem::ForLoopStatement>(
|
||||||
|
stmt, current_compound_statement_);
|
||||||
auto* sem_block_body = builder_->create<sem::LoopBlockStatement>(
|
builder_->Sem().Add(stmt, sem);
|
||||||
stmt->body(), current_statement_);
|
return Scope(sem, [&] {
|
||||||
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()) {
|
if (auto* initializer = stmt->initializer()) {
|
||||||
Mark(initializer);
|
Mark(initializer);
|
||||||
if (!Statement(initializer)) {
|
if (!Statement(initializer)) {
|
||||||
|
@ -1951,7 +1952,8 @@ bool Resolver::ForLoopStatement(ast::ForLoopStatement* stmt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TypeOf(condition)->Is<sem::Bool>()) {
|
if (!TypeOf(condition)->Is<sem::Bool>()) {
|
||||||
AddError("for-loop condition must be bool, got " + TypeNameOf(condition),
|
AddError(
|
||||||
|
"for-loop condition must be bool, got " + TypeNameOf(condition),
|
||||||
condition->source());
|
condition->source());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1964,8 +1966,13 @@ bool Resolver::ForLoopStatement(ast::ForLoopStatement* stmt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return BlockScope(stmt->body(),
|
Mark(stmt->body());
|
||||||
[&] { return Statements(stmt->body()->list()); });
|
|
||||||
|
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) {
|
bool Resolver::Expressions(const ast::ExpressionList& list) {
|
||||||
|
@ -3036,7 +3043,9 @@ bool Resolver::VariableDeclStatement(const ast::VariableDeclStatement* stmt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
variable_stack_.set(var->symbol(), info);
|
variable_stack_.set(var->symbol(), info);
|
||||||
|
if (current_block_) { // Not all statements are inside a block
|
||||||
current_block_->AddDecl(var);
|
current_block_->AddDecl(var);
|
||||||
|
}
|
||||||
|
|
||||||
if (!ValidateVariable(info)) {
|
if (!ValidateVariable(info)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -3858,26 +3867,26 @@ bool Resolver::ValidateSwitch(const ast::SwitchStatement* s) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resolver::Switch(ast::SwitchStatement* s) {
|
bool Resolver::SwitchStatement(ast::SwitchStatement* stmt) {
|
||||||
Mark(s->condition());
|
auto* sem =
|
||||||
if (!Expression(s->condition())) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
for (auto* case_stmt : s->body()) {
|
for (auto* case_stmt : stmt->body()) {
|
||||||
Mark(case_stmt);
|
Mark(case_stmt);
|
||||||
|
|
||||||
sem::Statement* sem_statement =
|
|
||||||
builder_->create<sem::Statement>(case_stmt, current_statement_);
|
|
||||||
builder_->Sem().Add(case_stmt, sem_statement);
|
|
||||||
TINT_SCOPED_ASSIGNMENT(current_statement_, sem_statement);
|
|
||||||
if (!CaseStatement(case_stmt)) {
|
if (!CaseStatement(case_stmt)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ValidateSwitch(s)) {
|
if (!ValidateSwitch(stmt)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resolver::Assignment(ast::AssignmentStatement* a) {
|
bool Resolver::Assignment(ast::AssignmentStatement* a) {
|
||||||
|
@ -4032,18 +4041,22 @@ bool Resolver::ApplyStorageClassUsageToType(ast::StorageClass sc,
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
bool Resolver::BlockScope(const ast::BlockStatement* block, F&& callback) {
|
bool Resolver::Scope(sem::CompoundStatement* stmt, F&& callback) {
|
||||||
auto* sem_block = builder_->Sem().Get<sem::BlockStatement>(block);
|
auto* prev_current_statement = current_statement_;
|
||||||
if (!sem_block) {
|
auto* prev_current_compound_statement = current_compound_statement_;
|
||||||
TINT_ICE(Resolver, diagnostics_)
|
auto* prev_current_block = current_block_;
|
||||||
<< "Resolver::BlockScope() called on a block for "
|
current_statement_ = stmt;
|
||||||
"which semantic information is not available";
|
current_compound_statement_ = stmt;
|
||||||
return false;
|
current_block_ = stmt->As<sem::BlockStatement>();
|
||||||
}
|
|
||||||
TINT_SCOPED_ASSIGNMENT(current_block_,
|
|
||||||
const_cast<sem::BlockStatement*>(sem_block));
|
|
||||||
variable_stack_.push_scope();
|
variable_stack_.push_scope();
|
||||||
|
|
||||||
|
TINT_DEFER({
|
||||||
TINT_DEFER(variable_stack_.pop_scope());
|
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();
|
return callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -242,9 +242,11 @@ class Resolver {
|
||||||
bool Assignment(ast::AssignmentStatement* a);
|
bool Assignment(ast::AssignmentStatement* a);
|
||||||
bool Binary(ast::BinaryExpression*);
|
bool Binary(ast::BinaryExpression*);
|
||||||
bool Bitcast(ast::BitcastExpression*);
|
bool Bitcast(ast::BitcastExpression*);
|
||||||
|
bool BlockStatement(ast::BlockStatement*);
|
||||||
bool Call(ast::CallExpression*);
|
bool Call(ast::CallExpression*);
|
||||||
bool CaseStatement(ast::CaseStatement*);
|
bool CaseStatement(ast::CaseStatement*);
|
||||||
bool Constructor(ast::ConstructorExpression*);
|
bool Constructor(ast::ConstructorExpression*);
|
||||||
|
bool ElseStatement(ast::ElseStatement*);
|
||||||
bool Expression(ast::Expression*);
|
bool Expression(ast::Expression*);
|
||||||
bool Expressions(const ast::ExpressionList&);
|
bool Expressions(const ast::ExpressionList&);
|
||||||
bool ForLoopStatement(ast::ForLoopStatement*);
|
bool ForLoopStatement(ast::ForLoopStatement*);
|
||||||
|
@ -260,7 +262,7 @@ class Resolver {
|
||||||
bool Return(ast::ReturnStatement* ret);
|
bool Return(ast::ReturnStatement* ret);
|
||||||
bool Statement(ast::Statement*);
|
bool Statement(ast::Statement*);
|
||||||
bool Statements(const ast::StatementList&);
|
bool Statements(const ast::StatementList&);
|
||||||
bool Switch(ast::SwitchStatement* s);
|
bool SwitchStatement(ast::SwitchStatement* s);
|
||||||
bool UnaryOp(ast::UnaryOpExpression*);
|
bool UnaryOp(ast::UnaryOpExpression*);
|
||||||
bool VariableDeclStatement(const ast::VariableDeclStatement*);
|
bool VariableDeclStatement(const ast::VariableDeclStatement*);
|
||||||
|
|
||||||
|
@ -394,11 +396,14 @@ class Resolver {
|
||||||
const sem::Type* type,
|
const sem::Type* type,
|
||||||
std::string type_name = "");
|
std::string type_name = "");
|
||||||
|
|
||||||
/// Constructs a new semantic BlockStatement with the given type and with
|
/// Assigns `stmt` to #current_statement_, #current_compound_statement_, and
|
||||||
/// #current_block_ as its parent, assigns this to #current_block_, and then
|
/// possibly #current_block_, pushes the variable scope, then calls
|
||||||
/// calls `callback`. The original #current_block_ is restored on exit.
|
/// `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>
|
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
|
/// Returns a human-readable string representation of the vector type name
|
||||||
/// with the given parameters.
|
/// with the given parameters.
|
||||||
|
@ -449,7 +454,6 @@ class Resolver {
|
||||||
ProgramBuilder* const builder_;
|
ProgramBuilder* const builder_;
|
||||||
diag::List& diagnostics_;
|
diag::List& diagnostics_;
|
||||||
std::unique_ptr<IntrinsicTable> const intrinsic_table_;
|
std::unique_ptr<IntrinsicTable> const intrinsic_table_;
|
||||||
sem::BlockStatement* current_block_ = nullptr;
|
|
||||||
ScopeStack<VariableInfo*> variable_stack_;
|
ScopeStack<VariableInfo*> variable_stack_;
|
||||||
std::unordered_map<Symbol, FunctionInfo*> symbol_to_function_;
|
std::unordered_map<Symbol, FunctionInfo*> symbol_to_function_;
|
||||||
std::vector<FunctionInfo*> entry_points_;
|
std::vector<FunctionInfo*> entry_points_;
|
||||||
|
@ -466,6 +470,8 @@ class Resolver {
|
||||||
|
|
||||||
FunctionInfo* current_function_ = nullptr;
|
FunctionInfo* current_function_ = nullptr;
|
||||||
sem::Statement* current_statement_ = nullptr;
|
sem::Statement* current_statement_ = nullptr;
|
||||||
|
sem::CompoundStatement* current_compound_statement_ = nullptr;
|
||||||
|
sem::BlockStatement* current_block_ = nullptr;
|
||||||
BlockAllocator<VariableInfo> variable_infos_;
|
BlockAllocator<VariableInfo> variable_infos_;
|
||||||
BlockAllocator<FunctionInfo> function_infos_;
|
BlockAllocator<FunctionInfo> function_infos_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,14 +21,12 @@
|
||||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::BlockStatement);
|
TINT_INSTANTIATE_TYPEINFO(tint::sem::BlockStatement);
|
||||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::FunctionBlockStatement);
|
TINT_INSTANTIATE_TYPEINFO(tint::sem::FunctionBlockStatement);
|
||||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::LoopBlockStatement);
|
TINT_INSTANTIATE_TYPEINFO(tint::sem::LoopBlockStatement);
|
||||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::LoopContinuingBlockStatement);
|
|
||||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::SwitchCaseBlockStatement);
|
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
namespace sem {
|
namespace sem {
|
||||||
|
|
||||||
BlockStatement::BlockStatement(const ast::BlockStatement* declaration,
|
BlockStatement::BlockStatement(const ast::BlockStatement* declaration,
|
||||||
const Statement* parent)
|
const CompoundStatement* parent)
|
||||||
: Base(declaration, parent) {}
|
: Base(declaration, parent) {}
|
||||||
|
|
||||||
BlockStatement::~BlockStatement() = default;
|
BlockStatement::~BlockStatement() = default;
|
||||||
|
@ -47,25 +45,15 @@ FunctionBlockStatement::FunctionBlockStatement(const ast::Function* function)
|
||||||
FunctionBlockStatement::~FunctionBlockStatement() = default;
|
FunctionBlockStatement::~FunctionBlockStatement() = default;
|
||||||
|
|
||||||
LoopBlockStatement::LoopBlockStatement(const ast::BlockStatement* declaration,
|
LoopBlockStatement::LoopBlockStatement(const ast::BlockStatement* declaration,
|
||||||
const Statement* parent)
|
const CompoundStatement* parent)
|
||||||
: Base(declaration, parent) {}
|
: Base(declaration, parent) {
|
||||||
|
TINT_ASSERT(Semantic, parent);
|
||||||
|
}
|
||||||
LoopBlockStatement::~LoopBlockStatement() = default;
|
LoopBlockStatement::~LoopBlockStatement() = default;
|
||||||
|
|
||||||
void LoopBlockStatement::SetFirstContinue(size_t first_continue) {
|
void LoopBlockStatement::SetFirstContinue(size_t first_continue) {
|
||||||
first_continue_ = 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 sem
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
|
|
|
@ -34,13 +34,13 @@ namespace sem {
|
||||||
|
|
||||||
/// Holds semantic information about a block, such as parent block and variables
|
/// Holds semantic information about a block, such as parent block and variables
|
||||||
/// declared in the block.
|
/// declared in the block.
|
||||||
class BlockStatement : public Castable<BlockStatement, Statement> {
|
class BlockStatement : public Castable<BlockStatement, CompoundStatement> {
|
||||||
public:
|
public:
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// @param declaration the AST node for this block statement
|
/// @param declaration the AST node for this block statement
|
||||||
/// @param parent the owning statement
|
/// @param parent the owning statement
|
||||||
BlockStatement(const ast::BlockStatement* declaration,
|
BlockStatement(const ast::BlockStatement* declaration,
|
||||||
const Statement* parent);
|
const CompoundStatement* parent);
|
||||||
|
|
||||||
/// Destructor
|
/// Destructor
|
||||||
~BlockStatement() override;
|
~BlockStatement() override;
|
||||||
|
@ -49,33 +49,6 @@ class BlockStatement : public Castable<BlockStatement, Statement> {
|
||||||
/// statement
|
/// statement
|
||||||
const ast::BlockStatement* Declaration() const;
|
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
|
/// @returns the declarations associated with this block
|
||||||
const std::vector<const ast::Variable*>& Decls() const { return decls_; }
|
const std::vector<const ast::Variable*>& Decls() const { return decls_; }
|
||||||
|
|
||||||
|
@ -105,14 +78,14 @@ class FunctionBlockStatement
|
||||||
ast::Function const* const function_;
|
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> {
|
class LoopBlockStatement : public Castable<LoopBlockStatement, BlockStatement> {
|
||||||
public:
|
public:
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// @param declaration the AST node for this block statement
|
/// @param declaration the AST node for this block statement
|
||||||
/// @param parent the owning statement
|
/// @param parent the owning statement
|
||||||
LoopBlockStatement(const ast::BlockStatement* declaration,
|
LoopBlockStatement(const ast::BlockStatement* declaration,
|
||||||
const Statement* parent);
|
const CompoundStatement* parent);
|
||||||
|
|
||||||
/// Destructor
|
/// Destructor
|
||||||
~LoopBlockStatement() override;
|
~LoopBlockStatement() override;
|
||||||
|
@ -134,34 +107,6 @@ class LoopBlockStatement : public Castable<LoopBlockStatement, BlockStatement> {
|
||||||
size_t first_continue_ = kNoContinue;
|
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 sem
|
||||||
} // namespace tint
|
} // 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"
|
#include "src/sem/statement.h"
|
||||||
|
|
||||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::Statement);
|
TINT_INSTANTIATE_TYPEINFO(tint::sem::Statement);
|
||||||
|
TINT_INSTANTIATE_TYPEINFO(tint::sem::CompoundStatement);
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
namespace sem {
|
namespace sem {
|
||||||
|
|
||||||
Statement::Statement(const ast::Statement* declaration, const Statement* parent)
|
Statement::Statement(const ast::Statement* declaration,
|
||||||
|
const CompoundStatement* parent)
|
||||||
: declaration_(declaration), parent_(parent) {}
|
: declaration_(declaration), parent_(parent) {}
|
||||||
|
|
||||||
const BlockStatement* Statement::Block() const {
|
const BlockStatement* Statement::Block() const {
|
||||||
auto* stmt = parent_;
|
return FindFirstParent<BlockStatement>();
|
||||||
while (stmt != nullptr) {
|
|
||||||
if (auto* block_stmt = stmt->As<BlockStatement>()) {
|
|
||||||
return block_stmt;
|
|
||||||
}
|
|
||||||
stmt = stmt->parent_;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ast::Function* Statement::Function() const {
|
const ast::Function* Statement::Function() const {
|
||||||
if (auto* block = Block()) {
|
if (auto* fbs = FindFirstParent<FunctionBlockStatement>()) {
|
||||||
if (auto* fbs = block->FindFirstParent<FunctionBlockStatement>()) {
|
|
||||||
return fbs->Function();
|
return fbs->Function();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CompoundStatement::CompoundStatement(const ast::Statement* declaration,
|
||||||
|
const CompoundStatement* parent)
|
||||||
|
: Base(declaration, parent) {}
|
||||||
|
|
||||||
|
CompoundStatement::~CompoundStatement() = default;
|
||||||
|
|
||||||
} // namespace sem
|
} // namespace sem
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
|
|
|
@ -31,19 +31,34 @@ class BlockStatement;
|
||||||
namespace tint {
|
namespace tint {
|
||||||
namespace sem {
|
namespace sem {
|
||||||
|
|
||||||
|
/// Forward declaration
|
||||||
|
class CompoundStatement;
|
||||||
|
|
||||||
/// Statement holds the semantic information for a statement.
|
/// Statement holds the semantic information for a statement.
|
||||||
class Statement : public Castable<Statement, Node> {
|
class Statement : public Castable<Statement, Node> {
|
||||||
public:
|
public:
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// @param declaration the AST node for this statement
|
/// @param declaration the AST node for this statement
|
||||||
/// @param parent the owning 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
|
/// @return the AST node for this statement
|
||||||
const ast::Statement* Declaration() const { return declaration_; }
|
const ast::Statement* Declaration() const { return declaration_; }
|
||||||
|
|
||||||
/// @return the statement that encloses this statement
|
/// @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
|
/// @return the closest enclosing block for this statement
|
||||||
const BlockStatement* Block() const;
|
const BlockStatement* Block() const;
|
||||||
|
@ -53,9 +68,52 @@ class Statement : public Castable<Statement, Node> {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ast::Statement const* const declaration_;
|
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 sem
|
||||||
} // namespace tint
|
} // 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/assignment_validation_test.cc",
|
||||||
"../src/resolver/atomics_test.cc",
|
"../src/resolver/atomics_test.cc",
|
||||||
"../src/resolver/atomics_validation_test.cc",
|
"../src/resolver/atomics_validation_test.cc",
|
||||||
"../src/resolver/block_test.cc",
|
|
||||||
"../src/resolver/builtins_validation_test.cc",
|
"../src/resolver/builtins_validation_test.cc",
|
||||||
"../src/resolver/call_test.cc",
|
"../src/resolver/call_test.cc",
|
||||||
"../src/resolver/call_validation_test.cc",
|
"../src/resolver/call_validation_test.cc",
|
||||||
|
"../src/resolver/compound_statement_test.cc",
|
||||||
"../src/resolver/control_block_validation_test.cc",
|
"../src/resolver/control_block_validation_test.cc",
|
||||||
"../src/resolver/decoration_validation_test.cc",
|
"../src/resolver/decoration_validation_test.cc",
|
||||||
"../src/resolver/entry_point_validation_test.cc",
|
"../src/resolver/entry_point_validation_test.cc",
|
||||||
|
|
Loading…
Reference in New Issue