[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:
dan sinclair 2022-10-19 15:55:02 +00:00 committed by Dawn LUCI CQ
parent d27151d333
commit f148f0891b
62 changed files with 1212 additions and 367 deletions

View File

@ -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",

View File

@ -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

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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");

View File

@ -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()),
}); });

View File

@ -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

View File

@ -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;
} }

View File

@ -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;
} }
} }

View File

@ -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: {

View File

@ -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

View File

@ -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();

View File

@ -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 ^: } }
^ ^
)"); )");

View File

@ -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) {

View File

@ -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>());
} }

View File

@ -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

View File

@ -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();

View File

@ -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());

View File

@ -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);
} }

View File

@ -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), //

View File

@ -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: {

View File

@ -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);

View File

@ -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},

View File

@ -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();

View File

@ -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();
} }

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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));
} }

View File

@ -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);
} }

View File

@ -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 << " {";
} }
} }

View File

@ -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());

View File

@ -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

View File

@ -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);
} }

View File

@ -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();

View File

@ -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);

View File

@ -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 << " {";
} }
} }

View File

@ -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());

View File

@ -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

View File

@ -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;
} }

View File

@ -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)))),
}); });

View File

@ -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;
} }

View File

@ -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());

View File

@ -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();

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}
}
}