[WGSL] Allow default as a case selector
This CL updates the WGSL parser to parse `default` as a case selector value. Bug: tint:1633 Change-Id: I57661d25924e36bec5c03f96399c557fb7bbf760 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/106382 Commit-Queue: Dan Sinclair <dsinclair@chromium.org> Commit-Queue: Ben Clayton <bclayton@google.com> Auto-Submit: Dan Sinclair <dsinclair@chromium.org> Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
d27151d333
commit
f148f0891b
|
@ -215,6 +215,8 @@ libtint_source_set("libtint_core_all_src") {
|
||||||
"ast/call_expression.h",
|
"ast/call_expression.h",
|
||||||
"ast/call_statement.cc",
|
"ast/call_statement.cc",
|
||||||
"ast/call_statement.h",
|
"ast/call_statement.h",
|
||||||
|
"ast/case_selector.cc",
|
||||||
|
"ast/case_selector.h",
|
||||||
"ast/case_statement.cc",
|
"ast/case_statement.cc",
|
||||||
"ast/case_statement.h",
|
"ast/case_statement.h",
|
||||||
"ast/compound_assignment_statement.cc",
|
"ast/compound_assignment_statement.cc",
|
||||||
|
@ -1021,6 +1023,7 @@ if (tint_build_unittests) {
|
||||||
"ast/builtin_value_test.cc",
|
"ast/builtin_value_test.cc",
|
||||||
"ast/call_expression_test.cc",
|
"ast/call_expression_test.cc",
|
||||||
"ast/call_statement_test.cc",
|
"ast/call_statement_test.cc",
|
||||||
|
"ast/case_selector_test.cc",
|
||||||
"ast/case_statement_test.cc",
|
"ast/case_statement_test.cc",
|
||||||
"ast/compound_assignment_statement_test.cc",
|
"ast/compound_assignment_statement_test.cc",
|
||||||
"ast/continue_statement_test.cc",
|
"ast/continue_statement_test.cc",
|
||||||
|
|
|
@ -83,6 +83,8 @@ set(TINT_LIB_SRCS
|
||||||
ast/call_expression.h
|
ast/call_expression.h
|
||||||
ast/call_statement.cc
|
ast/call_statement.cc
|
||||||
ast/call_statement.h
|
ast/call_statement.h
|
||||||
|
ast/case_selector.cc
|
||||||
|
ast/case_selector.h
|
||||||
ast/case_statement.cc
|
ast/case_statement.cc
|
||||||
ast/case_statement.h
|
ast/case_statement.h
|
||||||
ast/compound_assignment_statement.cc
|
ast/compound_assignment_statement.cc
|
||||||
|
@ -713,6 +715,7 @@ if(TINT_BUILD_TESTS)
|
||||||
ast/builtin_value_test.cc
|
ast/builtin_value_test.cc
|
||||||
ast/call_expression_test.cc
|
ast/call_expression_test.cc
|
||||||
ast/call_statement_test.cc
|
ast/call_statement_test.cc
|
||||||
|
ast/case_selector_test.cc
|
||||||
ast/case_statement_test.cc
|
ast/case_statement_test.cc
|
||||||
ast/compound_assignment_statement_test.cc
|
ast/compound_assignment_statement_test.cc
|
||||||
ast/continue_statement_test.cc
|
ast/continue_statement_test.cc
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright 2022 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/tint/ast/case_selector.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "src/tint/program_builder.h"
|
||||||
|
|
||||||
|
TINT_INSTANTIATE_TYPEINFO(tint::ast::CaseSelector);
|
||||||
|
|
||||||
|
namespace tint::ast {
|
||||||
|
|
||||||
|
CaseSelector::CaseSelector(ProgramID pid, NodeID nid, const Source& src, const ast::Expression* e)
|
||||||
|
: Base(pid, nid, src), expr(e) {}
|
||||||
|
|
||||||
|
CaseSelector::CaseSelector(CaseSelector&&) = default;
|
||||||
|
|
||||||
|
CaseSelector::~CaseSelector() = default;
|
||||||
|
|
||||||
|
const CaseSelector* CaseSelector::Clone(CloneContext* ctx) const {
|
||||||
|
// Clone arguments outside of create() call to have deterministic ordering
|
||||||
|
auto src = ctx->Clone(source);
|
||||||
|
auto ex = ctx->Clone(expr);
|
||||||
|
return ctx->dst->create<CaseSelector>(src, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tint::ast
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright 2022 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_TINT_AST_CASE_SELECTOR_H_
|
||||||
|
#define SRC_TINT_AST_CASE_SELECTOR_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "src/tint/ast/block_statement.h"
|
||||||
|
#include "src/tint/ast/expression.h"
|
||||||
|
|
||||||
|
namespace tint::ast {
|
||||||
|
|
||||||
|
/// A case selector
|
||||||
|
class CaseSelector final : public Castable<CaseSelector, Node> {
|
||||||
|
public:
|
||||||
|
/// Constructor
|
||||||
|
/// @param pid the identifier of the program that owns this node
|
||||||
|
/// @param nid the unique node identifier
|
||||||
|
/// @param src the source of this node
|
||||||
|
/// @param expr the selector expression, |nullptr| for a `default` selector
|
||||||
|
CaseSelector(ProgramID pid, NodeID nid, const Source& src, const Expression* expr = nullptr);
|
||||||
|
/// Move constructor
|
||||||
|
CaseSelector(CaseSelector&&);
|
||||||
|
~CaseSelector() override;
|
||||||
|
|
||||||
|
/// @returns true if this is a default statement
|
||||||
|
bool IsDefault() const { return expr == nullptr; }
|
||||||
|
|
||||||
|
/// Clones this node and all transitive child nodes using the `CloneContext` `ctx`.
|
||||||
|
/// @param ctx the clone context
|
||||||
|
/// @return the newly cloned node
|
||||||
|
const CaseSelector* Clone(CloneContext* ctx) const override;
|
||||||
|
|
||||||
|
/// The selector, nullptr for a default selector
|
||||||
|
const Expression* const expr = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace tint::ast
|
||||||
|
|
||||||
|
#endif // SRC_TINT_AST_CASE_SELECTOR_H_
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright 2022 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/tint/ast/case_selector.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest-spi.h"
|
||||||
|
#include "src/tint/ast/test_helper.h"
|
||||||
|
|
||||||
|
using namespace tint::number_suffixes; // NOLINT
|
||||||
|
|
||||||
|
namespace tint::ast {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using CaseSelectorTest = TestHelper;
|
||||||
|
|
||||||
|
TEST_F(CaseSelectorTest, NonDefault) {
|
||||||
|
auto* e = Expr(2_i);
|
||||||
|
auto* c = CaseSelector(e);
|
||||||
|
EXPECT_FALSE(c->IsDefault());
|
||||||
|
EXPECT_EQ(e, c->expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CaseSelectorTest, Default) {
|
||||||
|
auto* c = DefaultCaseSelector();
|
||||||
|
EXPECT_TRUE(c->IsDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace tint::ast
|
|
@ -25,10 +25,11 @@ namespace tint::ast {
|
||||||
CaseStatement::CaseStatement(ProgramID pid,
|
CaseStatement::CaseStatement(ProgramID pid,
|
||||||
NodeID nid,
|
NodeID nid,
|
||||||
const Source& src,
|
const Source& src,
|
||||||
utils::VectorRef<const Expression*> s,
|
utils::VectorRef<const CaseSelector*> s,
|
||||||
const BlockStatement* b)
|
const BlockStatement* b)
|
||||||
: Base(pid, nid, src), selectors(std::move(s)), body(b) {
|
: Base(pid, nid, src), selectors(std::move(s)), body(b) {
|
||||||
TINT_ASSERT(AST, body);
|
TINT_ASSERT(AST, body);
|
||||||
|
TINT_ASSERT(AST, !selectors.IsEmpty());
|
||||||
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, body, program_id);
|
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, body, program_id);
|
||||||
for (auto* selector : selectors) {
|
for (auto* selector : selectors) {
|
||||||
TINT_ASSERT(AST, selector);
|
TINT_ASSERT(AST, selector);
|
||||||
|
@ -40,6 +41,15 @@ CaseStatement::CaseStatement(CaseStatement&&) = default;
|
||||||
|
|
||||||
CaseStatement::~CaseStatement() = default;
|
CaseStatement::~CaseStatement() = default;
|
||||||
|
|
||||||
|
bool CaseStatement::ContainsDefault() const {
|
||||||
|
for (const auto* sel : selectors) {
|
||||||
|
if (sel->IsDefault()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const CaseStatement* CaseStatement::Clone(CloneContext* ctx) const {
|
const CaseStatement* CaseStatement::Clone(CloneContext* ctx) const {
|
||||||
// Clone arguments outside of create() call to have deterministic ordering
|
// Clone arguments outside of create() call to have deterministic ordering
|
||||||
auto src = ctx->Clone(source);
|
auto src = ctx->Clone(source);
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "src/tint/ast/block_statement.h"
|
#include "src/tint/ast/block_statement.h"
|
||||||
#include "src/tint/ast/expression.h"
|
#include "src/tint/ast/case_selector.h"
|
||||||
|
|
||||||
namespace tint::ast {
|
namespace tint::ast {
|
||||||
|
|
||||||
|
@ -34,23 +34,23 @@ class CaseStatement final : public Castable<CaseStatement, Statement> {
|
||||||
CaseStatement(ProgramID pid,
|
CaseStatement(ProgramID pid,
|
||||||
NodeID nid,
|
NodeID nid,
|
||||||
const Source& src,
|
const Source& src,
|
||||||
utils::VectorRef<const Expression*> selectors,
|
utils::VectorRef<const CaseSelector*> selectors,
|
||||||
const BlockStatement* body);
|
const BlockStatement* body);
|
||||||
/// Move constructor
|
/// Move constructor
|
||||||
CaseStatement(CaseStatement&&);
|
CaseStatement(CaseStatement&&);
|
||||||
~CaseStatement() override;
|
~CaseStatement() override;
|
||||||
|
|
||||||
/// @returns true if this is a default statement
|
|
||||||
bool IsDefault() const { return selectors.IsEmpty(); }
|
|
||||||
|
|
||||||
/// Clones this node and all transitive child nodes using the `CloneContext`
|
/// Clones this node and all transitive child nodes using the `CloneContext`
|
||||||
/// `ctx`.
|
/// `ctx`.
|
||||||
/// @param ctx the clone context
|
/// @param ctx the clone context
|
||||||
/// @return the newly cloned node
|
/// @return the newly cloned node
|
||||||
const CaseStatement* Clone(CloneContext* ctx) const override;
|
const CaseStatement* Clone(CloneContext* ctx) const override;
|
||||||
|
|
||||||
|
/// @returns true if this item contains a default selector
|
||||||
|
bool ContainsDefault() const;
|
||||||
|
|
||||||
/// The case selectors, empty if none set
|
/// The case selectors, empty if none set
|
||||||
const utils::Vector<const Expression*, 4> selectors;
|
const utils::Vector<const CaseSelector*, 4> selectors;
|
||||||
|
|
||||||
/// The case body
|
/// The case body
|
||||||
const BlockStatement* const body;
|
const BlockStatement* const body;
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace {
|
||||||
using CaseStatementTest = TestHelper;
|
using CaseStatementTest = TestHelper;
|
||||||
|
|
||||||
TEST_F(CaseStatementTest, Creation_i32) {
|
TEST_F(CaseStatementTest, Creation_i32) {
|
||||||
auto* selector = Expr(2_i);
|
auto* selector = CaseSelector(2_i);
|
||||||
utils::Vector b{selector};
|
utils::Vector b{selector};
|
||||||
|
|
||||||
auto* discard = create<DiscardStatement>();
|
auto* discard = create<DiscardStatement>();
|
||||||
|
@ -41,7 +41,7 @@ TEST_F(CaseStatementTest, Creation_i32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CaseStatementTest, Creation_u32) {
|
TEST_F(CaseStatementTest, Creation_u32) {
|
||||||
auto* selector = Expr(2_u);
|
auto* selector = CaseSelector(2_u);
|
||||||
utils::Vector b{selector};
|
utils::Vector b{selector};
|
||||||
|
|
||||||
auto* discard = create<DiscardStatement>();
|
auto* discard = create<DiscardStatement>();
|
||||||
|
@ -54,8 +54,20 @@ TEST_F(CaseStatementTest, Creation_u32) {
|
||||||
EXPECT_EQ(c->body->statements[0], discard);
|
EXPECT_EQ(c->body->statements[0], discard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(CaseStatementTest, ContainsDefault_WithDefault) {
|
||||||
|
utils::Vector b{CaseSelector(2_u), DefaultCaseSelector()};
|
||||||
|
auto* c = create<CaseStatement>(b, create<BlockStatement>(utils::Empty));
|
||||||
|
EXPECT_TRUE(c->ContainsDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CaseStatementTest, ContainsDefault_WithOutDefault) {
|
||||||
|
utils::Vector b{CaseSelector(2_u), CaseSelector(3_u)};
|
||||||
|
auto* c = create<CaseStatement>(b, create<BlockStatement>(utils::Empty));
|
||||||
|
EXPECT_FALSE(c->ContainsDefault());
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(CaseStatementTest, Creation_WithSource) {
|
TEST_F(CaseStatementTest, Creation_WithSource) {
|
||||||
utils::Vector b{Expr(2_i)};
|
utils::Vector b{CaseSelector(2_i)};
|
||||||
|
|
||||||
auto* body = create<BlockStatement>(utils::Vector{
|
auto* body = create<BlockStatement>(utils::Vector{
|
||||||
create<DiscardStatement>(),
|
create<DiscardStatement>(),
|
||||||
|
@ -66,22 +78,9 @@ TEST_F(CaseStatementTest, Creation_WithSource) {
|
||||||
EXPECT_EQ(src.range.begin.column, 2u);
|
EXPECT_EQ(src.range.begin.column, 2u);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CaseStatementTest, IsDefault_WithoutSelectors) {
|
|
||||||
auto* body = create<BlockStatement>(utils::Vector{
|
|
||||||
create<DiscardStatement>(),
|
|
||||||
});
|
|
||||||
auto* c = create<CaseStatement>(utils::Empty, body);
|
|
||||||
EXPECT_TRUE(c->IsDefault());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(CaseStatementTest, IsDefault_WithSelectors) {
|
|
||||||
utils::Vector b{Expr(2_i)};
|
|
||||||
auto* c = create<CaseStatement>(b, create<BlockStatement>(utils::Empty));
|
|
||||||
EXPECT_FALSE(c->IsDefault());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(CaseStatementTest, IsCase) {
|
TEST_F(CaseStatementTest, IsCase) {
|
||||||
auto* c = create<CaseStatement>(utils::Empty, create<BlockStatement>(utils::Empty));
|
auto* c = create<CaseStatement>(utils::Vector{DefaultCaseSelector()},
|
||||||
|
create<BlockStatement>(utils::Empty));
|
||||||
EXPECT_TRUE(c->Is<CaseStatement>());
|
EXPECT_TRUE(c->Is<CaseStatement>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +88,7 @@ TEST_F(CaseStatementTest, Assert_Null_Body) {
|
||||||
EXPECT_FATAL_FAILURE(
|
EXPECT_FATAL_FAILURE(
|
||||||
{
|
{
|
||||||
ProgramBuilder b;
|
ProgramBuilder b;
|
||||||
b.create<CaseStatement>(utils::Empty, nullptr);
|
b.create<CaseStatement>(utils::Vector{b.DefaultCaseSelector()}, nullptr);
|
||||||
},
|
},
|
||||||
"internal compiler error");
|
"internal compiler error");
|
||||||
}
|
}
|
||||||
|
@ -98,7 +97,7 @@ TEST_F(CaseStatementTest, Assert_Null_Selector) {
|
||||||
EXPECT_FATAL_FAILURE(
|
EXPECT_FATAL_FAILURE(
|
||||||
{
|
{
|
||||||
ProgramBuilder b;
|
ProgramBuilder b;
|
||||||
b.create<CaseStatement>(utils::Vector<const ast::IntLiteralExpression*, 1>{nullptr},
|
b.create<CaseStatement>(utils::Vector<const ast::CaseSelector*, 1>{nullptr},
|
||||||
b.create<BlockStatement>(utils::Empty));
|
b.create<BlockStatement>(utils::Empty));
|
||||||
},
|
},
|
||||||
"internal compiler error");
|
"internal compiler error");
|
||||||
|
@ -109,7 +108,8 @@ TEST_F(CaseStatementTest, Assert_DifferentProgramID_Call) {
|
||||||
{
|
{
|
||||||
ProgramBuilder b1;
|
ProgramBuilder b1;
|
||||||
ProgramBuilder b2;
|
ProgramBuilder b2;
|
||||||
b1.create<CaseStatement>(utils::Empty, b2.create<BlockStatement>(utils::Empty));
|
b1.create<CaseStatement>(utils::Vector{b1.DefaultCaseSelector()},
|
||||||
|
b2.create<BlockStatement>(utils::Empty));
|
||||||
},
|
},
|
||||||
"internal compiler error");
|
"internal compiler error");
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ TEST_F(CaseStatementTest, Assert_DifferentProgramID_Selector) {
|
||||||
{
|
{
|
||||||
ProgramBuilder b1;
|
ProgramBuilder b1;
|
||||||
ProgramBuilder b2;
|
ProgramBuilder b2;
|
||||||
b1.create<CaseStatement>(utils::Vector{b2.Expr(2_i)},
|
b1.create<CaseStatement>(utils::Vector{b2.CaseSelector(b2.Expr(2_i))},
|
||||||
b1.create<BlockStatement>(utils::Empty));
|
b1.create<BlockStatement>(utils::Empty));
|
||||||
},
|
},
|
||||||
"internal compiler error");
|
"internal compiler error");
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace {
|
||||||
using SwitchStatementTest = TestHelper;
|
using SwitchStatementTest = TestHelper;
|
||||||
|
|
||||||
TEST_F(SwitchStatementTest, Creation) {
|
TEST_F(SwitchStatementTest, Creation) {
|
||||||
auto* case_stmt = create<CaseStatement>(utils::Vector{Expr(1_u)}, Block());
|
auto* case_stmt = create<CaseStatement>(utils::Vector{CaseSelector(1_u)}, Block());
|
||||||
auto* ident = Expr("ident");
|
auto* ident = Expr("ident");
|
||||||
utils::Vector body{case_stmt};
|
utils::Vector body{case_stmt};
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ TEST_F(SwitchStatementTest, Creation_WithSource) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SwitchStatementTest, IsSwitch) {
|
TEST_F(SwitchStatementTest, IsSwitch) {
|
||||||
utils::Vector lit{Expr(2_i)};
|
utils::Vector lit{CaseSelector(2_i)};
|
||||||
auto* ident = Expr("ident");
|
auto* ident = Expr("ident");
|
||||||
utils::Vector body{create<CaseStatement>(lit, Block())};
|
utils::Vector body{create<CaseStatement>(lit, Block())};
|
||||||
|
|
||||||
|
@ -58,7 +58,8 @@ TEST_F(SwitchStatementTest, Assert_Null_Condition) {
|
||||||
{
|
{
|
||||||
ProgramBuilder b;
|
ProgramBuilder b;
|
||||||
CaseStatementList cases;
|
CaseStatementList cases;
|
||||||
cases.Push(b.create<CaseStatement>(utils::Vector{b.Expr(1_i)}, b.Block()));
|
cases.Push(
|
||||||
|
b.create<CaseStatement>(utils::Vector{b.CaseSelector(b.Expr(1_i))}, b.Block()));
|
||||||
b.create<SwitchStatement>(nullptr, cases);
|
b.create<SwitchStatement>(nullptr, cases);
|
||||||
},
|
},
|
||||||
"internal compiler error");
|
"internal compiler error");
|
||||||
|
@ -82,7 +83,7 @@ TEST_F(SwitchStatementTest, Assert_DifferentProgramID_Condition) {
|
||||||
b1.create<SwitchStatement>(b2.Expr(true), utils::Vector{
|
b1.create<SwitchStatement>(b2.Expr(true), utils::Vector{
|
||||||
b1.create<CaseStatement>(
|
b1.create<CaseStatement>(
|
||||||
utils::Vector{
|
utils::Vector{
|
||||||
b1.Expr(1_i),
|
b1.CaseSelector(b1.Expr(1_i)),
|
||||||
},
|
},
|
||||||
b1.Block()),
|
b1.Block()),
|
||||||
});
|
});
|
||||||
|
@ -98,7 +99,7 @@ TEST_F(SwitchStatementTest, Assert_DifferentProgramID_CaseStatement) {
|
||||||
b1.create<SwitchStatement>(b1.Expr(true), utils::Vector{
|
b1.create<SwitchStatement>(b1.Expr(true), utils::Vector{
|
||||||
b2.create<CaseStatement>(
|
b2.create<CaseStatement>(
|
||||||
utils::Vector{
|
utils::Vector{
|
||||||
b2.Expr(1_i),
|
b2.CaseSelector(b2.Expr(1_i)),
|
||||||
},
|
},
|
||||||
b2.Block()),
|
b2.Block()),
|
||||||
});
|
});
|
||||||
|
|
|
@ -2847,7 +2847,7 @@ class ProgramBuilder {
|
||||||
/// @param body the case body
|
/// @param body the case body
|
||||||
/// @returns the case statement pointer
|
/// @returns the case statement pointer
|
||||||
const ast::CaseStatement* Case(const Source& source,
|
const ast::CaseStatement* Case(const Source& source,
|
||||||
utils::VectorRef<const ast::Expression*> selectors,
|
utils::VectorRef<const ast::CaseSelector*> selectors,
|
||||||
const ast::BlockStatement* body = nullptr) {
|
const ast::BlockStatement* body = nullptr) {
|
||||||
return create<ast::CaseStatement>(source, std::move(selectors), body ? body : Block());
|
return create<ast::CaseStatement>(source, std::move(selectors), body ? body : Block());
|
||||||
}
|
}
|
||||||
|
@ -2856,7 +2856,7 @@ class ProgramBuilder {
|
||||||
/// @param selectors list of selectors
|
/// @param selectors list of selectors
|
||||||
/// @param body the case body
|
/// @param body the case body
|
||||||
/// @returns the case statement pointer
|
/// @returns the case statement pointer
|
||||||
const ast::CaseStatement* Case(utils::VectorRef<const ast::Expression*> selectors,
|
const ast::CaseStatement* Case(utils::VectorRef<const ast::CaseSelector*> selectors,
|
||||||
const ast::BlockStatement* body = nullptr) {
|
const ast::BlockStatement* body = nullptr) {
|
||||||
return create<ast::CaseStatement>(std::move(selectors), body ? body : Block());
|
return create<ast::CaseStatement>(std::move(selectors), body ? body : Block());
|
||||||
}
|
}
|
||||||
|
@ -2865,7 +2865,7 @@ class ProgramBuilder {
|
||||||
/// @param selector a single case selector
|
/// @param selector a single case selector
|
||||||
/// @param body the case body
|
/// @param body the case body
|
||||||
/// @returns the case statement pointer
|
/// @returns the case statement pointer
|
||||||
const ast::CaseStatement* Case(const ast::Expression* selector,
|
const ast::CaseStatement* Case(const ast::CaseSelector* selector,
|
||||||
const ast::BlockStatement* body = nullptr) {
|
const ast::BlockStatement* body = nullptr) {
|
||||||
return Case(utils::Vector{selector}, body);
|
return Case(utils::Vector{selector}, body);
|
||||||
}
|
}
|
||||||
|
@ -2876,16 +2876,44 @@ class ProgramBuilder {
|
||||||
/// @returns the case statement pointer
|
/// @returns the case statement pointer
|
||||||
const ast::CaseStatement* DefaultCase(const Source& source,
|
const ast::CaseStatement* DefaultCase(const Source& source,
|
||||||
const ast::BlockStatement* body = nullptr) {
|
const ast::BlockStatement* body = nullptr) {
|
||||||
return Case(source, utils::Empty, body);
|
return Case(source, utils::Vector{DefaultCaseSelector(source)}, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience function that creates a 'default' ast::CaseStatement
|
/// Convenience function that creates a 'default' ast::CaseStatement
|
||||||
/// @param body the case body
|
/// @param body the case body
|
||||||
/// @returns the case statement pointer
|
/// @returns the case statement pointer
|
||||||
const ast::CaseStatement* DefaultCase(const ast::BlockStatement* body = nullptr) {
|
const ast::CaseStatement* DefaultCase(const ast::BlockStatement* body = nullptr) {
|
||||||
return Case(utils::Empty, body);
|
return Case(utils::Vector{DefaultCaseSelector()}, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience function that creates a case selector
|
||||||
|
/// @param source the source information
|
||||||
|
/// @param expr the selector expression
|
||||||
|
/// @returns the selector pointer
|
||||||
|
template <typename EXPR>
|
||||||
|
const ast::CaseSelector* CaseSelector(const Source& source, EXPR&& expr) {
|
||||||
|
return create<ast::CaseSelector>(source, Expr(std::forward<EXPR>(expr)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience function that creates a case selector
|
||||||
|
/// @param expr the selector expression
|
||||||
|
/// @returns the selector pointer
|
||||||
|
template <typename EXPR>
|
||||||
|
const ast::CaseSelector* CaseSelector(EXPR&& expr) {
|
||||||
|
return create<ast::CaseSelector>(source_, Expr(std::forward<EXPR>(expr)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience function that creates a default case selector
|
||||||
|
/// @param source the source information
|
||||||
|
/// @returns the selector pointer
|
||||||
|
const ast::CaseSelector* DefaultCaseSelector(const Source& source) {
|
||||||
|
return create<ast::CaseSelector>(source, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience function that creates a default case selector
|
||||||
|
/// @returns the selector pointer
|
||||||
|
const ast::CaseSelector* DefaultCaseSelector() { return create<ast::CaseSelector>(nullptr); }
|
||||||
|
|
||||||
/// Creates an ast::FallthroughStatement
|
/// Creates an ast::FallthroughStatement
|
||||||
/// @param source the source information
|
/// @param source the source information
|
||||||
/// @returns the fallthrough statement pointer
|
/// @returns the fallthrough statement pointer
|
||||||
|
|
|
@ -3024,7 +3024,7 @@ bool FunctionEmitter::EmitSwitchStart(const BlockInfo& block_info) {
|
||||||
for (size_t i = last_clause_index;; --i) {
|
for (size_t i = last_clause_index;; --i) {
|
||||||
// Create a list of integer literals for the selector values leading to
|
// Create a list of integer literals for the selector values leading to
|
||||||
// this case clause.
|
// this case clause.
|
||||||
utils::Vector<const ast::Expression*, 4> selectors;
|
utils::Vector<const ast::CaseSelector*, 4> selectors;
|
||||||
const bool has_selectors = clause_heads[i]->case_values.has_value();
|
const bool has_selectors = clause_heads[i]->case_values.has_value();
|
||||||
if (has_selectors) {
|
if (has_selectors) {
|
||||||
auto values = clause_heads[i]->case_values.value();
|
auto values = clause_heads[i]->case_values.value();
|
||||||
|
@ -3034,15 +3034,26 @@ bool FunctionEmitter::EmitSwitchStart(const BlockInfo& block_info) {
|
||||||
// The Tint AST handles 32-bit values.
|
// The Tint AST handles 32-bit values.
|
||||||
const uint32_t value32 = uint32_t(value & 0xFFFFFFFF);
|
const uint32_t value32 = uint32_t(value & 0xFFFFFFFF);
|
||||||
if (selector.type->IsUnsignedScalarOrVector()) {
|
if (selector.type->IsUnsignedScalarOrVector()) {
|
||||||
selectors.Push(create<ast::IntLiteralExpression>(
|
selectors.Push(create<ast::CaseSelector>(
|
||||||
Source{}, value32, ast::IntLiteralExpression::Suffix::kU));
|
Source{}, create<ast::IntLiteralExpression>(
|
||||||
|
Source{}, value32, ast::IntLiteralExpression::Suffix::kU)));
|
||||||
} else {
|
} else {
|
||||||
selectors.Push(
|
selectors.Push(create<ast::CaseSelector>(
|
||||||
|
Source{},
|
||||||
create<ast::IntLiteralExpression>(Source{}, static_cast<int32_t>(value32),
|
create<ast::IntLiteralExpression>(Source{}, static_cast<int32_t>(value32),
|
||||||
ast::IntLiteralExpression::Suffix::kI));
|
ast::IntLiteralExpression::Suffix::kI)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((default_info == clause_heads[i]) && construct->ContainsPos(default_info->pos)) {
|
||||||
|
// Generate a default selector
|
||||||
|
selectors.Push(create<ast::CaseSelector>(Source{}));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Generate a default selector
|
||||||
|
selectors.Push(create<ast::CaseSelector>(Source{}));
|
||||||
}
|
}
|
||||||
|
TINT_ASSERT(Reader, !selectors.IsEmpty());
|
||||||
|
|
||||||
// Where does this clause end?
|
// Where does this clause end?
|
||||||
const auto end_id =
|
const auto end_id =
|
||||||
|
@ -3057,17 +3068,6 @@ bool FunctionEmitter::EmitSwitchStart(const BlockInfo& block_info) {
|
||||||
swch->cases[case_idx] = create<ast::CaseStatement>(Source{}, selectors, body);
|
swch->cases[case_idx] = create<ast::CaseStatement>(Source{}, selectors, body);
|
||||||
});
|
});
|
||||||
|
|
||||||
if ((default_info == clause_heads[i]) && has_selectors &&
|
|
||||||
construct->ContainsPos(default_info->pos)) {
|
|
||||||
// Generate a default clause with a just fallthrough.
|
|
||||||
auto* stmts = create<ast::BlockStatement>(
|
|
||||||
Source{}, StatementList{
|
|
||||||
create<ast::FallthroughStatement>(Source{}),
|
|
||||||
});
|
|
||||||
auto* case_stmt = create<ast::CaseStatement>(Source{}, utils::Empty, stmts);
|
|
||||||
swch->cases.Push(case_stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9349,10 +9349,7 @@ switch(42u) {
|
||||||
case 20u: {
|
case 20u: {
|
||||||
var_1 = 20u;
|
var_1 = 20u;
|
||||||
}
|
}
|
||||||
default: {
|
case 30u, default: {
|
||||||
fallthrough;
|
|
||||||
}
|
|
||||||
case 30u: {
|
|
||||||
var_1 = 30u;
|
var_1 = 30u;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1393,10 +1393,7 @@ TEST_F(SpvParserFunctionVarTest, EmitStatement_Phi_InMerge_PredecessorsDominatdB
|
||||||
auto got = test::ToString(p->program(), ast_body);
|
auto got = test::ToString(p->program(), ast_body);
|
||||||
auto* expect = R"(var x_41 : u32;
|
auto* expect = R"(var x_41 : u32;
|
||||||
switch(1u) {
|
switch(1u) {
|
||||||
default: {
|
case 0u, default: {
|
||||||
fallthrough;
|
|
||||||
}
|
|
||||||
case 0u: {
|
|
||||||
fallthrough;
|
fallthrough;
|
||||||
}
|
}
|
||||||
case 1u: {
|
case 1u: {
|
||||||
|
|
|
@ -2129,6 +2129,9 @@ Maybe<const ast::CaseStatement*> ParserImpl::switch_body() {
|
||||||
}
|
}
|
||||||
|
|
||||||
selector_list = std::move(selectors.value);
|
selector_list = std::move(selectors.value);
|
||||||
|
} else {
|
||||||
|
// Push the default case selector
|
||||||
|
selector_list.Push(create<ast::CaseSelector>(t.source()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consume the optional colon if present.
|
// Consume the optional colon if present.
|
||||||
|
@ -2148,12 +2151,12 @@ Maybe<const ast::CaseStatement*> ParserImpl::switch_body() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// case_selectors
|
// case_selectors
|
||||||
// : expression (COMMA expression)* COMMA?
|
// : case_selector (COMMA case_selector)* COMMA?
|
||||||
Expect<ParserImpl::CaseSelectorList> ParserImpl::expect_case_selectors() {
|
Expect<ParserImpl::CaseSelectorList> ParserImpl::expect_case_selectors() {
|
||||||
CaseSelectorList selectors;
|
CaseSelectorList selectors;
|
||||||
|
|
||||||
while (continue_parsing()) {
|
while (continue_parsing()) {
|
||||||
auto expr = expression();
|
auto expr = case_selector();
|
||||||
if (expr.errored) {
|
if (expr.errored) {
|
||||||
return Failure::kErrored;
|
return Failure::kErrored;
|
||||||
}
|
}
|
||||||
|
@ -2168,12 +2171,32 @@ Expect<ParserImpl::CaseSelectorList> ParserImpl::expect_case_selectors() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectors.IsEmpty()) {
|
if (selectors.IsEmpty()) {
|
||||||
return add_error(peek(), "unable to parse case selectors");
|
return add_error(peek(), "expected case selector expression or `default`");
|
||||||
}
|
}
|
||||||
|
|
||||||
return selectors;
|
return selectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// case_selector
|
||||||
|
// : DEFAULT
|
||||||
|
// | expression
|
||||||
|
Maybe<const ast::CaseSelector*> ParserImpl::case_selector() {
|
||||||
|
auto& p = peek();
|
||||||
|
|
||||||
|
if (match(Token::Type::kDefault)) {
|
||||||
|
return create<ast::CaseSelector>(p.source());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto expr = expression();
|
||||||
|
if (expr.errored) {
|
||||||
|
return Failure::kErrored;
|
||||||
|
}
|
||||||
|
if (!expr.matched) {
|
||||||
|
return Failure::kNoMatch;
|
||||||
|
}
|
||||||
|
return create<ast::CaseSelector>(p.source(), expr.value);
|
||||||
|
}
|
||||||
|
|
||||||
// case_body
|
// case_body
|
||||||
// :
|
// :
|
||||||
// | statement case_body
|
// | statement case_body
|
||||||
|
|
|
@ -74,7 +74,7 @@ class ParserImpl {
|
||||||
/// Pre-determined small vector sizes for AST pointers
|
/// Pre-determined small vector sizes for AST pointers
|
||||||
//! @cond Doxygen_Suppress
|
//! @cond Doxygen_Suppress
|
||||||
using AttributeList = utils::Vector<const ast::Attribute*, 4>;
|
using AttributeList = utils::Vector<const ast::Attribute*, 4>;
|
||||||
using CaseSelectorList = utils::Vector<const ast::Expression*, 4>;
|
using CaseSelectorList = utils::Vector<const ast::CaseSelector*, 4>;
|
||||||
using CaseStatementList = utils::Vector<const ast::CaseStatement*, 4>;
|
using CaseStatementList = utils::Vector<const ast::CaseStatement*, 4>;
|
||||||
using ExpressionList = utils::Vector<const ast::Expression*, 8>;
|
using ExpressionList = utils::Vector<const ast::Expression*, 8>;
|
||||||
using ParameterList = utils::Vector<const ast::Parameter*, 8>;
|
using ParameterList = utils::Vector<const ast::Parameter*, 8>;
|
||||||
|
@ -573,6 +573,9 @@ class ParserImpl {
|
||||||
/// Parses a `case_selectors` grammar element
|
/// Parses a `case_selectors` grammar element
|
||||||
/// @returns the list of literals
|
/// @returns the list of literals
|
||||||
Expect<CaseSelectorList> expect_case_selectors();
|
Expect<CaseSelectorList> expect_case_selectors();
|
||||||
|
/// Parses a `case_selector` grammar element
|
||||||
|
/// @returns the selector
|
||||||
|
Maybe<const ast::CaseSelector*> case_selector();
|
||||||
/// Parses a `case_body` grammar element
|
/// Parses a `case_body` grammar element
|
||||||
/// @returns the parsed statements
|
/// @returns the parsed statements
|
||||||
Maybe<const ast::BlockStatement*> case_body();
|
Maybe<const ast::BlockStatement*> case_body();
|
||||||
|
|
|
@ -1333,7 +1333,7 @@ fn f() { switch(1) {
|
||||||
|
|
||||||
TEST_F(ParserImplErrorTest, SwitchStmtInvalidCase) {
|
TEST_F(ParserImplErrorTest, SwitchStmtInvalidCase) {
|
||||||
EXPECT("fn f() { switch(1) { case ^: } }",
|
EXPECT("fn f() { switch(1) { case ^: } }",
|
||||||
R"(test.wgsl:1:27 error: unable to parse case selectors
|
R"(test.wgsl:1:27 error: expected case selector expression or `default`
|
||||||
fn f() { switch(1) { case ^: } }
|
fn f() { switch(1) { case ^: } }
|
||||||
^
|
^
|
||||||
)");
|
)");
|
||||||
|
|
|
@ -143,7 +143,7 @@ TEST_F(ParserImplTest, Statement_Switch_Invalid) {
|
||||||
EXPECT_TRUE(e.errored);
|
EXPECT_TRUE(e.errored);
|
||||||
EXPECT_FALSE(e.matched);
|
EXPECT_FALSE(e.matched);
|
||||||
EXPECT_EQ(e.value, nullptr);
|
EXPECT_EQ(e.value, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:18: unable to parse case selectors");
|
EXPECT_EQ(p->error(), "1:18: expected case selector expression or `default`");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, Statement_Loop) {
|
TEST_F(ParserImplTest, Statement_Loop) {
|
||||||
|
|
|
@ -25,13 +25,16 @@ TEST_F(ParserImplTest, SwitchBody_Case) {
|
||||||
EXPECT_FALSE(e.errored);
|
EXPECT_FALSE(e.errored);
|
||||||
ASSERT_NE(e.value, nullptr);
|
ASSERT_NE(e.value, nullptr);
|
||||||
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
||||||
EXPECT_FALSE(e->IsDefault());
|
EXPECT_FALSE(e->ContainsDefault());
|
||||||
|
|
||||||
auto* stmt = e->As<ast::CaseStatement>();
|
auto* stmt = e->As<ast::CaseStatement>();
|
||||||
ASSERT_EQ(stmt->selectors.Length(), 1u);
|
ASSERT_EQ(stmt->selectors.Length(), 1u);
|
||||||
ASSERT_TRUE(stmt->selectors[0]->Is<ast::IntLiteralExpression>());
|
|
||||||
|
|
||||||
auto* expr = stmt->selectors[0]->As<ast::IntLiteralExpression>();
|
auto* sel = stmt->selectors[0];
|
||||||
|
EXPECT_FALSE(sel->IsDefault());
|
||||||
|
ASSERT_TRUE(sel->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
|
||||||
|
auto* expr = sel->expr->As<ast::IntLiteralExpression>();
|
||||||
EXPECT_EQ(expr->value, 1);
|
EXPECT_EQ(expr->value, 1);
|
||||||
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
ASSERT_EQ(e->body->statements.Length(), 1u);
|
ASSERT_EQ(e->body->statements.Length(), 1u);
|
||||||
|
@ -46,12 +49,16 @@ TEST_F(ParserImplTest, SwitchBody_Case_Expression) {
|
||||||
EXPECT_FALSE(e.errored);
|
EXPECT_FALSE(e.errored);
|
||||||
ASSERT_NE(e.value, nullptr);
|
ASSERT_NE(e.value, nullptr);
|
||||||
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
||||||
EXPECT_FALSE(e->IsDefault());
|
EXPECT_FALSE(e->ContainsDefault());
|
||||||
|
|
||||||
auto* stmt = e->As<ast::CaseStatement>();
|
auto* stmt = e->As<ast::CaseStatement>();
|
||||||
ASSERT_EQ(stmt->selectors.Length(), 1u);
|
ASSERT_EQ(stmt->selectors.Length(), 1u);
|
||||||
ASSERT_TRUE(stmt->selectors[0]->Is<ast::BinaryExpression>());
|
|
||||||
auto* expr = stmt->selectors[0]->As<ast::BinaryExpression>();
|
auto* sel = stmt->selectors[0];
|
||||||
|
EXPECT_FALSE(sel->IsDefault());
|
||||||
|
|
||||||
|
ASSERT_TRUE(sel->expr->Is<ast::BinaryExpression>());
|
||||||
|
auto* expr = sel->expr->As<ast::BinaryExpression>();
|
||||||
|
|
||||||
EXPECT_EQ(ast::BinaryOp::kAdd, expr->op);
|
EXPECT_EQ(ast::BinaryOp::kAdd, expr->op);
|
||||||
auto* v = expr->lhs->As<ast::IntLiteralExpression>();
|
auto* v = expr->lhs->As<ast::IntLiteralExpression>();
|
||||||
|
@ -74,13 +81,16 @@ TEST_F(ParserImplTest, SwitchBody_Case_WithColon) {
|
||||||
EXPECT_FALSE(e.errored);
|
EXPECT_FALSE(e.errored);
|
||||||
ASSERT_NE(e.value, nullptr);
|
ASSERT_NE(e.value, nullptr);
|
||||||
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
||||||
EXPECT_FALSE(e->IsDefault());
|
EXPECT_FALSE(e->ContainsDefault());
|
||||||
|
|
||||||
auto* stmt = e->As<ast::CaseStatement>();
|
auto* stmt = e->As<ast::CaseStatement>();
|
||||||
ASSERT_EQ(stmt->selectors.Length(), 1u);
|
ASSERT_EQ(stmt->selectors.Length(), 1u);
|
||||||
ASSERT_TRUE(stmt->selectors[0]->Is<ast::IntLiteralExpression>());
|
auto* sel = stmt->selectors[0];
|
||||||
|
EXPECT_FALSE(sel->IsDefault());
|
||||||
|
|
||||||
|
ASSERT_TRUE(sel->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
auto* expr = sel->expr->As<ast::IntLiteralExpression>();
|
||||||
|
|
||||||
auto* expr = stmt->selectors[0]->As<ast::IntLiteralExpression>();
|
|
||||||
EXPECT_EQ(expr->value, 1);
|
EXPECT_EQ(expr->value, 1);
|
||||||
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
ASSERT_EQ(e->body->statements.Length(), 1u);
|
ASSERT_EQ(e->body->statements.Length(), 1u);
|
||||||
|
@ -95,17 +105,20 @@ TEST_F(ParserImplTest, SwitchBody_Case_TrailingComma) {
|
||||||
EXPECT_FALSE(e.errored);
|
EXPECT_FALSE(e.errored);
|
||||||
ASSERT_NE(e.value, nullptr);
|
ASSERT_NE(e.value, nullptr);
|
||||||
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
||||||
EXPECT_FALSE(e->IsDefault());
|
EXPECT_FALSE(e->ContainsDefault());
|
||||||
|
|
||||||
auto* stmt = e->As<ast::CaseStatement>();
|
auto* stmt = e->As<ast::CaseStatement>();
|
||||||
ASSERT_EQ(stmt->selectors.Length(), 2u);
|
ASSERT_EQ(stmt->selectors.Length(), 2u);
|
||||||
ASSERT_TRUE(stmt->selectors[0]->Is<ast::IntLiteralExpression>());
|
auto* sel = stmt->selectors[0];
|
||||||
|
|
||||||
auto* expr = stmt->selectors[0]->As<ast::IntLiteralExpression>();
|
ASSERT_TRUE(sel->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
auto* expr = sel->expr->As<ast::IntLiteralExpression>();
|
||||||
EXPECT_EQ(expr->value, 1);
|
EXPECT_EQ(expr->value, 1);
|
||||||
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
|
|
||||||
ASSERT_TRUE(stmt->selectors[1]->Is<ast::IntLiteralExpression>());
|
sel = stmt->selectors[1];
|
||||||
expr = stmt->selectors[1]->As<ast::IntLiteralExpression>();
|
ASSERT_TRUE(sel->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
expr = sel->expr->As<ast::IntLiteralExpression>();
|
||||||
EXPECT_EQ(expr->value, 2);
|
EXPECT_EQ(expr->value, 2);
|
||||||
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
}
|
}
|
||||||
|
@ -118,18 +131,20 @@ TEST_F(ParserImplTest, SwitchBody_Case_TrailingComma_WithColon) {
|
||||||
EXPECT_FALSE(e.errored);
|
EXPECT_FALSE(e.errored);
|
||||||
ASSERT_NE(e.value, nullptr);
|
ASSERT_NE(e.value, nullptr);
|
||||||
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
||||||
EXPECT_FALSE(e->IsDefault());
|
EXPECT_FALSE(e->ContainsDefault());
|
||||||
|
|
||||||
auto* stmt = e->As<ast::CaseStatement>();
|
auto* stmt = e->As<ast::CaseStatement>();
|
||||||
ASSERT_EQ(stmt->selectors.Length(), 2u);
|
ASSERT_EQ(stmt->selectors.Length(), 2u);
|
||||||
ASSERT_TRUE(stmt->selectors[0]->Is<ast::IntLiteralExpression>());
|
auto* sel = stmt->selectors[0];
|
||||||
|
|
||||||
auto* expr = stmt->selectors[0]->As<ast::IntLiteralExpression>();
|
ASSERT_TRUE(sel->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
auto* expr = sel->expr->As<ast::IntLiteralExpression>();
|
||||||
EXPECT_EQ(expr->value, 1);
|
EXPECT_EQ(expr->value, 1);
|
||||||
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
|
|
||||||
ASSERT_TRUE(stmt->selectors[1]->Is<ast::IntLiteralExpression>());
|
sel = stmt->selectors[1];
|
||||||
expr = stmt->selectors[1]->As<ast::IntLiteralExpression>();
|
ASSERT_TRUE(sel->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
expr = sel->expr->As<ast::IntLiteralExpression>();
|
||||||
EXPECT_EQ(expr->value, 2);
|
EXPECT_EQ(expr->value, 2);
|
||||||
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
}
|
}
|
||||||
|
@ -141,7 +156,7 @@ TEST_F(ParserImplTest, SwitchBody_Case_Invalid) {
|
||||||
EXPECT_TRUE(e.errored);
|
EXPECT_TRUE(e.errored);
|
||||||
EXPECT_FALSE(e.matched);
|
EXPECT_FALSE(e.matched);
|
||||||
EXPECT_EQ(e.value, nullptr);
|
EXPECT_EQ(e.value, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:6: unable to parse case selectors");
|
EXPECT_EQ(p->error(), "1:6: expected case selector expression or `default`");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, SwitchBody_Case_MissingConstLiteral) {
|
TEST_F(ParserImplTest, SwitchBody_Case_MissingConstLiteral) {
|
||||||
|
@ -151,7 +166,7 @@ TEST_F(ParserImplTest, SwitchBody_Case_MissingConstLiteral) {
|
||||||
EXPECT_TRUE(e.errored);
|
EXPECT_TRUE(e.errored);
|
||||||
EXPECT_FALSE(e.matched);
|
EXPECT_FALSE(e.matched);
|
||||||
EXPECT_EQ(e.value, nullptr);
|
EXPECT_EQ(e.value, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:5: unable to parse case selectors");
|
EXPECT_EQ(p->error(), "1:5: expected case selector expression or `default`");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, SwitchBody_Case_MissingBracketLeft) {
|
TEST_F(ParserImplTest, SwitchBody_Case_MissingBracketLeft) {
|
||||||
|
@ -202,17 +217,46 @@ TEST_F(ParserImplTest, SwitchBody_Case_MultipleSelectors) {
|
||||||
EXPECT_FALSE(e.errored);
|
EXPECT_FALSE(e.errored);
|
||||||
ASSERT_NE(e.value, nullptr);
|
ASSERT_NE(e.value, nullptr);
|
||||||
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
||||||
EXPECT_FALSE(e->IsDefault());
|
EXPECT_FALSE(e->ContainsDefault());
|
||||||
ASSERT_EQ(e->body->statements.Length(), 0u);
|
ASSERT_EQ(e->body->statements.Length(), 0u);
|
||||||
ASSERT_EQ(e->selectors.Length(), 2u);
|
ASSERT_EQ(e->selectors.Length(), 2u);
|
||||||
ASSERT_TRUE(e->selectors[0]->Is<ast::IntLiteralExpression>());
|
|
||||||
|
|
||||||
auto* expr = e->selectors[0]->As<ast::IntLiteralExpression>();
|
auto* sel = e->selectors[0];
|
||||||
|
ASSERT_TRUE(sel->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
auto* expr = sel->expr->As<ast::IntLiteralExpression>();
|
||||||
ASSERT_EQ(expr->value, 1);
|
ASSERT_EQ(expr->value, 1);
|
||||||
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
|
|
||||||
ASSERT_TRUE(e->selectors[1]->Is<ast::IntLiteralExpression>());
|
sel = e->selectors[1];
|
||||||
expr = e->selectors[1]->As<ast::IntLiteralExpression>();
|
ASSERT_TRUE(sel->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
expr = sel->expr->As<ast::IntLiteralExpression>();
|
||||||
|
ASSERT_EQ(expr->value, 2);
|
||||||
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ParserImplTest, SwitchBody_Case_MultipleSelectors_with_default) {
|
||||||
|
auto p = parser("case 1, default, 2 { }");
|
||||||
|
auto e = p->switch_body();
|
||||||
|
EXPECT_FALSE(p->has_error()) << p->error();
|
||||||
|
EXPECT_TRUE(e.matched);
|
||||||
|
EXPECT_FALSE(e.errored);
|
||||||
|
ASSERT_NE(e.value, nullptr);
|
||||||
|
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
||||||
|
EXPECT_TRUE(e->ContainsDefault());
|
||||||
|
ASSERT_EQ(e->body->statements.Length(), 0u);
|
||||||
|
ASSERT_EQ(e->selectors.Length(), 3u);
|
||||||
|
|
||||||
|
auto* sel = e->selectors[0];
|
||||||
|
ASSERT_TRUE(sel->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
auto* expr = sel->expr->As<ast::IntLiteralExpression>();
|
||||||
|
ASSERT_EQ(expr->value, 1);
|
||||||
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
|
|
||||||
|
EXPECT_TRUE(e->selectors[1]->IsDefault());
|
||||||
|
|
||||||
|
sel = e->selectors[2];
|
||||||
|
ASSERT_TRUE(sel->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
expr = sel->expr->As<ast::IntLiteralExpression>();
|
||||||
ASSERT_EQ(expr->value, 2);
|
ASSERT_EQ(expr->value, 2);
|
||||||
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
}
|
}
|
||||||
|
@ -225,17 +269,19 @@ TEST_F(ParserImplTest, SwitchBody_Case_MultipleSelectors_WithColon) {
|
||||||
EXPECT_FALSE(e.errored);
|
EXPECT_FALSE(e.errored);
|
||||||
ASSERT_NE(e.value, nullptr);
|
ASSERT_NE(e.value, nullptr);
|
||||||
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
||||||
EXPECT_FALSE(e->IsDefault());
|
EXPECT_FALSE(e->ContainsDefault());
|
||||||
ASSERT_EQ(e->body->statements.Length(), 0u);
|
ASSERT_EQ(e->body->statements.Length(), 0u);
|
||||||
ASSERT_EQ(e->selectors.Length(), 2u);
|
ASSERT_EQ(e->selectors.Length(), 2u);
|
||||||
ASSERT_TRUE(e->selectors[0]->Is<ast::IntLiteralExpression>());
|
|
||||||
|
|
||||||
auto* expr = e->selectors[0]->As<ast::IntLiteralExpression>();
|
auto* sel = e->selectors[0];
|
||||||
|
ASSERT_TRUE(sel->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
auto* expr = sel->expr->As<ast::IntLiteralExpression>();
|
||||||
ASSERT_EQ(expr->value, 1);
|
ASSERT_EQ(expr->value, 1);
|
||||||
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
|
|
||||||
ASSERT_TRUE(e->selectors[1]->Is<ast::IntLiteralExpression>());
|
sel = e->selectors[1];
|
||||||
expr = e->selectors[1]->As<ast::IntLiteralExpression>();
|
ASSERT_TRUE(sel->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
expr = sel->expr->As<ast::IntLiteralExpression>();
|
||||||
ASSERT_EQ(expr->value, 2);
|
ASSERT_EQ(expr->value, 2);
|
||||||
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
}
|
}
|
||||||
|
@ -257,7 +303,7 @@ TEST_F(ParserImplTest, SwitchBody_Case_MultipleSelectorsStartsWithComma) {
|
||||||
EXPECT_TRUE(e.errored);
|
EXPECT_TRUE(e.errored);
|
||||||
EXPECT_FALSE(e.matched);
|
EXPECT_FALSE(e.matched);
|
||||||
EXPECT_EQ(e.value, nullptr);
|
EXPECT_EQ(e.value, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:6: unable to parse case selectors");
|
EXPECT_EQ(p->error(), "1:6: expected case selector expression or `default`");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, SwitchBody_Default) {
|
TEST_F(ParserImplTest, SwitchBody_Default) {
|
||||||
|
@ -268,7 +314,7 @@ TEST_F(ParserImplTest, SwitchBody_Default) {
|
||||||
EXPECT_FALSE(e.errored);
|
EXPECT_FALSE(e.errored);
|
||||||
ASSERT_NE(e.value, nullptr);
|
ASSERT_NE(e.value, nullptr);
|
||||||
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
||||||
EXPECT_TRUE(e->IsDefault());
|
EXPECT_TRUE(e->ContainsDefault());
|
||||||
ASSERT_EQ(e->body->statements.Length(), 1u);
|
ASSERT_EQ(e->body->statements.Length(), 1u);
|
||||||
EXPECT_TRUE(e->body->statements[0]->Is<ast::AssignmentStatement>());
|
EXPECT_TRUE(e->body->statements[0]->Is<ast::AssignmentStatement>());
|
||||||
}
|
}
|
||||||
|
@ -281,7 +327,7 @@ TEST_F(ParserImplTest, SwitchBody_Default_WithColon) {
|
||||||
EXPECT_FALSE(e.errored);
|
EXPECT_FALSE(e.errored);
|
||||||
ASSERT_NE(e.value, nullptr);
|
ASSERT_NE(e.value, nullptr);
|
||||||
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
ASSERT_TRUE(e->Is<ast::CaseStatement>());
|
||||||
EXPECT_TRUE(e->IsDefault());
|
EXPECT_TRUE(e->ContainsDefault());
|
||||||
ASSERT_EQ(e->body->statements.Length(), 1u);
|
ASSERT_EQ(e->body->statements.Length(), 1u);
|
||||||
EXPECT_TRUE(e->body->statements[0]->Is<ast::AssignmentStatement>());
|
EXPECT_TRUE(e->body->statements[0]->Is<ast::AssignmentStatement>());
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,8 +29,8 @@ TEST_F(ParserImplTest, SwitchStmt_WithoutDefault) {
|
||||||
ASSERT_NE(e.value, nullptr);
|
ASSERT_NE(e.value, nullptr);
|
||||||
ASSERT_TRUE(e->Is<ast::SwitchStatement>());
|
ASSERT_TRUE(e->Is<ast::SwitchStatement>());
|
||||||
ASSERT_EQ(e->body.Length(), 2u);
|
ASSERT_EQ(e->body.Length(), 2u);
|
||||||
EXPECT_FALSE(e->body[0]->IsDefault());
|
EXPECT_FALSE(e->body[0]->ContainsDefault());
|
||||||
EXPECT_FALSE(e->body[1]->IsDefault());
|
EXPECT_FALSE(e->body[1]->ContainsDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, SwitchStmt_Empty) {
|
TEST_F(ParserImplTest, SwitchStmt_Empty) {
|
||||||
|
@ -58,9 +58,24 @@ TEST_F(ParserImplTest, SwitchStmt_DefaultInMiddle) {
|
||||||
ASSERT_TRUE(e->Is<ast::SwitchStatement>());
|
ASSERT_TRUE(e->Is<ast::SwitchStatement>());
|
||||||
|
|
||||||
ASSERT_EQ(e->body.Length(), 3u);
|
ASSERT_EQ(e->body.Length(), 3u);
|
||||||
ASSERT_FALSE(e->body[0]->IsDefault());
|
ASSERT_FALSE(e->body[0]->ContainsDefault());
|
||||||
ASSERT_TRUE(e->body[1]->IsDefault());
|
ASSERT_TRUE(e->body[1]->ContainsDefault());
|
||||||
ASSERT_FALSE(e->body[2]->IsDefault());
|
ASSERT_FALSE(e->body[2]->ContainsDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ParserImplTest, SwitchStmt_Default_Mixed) {
|
||||||
|
auto p = parser(R"(switch a {
|
||||||
|
case 1, default, 2: {}
|
||||||
|
})");
|
||||||
|
auto e = p->switch_statement();
|
||||||
|
EXPECT_TRUE(e.matched);
|
||||||
|
EXPECT_FALSE(e.errored);
|
||||||
|
EXPECT_FALSE(p->has_error()) << p->error();
|
||||||
|
ASSERT_NE(e.value, nullptr);
|
||||||
|
ASSERT_TRUE(e->Is<ast::SwitchStatement>());
|
||||||
|
|
||||||
|
ASSERT_EQ(e->body.Length(), 1u);
|
||||||
|
ASSERT_TRUE(e->body[0]->ContainsDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, SwitchStmt_WithParens) {
|
TEST_F(ParserImplTest, SwitchStmt_WithParens) {
|
||||||
|
@ -123,7 +138,7 @@ TEST_F(ParserImplTest, SwitchStmt_InvalidBody) {
|
||||||
EXPECT_TRUE(e.errored);
|
EXPECT_TRUE(e.errored);
|
||||||
EXPECT_EQ(e.value, nullptr);
|
EXPECT_EQ(e.value, nullptr);
|
||||||
EXPECT_TRUE(p->has_error());
|
EXPECT_TRUE(p->has_error());
|
||||||
EXPECT_EQ(p->error(), "2:7: unable to parse case selectors");
|
EXPECT_EQ(p->error(), "2:7: expected case selector expression or `default`");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -390,8 +390,8 @@ TEST_F(ResolverCompoundStatementTest, Switch) {
|
||||||
auto* stmt_a = Ignore(1_i);
|
auto* stmt_a = Ignore(1_i);
|
||||||
auto* stmt_b = Ignore(1_i);
|
auto* stmt_b = Ignore(1_i);
|
||||||
auto* stmt_c = Ignore(1_i);
|
auto* stmt_c = Ignore(1_i);
|
||||||
auto* swi = Switch(expr, Case(Expr(1_i), Block(stmt_a)), Case(Expr(2_i), Block(stmt_b)),
|
auto* swi = Switch(expr, Case(CaseSelector(1_i), Block(stmt_a)),
|
||||||
DefaultCase(Block(stmt_c)));
|
Case(CaseSelector(2_i), Block(stmt_b)), DefaultCase(Block(stmt_c)));
|
||||||
WrapInFunction(swi);
|
WrapInFunction(swi);
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
|
|
@ -70,7 +70,7 @@ TEST_F(ResolverControlBlockValidationTest, SwitchWithoutDefault_Fail) {
|
||||||
|
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch(Source{{12, 34}}, "a", //
|
Switch(Source{{12, 34}}, "a", //
|
||||||
Case(Expr(1_i))));
|
Case(CaseSelector(1_i))));
|
||||||
|
|
||||||
WrapInFunction(block);
|
WrapInFunction(block);
|
||||||
|
|
||||||
|
@ -87,16 +87,79 @@ TEST_F(ResolverControlBlockValidationTest, SwitchWithTwoDefault_Fail) {
|
||||||
// }
|
// }
|
||||||
auto* var = Var("a", ty.i32(), Expr(2_i));
|
auto* var = Var("a", ty.i32(), Expr(2_i));
|
||||||
|
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
DefaultCase(), //
|
DefaultCase(Source{{9, 2}}), //
|
||||||
Case(Expr(1_i)), //
|
Case(CaseSelector(1_i)), //
|
||||||
DefaultCase(Source{{12, 34}})));
|
DefaultCase(Source{{12, 34}})));
|
||||||
|
|
||||||
WrapInFunction(block);
|
WrapInFunction(block);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: switch statement must have exactly one default clause");
|
EXPECT_EQ(r()->error(), R"(12:34 error: switch statement must have exactly one default clause
|
||||||
|
9:2 note: previous default case)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverControlBlockValidationTest, SwitchWithTwoDefault_OneInCase_Fail) {
|
||||||
|
// var a : i32 = 2;
|
||||||
|
// switch (a) {
|
||||||
|
// case 1, default: {}
|
||||||
|
// default: {}
|
||||||
|
// }
|
||||||
|
auto* var = Var("a", ty.i32(), Expr(2_i));
|
||||||
|
|
||||||
|
auto* block = Block(
|
||||||
|
Decl(var), //
|
||||||
|
Switch("a", //
|
||||||
|
Case(utils::Vector{CaseSelector(1_i), DefaultCaseSelector(Source{{9, 2}})}), //
|
||||||
|
DefaultCase(Source{{12, 34}})));
|
||||||
|
|
||||||
|
WrapInFunction(block);
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(), R"(12:34 error: switch statement must have exactly one default clause
|
||||||
|
9:2 note: previous default case)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverControlBlockValidationTest, SwitchWithTwoDefault_SameCase) {
|
||||||
|
// var a : i32 = 2;
|
||||||
|
// switch (a) {
|
||||||
|
// case default, 1, default: {}
|
||||||
|
// }
|
||||||
|
auto* var = Var("a", ty.i32(), Expr(2_i));
|
||||||
|
|
||||||
|
auto* block =
|
||||||
|
Block(Decl(var), //
|
||||||
|
Switch("a", //
|
||||||
|
Case(utils::Vector{DefaultCaseSelector(Source{{9, 2}}), CaseSelector(1_i),
|
||||||
|
DefaultCaseSelector(Source{{12, 34}})})));
|
||||||
|
|
||||||
|
WrapInFunction(block);
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(), R"(12:34 error: switch statement must have exactly one default clause
|
||||||
|
9:2 note: previous default case)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverControlBlockValidationTest, SwitchWithTwoDefault_DifferentMultiCase) {
|
||||||
|
// var a : i32 = 2;
|
||||||
|
// switch (a) {
|
||||||
|
// case 1, default: {}
|
||||||
|
// case default, 2: {}
|
||||||
|
// }
|
||||||
|
auto* var = Var("a", ty.i32(), Expr(2_i));
|
||||||
|
|
||||||
|
auto* block = Block(
|
||||||
|
Decl(var), //
|
||||||
|
Switch("a", //
|
||||||
|
Case(utils::Vector{CaseSelector(1_i), DefaultCaseSelector(Source{{9, 2}})}),
|
||||||
|
Case(utils::Vector{DefaultCaseSelector(Source{{12, 34}}), CaseSelector(2_i)})));
|
||||||
|
|
||||||
|
WrapInFunction(block);
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(), R"(12:34 error: switch statement must have exactly one default clause
|
||||||
|
9:2 note: previous default case)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverControlBlockValidationTest, UnreachableCode_Loop_continue) {
|
TEST_F(ResolverControlBlockValidationTest, UnreachableCode_Loop_continue) {
|
||||||
|
@ -187,9 +250,9 @@ TEST_F(ResolverControlBlockValidationTest, UnreachableCode_break) {
|
||||||
auto* decl_z = Decl(Var("z", ty.i32()));
|
auto* decl_z = Decl(Var("z", ty.i32()));
|
||||||
auto* brk = Break();
|
auto* brk = Break();
|
||||||
auto* assign_z = Assign(Source{{12, 34}}, "z", 1_i);
|
auto* assign_z = Assign(Source{{12, 34}}, "z", 1_i);
|
||||||
WrapInFunction( //
|
WrapInFunction( //
|
||||||
Block(Switch(1_i, //
|
Block(Switch(1_i, //
|
||||||
Case(Expr(1_i), Block(decl_z, brk, assign_z)), //
|
Case(CaseSelector(1_i), Block(decl_z, brk, assign_z)), //
|
||||||
DefaultCase())));
|
DefaultCase())));
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
@ -210,11 +273,11 @@ TEST_F(ResolverControlBlockValidationTest, UnreachableCode_break_InBlocks) {
|
||||||
auto* decl_z = Decl(Var("z", ty.i32()));
|
auto* decl_z = Decl(Var("z", ty.i32()));
|
||||||
auto* brk = Break();
|
auto* brk = Break();
|
||||||
auto* assign_z = Assign(Source{{12, 34}}, "z", 1_i);
|
auto* assign_z = Assign(Source{{12, 34}}, "z", 1_i);
|
||||||
WrapInFunction(
|
WrapInFunction(Loop(
|
||||||
Loop(Block(Switch(1_i, //
|
Block(Switch(1_i, //
|
||||||
Case(Expr(1_i), Block(decl_z, Block(Block(Block(brk))), assign_z)),
|
Case(CaseSelector(1_i), Block(decl_z, Block(Block(Block(brk))), assign_z)),
|
||||||
DefaultCase()), //
|
DefaultCase()), //
|
||||||
Break())));
|
Break())));
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
|
EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
|
||||||
|
@ -231,8 +294,8 @@ TEST_F(ResolverControlBlockValidationTest, SwitchConditionTypeMustMatchSelectorT
|
||||||
// }
|
// }
|
||||||
auto* var = Var("a", ty.i32(), Expr(2_i));
|
auto* var = Var("a", ty.i32(), Expr(2_i));
|
||||||
|
|
||||||
auto* block = Block(Decl(var), Switch("a", //
|
auto* block = Block(Decl(var), Switch("a", //
|
||||||
Case(Expr(Source{{12, 34}}, 1_u)), //
|
Case(CaseSelector(Source{{12, 34}}, 1_u)), //
|
||||||
DefaultCase()));
|
DefaultCase()));
|
||||||
WrapInFunction(block);
|
WrapInFunction(block);
|
||||||
|
|
||||||
|
@ -250,9 +313,9 @@ TEST_F(ResolverControlBlockValidationTest, SwitchConditionTypeMustMatchSelectorT
|
||||||
// }
|
// }
|
||||||
auto* var = Var("a", ty.u32(), Expr(2_u));
|
auto* var = Var("a", ty.u32(), Expr(2_u));
|
||||||
|
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
Case(utils::Vector{Expr(Source{{12, 34}}, -1_i)}), //
|
Case(CaseSelector(Source{{12, 34}}, -1_i)), //
|
||||||
DefaultCase()));
|
DefaultCase()));
|
||||||
WrapInFunction(block);
|
WrapInFunction(block);
|
||||||
|
|
||||||
|
@ -273,11 +336,11 @@ TEST_F(ResolverControlBlockValidationTest, NonUniqueCaseSelectorValueUint_Fail)
|
||||||
|
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
Case(Expr(0_u)),
|
Case(CaseSelector(0_u)),
|
||||||
Case(utils::Vector{
|
Case(utils::Vector{
|
||||||
Expr(Source{{12, 34}}, 2_u),
|
CaseSelector(Source{{12, 34}}, 2_u),
|
||||||
Expr(3_u),
|
CaseSelector(3_u),
|
||||||
Expr(Source{{56, 78}}, 2_u),
|
CaseSelector(Source{{56, 78}}, 2_u),
|
||||||
}),
|
}),
|
||||||
DefaultCase()));
|
DefaultCase()));
|
||||||
WrapInFunction(block);
|
WrapInFunction(block);
|
||||||
|
@ -299,12 +362,12 @@ TEST_F(ResolverControlBlockValidationTest, NonUniqueCaseSelectorValueSint_Fail)
|
||||||
|
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
Case(Expr(Source{{12, 34}}, -10_i)),
|
Case(CaseSelector(Source{{12, 34}}, -10_i)),
|
||||||
Case(utils::Vector{
|
Case(utils::Vector{
|
||||||
Expr(0_i),
|
CaseSelector(0_i),
|
||||||
Expr(1_i),
|
CaseSelector(1_i),
|
||||||
Expr(2_i),
|
CaseSelector(2_i),
|
||||||
Expr(Source{{56, 78}}, -10_i),
|
CaseSelector(Source{{56, 78}}, -10_i),
|
||||||
}),
|
}),
|
||||||
DefaultCase()));
|
DefaultCase()));
|
||||||
WrapInFunction(block);
|
WrapInFunction(block);
|
||||||
|
@ -344,7 +407,7 @@ TEST_F(ResolverControlBlockValidationTest, SwitchCase_Pass) {
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
DefaultCase(Source{{12, 34}}), //
|
DefaultCase(Source{{12, 34}}), //
|
||||||
Case(Expr(5_i))));
|
Case(CaseSelector(5_i))));
|
||||||
WrapInFunction(block);
|
WrapInFunction(block);
|
||||||
|
|
||||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
@ -361,7 +424,7 @@ TEST_F(ResolverControlBlockValidationTest, SwitchCase_Expression_Pass) {
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
DefaultCase(Source{{12, 34}}), //
|
DefaultCase(Source{{12, 34}}), //
|
||||||
Case(Add(5_i, 6_i))));
|
Case(CaseSelector(Add(5_i, 6_i)))));
|
||||||
WrapInFunction(block);
|
WrapInFunction(block);
|
||||||
|
|
||||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
@ -378,7 +441,7 @@ TEST_F(ResolverControlBlockValidationTest, SwitchCase_Expression_MixI32_Abstract
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
DefaultCase(Source{{12, 34}}), //
|
DefaultCase(Source{{12, 34}}), //
|
||||||
Case(Add(5_i, 6_i))));
|
Case(CaseSelector(Add(5_i, 6_i)))));
|
||||||
WrapInFunction(block);
|
WrapInFunction(block);
|
||||||
|
|
||||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
@ -395,7 +458,7 @@ TEST_F(ResolverControlBlockValidationTest, SwitchCase_Expression_MixU32_Abstract
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
DefaultCase(Source{{12, 34}}), //
|
DefaultCase(Source{{12, 34}}), //
|
||||||
Case(Add(5_a, 6_a))));
|
Case(CaseSelector(Add(5_a, 6_a)))));
|
||||||
WrapInFunction(block);
|
WrapInFunction(block);
|
||||||
|
|
||||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
@ -409,10 +472,12 @@ TEST_F(ResolverControlBlockValidationTest, SwitchCase_Expression_Multiple) {
|
||||||
// }
|
// }
|
||||||
auto* var = Var("a", Expr(2_u));
|
auto* var = Var("a", Expr(2_u));
|
||||||
|
|
||||||
auto* block = Block(Decl(var), //
|
auto* block =
|
||||||
Switch("a", //
|
Block(Decl(var), //
|
||||||
DefaultCase(Source{{12, 34}}), //
|
Switch("a", //
|
||||||
Case(utils::Vector{Add(5_u, 6_u), Add(7_u, 9_u), Mul(2_u, 4_u)})));
|
DefaultCase(Source{{12, 34}}), //
|
||||||
|
Case(utils::Vector{CaseSelector(Add(5_u, 6_u)), CaseSelector(Add(7_u, 9_u)),
|
||||||
|
CaseSelector(Mul(2_u, 4_u))})));
|
||||||
WrapInFunction(block);
|
WrapInFunction(block);
|
||||||
|
|
||||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
@ -446,8 +511,8 @@ TEST_F(ResolverControlBlockValidationTest, NonUniqueCaseSelector_Expression_Fail
|
||||||
|
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
Case(Expr(Source{{12, 34}}, 10_i)),
|
Case(CaseSelector(Source{{12, 34}}, 10_i)),
|
||||||
Case(Add(Source{{56, 78}}, 5_i, 5_i)), DefaultCase()));
|
Case(CaseSelector(Source{{56, 78}}, Add(5_i, 5_i))), DefaultCase()));
|
||||||
WrapInFunction(block);
|
WrapInFunction(block);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
@ -466,8 +531,8 @@ TEST_F(ResolverControlBlockValidationTest, NonUniqueCaseSelectorSameCase_BothExp
|
||||||
|
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
Case(utils::Vector{Add(Source{{56, 78}}, 5_i, 5_i),
|
Case(utils::Vector{CaseSelector(Source{{56, 78}}, Add(5_i, 5_i)),
|
||||||
Add(Source{{12, 34}}, 6_i, 4_i)}),
|
CaseSelector(Source{{12, 34}}, Add(6_i, 4_i))}),
|
||||||
DefaultCase()));
|
DefaultCase()));
|
||||||
WrapInFunction(block);
|
WrapInFunction(block);
|
||||||
|
|
||||||
|
@ -485,11 +550,11 @@ TEST_F(ResolverControlBlockValidationTest, NonUniqueCaseSelectorSame_Case_Expres
|
||||||
// }
|
// }
|
||||||
auto* var = Var("a", ty.i32(), Expr(2_i));
|
auto* var = Var("a", ty.i32(), Expr(2_i));
|
||||||
|
|
||||||
auto* block = Block(
|
auto* block = Block(Decl(var), //
|
||||||
Decl(var), //
|
Switch("a", //
|
||||||
Switch("a", //
|
Case(utils::Vector{CaseSelector(Source{{56, 78}}, Add(5_i, 5_i)),
|
||||||
Case(utils::Vector{Add(Source{{56, 78}}, 5_i, 5_i), Expr(Source{{12, 34}}, 10_i)}),
|
CaseSelector(Source{{12, 34}}, 10_i)}),
|
||||||
DefaultCase()));
|
DefaultCase()));
|
||||||
WrapInFunction(block);
|
WrapInFunction(block);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
@ -508,7 +573,7 @@ TEST_F(ResolverControlBlockValidationTest, Switch_OverrideCondition_Fail) {
|
||||||
|
|
||||||
auto* block = Block(Decl(var), //
|
auto* block = Block(Decl(var), //
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
Case(Expr(Source{{12, 34}}, "b")), DefaultCase()));
|
Case(CaseSelector(Source{{12, 34}}, "b")), DefaultCase()));
|
||||||
WrapInFunction(block);
|
WrapInFunction(block);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
|
|
@ -299,7 +299,7 @@ class DependencyScanner {
|
||||||
TraverseExpression(s->condition);
|
TraverseExpression(s->condition);
|
||||||
for (auto* c : s->body) {
|
for (auto* c : s->body) {
|
||||||
for (auto* sel : c->selectors) {
|
for (auto* sel : c->selectors) {
|
||||||
TraverseExpression(sel);
|
TraverseExpression(sel->expr);
|
||||||
}
|
}
|
||||||
TraverseStatement(c->body);
|
TraverseStatement(c->body);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1262,9 +1262,9 @@ TEST_F(ResolverDependencyGraphTraversalTest, SymbolsReached) {
|
||||||
Loop(Block(Assign(V, V)), //
|
Loop(Block(Assign(V, V)), //
|
||||||
Block(Assign(V, V))), //
|
Block(Assign(V, V))), //
|
||||||
Switch(V, //
|
Switch(V, //
|
||||||
Case(Expr(1_i), //
|
Case(CaseSelector(1_i), //
|
||||||
Block(Assign(V, V))), //
|
Block(Assign(V, V))), //
|
||||||
Case(Expr(2_i), //
|
Case(CaseSelector(2_i), //
|
||||||
Block(Fallthrough())), //
|
Block(Fallthrough())), //
|
||||||
DefaultCase(Block(Assign(V, V)))), //
|
DefaultCase(Block(Assign(V, V)))), //
|
||||||
Return(V), //
|
Return(V), //
|
||||||
|
|
|
@ -356,26 +356,30 @@ TEST_P(MaterializeAbstractNumericToConcreteType, Test) {
|
||||||
WrapInFunction(Add(target_expr(), abstract_expr));
|
WrapInFunction(Add(target_expr(), abstract_expr));
|
||||||
break;
|
break;
|
||||||
case Method::kSwitchCond:
|
case Method::kSwitchCond:
|
||||||
WrapInFunction(Switch(abstract_expr, //
|
WrapInFunction(
|
||||||
Case(target_expr()->As<ast::IntLiteralExpression>()), //
|
Switch(abstract_expr, //
|
||||||
DefaultCase()));
|
Case(CaseSelector(target_expr()->As<ast::IntLiteralExpression>())), //
|
||||||
|
DefaultCase()));
|
||||||
break;
|
break;
|
||||||
case Method::kSwitchCase:
|
case Method::kSwitchCase:
|
||||||
WrapInFunction(Switch(target_expr(), //
|
WrapInFunction(
|
||||||
Case(abstract_expr->As<ast::IntLiteralExpression>()), //
|
Switch(target_expr(), //
|
||||||
DefaultCase()));
|
Case(CaseSelector(abstract_expr->As<ast::IntLiteralExpression>())), //
|
||||||
|
DefaultCase()));
|
||||||
break;
|
break;
|
||||||
case Method::kSwitchCondWithAbstractCase:
|
case Method::kSwitchCondWithAbstractCase:
|
||||||
WrapInFunction(Switch(abstract_expr, //
|
WrapInFunction(
|
||||||
Case(Expr(123_a)), //
|
Switch(abstract_expr, //
|
||||||
Case(target_expr()->As<ast::IntLiteralExpression>()), //
|
Case(CaseSelector(123_a)), //
|
||||||
DefaultCase()));
|
Case(CaseSelector(target_expr()->As<ast::IntLiteralExpression>())), //
|
||||||
|
DefaultCase()));
|
||||||
break;
|
break;
|
||||||
case Method::kSwitchCaseWithAbstractCase:
|
case Method::kSwitchCaseWithAbstractCase:
|
||||||
WrapInFunction(Switch(target_expr(), //
|
WrapInFunction(
|
||||||
Case(Expr(123_a)), //
|
Switch(target_expr(), //
|
||||||
Case(abstract_expr->As<ast::IntLiteralExpression>()), //
|
Case(CaseSelector(123_a)), //
|
||||||
DefaultCase()));
|
Case(CaseSelector(abstract_expr->As<ast::IntLiteralExpression>())), //
|
||||||
|
DefaultCase()));
|
||||||
break;
|
break;
|
||||||
case Method::kWorkgroupSize:
|
case Method::kWorkgroupSize:
|
||||||
Func("f", utils::Empty, ty.void_(), utils::Empty,
|
Func("f", utils::Empty, ty.void_(), utils::Empty,
|
||||||
|
@ -903,9 +907,10 @@ TEST_P(MaterializeAbstractNumericToDefaultType, Test) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Method::kSwitch: {
|
case Method::kSwitch: {
|
||||||
WrapInFunction(Switch(abstract_expr(),
|
WrapInFunction(
|
||||||
Case(abstract_expr()->As<ast::IntLiteralExpression>()),
|
Switch(abstract_expr(),
|
||||||
DefaultCase()));
|
Case(CaseSelector(abstract_expr()->As<ast::IntLiteralExpression>())),
|
||||||
|
DefaultCase()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Method::kWorkgroupSize: {
|
case Method::kWorkgroupSize: {
|
||||||
|
|
|
@ -1240,26 +1240,31 @@ sem::CaseStatement* Resolver::CaseStatement(const ast::CaseStatement* stmt, cons
|
||||||
return StatementScope(stmt, sem, [&] {
|
return StatementScope(stmt, sem, [&] {
|
||||||
sem->Selectors().reserve(stmt->selectors.Length());
|
sem->Selectors().reserve(stmt->selectors.Length());
|
||||||
for (auto* sel : stmt->selectors) {
|
for (auto* sel : stmt->selectors) {
|
||||||
|
Mark(sel);
|
||||||
|
|
||||||
ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "case selector"};
|
ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "case selector"};
|
||||||
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
|
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
|
||||||
|
|
||||||
// The sem statement is created in the switch when attempting to determine the common
|
const sem::Constant* const_value = nullptr;
|
||||||
// type.
|
if (!sel->IsDefault()) {
|
||||||
auto* materialized = Materialize(sem_.Get(sel), ty);
|
// The sem statement was created in the switch when attempting to determine the
|
||||||
if (!materialized) {
|
// common type.
|
||||||
return false;
|
auto* materialized = Materialize(sem_.Get(sel->expr), ty);
|
||||||
}
|
if (!materialized) {
|
||||||
if (!materialized->Type()->IsAnyOf<sem::I32, sem::U32>()) {
|
return false;
|
||||||
AddError("case selector must be an i32 or u32 value", sel->source);
|
}
|
||||||
return false;
|
if (!materialized->Type()->IsAnyOf<sem::I32, sem::U32>()) {
|
||||||
}
|
AddError("case selector must be an i32 or u32 value", sel->source);
|
||||||
auto const_value = materialized->ConstantValue();
|
return false;
|
||||||
if (!const_value) {
|
}
|
||||||
AddError("case selector must be a constant expression", sel->source);
|
const_value = materialized->ConstantValue();
|
||||||
return false;
|
if (!const_value) {
|
||||||
|
AddError("case selector must be a constant expression", sel->source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sem->Selectors().emplace_back(const_value);
|
sem->Selectors().emplace_back(builder_->create<sem::CaseSelector>(sel, const_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
Mark(stmt->body);
|
Mark(stmt->body);
|
||||||
|
@ -3103,8 +3108,11 @@ sem::SwitchStatement* Resolver::SwitchStatement(const ast::SwitchStatement* stmt
|
||||||
utils::Vector<const sem::Type*, 8> types;
|
utils::Vector<const sem::Type*, 8> types;
|
||||||
types.Push(cond_ty);
|
types.Push(cond_ty);
|
||||||
for (auto* case_stmt : stmt->body) {
|
for (auto* case_stmt : stmt->body) {
|
||||||
for (auto* expr : case_stmt->selectors) {
|
for (auto* sel : case_stmt->selectors) {
|
||||||
auto* sem_expr = Expression(expr);
|
if (sel->IsDefault()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto* sem_expr = Expression(sel->expr);
|
||||||
types.Push(sem_expr->Type()->UnwrapRef());
|
types.Push(sem_expr->Type()->UnwrapRef());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3127,9 +3135,6 @@ sem::SwitchStatement* Resolver::SwitchStatement(const ast::SwitchStatement* stmt
|
||||||
if (!c) {
|
if (!c) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (auto* expr : c->Selectors()) {
|
|
||||||
types.Push(expr->Type()->UnwrapRef());
|
|
||||||
}
|
|
||||||
cases.Push(c);
|
cases.Push(c);
|
||||||
behaviors.Add(c->Behaviors());
|
behaviors.Add(c->Behaviors());
|
||||||
sem->Cases().emplace_back(c);
|
sem->Cases().emplace_back(c);
|
||||||
|
|
|
@ -633,7 +633,7 @@ TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_DefaultReturn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Empty_DefaultEmpty) {
|
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Empty_DefaultEmpty) {
|
||||||
auto* stmt = Switch(1_i, Case(Expr(0_i), Block()), DefaultCase(Block()));
|
auto* stmt = Switch(1_i, Case(CaseSelector(0_i), Block()), DefaultCase(Block()));
|
||||||
WrapInFunction(stmt);
|
WrapInFunction(stmt);
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
@ -643,7 +643,7 @@ TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Empty_DefaultEmpty) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Empty_DefaultDiscard) {
|
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Empty_DefaultDiscard) {
|
||||||
auto* stmt = Switch(1_i, Case(Expr(0_i), Block()), DefaultCase(Block(Discard())));
|
auto* stmt = Switch(1_i, Case(CaseSelector(0_i), Block()), DefaultCase(Block(Discard())));
|
||||||
|
|
||||||
Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
|
Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
|
||||||
utils::Vector{Stage(ast::PipelineStage::kFragment)});
|
utils::Vector{Stage(ast::PipelineStage::kFragment)});
|
||||||
|
@ -655,7 +655,7 @@ TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Empty_DefaultDiscard) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Empty_DefaultReturn) {
|
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Empty_DefaultReturn) {
|
||||||
auto* stmt = Switch(1_i, Case(Expr(0_i), Block()), DefaultCase(Block(Return())));
|
auto* stmt = Switch(1_i, Case(CaseSelector(0_i), Block()), DefaultCase(Block(Return())));
|
||||||
WrapInFunction(stmt);
|
WrapInFunction(stmt);
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
@ -665,7 +665,7 @@ TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Empty_DefaultReturn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Discard_DefaultEmpty) {
|
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Discard_DefaultEmpty) {
|
||||||
auto* stmt = Switch(1_i, Case(Expr(0_i), Block(Discard())), DefaultCase(Block()));
|
auto* stmt = Switch(1_i, Case(CaseSelector(0_i), Block(Discard())), DefaultCase(Block()));
|
||||||
|
|
||||||
Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
|
Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
|
||||||
utils::Vector{Stage(ast::PipelineStage::kFragment)});
|
utils::Vector{Stage(ast::PipelineStage::kFragment)});
|
||||||
|
@ -677,7 +677,8 @@ TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Discard_DefaultEmpty) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Discard_DefaultDiscard) {
|
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Discard_DefaultDiscard) {
|
||||||
auto* stmt = Switch(1_i, Case(Expr(0_i), Block(Discard())), DefaultCase(Block(Discard())));
|
auto* stmt =
|
||||||
|
Switch(1_i, Case(CaseSelector(0_i), Block(Discard())), DefaultCase(Block(Discard())));
|
||||||
|
|
||||||
Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
|
Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
|
||||||
utils::Vector{Stage(ast::PipelineStage::kFragment)});
|
utils::Vector{Stage(ast::PipelineStage::kFragment)});
|
||||||
|
@ -689,7 +690,8 @@ TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Discard_DefaultDiscard)
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Discard_DefaultReturn) {
|
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Discard_DefaultReturn) {
|
||||||
auto* stmt = Switch(1_i, Case(Expr(0_i), Block(Discard())), DefaultCase(Block(Return())));
|
auto* stmt =
|
||||||
|
Switch(1_i, Case(CaseSelector(0_i), Block(Discard())), DefaultCase(Block(Return())));
|
||||||
|
|
||||||
Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
|
Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
|
||||||
utils::Vector{Stage(ast::PipelineStage::kFragment)});
|
utils::Vector{Stage(ast::PipelineStage::kFragment)});
|
||||||
|
@ -701,9 +703,9 @@ TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Discard_DefaultReturn)
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Discard_Case1Return_DefaultEmpty) {
|
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Discard_Case1Return_DefaultEmpty) {
|
||||||
auto* stmt = Switch(1_i, //
|
auto* stmt = Switch(1_i, //
|
||||||
Case(Expr(0_i), Block(Discard())), //
|
Case(CaseSelector(0_i), Block(Discard())), //
|
||||||
Case(Expr(1_i), Block(Return())), //
|
Case(CaseSelector(1_i), Block(Return())), //
|
||||||
DefaultCase(Block()));
|
DefaultCase(Block()));
|
||||||
|
|
||||||
Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
|
Func("F", utils::Empty, ty.void_(), utils::Vector{stmt},
|
||||||
|
|
|
@ -112,7 +112,7 @@ TEST_F(ResolverTest, Stmt_Case) {
|
||||||
|
|
||||||
auto* assign = Assign(lhs, rhs);
|
auto* assign = Assign(lhs, rhs);
|
||||||
auto* block = Block(assign);
|
auto* block = Block(assign);
|
||||||
auto* sel = Expr(3_i);
|
auto* sel = CaseSelector(3_i);
|
||||||
auto* cse = Case(sel, block);
|
auto* cse = Case(sel, block);
|
||||||
auto* def = DefaultCase();
|
auto* def = DefaultCase();
|
||||||
auto* cond_var = Var("c", ty.i32());
|
auto* cond_var = Var("c", ty.i32());
|
||||||
|
@ -132,7 +132,7 @@ TEST_F(ResolverTest, Stmt_Case) {
|
||||||
ASSERT_EQ(sem->Cases().size(), 2u);
|
ASSERT_EQ(sem->Cases().size(), 2u);
|
||||||
EXPECT_EQ(sem->Cases()[0]->Declaration(), cse);
|
EXPECT_EQ(sem->Cases()[0]->Declaration(), cse);
|
||||||
ASSERT_EQ(sem->Cases()[0]->Selectors().size(), 1u);
|
ASSERT_EQ(sem->Cases()[0]->Selectors().size(), 1u);
|
||||||
EXPECT_EQ(sem->Cases()[1]->Selectors().size(), 0u);
|
EXPECT_EQ(sem->Cases()[1]->Selectors().size(), 1u);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverTest, Stmt_Block) {
|
TEST_F(ResolverTest, Stmt_Block) {
|
||||||
|
@ -251,7 +251,7 @@ TEST_F(ResolverTest, Stmt_Switch) {
|
||||||
auto* lhs = Expr("v");
|
auto* lhs = Expr("v");
|
||||||
auto* rhs = Expr(2.3_f);
|
auto* rhs = Expr(2.3_f);
|
||||||
auto* case_block = Block(Assign(lhs, rhs));
|
auto* case_block = Block(Assign(lhs, rhs));
|
||||||
auto* stmt = Switch(Expr(2_i), Case(Expr(3_i), case_block), DefaultCase());
|
auto* stmt = Switch(Expr(2_i), Case(CaseSelector(3_i), case_block), DefaultCase());
|
||||||
WrapInFunction(v, stmt);
|
WrapInFunction(v, stmt);
|
||||||
|
|
||||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
|
|
@ -1039,11 +1039,11 @@ TEST_F(ResolverValidationTest, Stmt_BreakInLoop) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverValidationTest, Stmt_BreakInSwitch) {
|
TEST_F(ResolverValidationTest, Stmt_BreakInSwitch) {
|
||||||
WrapInFunction(Loop(Block(Switch(Expr(1_i), //
|
WrapInFunction(Loop(Block(Switch(Expr(1_i), //
|
||||||
Case(Expr(1_i), //
|
Case(CaseSelector(1_i), //
|
||||||
Block(Break())), //
|
Block(Break())), //
|
||||||
DefaultCase()), //
|
DefaultCase()), //
|
||||||
Break()))); //
|
Break()))); //
|
||||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2324,54 +2324,50 @@ bool Validator::SwitchStatement(const ast::SwitchStatement* s) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_default = false;
|
const sem::CaseSelector* default_selector = nullptr;
|
||||||
std::unordered_map<int64_t, Source> selectors;
|
std::unordered_map<int64_t, Source> selectors;
|
||||||
|
|
||||||
for (auto* case_stmt : s->body) {
|
for (auto* case_stmt : s->body) {
|
||||||
if (case_stmt->IsDefault()) {
|
|
||||||
if (has_default) {
|
|
||||||
// More than one default clause
|
|
||||||
AddError("switch statement must have exactly one default clause",
|
|
||||||
case_stmt->source);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
has_default = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* case_sem = sem_.Get<sem::CaseStatement>(case_stmt);
|
auto* case_sem = sem_.Get<sem::CaseStatement>(case_stmt);
|
||||||
|
for (auto* selector : case_sem->Selectors()) {
|
||||||
|
if (selector->IsDefault()) {
|
||||||
|
if (default_selector != nullptr) {
|
||||||
|
// More than one default clause
|
||||||
|
AddError("switch statement must have exactly one default clause",
|
||||||
|
selector->Declaration()->source);
|
||||||
|
|
||||||
auto& case_selectors = case_stmt->selectors;
|
AddNote("previous default case", default_selector->Declaration()->source);
|
||||||
auto& selector_values = case_sem->Selectors();
|
return false;
|
||||||
TINT_ASSERT(Resolver, case_selectors.Length() == selector_values.size());
|
}
|
||||||
for (size_t i = 0; i < case_sem->Selectors().size(); ++i) {
|
default_selector = selector;
|
||||||
auto* selector = selector_values[i];
|
continue;
|
||||||
if (cond_ty != selector->Type()) {
|
}
|
||||||
|
|
||||||
|
auto* decl_ty = selector->Value()->Type();
|
||||||
|
if (cond_ty != decl_ty) {
|
||||||
AddError(
|
AddError(
|
||||||
"the case selector values must have the same type as the selector expression.",
|
"the case selector values must have the same type as the selector expression.",
|
||||||
case_selectors[i]->source);
|
selector->Declaration()->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto value = selector->As<uint32_t>();
|
auto value = selector->Value()->As<uint32_t>();
|
||||||
auto it = selectors.find(value);
|
auto it = selectors.find(value);
|
||||||
if (it != selectors.end()) {
|
if (it != selectors.end()) {
|
||||||
std::string err = "duplicate switch case '";
|
AddError("duplicate switch case '" +
|
||||||
if (selector->Type()->Is<sem::I32>()) {
|
(decl_ty->IsAnyOf<sem::I32, sem::AbstractNumeric>()
|
||||||
err += std::to_string(selector->As<int32_t>());
|
? std::to_string(i32(value))
|
||||||
} else {
|
: std::to_string(value)) +
|
||||||
err += std::to_string(value);
|
"'",
|
||||||
}
|
selector->Declaration()->source);
|
||||||
err += "'";
|
|
||||||
|
|
||||||
AddError(err, case_selectors[i]->source);
|
|
||||||
AddNote("previous case declared here", it->second);
|
AddNote("previous case declared here", it->second);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
selectors.emplace(value, case_selectors[i]->source);
|
selectors.emplace(value, selector->Declaration()->source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!has_default) {
|
if (default_selector == nullptr) {
|
||||||
// No default clause
|
// No default clause
|
||||||
AddError("switch statement must have a default clause", s->source);
|
AddError("switch statement must have a default clause", s->source);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "src/tint/program_builder.h"
|
#include "src/tint/program_builder.h"
|
||||||
|
|
||||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::CaseStatement);
|
TINT_INSTANTIATE_TYPEINFO(tint::sem::CaseStatement);
|
||||||
|
TINT_INSTANTIATE_TYPEINFO(tint::sem::CaseSelector);
|
||||||
TINT_INSTANTIATE_TYPEINFO(tint::sem::SwitchStatement);
|
TINT_INSTANTIATE_TYPEINFO(tint::sem::SwitchStatement);
|
||||||
|
|
||||||
namespace tint::sem {
|
namespace tint::sem {
|
||||||
|
@ -48,4 +49,13 @@ const ast::CaseStatement* CaseStatement::Declaration() const {
|
||||||
return static_cast<const ast::CaseStatement*>(Base::Declaration());
|
return static_cast<const ast::CaseStatement*>(Base::Declaration());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CaseSelector::CaseSelector(const ast::CaseSelector* decl, const Constant* val)
|
||||||
|
: Base(), decl_(decl), val_(val) {}
|
||||||
|
|
||||||
|
CaseSelector::~CaseSelector() = default;
|
||||||
|
|
||||||
|
const ast::CaseSelector* CaseSelector::Declaration() const {
|
||||||
|
return decl_;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace tint::sem
|
} // namespace tint::sem
|
||||||
|
|
|
@ -22,10 +22,12 @@
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
namespace tint::ast {
|
namespace tint::ast {
|
||||||
class CaseStatement;
|
class CaseStatement;
|
||||||
|
class CaseSelector;
|
||||||
class SwitchStatement;
|
class SwitchStatement;
|
||||||
} // namespace tint::ast
|
} // namespace tint::ast
|
||||||
namespace tint::sem {
|
namespace tint::sem {
|
||||||
class CaseStatement;
|
class CaseStatement;
|
||||||
|
class CaseSelector;
|
||||||
class Constant;
|
class Constant;
|
||||||
class Expression;
|
class Expression;
|
||||||
} // namespace tint::sem
|
} // namespace tint::sem
|
||||||
|
@ -83,14 +85,39 @@ class CaseStatement final : public Castable<CaseStatement, CompoundStatement> {
|
||||||
const BlockStatement* Body() const { return body_; }
|
const BlockStatement* Body() const { return body_; }
|
||||||
|
|
||||||
/// @returns the selectors for the case
|
/// @returns the selectors for the case
|
||||||
std::vector<const Constant*>& Selectors() { return selectors_; }
|
std::vector<const CaseSelector*>& Selectors() { return selectors_; }
|
||||||
|
|
||||||
/// @returns the selectors for the case
|
/// @returns the selectors for the case
|
||||||
const std::vector<const Constant*>& Selectors() const { return selectors_; }
|
const std::vector<const CaseSelector*>& Selectors() const { return selectors_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const BlockStatement* body_ = nullptr;
|
const BlockStatement* body_ = nullptr;
|
||||||
std::vector<const Constant*> selectors_;
|
std::vector<const CaseSelector*> selectors_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Holds semantic information about a switch case selector
|
||||||
|
class CaseSelector final : public Castable<CaseSelector, Node> {
|
||||||
|
public:
|
||||||
|
/// Constructor
|
||||||
|
/// @param decl the selector declaration
|
||||||
|
/// @param val the case selector value, nullptr for a default selector
|
||||||
|
explicit CaseSelector(const ast::CaseSelector* decl, const Constant* val = nullptr);
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
~CaseSelector() override;
|
||||||
|
|
||||||
|
/// @returns true if this is a default selector
|
||||||
|
bool IsDefault() const { return val_ == nullptr; }
|
||||||
|
|
||||||
|
/// @returns the case selector declaration
|
||||||
|
const ast::CaseSelector* Declaration() const;
|
||||||
|
|
||||||
|
/// @returns the selector constant value, or nullptr if this is the default selector
|
||||||
|
const Constant* Value() const { return val_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const ast::CaseSelector* const decl_;
|
||||||
|
const Constant* const val_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tint::sem
|
} // namespace tint::sem
|
||||||
|
|
|
@ -944,7 +944,7 @@ struct Std140::State {
|
||||||
ret_ty = ty;
|
ret_ty = ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* case_sel = b.Expr(u32(column_idx));
|
auto* case_sel = b.CaseSelector(b.Expr(u32(column_idx)));
|
||||||
auto* case_body = b.Block(utils::Vector{b.Return(expr)});
|
auto* case_body = b.Block(utils::Vector{b.Return(expr)});
|
||||||
cases.Push(b.Case(case_sel, case_body));
|
cases.Push(b.Case(case_sel, case_body));
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,7 +115,12 @@ class TransformTestBase : public BASE {
|
||||||
/// @return true if the transform should be run for the given input.
|
/// @return true if the transform should be run for the given input.
|
||||||
template <typename TRANSFORM>
|
template <typename TRANSFORM>
|
||||||
bool ShouldRun(Program&& program, const DataMap& data = {}) {
|
bool ShouldRun(Program&& program, const DataMap& data = {}) {
|
||||||
EXPECT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
if (!program.IsValid()) {
|
||||||
|
ADD_FAILURE() << "ShouldRun() called with invalid program: "
|
||||||
|
<< program.Diagnostics().str();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const Transform& t = TRANSFORM();
|
const Transform& t = TRANSFORM();
|
||||||
return t.ShouldRun(&program, data);
|
return t.ShouldRun(&program, data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1687,20 +1687,21 @@ std::string GeneratorImpl::generate_builtin_name(const sem::Builtin* builtin) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GeneratorImpl::EmitCase(const ast::CaseStatement* stmt) {
|
bool GeneratorImpl::EmitCase(const ast::CaseStatement* stmt) {
|
||||||
if (stmt->IsDefault()) {
|
auto* sem = builder_.Sem().Get<sem::CaseStatement>(stmt);
|
||||||
line() << "default: {";
|
for (auto* selector : sem->Selectors()) {
|
||||||
} else {
|
auto out = line();
|
||||||
auto* sem = builder_.Sem().Get<sem::CaseStatement>(stmt);
|
|
||||||
for (auto* selector : sem->Selectors()) {
|
if (selector->IsDefault()) {
|
||||||
auto out = line();
|
out << "default";
|
||||||
|
} else {
|
||||||
out << "case ";
|
out << "case ";
|
||||||
if (!EmitConstant(out, selector)) {
|
if (!EmitConstant(out, selector->Value())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out << ":";
|
}
|
||||||
if (selector == sem->Selectors().back()) {
|
out << ":";
|
||||||
out << " {";
|
if (selector == sem->Selectors().back()) {
|
||||||
}
|
out << " {";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,8 @@ namespace {
|
||||||
using GlslGeneratorImplTest_Case = TestHelper;
|
using GlslGeneratorImplTest_Case = TestHelper;
|
||||||
|
|
||||||
TEST_F(GlslGeneratorImplTest_Case, Emit_Case) {
|
TEST_F(GlslGeneratorImplTest_Case, Emit_Case) {
|
||||||
auto* s = Switch(1_i, Case(Expr(5_i), Block(create<ast::BreakStatement>())), DefaultCase());
|
auto* s =
|
||||||
|
Switch(1_i, Case(CaseSelector(5_i), Block(create<ast::BreakStatement>())), DefaultCase());
|
||||||
WrapInFunction(s);
|
WrapInFunction(s);
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
@ -38,7 +39,7 @@ TEST_F(GlslGeneratorImplTest_Case, Emit_Case) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(GlslGeneratorImplTest_Case, Emit_Case_BreaksByDefault) {
|
TEST_F(GlslGeneratorImplTest_Case, Emit_Case_BreaksByDefault) {
|
||||||
auto* s = Switch(1_i, Case(Expr(5_i), Block()), DefaultCase());
|
auto* s = Switch(1_i, Case(CaseSelector(5_i), Block()), DefaultCase());
|
||||||
WrapInFunction(s);
|
WrapInFunction(s);
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
@ -53,8 +54,8 @@ TEST_F(GlslGeneratorImplTest_Case, Emit_Case_BreaksByDefault) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(GlslGeneratorImplTest_Case, Emit_Case_WithFallthrough) {
|
TEST_F(GlslGeneratorImplTest_Case, Emit_Case_WithFallthrough) {
|
||||||
auto* s =
|
auto* s = Switch(1_i, Case(CaseSelector(5_i), Block(create<ast::FallthroughStatement>())),
|
||||||
Switch(1_i, Case(Expr(5_i), Block(create<ast::FallthroughStatement>())), DefaultCase());
|
DefaultCase());
|
||||||
WrapInFunction(s);
|
WrapInFunction(s);
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
@ -72,8 +73,8 @@ TEST_F(GlslGeneratorImplTest_Case, Emit_Case_MultipleSelectors) {
|
||||||
auto* s = Switch(1_i,
|
auto* s = Switch(1_i,
|
||||||
Case(
|
Case(
|
||||||
utils::Vector{
|
utils::Vector{
|
||||||
Expr(5_i),
|
CaseSelector(5_i),
|
||||||
Expr(6_i),
|
CaseSelector(6_i),
|
||||||
},
|
},
|
||||||
Block(create<ast::BreakStatement>())),
|
Block(create<ast::BreakStatement>())),
|
||||||
DefaultCase());
|
DefaultCase());
|
||||||
|
|
|
@ -25,21 +25,13 @@ TEST_F(GlslGeneratorImplTest_Switch, Emit_Switch) {
|
||||||
GlobalVar("cond", ty.i32(), ast::AddressSpace::kPrivate);
|
GlobalVar("cond", ty.i32(), ast::AddressSpace::kPrivate);
|
||||||
|
|
||||||
auto* def_body = Block(create<ast::BreakStatement>());
|
auto* def_body = Block(create<ast::BreakStatement>());
|
||||||
auto* def = create<ast::CaseStatement>(utils::Empty, def_body);
|
auto* def = create<ast::CaseStatement>(utils::Vector{DefaultCaseSelector()}, def_body);
|
||||||
|
|
||||||
utils::Vector case_val{Expr(5_i)};
|
|
||||||
|
|
||||||
auto* case_body = Block(create<ast::BreakStatement>());
|
auto* case_body = Block(create<ast::BreakStatement>());
|
||||||
|
auto* case_stmt = create<ast::CaseStatement>(utils::Vector{CaseSelector(5_i)}, case_body);
|
||||||
auto* case_stmt = create<ast::CaseStatement>(case_val, case_body);
|
|
||||||
|
|
||||||
utils::Vector body{
|
|
||||||
case_stmt,
|
|
||||||
def,
|
|
||||||
};
|
|
||||||
|
|
||||||
auto* cond = Expr("cond");
|
auto* cond = Expr("cond");
|
||||||
auto* s = create<ast::SwitchStatement>(cond, body);
|
auto* s = create<ast::SwitchStatement>(cond, utils::Vector{case_stmt, def});
|
||||||
WrapInFunction(s);
|
WrapInFunction(s);
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
@ -58,5 +50,30 @@ TEST_F(GlslGeneratorImplTest_Switch, Emit_Switch) {
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(GlslGeneratorImplTest_Switch, Emit_Switch_MixedDefault) {
|
||||||
|
GlobalVar("cond", ty.i32(), ast::AddressSpace::kPrivate);
|
||||||
|
|
||||||
|
auto* def_body = Block(create<ast::BreakStatement>());
|
||||||
|
auto* def = create<ast::CaseStatement>(utils::Vector{CaseSelector(5_i), DefaultCaseSelector()},
|
||||||
|
def_body);
|
||||||
|
|
||||||
|
auto* cond = Expr("cond");
|
||||||
|
auto* s = create<ast::SwitchStatement>(cond, utils::Vector{def});
|
||||||
|
WrapInFunction(s);
|
||||||
|
|
||||||
|
GeneratorImpl& gen = Build();
|
||||||
|
|
||||||
|
gen.increment_indent();
|
||||||
|
|
||||||
|
ASSERT_TRUE(gen.EmitStatement(s)) << gen.error();
|
||||||
|
EXPECT_EQ(gen.result(), R"( switch(cond) {
|
||||||
|
case 5:
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace tint::writer::glsl
|
} // namespace tint::writer::glsl
|
||||||
|
|
|
@ -2562,20 +2562,20 @@ std::string GeneratorImpl::generate_builtin_name(const sem::Builtin* builtin) {
|
||||||
|
|
||||||
bool GeneratorImpl::EmitCase(const ast::SwitchStatement* s, size_t case_idx) {
|
bool GeneratorImpl::EmitCase(const ast::SwitchStatement* s, size_t case_idx) {
|
||||||
auto* stmt = s->body[case_idx];
|
auto* stmt = s->body[case_idx];
|
||||||
if (stmt->IsDefault()) {
|
auto* sem = builder_.Sem().Get<sem::CaseStatement>(stmt);
|
||||||
line() << "default: {";
|
for (auto* selector : sem->Selectors()) {
|
||||||
} else {
|
auto out = line();
|
||||||
auto* sem = builder_.Sem().Get<sem::CaseStatement>(stmt);
|
if (selector->IsDefault()) {
|
||||||
for (auto* selector : sem->Selectors()) {
|
out << "default";
|
||||||
auto out = line();
|
} else {
|
||||||
out << "case ";
|
out << "case ";
|
||||||
if (!EmitConstant(out, selector)) {
|
if (!EmitConstant(out, selector->Value())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out << ":";
|
}
|
||||||
if (selector == sem->Selectors().back()) {
|
out << ":";
|
||||||
out << " {";
|
if (selector == sem->Selectors().back()) {
|
||||||
}
|
out << " {";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3652,7 +3652,7 @@ bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GeneratorImpl::EmitDefaultOnlySwitch(const ast::SwitchStatement* stmt) {
|
bool GeneratorImpl::EmitDefaultOnlySwitch(const ast::SwitchStatement* stmt) {
|
||||||
TINT_ASSERT(Writer, stmt->body.Length() == 1 && stmt->body[0]->IsDefault());
|
TINT_ASSERT(Writer, stmt->body.Length() == 1 && stmt->body[0]->ContainsDefault());
|
||||||
|
|
||||||
// FXC fails to compile a switch with just a default case, ignoring the
|
// FXC fails to compile a switch with just a default case, ignoring the
|
||||||
// default case body. We work around this here by emitting the default case
|
// default case body. We work around this here by emitting the default case
|
||||||
|
@ -3685,7 +3685,8 @@ bool GeneratorImpl::EmitDefaultOnlySwitch(const ast::SwitchStatement* stmt) {
|
||||||
|
|
||||||
bool GeneratorImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
|
bool GeneratorImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
|
||||||
// BUG(crbug.com/tint/1188): work around default-only switches
|
// BUG(crbug.com/tint/1188): work around default-only switches
|
||||||
if (stmt->body.Length() == 1 && stmt->body[0]->IsDefault()) {
|
if (stmt->body.Length() == 1 && stmt->body[0]->selectors.Length() == 1 &&
|
||||||
|
stmt->body[0]->ContainsDefault()) {
|
||||||
return EmitDefaultOnlySwitch(stmt);
|
return EmitDefaultOnlySwitch(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,8 @@ namespace {
|
||||||
using HlslGeneratorImplTest_Case = TestHelper;
|
using HlslGeneratorImplTest_Case = TestHelper;
|
||||||
|
|
||||||
TEST_F(HlslGeneratorImplTest_Case, Emit_Case) {
|
TEST_F(HlslGeneratorImplTest_Case, Emit_Case) {
|
||||||
auto* s = Switch(1_i, Case(Expr(5_i), Block(create<ast::BreakStatement>())), DefaultCase());
|
auto* s =
|
||||||
|
Switch(1_i, Case(CaseSelector(5_i), Block(create<ast::BreakStatement>())), DefaultCase());
|
||||||
WrapInFunction(s);
|
WrapInFunction(s);
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
@ -38,7 +39,7 @@ TEST_F(HlslGeneratorImplTest_Case, Emit_Case) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(HlslGeneratorImplTest_Case, Emit_Case_BreaksByDefault) {
|
TEST_F(HlslGeneratorImplTest_Case, Emit_Case_BreaksByDefault) {
|
||||||
auto* s = Switch(1_i, Case(Expr(5_i), Block()), DefaultCase());
|
auto* s = Switch(1_i, Case(CaseSelector(5_i), Block()), DefaultCase());
|
||||||
WrapInFunction(s);
|
WrapInFunction(s);
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
@ -53,9 +54,9 @@ TEST_F(HlslGeneratorImplTest_Case, Emit_Case_BreaksByDefault) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(HlslGeneratorImplTest_Case, Emit_Case_WithFallthrough) {
|
TEST_F(HlslGeneratorImplTest_Case, Emit_Case_WithFallthrough) {
|
||||||
auto* s = Switch(1_i, //
|
auto* s = Switch(1_i, //
|
||||||
Case(Expr(4_i), Block(create<ast::FallthroughStatement>())), //
|
Case(CaseSelector(4_i), Block(create<ast::FallthroughStatement>())), //
|
||||||
Case(Expr(5_i), Block(create<ast::ReturnStatement>())), //
|
Case(CaseSelector(5_i), Block(create<ast::ReturnStatement>())), //
|
||||||
DefaultCase());
|
DefaultCase());
|
||||||
WrapInFunction(s);
|
WrapInFunction(s);
|
||||||
|
|
||||||
|
@ -75,9 +76,10 @@ TEST_F(HlslGeneratorImplTest_Case, Emit_Case_WithFallthrough) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(HlslGeneratorImplTest_Case, Emit_Case_MultipleSelectors) {
|
TEST_F(HlslGeneratorImplTest_Case, Emit_Case_MultipleSelectors) {
|
||||||
auto* s =
|
auto* s = Switch(1_i,
|
||||||
Switch(1_i, Case(utils::Vector{Expr(5_i), Expr(6_i)}, Block(create<ast::BreakStatement>())),
|
Case(utils::Vector{CaseSelector(5_i), CaseSelector(6_i)},
|
||||||
DefaultCase());
|
Block(create<ast::BreakStatement>())),
|
||||||
|
DefaultCase());
|
||||||
WrapInFunction(s);
|
WrapInFunction(s);
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
|
|
@ -23,9 +23,9 @@ using HlslGeneratorImplTest_Switch = TestHelper;
|
||||||
|
|
||||||
TEST_F(HlslGeneratorImplTest_Switch, Emit_Switch) {
|
TEST_F(HlslGeneratorImplTest_Switch, Emit_Switch) {
|
||||||
GlobalVar("cond", ty.i32(), ast::AddressSpace::kPrivate);
|
GlobalVar("cond", ty.i32(), ast::AddressSpace::kPrivate);
|
||||||
auto* s = Switch( //
|
auto* s = Switch( //
|
||||||
Expr("cond"), //
|
Expr("cond"), //
|
||||||
Case(Expr(5_i), Block(Break())), //
|
Case(CaseSelector(5_i), Block(Break())), //
|
||||||
DefaultCase());
|
DefaultCase());
|
||||||
WrapInFunction(s);
|
WrapInFunction(s);
|
||||||
|
|
||||||
|
@ -45,6 +45,27 @@ TEST_F(HlslGeneratorImplTest_Switch, Emit_Switch) {
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(HlslGeneratorImplTest_Switch, Emit_Switch_MixedDefault) {
|
||||||
|
GlobalVar("cond", ty.i32(), ast::AddressSpace::kPrivate);
|
||||||
|
auto* s = Switch( //
|
||||||
|
Expr("cond"), //
|
||||||
|
Case(utils::Vector{CaseSelector(5_i), DefaultCaseSelector()}, Block(Break())));
|
||||||
|
WrapInFunction(s);
|
||||||
|
|
||||||
|
GeneratorImpl& gen = Build();
|
||||||
|
|
||||||
|
gen.increment_indent();
|
||||||
|
|
||||||
|
ASSERT_TRUE(gen.EmitStatement(s)) << gen.error();
|
||||||
|
EXPECT_EQ(gen.result(), R"( switch(cond) {
|
||||||
|
case 5:
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(HlslGeneratorImplTest_Switch, Emit_Switch_OnlyDefaultCase) {
|
TEST_F(HlslGeneratorImplTest_Switch, Emit_Switch_OnlyDefaultCase) {
|
||||||
GlobalVar("cond", ty.i32(), ast::AddressSpace::kPrivate);
|
GlobalVar("cond", ty.i32(), ast::AddressSpace::kPrivate);
|
||||||
GlobalVar("a", ty.i32(), ast::AddressSpace::kPrivate);
|
GlobalVar("a", ty.i32(), ast::AddressSpace::kPrivate);
|
||||||
|
|
|
@ -1589,20 +1589,21 @@ std::string GeneratorImpl::generate_builtin_name(const sem::Builtin* builtin) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GeneratorImpl::EmitCase(const ast::CaseStatement* stmt) {
|
bool GeneratorImpl::EmitCase(const ast::CaseStatement* stmt) {
|
||||||
if (stmt->IsDefault()) {
|
auto* sem = builder_.Sem().Get<sem::CaseStatement>(stmt);
|
||||||
line() << "default: {";
|
for (auto* selector : sem->Selectors()) {
|
||||||
} else {
|
auto out = line();
|
||||||
auto* sem = builder_.Sem().Get<sem::CaseStatement>(stmt);
|
|
||||||
for (auto* selector : sem->Selectors()) {
|
if (selector->IsDefault()) {
|
||||||
auto out = line();
|
out << "default";
|
||||||
|
} else {
|
||||||
out << "case ";
|
out << "case ";
|
||||||
if (!EmitConstant(out, selector)) {
|
if (!EmitConstant(out, selector->Value())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out << ":";
|
}
|
||||||
if (selector == sem->Selectors().back()) {
|
out << ":";
|
||||||
out << " {";
|
if (selector == sem->Selectors().back()) {
|
||||||
}
|
out << " {";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,8 @@ namespace {
|
||||||
using MslGeneratorImplTest = TestHelper;
|
using MslGeneratorImplTest = TestHelper;
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, Emit_Case) {
|
TEST_F(MslGeneratorImplTest, Emit_Case) {
|
||||||
auto* s = Switch(1_i, Case(Expr(5_i), Block(create<ast::BreakStatement>())), DefaultCase());
|
auto* s =
|
||||||
|
Switch(1_i, Case(CaseSelector(5_i), Block(create<ast::BreakStatement>())), DefaultCase());
|
||||||
WrapInFunction(s);
|
WrapInFunction(s);
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
@ -38,7 +39,7 @@ TEST_F(MslGeneratorImplTest, Emit_Case) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, Emit_Case_BreaksByDefault) {
|
TEST_F(MslGeneratorImplTest, Emit_Case_BreaksByDefault) {
|
||||||
auto* s = Switch(1_i, Case(Expr(5_i), Block()), DefaultCase());
|
auto* s = Switch(1_i, Case(CaseSelector(5_i), Block()), DefaultCase());
|
||||||
WrapInFunction(s);
|
WrapInFunction(s);
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
@ -53,8 +54,8 @@ TEST_F(MslGeneratorImplTest, Emit_Case_BreaksByDefault) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, Emit_Case_WithFallthrough) {
|
TEST_F(MslGeneratorImplTest, Emit_Case_WithFallthrough) {
|
||||||
auto* s =
|
auto* s = Switch(1_i, Case(CaseSelector(5_i), Block(create<ast::FallthroughStatement>())),
|
||||||
Switch(1_i, Case(Expr(5_i), Block(create<ast::FallthroughStatement>())), DefaultCase());
|
DefaultCase());
|
||||||
WrapInFunction(s);
|
WrapInFunction(s);
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
@ -72,8 +73,8 @@ TEST_F(MslGeneratorImplTest, Emit_Case_MultipleSelectors) {
|
||||||
auto* s = Switch(1_i,
|
auto* s = Switch(1_i,
|
||||||
Case(
|
Case(
|
||||||
utils::Vector{
|
utils::Vector{
|
||||||
Expr(5_i),
|
CaseSelector(5_i),
|
||||||
Expr(6_i),
|
CaseSelector(6_i),
|
||||||
},
|
},
|
||||||
Block(create<ast::BreakStatement>())),
|
Block(create<ast::BreakStatement>())),
|
||||||
DefaultCase());
|
DefaultCase());
|
||||||
|
|
|
@ -25,16 +25,13 @@ TEST_F(MslGeneratorImplTest, Emit_Switch) {
|
||||||
auto* cond = Var("cond", ty.i32());
|
auto* cond = Var("cond", ty.i32());
|
||||||
|
|
||||||
auto* def_body = Block(create<ast::BreakStatement>());
|
auto* def_body = Block(create<ast::BreakStatement>());
|
||||||
auto* def = create<ast::CaseStatement>(utils::Empty, def_body);
|
auto* def = Case(DefaultCaseSelector(), def_body);
|
||||||
|
|
||||||
utils::Vector case_val{Expr(5_i)};
|
|
||||||
|
|
||||||
auto* case_body = Block(create<ast::BreakStatement>());
|
auto* case_body = Block(create<ast::BreakStatement>());
|
||||||
|
auto* case_stmt = Case(CaseSelector(5_i), case_body);
|
||||||
auto* case_stmt = create<ast::CaseStatement>(case_val, case_body);
|
|
||||||
|
|
||||||
utils::Vector body{case_stmt, def};
|
utils::Vector body{case_stmt, def};
|
||||||
auto* s = create<ast::SwitchStatement>(Expr(cond), body);
|
auto* s = Switch(cond, body);
|
||||||
WrapInFunction(cond, s);
|
WrapInFunction(cond, s);
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
|
||||||
|
@ -52,5 +49,27 @@ TEST_F(MslGeneratorImplTest, Emit_Switch) {
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(MslGeneratorImplTest, Emit_Switch_MixedDefault) {
|
||||||
|
auto* cond = Var("cond", ty.i32());
|
||||||
|
|
||||||
|
auto* def_body = Block(create<ast::BreakStatement>());
|
||||||
|
auto* def = Case(utils::Vector{CaseSelector(5_i), DefaultCaseSelector()}, def_body);
|
||||||
|
|
||||||
|
auto* s = Switch(cond, def);
|
||||||
|
WrapInFunction(cond, s);
|
||||||
|
GeneratorImpl& gen = Build();
|
||||||
|
|
||||||
|
gen.increment_indent();
|
||||||
|
|
||||||
|
ASSERT_TRUE(gen.EmitStatement(s)) << gen.error();
|
||||||
|
EXPECT_EQ(gen.result(), R"( switch(cond) {
|
||||||
|
case 5:
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace tint::writer::msl
|
} // namespace tint::writer::msl
|
||||||
|
|
|
@ -3456,19 +3456,26 @@ bool Builder::GenerateSwitchStatement(const ast::SwitchStatement* stmt) {
|
||||||
|
|
||||||
std::vector<uint32_t> case_ids;
|
std::vector<uint32_t> case_ids;
|
||||||
for (const auto* item : stmt->body) {
|
for (const auto* item : stmt->body) {
|
||||||
if (item->IsDefault()) {
|
auto block_id = default_block_id;
|
||||||
case_ids.push_back(default_block_id);
|
if (!item->ContainsDefault()) {
|
||||||
|
auto block = result_op();
|
||||||
|
block_id = std::get<uint32_t>(block);
|
||||||
|
}
|
||||||
|
case_ids.push_back(block_id);
|
||||||
|
|
||||||
|
// If this case statement is only a default selector skip adding the block
|
||||||
|
// as it will be done below.
|
||||||
|
if (item->selectors.Length() == 1 && item->ContainsDefault()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto block = result_op();
|
|
||||||
auto block_id = std::get<uint32_t>(block);
|
|
||||||
|
|
||||||
case_ids.push_back(block_id);
|
|
||||||
|
|
||||||
auto* sem = builder_.Sem().Get<sem::CaseStatement>(item);
|
auto* sem = builder_.Sem().Get<sem::CaseStatement>(item);
|
||||||
for (auto* selector : sem->Selectors()) {
|
for (auto* selector : sem->Selectors()) {
|
||||||
params.push_back(Operand(selector->As<uint32_t>()));
|
if (selector->IsDefault()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
params.push_back(Operand(selector->Value()->As<uint32_t>()));
|
||||||
params.push_back(Operand(block_id));
|
params.push_back(Operand(block_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3490,7 +3497,7 @@ bool Builder::GenerateSwitchStatement(const ast::SwitchStatement* stmt) {
|
||||||
for (uint32_t i = 0; i < body.Length(); i++) {
|
for (uint32_t i = 0; i < body.Length(); i++) {
|
||||||
auto* item = body[i];
|
auto* item = body[i];
|
||||||
|
|
||||||
if (item->IsDefault()) {
|
if (item->ContainsDefault()) {
|
||||||
generated_default = true;
|
generated_default = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,9 +62,9 @@ TEST_F(BuilderTest, Switch_WithCase) {
|
||||||
|
|
||||||
auto* func = Func("a_func", utils::Empty, ty.void_(),
|
auto* func = Func("a_func", utils::Empty, ty.void_(),
|
||||||
utils::Vector{
|
utils::Vector{
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
Case(Expr(1_i), Block(Assign("v", 1_i))), //
|
Case(CaseSelector(1_i), Block(Assign("v", 1_i))), //
|
||||||
Case(Expr(2_i), Block(Assign("v", 2_i))), //
|
Case(CaseSelector(2_i), Block(Assign("v", 2_i))), //
|
||||||
DefaultCase()),
|
DefaultCase()),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -119,9 +119,9 @@ TEST_F(BuilderTest, Switch_WithCase_Unsigned) {
|
||||||
|
|
||||||
auto* func = Func("a_func", utils::Empty, ty.void_(),
|
auto* func = Func("a_func", utils::Empty, ty.void_(),
|
||||||
utils::Vector{
|
utils::Vector{
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
Case(Expr(1_u), Block(Assign("v", 1_i))), //
|
Case(CaseSelector(1_u), Block(Assign("v", 1_i))), //
|
||||||
Case(Expr(2_u), Block(Assign("v", 2_i))), //
|
Case(CaseSelector(2_u), Block(Assign("v", 2_i))), //
|
||||||
DefaultCase()),
|
DefaultCase()),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -226,11 +226,11 @@ TEST_F(BuilderTest, Switch_WithCaseAndDefault) {
|
||||||
|
|
||||||
auto* func = Func("a_func", utils::Empty, ty.void_(),
|
auto* func = Func("a_func", utils::Empty, ty.void_(),
|
||||||
utils::Vector{
|
utils::Vector{
|
||||||
Switch(Expr("a"), //
|
Switch(Expr("a"), //
|
||||||
Case(Expr(1_i), //
|
Case(CaseSelector(1_i), //
|
||||||
Block(Assign("v", 1_i))), //
|
Block(Assign("v", 1_i))), //
|
||||||
Case(utils::Vector{Expr(2_i), Expr(3_i)}, //
|
Case(utils::Vector{CaseSelector(2_i), CaseSelector(3_i)}, //
|
||||||
Block(Assign("v", 2_i))), //
|
Block(Assign("v", 2_i))), //
|
||||||
DefaultCase(Block(Assign("v", 3_i)))),
|
DefaultCase(Block(Assign("v", 3_i)))),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -273,6 +273,61 @@ OpFunctionEnd
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(BuilderTest, Switch_WithCaseAndMixedDefault) {
|
||||||
|
// switch(a) {
|
||||||
|
// case 1i:
|
||||||
|
// v = 1i;
|
||||||
|
// case 2i, 3i, default:
|
||||||
|
// v = 2i;
|
||||||
|
// }
|
||||||
|
|
||||||
|
auto* v = GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
|
||||||
|
auto* a = GlobalVar("a", ty.i32(), ast::AddressSpace::kPrivate);
|
||||||
|
|
||||||
|
auto* func = Func("a_func", utils::Empty, ty.void_(),
|
||||||
|
utils::Vector{Switch(Expr("a"), //
|
||||||
|
Case(CaseSelector(1_i), //
|
||||||
|
Block(Assign("v", 1_i))), //
|
||||||
|
Case(utils::Vector{CaseSelector(2_i), CaseSelector(3_i),
|
||||||
|
DefaultCaseSelector()}, //
|
||||||
|
Block(Assign("v", 2_i))) //
|
||||||
|
)});
|
||||||
|
|
||||||
|
spirv::Builder& b = Build();
|
||||||
|
|
||||||
|
ASSERT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
|
||||||
|
ASSERT_TRUE(b.GenerateGlobalVariable(a)) << b.error();
|
||||||
|
ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
|
||||||
|
|
||||||
|
EXPECT_EQ(DumpBuilder(b), R"(OpName %1 "v"
|
||||||
|
OpName %5 "a"
|
||||||
|
OpName %8 "a_func"
|
||||||
|
%3 = OpTypeInt 32 1
|
||||||
|
%2 = OpTypePointer Private %3
|
||||||
|
%4 = OpConstantNull %3
|
||||||
|
%1 = OpVariable %2 Private %4
|
||||||
|
%5 = OpVariable %2 Private %4
|
||||||
|
%7 = OpTypeVoid
|
||||||
|
%6 = OpTypeFunction %7
|
||||||
|
%14 = OpConstant %3 1
|
||||||
|
%15 = OpConstant %3 2
|
||||||
|
%8 = OpFunction %7 None %6
|
||||||
|
%9 = OpLabel
|
||||||
|
%11 = OpLoad %3 %5
|
||||||
|
OpSelectionMerge %10 None
|
||||||
|
OpSwitch %11 %12 1 %13 2 %12 3 %12
|
||||||
|
%13 = OpLabel
|
||||||
|
OpStore %1 %14
|
||||||
|
OpBranch %10
|
||||||
|
%12 = OpLabel
|
||||||
|
OpStore %1 %15
|
||||||
|
OpBranch %10
|
||||||
|
%10 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(BuilderTest, Switch_CaseWithFallthrough) {
|
TEST_F(BuilderTest, Switch_CaseWithFallthrough) {
|
||||||
// switch(a) {
|
// switch(a) {
|
||||||
// case 1i:
|
// case 1i:
|
||||||
|
@ -290,9 +345,9 @@ TEST_F(BuilderTest, Switch_CaseWithFallthrough) {
|
||||||
auto* func = Func("a_func", utils::Empty, ty.void_(),
|
auto* func = Func("a_func", utils::Empty, ty.void_(),
|
||||||
utils::Vector{
|
utils::Vector{
|
||||||
Switch(Expr("a"), //
|
Switch(Expr("a"), //
|
||||||
Case(Expr(1_i), //
|
Case(CaseSelector(1_i), //
|
||||||
Block(Assign("v", 1_i), Fallthrough())), //
|
Block(Assign("v", 1_i), Fallthrough())), //
|
||||||
Case(Expr(2_i), //
|
Case(CaseSelector(2_i), //
|
||||||
Block(Assign("v", 2_i))), //
|
Block(Assign("v", 2_i))), //
|
||||||
DefaultCase(Block(Assign("v", 3_i)))),
|
DefaultCase(Block(Assign("v", 3_i)))),
|
||||||
});
|
});
|
||||||
|
@ -351,9 +406,9 @@ TEST_F(BuilderTest, Switch_WithNestedBreak) {
|
||||||
|
|
||||||
auto* func = Func("a_func", utils::Empty, ty.void_(),
|
auto* func = Func("a_func", utils::Empty, ty.void_(),
|
||||||
utils::Vector{
|
utils::Vector{
|
||||||
Switch("a", //
|
Switch("a", //
|
||||||
Case(Expr(1_i), //
|
Case(CaseSelector(1_i), //
|
||||||
Block( //
|
Block( //
|
||||||
If(Expr(true), Block(create<ast::BreakStatement>())),
|
If(Expr(true), Block(create<ast::BreakStatement>())),
|
||||||
Assign("v", 1_i))),
|
Assign("v", 1_i))),
|
||||||
DefaultCase()),
|
DefaultCase()),
|
||||||
|
@ -414,9 +469,9 @@ TEST_F(BuilderTest, Switch_AllReturn) {
|
||||||
|
|
||||||
auto* fn = Func("f", utils::Empty, ty.i32(),
|
auto* fn = Func("f", utils::Empty, ty.i32(),
|
||||||
utils::Vector{
|
utils::Vector{
|
||||||
Switch(1_i, //
|
Switch(1_i, //
|
||||||
Case(Expr(1_i), Block(Return(1_i))), //
|
Case(CaseSelector(1_i), Block(Return(1_i))), //
|
||||||
Case(Expr(2_i), Block(Fallthrough())), //
|
Case(CaseSelector(2_i), Block(Fallthrough())), //
|
||||||
DefaultCase(Block(Return(3_i)))),
|
DefaultCase(Block(Return(3_i)))),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1024,26 +1024,28 @@ bool GeneratorImpl::EmitBreak(const ast::BreakStatement*) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GeneratorImpl::EmitCase(const ast::CaseStatement* stmt) {
|
bool GeneratorImpl::EmitCase(const ast::CaseStatement* stmt) {
|
||||||
if (stmt->IsDefault()) {
|
if (stmt->selectors.Length() == 1 && stmt->ContainsDefault()) {
|
||||||
line() << "default: {";
|
line() << "default: {";
|
||||||
} else {
|
} else {
|
||||||
auto out = line();
|
auto out = line();
|
||||||
out << "case ";
|
out << "case ";
|
||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto* expr : stmt->selectors) {
|
for (auto* sel : stmt->selectors) {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
out << ", ";
|
out << ", ";
|
||||||
}
|
}
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
if (!EmitExpression(out, expr)) {
|
|
||||||
|
if (sel->IsDefault()) {
|
||||||
|
out << "default";
|
||||||
|
} else if (!EmitExpression(out, sel->expr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out << ": {";
|
out << ": {";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!EmitStatementsWithIndent(stmt->body->statements)) {
|
if (!EmitStatementsWithIndent(stmt->body->statements)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,8 @@ namespace {
|
||||||
using WgslGeneratorImplTest = TestHelper;
|
using WgslGeneratorImplTest = TestHelper;
|
||||||
|
|
||||||
TEST_F(WgslGeneratorImplTest, Emit_Case) {
|
TEST_F(WgslGeneratorImplTest, Emit_Case) {
|
||||||
auto* s = Switch(1_i, Case(Expr(5_i), Block(create<ast::BreakStatement>())), DefaultCase());
|
auto* s =
|
||||||
|
Switch(1_i, Case(CaseSelector(5_i), Block(create<ast::BreakStatement>())), DefaultCase());
|
||||||
WrapInFunction(s);
|
WrapInFunction(s);
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
@ -40,8 +41,8 @@ TEST_F(WgslGeneratorImplTest, Emit_Case_MultipleSelectors) {
|
||||||
auto* s = Switch(1_i,
|
auto* s = Switch(1_i,
|
||||||
Case(
|
Case(
|
||||||
utils::Vector{
|
utils::Vector{
|
||||||
Expr(5_i),
|
CaseSelector(5_i),
|
||||||
Expr(6_i),
|
CaseSelector(6_i),
|
||||||
},
|
},
|
||||||
Block(create<ast::BreakStatement>())),
|
Block(create<ast::BreakStatement>())),
|
||||||
DefaultCase());
|
DefaultCase());
|
||||||
|
|
|
@ -23,8 +23,8 @@ using WgslGeneratorImplTest = TestHelper;
|
||||||
|
|
||||||
TEST_F(WgslGeneratorImplTest, Emit_Fallthrough) {
|
TEST_F(WgslGeneratorImplTest, Emit_Fallthrough) {
|
||||||
auto* f = create<ast::FallthroughStatement>();
|
auto* f = create<ast::FallthroughStatement>();
|
||||||
WrapInFunction(Switch(1_i, //
|
WrapInFunction(Switch(1_i, //
|
||||||
Case(Expr(1_i), Block(f)), //
|
Case(CaseSelector(1_i), Block(f)), //
|
||||||
DefaultCase()));
|
DefaultCase()));
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
|
|
@ -25,13 +25,10 @@ TEST_F(WgslGeneratorImplTest, Emit_Switch) {
|
||||||
GlobalVar("cond", ty.i32(), ast::AddressSpace::kPrivate);
|
GlobalVar("cond", ty.i32(), ast::AddressSpace::kPrivate);
|
||||||
|
|
||||||
auto* def_body = Block(create<ast::BreakStatement>());
|
auto* def_body = Block(create<ast::BreakStatement>());
|
||||||
auto* def = create<ast::CaseStatement>(utils::Empty, def_body);
|
auto* def = Case(DefaultCaseSelector(), def_body);
|
||||||
|
|
||||||
utils::Vector case_val{Expr(5_i)};
|
|
||||||
|
|
||||||
auto* case_body = Block(create<ast::BreakStatement>());
|
auto* case_body = Block(create<ast::BreakStatement>());
|
||||||
|
auto* case_stmt = Case(utils::Vector{CaseSelector(5_i)}, case_body);
|
||||||
auto* case_stmt = create<ast::CaseStatement>(case_val, case_body);
|
|
||||||
|
|
||||||
utils::Vector body{
|
utils::Vector body{
|
||||||
case_stmt,
|
case_stmt,
|
||||||
|
@ -39,7 +36,7 @@ TEST_F(WgslGeneratorImplTest, Emit_Switch) {
|
||||||
};
|
};
|
||||||
|
|
||||||
auto* cond = Expr("cond");
|
auto* cond = Expr("cond");
|
||||||
auto* s = create<ast::SwitchStatement>(cond, body);
|
auto* s = Switch(cond, body);
|
||||||
WrapInFunction(s);
|
WrapInFunction(s);
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
@ -58,5 +55,28 @@ TEST_F(WgslGeneratorImplTest, Emit_Switch) {
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(WgslGeneratorImplTest, Emit_Switch_MixedDefault) {
|
||||||
|
GlobalVar("cond", ty.i32(), ast::AddressSpace::kPrivate);
|
||||||
|
|
||||||
|
auto* def_body = Block(create<ast::BreakStatement>());
|
||||||
|
auto* def = Case(utils::Vector{CaseSelector(5_i), DefaultCaseSelector()}, def_body);
|
||||||
|
|
||||||
|
auto* cond = Expr("cond");
|
||||||
|
auto* s = Switch(cond, utils::Vector{def});
|
||||||
|
WrapInFunction(s);
|
||||||
|
|
||||||
|
GeneratorImpl& gen = Build();
|
||||||
|
|
||||||
|
gen.increment_indent();
|
||||||
|
|
||||||
|
ASSERT_TRUE(gen.EmitStatement(s)) << gen.error();
|
||||||
|
EXPECT_EQ(gen.result(), R"( switch(cond) {
|
||||||
|
case 5i, default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace tint::writer::wgsl
|
} // namespace tint::writer::wgsl
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
@compute @workgroup_size(1)
|
||||||
|
fn f() {
|
||||||
|
var i : i32;
|
||||||
|
var result : i32;
|
||||||
|
switch(i) {
|
||||||
|
case default: {
|
||||||
|
result = 10;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
result = 22;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
result = 33;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
[numthreads(1, 1, 1)]
|
||||||
|
void f() {
|
||||||
|
int i = 0;
|
||||||
|
int result = 0;
|
||||||
|
switch(i) {
|
||||||
|
default: {
|
||||||
|
result = 10;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
result = 22;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
result = 33;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
[numthreads(1, 1, 1)]
|
||||||
|
void f() {
|
||||||
|
int i = 0;
|
||||||
|
int result = 0;
|
||||||
|
switch(i) {
|
||||||
|
default: {
|
||||||
|
result = 10;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
result = 22;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
result = 33;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
#version 310 es
|
||||||
|
|
||||||
|
void f() {
|
||||||
|
int i = 0;
|
||||||
|
int result = 0;
|
||||||
|
switch(i) {
|
||||||
|
default: {
|
||||||
|
result = 10;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
result = 22;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
result = 33;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||||
|
void main() {
|
||||||
|
f();
|
||||||
|
return;
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
#include <metal_stdlib>
|
||||||
|
|
||||||
|
using namespace metal;
|
||||||
|
kernel void f() {
|
||||||
|
int i = 0;
|
||||||
|
int result = 0;
|
||||||
|
switch(i) {
|
||||||
|
default: {
|
||||||
|
result = 10;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
result = 22;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
result = 33;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
; SPIR-V
|
||||||
|
; Version: 1.3
|
||||||
|
; Generator: Google Tint Compiler; 0
|
||||||
|
; Bound: 18
|
||||||
|
; Schema: 0
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint GLCompute %f "f"
|
||||||
|
OpExecutionMode %f LocalSize 1 1 1
|
||||||
|
OpName %f "f"
|
||||||
|
OpName %i "i"
|
||||||
|
OpName %result "result"
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%1 = OpTypeFunction %void
|
||||||
|
%int = OpTypeInt 32 1
|
||||||
|
%_ptr_Function_int = OpTypePointer Function %int
|
||||||
|
%8 = OpConstantNull %int
|
||||||
|
%int_10 = OpConstant %int 10
|
||||||
|
%int_22 = OpConstant %int 22
|
||||||
|
%int_33 = OpConstant %int 33
|
||||||
|
%f = OpFunction %void None %1
|
||||||
|
%4 = OpLabel
|
||||||
|
%i = OpVariable %_ptr_Function_int Function %8
|
||||||
|
%result = OpVariable %_ptr_Function_int Function %8
|
||||||
|
%11 = OpLoad %int %i
|
||||||
|
OpSelectionMerge %10 None
|
||||||
|
OpSwitch %11 %12 1 %13 2 %14
|
||||||
|
%12 = OpLabel
|
||||||
|
OpStore %result %int_10
|
||||||
|
OpBranch %10
|
||||||
|
%13 = OpLabel
|
||||||
|
OpStore %result %int_22
|
||||||
|
OpBranch %10
|
||||||
|
%14 = OpLabel
|
||||||
|
OpStore %result %int_33
|
||||||
|
OpBranch %10
|
||||||
|
%10 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
|
@ -0,0 +1,16 @@
|
||||||
|
@compute @workgroup_size(1)
|
||||||
|
fn f() {
|
||||||
|
var i : i32;
|
||||||
|
var result : i32;
|
||||||
|
switch(i) {
|
||||||
|
default: {
|
||||||
|
result = 10;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
result = 22;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
result = 33;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
@compute @workgroup_size(1)
|
||||||
|
fn f() {
|
||||||
|
var i : i32;
|
||||||
|
var result : i32;
|
||||||
|
switch(i) {
|
||||||
|
case 0: {
|
||||||
|
result = 10;
|
||||||
|
}
|
||||||
|
case 1, default: {
|
||||||
|
result = 22;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
result = 33;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
[numthreads(1, 1, 1)]
|
||||||
|
void f() {
|
||||||
|
int i = 0;
|
||||||
|
int result = 0;
|
||||||
|
switch(i) {
|
||||||
|
case 0: {
|
||||||
|
result = 10;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
default: {
|
||||||
|
result = 22;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
result = 33;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
[numthreads(1, 1, 1)]
|
||||||
|
void f() {
|
||||||
|
int i = 0;
|
||||||
|
int result = 0;
|
||||||
|
switch(i) {
|
||||||
|
case 0: {
|
||||||
|
result = 10;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
default: {
|
||||||
|
result = 22;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
result = 33;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
#version 310 es
|
||||||
|
|
||||||
|
void f() {
|
||||||
|
int i = 0;
|
||||||
|
int result = 0;
|
||||||
|
switch(i) {
|
||||||
|
case 0: {
|
||||||
|
result = 10;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
default: {
|
||||||
|
result = 22;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
result = 33;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||||
|
void main() {
|
||||||
|
f();
|
||||||
|
return;
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
#include <metal_stdlib>
|
||||||
|
|
||||||
|
using namespace metal;
|
||||||
|
kernel void f() {
|
||||||
|
int i = 0;
|
||||||
|
int result = 0;
|
||||||
|
switch(i) {
|
||||||
|
case 0: {
|
||||||
|
result = 10;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
default: {
|
||||||
|
result = 22;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
result = 33;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
; SPIR-V
|
||||||
|
; Version: 1.3
|
||||||
|
; Generator: Google Tint Compiler; 0
|
||||||
|
; Bound: 18
|
||||||
|
; Schema: 0
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint GLCompute %f "f"
|
||||||
|
OpExecutionMode %f LocalSize 1 1 1
|
||||||
|
OpName %f "f"
|
||||||
|
OpName %i "i"
|
||||||
|
OpName %result "result"
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%1 = OpTypeFunction %void
|
||||||
|
%int = OpTypeInt 32 1
|
||||||
|
%_ptr_Function_int = OpTypePointer Function %int
|
||||||
|
%8 = OpConstantNull %int
|
||||||
|
%int_10 = OpConstant %int 10
|
||||||
|
%int_22 = OpConstant %int 22
|
||||||
|
%int_33 = OpConstant %int 33
|
||||||
|
%f = OpFunction %void None %1
|
||||||
|
%4 = OpLabel
|
||||||
|
%i = OpVariable %_ptr_Function_int Function %8
|
||||||
|
%result = OpVariable %_ptr_Function_int Function %8
|
||||||
|
%11 = OpLoad %int %i
|
||||||
|
OpSelectionMerge %10 None
|
||||||
|
OpSwitch %11 %12 0 %13 1 %12 2 %14
|
||||||
|
%13 = OpLabel
|
||||||
|
OpStore %result %int_10
|
||||||
|
OpBranch %10
|
||||||
|
%12 = OpLabel
|
||||||
|
OpStore %result %int_22
|
||||||
|
OpBranch %10
|
||||||
|
%14 = OpLabel
|
||||||
|
OpStore %result %int_33
|
||||||
|
OpBranch %10
|
||||||
|
%10 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
|
@ -0,0 +1,16 @@
|
||||||
|
@compute @workgroup_size(1)
|
||||||
|
fn f() {
|
||||||
|
var i : i32;
|
||||||
|
var result : i32;
|
||||||
|
switch(i) {
|
||||||
|
case 0: {
|
||||||
|
result = 10;
|
||||||
|
}
|
||||||
|
case 1, default: {
|
||||||
|
result = 22;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
result = 33;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue