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>
diff --git a/docs/compound_statements.md b/docs/compound_statements.md
new file mode 100644
index 0000000..7d28b1d
--- /dev/null
+++ b/docs/compound_statements.md
@@ -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
+ }
+}
+```
diff --git a/src/BUILD.gn b/src/BUILD.gn
index a7c9334..83c42bf 100644
--- a/src/BUILD.gn
+++ b/src/BUILD.gn
@@ -497,58 +497,40 @@
"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_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" ]
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9a98007..f5685bd 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -336,8 +336,14 @@
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 @@
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 @@
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
diff --git a/src/resolver/block_test.cc b/src/resolver/block_test.cc
deleted file mode 100644
index ab213f6..0000000
--- a/src/resolver/block_test.cc
+++ /dev/null
@@ -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
diff --git a/src/resolver/compound_statement_test.cc b/src/resolver/compound_statement_test.cc
new file mode 100644
index 0000000..a13cac6
--- /dev/null
+++ b/src/resolver/compound_statement_test.cc
@@ -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
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index 8390108..eac0b63 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -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 @@
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::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 @@
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 @@
}
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 @@
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::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 @@
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 @@
}
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 @@
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 @@
}
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();
}
diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h
index bd2bf62..680efe5 100644
--- a/src/resolver/resolver.h
+++ b/src/resolver/resolver.h
@@ -242,9 +242,11 @@
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 @@
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 @@
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 @@
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 @@
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_;
};
diff --git a/src/sem/block_statement.cc b/src/sem/block_statement.cc
index 9cdb421..614fd06 100644
--- a/src/sem/block_statement.cc
+++ b/src/sem/block_statement.cc
@@ -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() = 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
diff --git a/src/sem/block_statement.h b/src/sem/block_statement.h
index c61c3f8..cc76148 100644
--- a/src/sem/block_statement.h
+++ b/src/sem/block_statement.h
@@ -34,13 +34,13 @@
/// 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 @@
/// 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 @@
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 @@
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
diff --git a/src/sem/for_loop_statement.cc b/src/sem/for_loop_statement.cc
new file mode 100644
index 0000000..59b6738
--- /dev/null
+++ b/src/sem/for_loop_statement.cc
@@ -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
diff --git a/src/sem/for_loop_statement.h b/src/sem/for_loop_statement.h
new file mode 100644
index 0000000..2ee92e4
--- /dev/null
+++ b/src/sem/for_loop_statement.h
@@ -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_
diff --git a/src/sem/if_statement.cc b/src/sem/if_statement.cc
new file mode 100644
index 0000000..4b5d843
--- /dev/null
+++ b/src/sem/if_statement.cc
@@ -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
diff --git a/src/sem/if_statement.h b/src/sem/if_statement.h
new file mode 100644
index 0000000..d615a44
--- /dev/null
+++ b/src/sem/if_statement.h
@@ -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_
diff --git a/src/sem/loop_statement.cc b/src/sem/loop_statement.cc
new file mode 100644
index 0000000..62ea00d
--- /dev/null
+++ b/src/sem/loop_statement.cc
@@ -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
diff --git a/src/sem/loop_statement.h b/src/sem/loop_statement.h
new file mode 100644
index 0000000..c80bc8c
--- /dev/null
+++ b/src/sem/loop_statement.h
@@ -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_
diff --git a/src/sem/statement.cc b/src/sem/statement.cc
index 85ece66..0accb48 100644
--- a/src/sem/statement.cc
+++ b/src/sem/statement.cc
@@ -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 FindFirstParent<BlockStatement>();
+}
+
+const ast::Function* Statement::Function() const {
+ if (auto* fbs = FindFirstParent<FunctionBlockStatement>()) {
+ return fbs->Function();
}
return nullptr;
}
-const ast::Function* Statement::Function() const {
- if (auto* block = Block()) {
- if (auto* fbs = block->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
diff --git a/src/sem/statement.h b/src/sem/statement.h
index f58e7a7..821a8a5 100644
--- a/src/sem/statement.h
+++ b/src/sem/statement.h
@@ -31,19 +31,34 @@
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 @@
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
diff --git a/src/sem/switch_statement.cc b/src/sem/switch_statement.cc
new file mode 100644
index 0000000..c99ff71
--- /dev/null
+++ b/src/sem/switch_statement.cc
@@ -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
diff --git a/src/sem/switch_statement.h b/src/sem/switch_statement.h
new file mode 100644
index 0000000..f7bb735
--- /dev/null
+++ b/src/sem/switch_statement.h
@@ -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_
diff --git a/test/BUILD.gn b/test/BUILD.gn
index f80e072..5192ad9 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -229,10 +229,10 @@
"../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",