reader/wgsl: Allow trailing commas in lists
Also allows empty constexpr type constructors.
Fixed: tint:739
Change-Id: Ic4729c13b6ac538491d5d1d3c7960e78fac80127
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/49443
Commit-Queue: James Price <jrprice@google.com>
Auto-Submit: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 6f75fde..d303610 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -1416,16 +1416,16 @@
// param_list
// :
-// | (param COMMA)* param
+// | (param COMMA)* param COMMA?
Expect<ast::VariableList> ParserImpl::expect_param_list() {
- // Check for an empty list.
- auto t = peek();
- if (!t.IsIdentifier() && !t.IsAttrLeft()) {
- return ast::VariableList{};
- }
-
ast::VariableList ret;
while (synchronized_) {
+ // Check for the end of the list.
+ auto t = peek();
+ if (!t.IsIdentifier() && !t.IsAttrLeft()) {
+ break;
+ }
+
auto param = expect_param();
if (param.errored)
return Failure::kErrored;
@@ -1912,34 +1912,26 @@
}
// case_selectors
-// : const_literal (COMMA const_literal)*
+// : const_literal (COMMA const_literal)* COMMA?
Expect<ast::CaseSelectorList> ParserImpl::expect_case_selectors() {
ast::CaseSelectorList selectors;
- for (;;) {
- auto t = peek();
- auto matched_comma = match(Token::Type::kComma);
-
- if (selectors.empty() && matched_comma)
- return add_error(t, "a selector is expected before the comma");
- if (matched_comma)
- t = peek();
-
+ while (synchronized_) {
auto cond = const_literal();
- if (cond.errored)
+ if (cond.errored) {
return Failure::kErrored;
- if (!cond.matched) {
- if (matched_comma) {
- return add_error(t, "a selector is expected after the comma");
- }
+ } else if (!cond.matched) {
break;
+ } else if (!cond->Is<ast::IntLiteral>()) {
+ return add_error(cond.value->source(),
+ "invalid case selector must be an integer value");
}
- if (!cond->Is<ast::IntLiteral>())
- return add_error(t, "invalid case selector must be an integer value");
- if (!selectors.empty() && !matched_comma)
- return add_error(t, "expected a comma after the previous selector");
selectors.push_back(cond.value->As<ast::IntLiteral>());
+
+ if (!match(Token::Type::kComma)) {
+ break;
+ }
}
if (selectors.empty())
@@ -2305,25 +2297,18 @@
}
// argument_expression_list
-// : PAREN_LEFT (logical_or_expression COMMA)* logical_or_expression
+// : PAREN_LEFT ((logical_or_expression COMMA)* logical_or_expression COMMA?)?
// PAREN_RIGHT
Expect<ast::ExpressionList> ParserImpl::expect_argument_expression_list(
const std::string& use) {
return expect_paren_block(use, [&]() -> Expect<ast::ExpressionList> {
- // Check for empty list.
- // TODO(crbug.com/tint/739): Remove this (handled by !arg.matched).
- if (peek().IsParenRight()) {
- return ast::ExpressionList{};
- }
-
ast::ExpressionList ret;
while (synchronized_) {
auto arg = logical_or_expression();
- if (arg.errored)
+ if (arg.errored) {
return Failure::kErrored;
- if (!arg.matched) {
- // TODO(crbug.com/tint/739): remove error to allow trailing commas.
- return add_error(peek(), "unable to parse argument expression");
+ } else if (!arg.matched) {
+ break;
}
ret.push_back(arg.value);
@@ -2838,7 +2823,7 @@
}
// const_expr
-// : type_decl PAREN_LEFT (const_expr COMMA)? const_expr PAREN_RIGHT
+// : type_decl PAREN_LEFT ((const_expr COMMA)? const_expr COMMA?)? PAREN_RIGHT
// | const_literal
Expect<ast::ConstructorExpression*> ParserImpl::expect_const_expr() {
auto t = peek();
@@ -2852,15 +2837,20 @@
auto params = expect_paren_block("type constructor",
[&]() -> Expect<ast::ExpressionList> {
ast::ExpressionList list;
- auto param = expect_const_expr();
- if (param.errored)
- return Failure::kErrored;
- list.emplace_back(param.value);
- while (match(Token::Type::kComma)) {
- param = expect_const_expr();
- if (param.errored)
+ while (synchronized_) {
+ if (peek().IsParenRight()) {
+ break;
+ }
+
+ auto arg = expect_const_expr();
+ if (arg.errored) {
return Failure::kErrored;
- list.emplace_back(param.value);
+ }
+ list.emplace_back(arg.value);
+
+ if (!match(Token::Type::kComma)) {
+ break;
+ }
}
return list;
});
diff --git a/src/reader/wgsl/parser_impl_argument_expression_list_test.cc b/src/reader/wgsl/parser_impl_argument_expression_list_test.cc
index ca8ed6c..bb73660 100644
--- a/src/reader/wgsl/parser_impl_argument_expression_list_test.cc
+++ b/src/reader/wgsl/parser_impl_argument_expression_list_test.cc
@@ -50,6 +50,17 @@
ASSERT_TRUE(e.value[2]->Is<ast::BinaryExpression>());
}
+TEST_F(ParserImplTest, ArgumentExpressionList_TrailingComma) {
+ auto p = parser("(a, 42,)");
+ auto e = p->expect_argument_expression_list("argument list");
+ ASSERT_FALSE(p->has_error()) << p->error();
+ ASSERT_FALSE(e.errored);
+
+ ASSERT_EQ(e.value.size(), 2u);
+ ASSERT_TRUE(e.value[0]->Is<ast::IdentifierExpression>());
+ ASSERT_TRUE(e.value[1]->Is<ast::ConstructorExpression>());
+}
+
TEST_F(ParserImplTest, ArgumentExpressionList_HandlesMissingLeftParen) {
auto p = parser("a)");
auto e = p->expect_argument_expression_list("argument list");
@@ -66,12 +77,20 @@
EXPECT_EQ(p->error(), "1:3: expected ')' for argument list");
}
-TEST_F(ParserImplTest, ArgumentExpressionList_HandlesMissingExpression) {
- auto p = parser("(a, )");
+TEST_F(ParserImplTest, ArgumentExpressionList_HandlesMissingExpression_0) {
+ auto p = parser("(,)");
auto e = p->expect_argument_expression_list("argument list");
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(e.errored);
- EXPECT_EQ(p->error(), "1:5: unable to parse argument expression");
+ EXPECT_EQ(p->error(), "1:2: expected ')' for argument list");
+}
+
+TEST_F(ParserImplTest, ArgumentExpressionList_HandlesMissingExpression_1) {
+ auto p = parser("(a, ,)");
+ auto e = p->expect_argument_expression_list("argument list");
+ ASSERT_TRUE(p->has_error());
+ ASSERT_TRUE(e.errored);
+ EXPECT_EQ(p->error(), "1:5: expected ')' for argument list");
}
TEST_F(ParserImplTest, ArgumentExpressionList_HandlesInvalidExpression) {
@@ -79,7 +98,7 @@
auto e = p->expect_argument_expression_list("argument list");
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(e.errored);
- EXPECT_EQ(p->error(), "1:2: unable to parse argument expression");
+ EXPECT_EQ(p->error(), "1:2: expected ')' for argument list");
}
} // namespace
diff --git a/src/reader/wgsl/parser_impl_call_stmt_test.cc b/src/reader/wgsl/parser_impl_call_stmt_test.cc
index 3a4d206..ec41195 100644
--- a/src/reader/wgsl/parser_impl_call_stmt_test.cc
+++ b/src/reader/wgsl/parser_impl_call_stmt_test.cc
@@ -59,13 +59,33 @@
EXPECT_TRUE(c->params()[2]->Is<ast::BinaryExpression>());
}
+TEST_F(ParserImplTest, Statement_Call_WithParams_TrailingComma) {
+ auto p = parser("a(1, b,);");
+ auto e = p->statement();
+ ASSERT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(e.value, nullptr);
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+
+ ASSERT_TRUE(e->Is<ast::CallStatement>());
+ auto* c = e->As<ast::CallStatement>()->expr();
+
+ ASSERT_TRUE(c->func()->Is<ast::IdentifierExpression>());
+ auto* ident = c->func()->As<ast::IdentifierExpression>();
+ EXPECT_EQ(ident->symbol(), p->builder().Symbols().Get("a"));
+
+ EXPECT_EQ(c->params().size(), 2u);
+ EXPECT_TRUE(c->params()[0]->Is<ast::ConstructorExpression>());
+ EXPECT_TRUE(c->params()[1]->Is<ast::IdentifierExpression>());
+}
+
TEST_F(ParserImplTest, Statement_Call_Missing_RightParen) {
auto p = parser("a(");
auto e = p->statement();
EXPECT_TRUE(p->has_error());
EXPECT_TRUE(e.errored);
EXPECT_FALSE(e.matched);
- EXPECT_EQ(p->error(), "1:3: unable to parse argument expression");
+ EXPECT_EQ(p->error(), "1:3: expected ')' for function call");
}
TEST_F(ParserImplTest, Statement_Call_Missing_Semi) {
diff --git a/src/reader/wgsl/parser_impl_const_expr_test.cc b/src/reader/wgsl/parser_impl_const_expr_test.cc
index 8498ed4..00a053c 100644
--- a/src/reader/wgsl/parser_impl_const_expr_test.cc
+++ b/src/reader/wgsl/parser_impl_const_expr_test.cc
@@ -47,6 +47,38 @@
EXPECT_FLOAT_EQ(c->literal()->As<ast::FloatLiteral>()->value(), 2.);
}
+TEST_F(ParserImplTest, ConstExpr_TypeDecl_Empty) {
+ auto p = parser("vec2<f32>()");
+ auto e = p->expect_const_expr();
+ ASSERT_FALSE(p->has_error()) << p->error();
+ ASSERT_FALSE(e.errored);
+ ASSERT_TRUE(e->Is<ast::ConstructorExpression>());
+ ASSERT_TRUE(e->Is<ast::TypeConstructorExpression>());
+
+ auto* t = e->As<ast::TypeConstructorExpression>();
+ ASSERT_TRUE(t->type()->Is<sem::Vector>());
+ EXPECT_EQ(t->type()->As<sem::Vector>()->size(), 2u);
+
+ ASSERT_EQ(t->values().size(), 0u);
+}
+
+TEST_F(ParserImplTest, ConstExpr_TypeDecl_TrailingComma) {
+ auto p = parser("vec2<f32>(1., 2.,)");
+ auto e = p->expect_const_expr();
+ ASSERT_FALSE(p->has_error()) << p->error();
+ ASSERT_FALSE(e.errored);
+ ASSERT_TRUE(e->Is<ast::ConstructorExpression>());
+ ASSERT_TRUE(e->Is<ast::TypeConstructorExpression>());
+
+ auto* t = e->As<ast::TypeConstructorExpression>();
+ ASSERT_TRUE(t->type()->Is<sem::Vector>());
+ EXPECT_EQ(t->type()->As<sem::Vector>()->size(), 2u);
+
+ ASSERT_EQ(t->values().size(), 2u);
+ ASSERT_TRUE(t->values()[0]->Is<ast::ScalarConstructorExpression>());
+ ASSERT_TRUE(t->values()[1]->Is<ast::ScalarConstructorExpression>());
+}
+
TEST_F(ParserImplTest, ConstExpr_TypeDecl_MissingRightParen) {
auto p = parser("vec2<f32>(1., 2.");
auto e = p->expect_const_expr();
@@ -65,15 +97,6 @@
EXPECT_EQ(p->error(), "1:11: expected '(' for type constructor");
}
-TEST_F(ParserImplTest, ConstExpr_TypeDecl_HangingComma) {
- auto p = parser("vec2<f32>(1.,)");
- auto e = p->expect_const_expr();
- ASSERT_TRUE(p->has_error());
- ASSERT_TRUE(e.errored);
- ASSERT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:14: unable to parse constant literal");
-}
-
TEST_F(ParserImplTest, ConstExpr_TypeDecl_MissingComma) {
auto p = parser("vec2<f32>(1. 2.");
auto e = p->expect_const_expr();
@@ -83,15 +106,6 @@
EXPECT_EQ(p->error(), "1:14: expected ')' for type constructor");
}
-TEST_F(ParserImplTest, ConstExpr_MissingExpr) {
- auto p = parser("vec2<f32>()");
- auto e = p->expect_const_expr();
- ASSERT_TRUE(p->has_error());
- ASSERT_TRUE(e.errored);
- ASSERT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:11: unable to parse constant literal");
-}
-
TEST_F(ParserImplTest, ConstExpr_InvalidExpr) {
auto p = parser("vec2<f32>(1., if(a) {})");
auto e = p->expect_const_expr();
diff --git a/src/reader/wgsl/parser_impl_error_msg_test.cc b/src/reader/wgsl/parser_impl_error_msg_test.cc
index 17d5cd4..bca27f3 100644
--- a/src/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/reader/wgsl/parser_impl_error_msg_test.cc
@@ -137,14 +137,14 @@
TEST_F(ParserImplErrorTest, CallStmtInvalidArgument0) {
EXPECT("fn f() { f(<); }",
- "test.wgsl:1:12 error: unable to parse argument expression\n"
+ "test.wgsl:1:12 error: expected ')' for function call\n"
"fn f() { f(<); }\n"
" ^\n");
}
TEST_F(ParserImplErrorTest, CallStmtInvalidArgument1) {
EXPECT("fn f() { f(1.0, <); }",
- "test.wgsl:1:17 error: unable to parse argument expression\n"
+ "test.wgsl:1:17 error: expected ')' for function call\n"
"fn f() { f(1.0, <); }\n"
" ^\n");
}
@@ -446,9 +446,9 @@
}
TEST_F(ParserImplErrorTest, FunctionDeclParamMissing) {
- EXPECT("fn f(x : i32, ) {}",
- "test.wgsl:1:15 error: expected identifier for parameter\n"
- "fn f(x : i32, ) {}\n"
+ EXPECT("fn f(x : i32, ,) {}",
+ "test.wgsl:1:15 error: expected ')' for function declaration\n"
+ "fn f(x : i32, ,) {}\n"
" ^\n");
}
diff --git a/src/reader/wgsl/parser_impl_for_stmt_test.cc b/src/reader/wgsl/parser_impl_for_stmt_test.cc
index e82521d..5b72897 100644
--- a/src/reader/wgsl/parser_impl_for_stmt_test.cc
+++ b/src/reader/wgsl/parser_impl_for_stmt_test.cc
@@ -250,7 +250,7 @@
// Test a for loop with an invalid continuing statement.
TEST_F(ForStmtErrorTest, InvalidContinuingAsFuncCall) {
std::string for_str = "for (;; a(,) ) { }";
- std::string error_str = "1:11: unable to parse argument expression";
+ std::string error_str = "1:11: expected ')' for function call";
TestForWithError(for_str, error_str);
}
diff --git a/src/reader/wgsl/parser_impl_function_header_test.cc b/src/reader/wgsl/parser_impl_function_header_test.cc
index fba796e..f509264 100644
--- a/src/reader/wgsl/parser_impl_function_header_test.cc
+++ b/src/reader/wgsl/parser_impl_function_header_test.cc
@@ -33,6 +33,18 @@
EXPECT_TRUE(f->return_type->Is<sem::Void>());
}
+TEST_F(ParserImplTest, FunctionHeader_TrailingComma) {
+ auto p = parser("fn main(a :i32,)");
+ auto f = p->function_header();
+ EXPECT_TRUE(f.matched);
+ EXPECT_FALSE(f.errored);
+
+ EXPECT_EQ(f->name, "main");
+ ASSERT_EQ(f->params.size(), 1u);
+ EXPECT_EQ(f->params[0]->symbol(), p->builder().Symbols().Get("a"));
+ EXPECT_TRUE(f->return_type->Is<sem::Void>());
+}
+
TEST_F(ParserImplTest, FunctionHeader_DecoratedReturnType) {
auto p = parser("fn main() -> [[location(1)]] f32");
auto f = p->function_header();
@@ -77,12 +89,12 @@
}
TEST_F(ParserImplTest, FunctionHeader_InvalidParamList) {
- auto p = parser("fn main(a :i32,) -> i32");
+ auto p = parser("fn main(a :i32, ,) -> i32");
auto f = p->function_header();
EXPECT_FALSE(f.matched);
EXPECT_TRUE(f.errored);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:16: expected identifier for parameter");
+ EXPECT_EQ(p->error(), "1:17: expected ')' for function declaration");
}
TEST_F(ParserImplTest, FunctionHeader_MissingParenRight) {
diff --git a/src/reader/wgsl/parser_impl_param_list_test.cc b/src/reader/wgsl/parser_impl_param_list_test.cc
index 54e30bd..60c97d2 100644
--- a/src/reader/wgsl/parser_impl_param_list_test.cc
+++ b/src/reader/wgsl/parser_impl_param_list_test.cc
@@ -87,12 +87,12 @@
EXPECT_EQ(e.value.size(), 0u);
}
-TEST_F(ParserImplTest, ParamList_HangingComma) {
+TEST_F(ParserImplTest, ParamList_TrailingComma) {
auto p = parser("a : i32,");
auto e = p->expect_param_list();
- ASSERT_TRUE(p->has_error());
- ASSERT_TRUE(e.errored);
- EXPECT_EQ(p->error(), "1:9: expected identifier for parameter");
+ ASSERT_FALSE(p->has_error());
+ ASSERT_FALSE(e.errored);
+ EXPECT_EQ(e.value.size(), 1u);
}
TEST_F(ParserImplTest, ParamList_Decorations) {
diff --git a/src/reader/wgsl/parser_impl_primary_expression_test.cc b/src/reader/wgsl/parser_impl_primary_expression_test.cc
index 884f1c4..dac0e80 100644
--- a/src/reader/wgsl/parser_impl_primary_expression_test.cc
+++ b/src/reader/wgsl/parser_impl_primary_expression_test.cc
@@ -121,7 +121,7 @@
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
ASSERT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:5: unable to parse argument expression");
+ EXPECT_EQ(p->error(), "1:5: expected ')' for type constructor");
}
TEST_F(ParserImplTest, PrimaryExpression_TypeDecl_StructConstructor_Empty) {
diff --git a/src/reader/wgsl/parser_impl_singular_expression_test.cc b/src/reader/wgsl/parser_impl_singular_expression_test.cc
index 2d40f24..d74d1bf 100644
--- a/src/reader/wgsl/parser_impl_singular_expression_test.cc
+++ b/src/reader/wgsl/parser_impl_singular_expression_test.cc
@@ -128,6 +128,18 @@
EXPECT_TRUE(c->params()[2]->Is<ast::BinaryExpression>());
}
+TEST_F(ParserImplTest, SingularExpression_Call_TrailingComma) {
+ auto p = parser("a(b, )");
+ auto e = p->singular_expression();
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+ ASSERT_NE(e.value, nullptr);
+
+ ASSERT_TRUE(e->Is<ast::CallExpression>());
+ auto* c = e->As<ast::CallExpression>();
+ EXPECT_EQ(c->params().size(), 1u);
+}
+
TEST_F(ParserImplTest, SingularExpression_Call_InvalidArg) {
auto p = parser("a(if(a) {})");
auto e = p->singular_expression();
@@ -135,17 +147,7 @@
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:3: unable to parse argument expression");
-}
-
-TEST_F(ParserImplTest, SingularExpression_Call_HangingComma) {
- auto p = parser("a(b, )");
- auto e = p->singular_expression();
- EXPECT_FALSE(e.matched);
- EXPECT_TRUE(e.errored);
- EXPECT_EQ(e.value, nullptr);
- EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:6: unable to parse argument expression");
+ EXPECT_EQ(p->error(), "1:3: expected ')' for function call");
}
TEST_F(ParserImplTest, SingularExpression_Call_MissingRightParen) {
@@ -155,7 +157,7 @@
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:3: unable to parse argument expression");
+ EXPECT_EQ(p->error(), "1:3: expected ')' for function call");
}
TEST_F(ParserImplTest, SingularExpression_MemberAccessor) {
diff --git a/src/reader/wgsl/parser_impl_switch_body_test.cc b/src/reader/wgsl/parser_impl_switch_body_test.cc
index 39f33b4..99d3899 100644
--- a/src/reader/wgsl/parser_impl_switch_body_test.cc
+++ b/src/reader/wgsl/parser_impl_switch_body_test.cc
@@ -28,10 +28,28 @@
ASSERT_NE(e.value, nullptr);
ASSERT_TRUE(e->Is<ast::CaseStatement>());
EXPECT_FALSE(e->IsDefault());
+ auto* stmt = e->As<ast::CaseStatement>();
+ ASSERT_EQ(stmt->selectors().size(), 1u);
+ EXPECT_EQ(stmt->selectors()[0]->value_as_u32(), 1u);
ASSERT_EQ(e->body()->size(), 1u);
EXPECT_TRUE(e->body()->get(0)->Is<ast::AssignmentStatement>());
}
+TEST_F(ParserImplTest, SwitchBody_Case_TrailingComma) {
+ auto p = parser("case 1, 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_FALSE(e->IsDefault());
+ auto* stmt = e->As<ast::CaseStatement>();
+ ASSERT_EQ(stmt->selectors().size(), 2u);
+ EXPECT_EQ(stmt->selectors()[0]->value_as_u32(), 1u);
+ EXPECT_EQ(stmt->selectors()[1]->value_as_u32(), 2u);
+}
+
TEST_F(ParserImplTest, SwitchBody_Case_InvalidConstLiteral) {
auto p = parser("case a == 4: { a = 4; }");
auto e = p->switch_body();
@@ -134,17 +152,7 @@
EXPECT_TRUE(e.errored);
EXPECT_FALSE(e.matched);
EXPECT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:8: expected a comma after the previous selector");
-}
-
-TEST_F(ParserImplTest, SwitchBody_Case_MultipleSelectorsEndsWithComma) {
- auto p = parser("case 1, 2,: { }");
- auto e = p->switch_body();
- EXPECT_TRUE(p->has_error());
- EXPECT_TRUE(e.errored);
- EXPECT_FALSE(e.matched);
- EXPECT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:11: a selector is expected after the comma");
+ EXPECT_EQ(p->error(), "1:8: expected ':' for case statement");
}
TEST_F(ParserImplTest, SwitchBody_Case_MultipleSelectorsStartsWithComma) {
@@ -154,7 +162,7 @@
EXPECT_TRUE(e.errored);
EXPECT_FALSE(e.matched);
EXPECT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:6: a selector is expected before the comma");
+ EXPECT_EQ(p->error(), "1:6: unable to parse case selectors");
}
TEST_F(ParserImplTest, SwitchBody_Default) {