Skip to content

Commit 295b817

Browse files
committed
[Clang] Implement P0963R3 "Structured binding declaration as a condition"
1 parent 3e53aea commit 295b817

18 files changed

+136
-72
lines changed

clang/include/clang/AST/DeclCXX.h

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4224,15 +4224,18 @@ class DecompositionDecl final
42244224
: public VarDecl,
42254225
private llvm::TrailingObjects<DecompositionDecl, BindingDecl *> {
42264226
/// The number of BindingDecl*s following this object.
4227-
unsigned NumBindings;
4227+
unsigned NumBindings : 31;
4228+
4229+
LLVM_PREFERRED_TYPE(bool)
4230+
unsigned IsDecisionVariable : 1;
42284231

42294232
DecompositionDecl(ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
42304233
SourceLocation LSquareLoc, QualType T,
42314234
TypeSourceInfo *TInfo, StorageClass SC,
4232-
ArrayRef<BindingDecl *> Bindings)
4235+
ArrayRef<BindingDecl *> Bindings, bool IsDecisionVariable)
42334236
: VarDecl(Decomposition, C, DC, StartLoc, LSquareLoc, nullptr, T, TInfo,
42344237
SC),
4235-
NumBindings(Bindings.size()) {
4238+
NumBindings(Bindings.size()), IsDecisionVariable(IsDecisionVariable) {
42364239
std::uninitialized_copy(Bindings.begin(), Bindings.end(),
42374240
getTrailingObjects<BindingDecl *>());
42384241
for (auto *B : Bindings) {
@@ -4253,12 +4256,14 @@ class DecompositionDecl final
42534256

42544257
static DecompositionDecl *Create(ASTContext &C, DeclContext *DC,
42554258
SourceLocation StartLoc,
4256-
SourceLocation LSquareLoc,
4257-
QualType T, TypeSourceInfo *TInfo,
4258-
StorageClass S,
4259-
ArrayRef<BindingDecl *> Bindings);
4259+
SourceLocation LSquareLoc, QualType T,
4260+
TypeSourceInfo *TInfo, StorageClass S,
4261+
ArrayRef<BindingDecl *> Bindings,
4262+
bool IsDecisionVariable);
4263+
42604264
static DecompositionDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
4261-
unsigned NumBindings);
4265+
unsigned NumBindings,
4266+
bool IsDecisionVariable);
42624267

42634268
// Provide the range of bindings which may have a nested pack.
42644269
llvm::ArrayRef<BindingDecl *> bindings() const {
@@ -4285,6 +4290,8 @@ class DecompositionDecl final
42854290
std::move(Bindings));
42864291
}
42874292

4293+
bool isDecisionVariable() const { return IsDecisionVariable; }
4294+
42884295
void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;
42894296

42904297
static bool classof(const Decl *D) { return classofKind(D->getKind()); }

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -529,8 +529,12 @@ def warn_cxx14_compat_decomp_decl : Warning<
529529
def ext_decomp_decl : ExtWarn<
530530
"decomposition declarations are a C++17 extension">, InGroup<CXX17>;
531531
def ext_decomp_decl_cond : ExtWarn<
532-
"ISO C++17 does not permit structured binding declaration in a condition">,
533-
InGroup<DiagGroup<"binding-in-condition">>;
532+
"structured binding declaration in a condition is a C++2c extenstion">,
533+
InGroup<CXX26>;
534+
def warn_cxx26_decomp_decl_cond : Warning<
535+
"structured binding declaration in a condition is incompatible with "
536+
"C++ standards before C++2c">,
537+
InGroup<CXXPre26Compat>, DefaultIgnore;
534538
def err_decomp_decl_spec : Error<
535539
"decomposition declaration cannot be declared "
536540
"%plural{1:'%1'|:with '%1' specifiers}0">;

clang/lib/AST/ASTImporter.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4614,9 +4614,10 @@ ExpectedDecl ASTNodeImporter::VisitVarDecl(VarDecl *D) {
46144614
ImportArrayChecked(FromDecomp->bindings(), Bindings.begin()))
46154615
return std::move(Err);
46164616
DecompositionDecl *ToDecomp;
4617-
if (GetImportedOrCreateDecl(
4618-
ToDecomp, FromDecomp, Importer.getToContext(), DC, ToInnerLocStart,
4619-
Loc, ToType, ToTypeSourceInfo, D->getStorageClass(), Bindings))
4617+
if (GetImportedOrCreateDecl(ToDecomp, FromDecomp, Importer.getToContext(),
4618+
DC, ToInnerLocStart, Loc, ToType,
4619+
ToTypeSourceInfo, D->getStorageClass(),
4620+
Bindings, FromDecomp->isDecisionVariable()))
46204621
return ToDecomp;
46214622
ToVar = ToDecomp;
46224623
} else {

clang/lib/AST/DeclCXX.cpp

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3520,21 +3520,24 @@ DecompositionDecl *DecompositionDecl::Create(ASTContext &C, DeclContext *DC,
35203520
SourceLocation LSquareLoc,
35213521
QualType T, TypeSourceInfo *TInfo,
35223522
StorageClass SC,
3523-
ArrayRef<BindingDecl *> Bindings) {
3523+
ArrayRef<BindingDecl *> Bindings,
3524+
bool IsDecisionVariable) {
35243525
size_t Extra = additionalSizeToAlloc<BindingDecl *>(Bindings.size());
3525-
return new (C, DC, Extra)
3526-
DecompositionDecl(C, DC, StartLoc, LSquareLoc, T, TInfo, SC, Bindings);
3526+
return new (C, DC, Extra) DecompositionDecl(
3527+
C, DC, StartLoc, LSquareLoc, T, TInfo, SC, Bindings, IsDecisionVariable);
35273528
}
35283529

3529-
DecompositionDecl *DecompositionDecl::CreateDeserialized(ASTContext &C,
3530-
GlobalDeclID ID,
3531-
unsigned NumBindings) {
3530+
DecompositionDecl *
3531+
DecompositionDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
3532+
unsigned NumBindings,
3533+
bool IsDecisionVariable) {
35323534
size_t Extra = additionalSizeToAlloc<BindingDecl *>(NumBindings);
3533-
auto *Result = new (C, ID, Extra)
3534-
DecompositionDecl(C, nullptr, SourceLocation(), SourceLocation(),
3535-
QualType(), nullptr, StorageClass(), {});
3535+
auto *Result = new (C, ID, Extra) DecompositionDecl(
3536+
C, nullptr, SourceLocation(), SourceLocation(), QualType(), nullptr,
3537+
StorageClass(), {}, IsDecisionVariable);
35363538
// Set up and clean out the bindings array.
35373539
Result->NumBindings = NumBindings;
3540+
Result->IsDecisionVariable = IsDecisionVariable;
35383541
auto *Trail = Result->getTrailingObjects<BindingDecl *>();
35393542
for (unsigned I = 0; I != NumBindings; ++I)
35403543
new (Trail + I) BindingDecl*(nullptr);

clang/lib/AST/ExprConstant.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5218,16 +5218,28 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) {
52185218
return true;
52195219
}
52205220

5221+
static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
5222+
const DecompositionDecl *DD);
5223+
52215224
static bool EvaluateDecl(EvalInfo &Info, const Decl *D) {
52225225
bool OK = true;
52235226

52245227
if (const VarDecl *VD = dyn_cast<VarDecl>(D))
52255228
OK &= EvaluateVarDecl(Info, VD);
52265229

5227-
if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D))
5228-
for (auto *BD : DD->flat_bindings())
5229-
if (auto *VD = BD->getHoldingVar())
5230-
OK &= EvaluateDecl(Info, VD);
5230+
if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D);
5231+
DD && !DD->isDecisionVariable())
5232+
OK &= EvaluateDecompositionDeclInit(Info, DD);
5233+
5234+
return OK;
5235+
}
5236+
5237+
static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
5238+
const DecompositionDecl *DD) {
5239+
bool OK = true;
5240+
for (auto *BD : DD->flat_bindings())
5241+
if (auto *VD = BD->getHoldingVar())
5242+
OK &= EvaluateDecl(Info, VD);
52315243

52325244
return OK;
52335245
}
@@ -5251,6 +5263,10 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl,
52515263
return false;
52525264
if (!EvaluateAsBooleanCondition(Cond, Result, Info))
52535265
return false;
5266+
if (auto *DD = dyn_cast_if_present<DecompositionDecl>(CondDecl);
5267+
DD && DD->isDecisionVariable() &&
5268+
!EvaluateDecompositionDeclInit(Info, DD))
5269+
return false;
52545270
return Scope.destroy();
52555271
}
52565272

clang/lib/CodeGen/CGDecl.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,9 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
164164
assert(VD.isLocalVarDecl() &&
165165
"Should not see file-scope variables inside a function!");
166166
EmitVarDecl(VD);
167-
if (auto *DD = dyn_cast<DecompositionDecl>(&VD))
168-
for (auto *B : DD->flat_bindings())
169-
if (auto *HD = B->getHoldingVar())
170-
EmitVarDecl(*HD);
167+
if (auto *DD = dyn_cast<DecompositionDecl>(&VD);
168+
DD && !DD->isDecisionVariable())
169+
EmitDecompositionVarInit(*DD);
171170

172171
return;
173172
}
@@ -2057,6 +2056,12 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
20572056
/*IsAutoInit=*/false);
20582057
}
20592058

2059+
void CodeGenFunction::EmitDecompositionVarInit(const DecompositionDecl &DD) {
2060+
for (auto *B : DD.flat_bindings())
2061+
if (auto *HD = B->getHoldingVar())
2062+
EmitVarDecl(*HD);
2063+
}
2064+
20602065
/// Emit an expression as an initializer for an object (variable, field, etc.)
20612066
/// at the given location. The expression is not necessarily the normal
20622067
/// initializer for the object, and the address is not necessarily

clang/lib/CodeGen/CGStmt.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,11 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
913913
if (CondConstant)
914914
incrementProfileCounter(&S);
915915
if (Executed) {
916+
if (auto *DD = dyn_cast_if_present<DecompositionDecl>(
917+
S.getConditionVariable())) {
918+
assert(DD->isDecisionVariable());
919+
EmitDecompositionVarInit(*DD);
920+
}
916921
RunCleanupsScope ExecutedScope(*this);
917922
EmitStmt(Executed);
918923
}
@@ -952,10 +957,16 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
952957
// there is a 'return' within the body, but this is particularly beneficial
953958
// when one if-stmt is nested within another if-stmt so that all of the MC/DC
954959
// updates are kept linear and consistent.
955-
if (!CGM.getCodeGenOpts().MCDCCoverage)
956-
EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, ThenCount, LH);
957-
else {
960+
if (!CGM.getCodeGenOpts().MCDCCoverage) {
961+
EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, ThenCount, LH,
962+
nullptr, S.getConditionVariable());
963+
} else {
958964
llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
965+
if (auto *DD =
966+
dyn_cast_if_present<DecompositionDecl>(S.getConditionVariable())) {
967+
assert(DD->isDecisionVariable());
968+
EmitDecompositionVarInit(*DD);
969+
}
959970
Builder.CreateCondBr(BoolCondVal, ThenBlock, ElseBlock);
960971
}
961972

clang/lib/CodeGen/CodeGenFunction.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1846,7 +1846,8 @@ void CodeGenFunction::EmitBranchToCounterBlock(
18461846
/// LHS and RHS nodes.
18471847
void CodeGenFunction::EmitBranchOnBoolExpr(
18481848
const Expr *Cond, llvm::BasicBlock *TrueBlock, llvm::BasicBlock *FalseBlock,
1849-
uint64_t TrueCount, Stmt::Likelihood LH, const Expr *ConditionalOp) {
1849+
uint64_t TrueCount, Stmt::Likelihood LH, const Expr *ConditionalOp,
1850+
const VarDecl *ConditionalDecl) {
18501851
Cond = Cond->IgnoreParens();
18511852

18521853
if (const BinaryOperator *CondBOp = dyn_cast<BinaryOperator>(Cond)) {
@@ -2047,6 +2048,10 @@ void CodeGenFunction::EmitBranchOnBoolExpr(
20472048
CondV = EvaluateExprAsBool(Cond);
20482049
}
20492050

2051+
if (auto *DD = dyn_cast_if_present<DecompositionDecl>(ConditionalDecl);
2052+
DD && DD->isDecisionVariable())
2053+
EmitDecompositionVarInit(*DD);
2054+
20502055
// If not at the top of the logical operator nest, update MCDC temp with the
20512056
// boolean result of the evaluated condition.
20522057
if (!MCDCLogOpStack.empty()) {

clang/lib/CodeGen/CodeGenFunction.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3474,6 +3474,8 @@ class CodeGenFunction : public CodeGenTypeCache {
34743474
void emitAutoVarTypeCleanup(const AutoVarEmission &emission,
34753475
QualType::DestructionKind dtorKind);
34763476

3477+
void EmitDecompositionVarInit(const DecompositionDecl &var);
3478+
34773479
/// Emits the alloca and debug information for the size expressions for each
34783480
/// dimension of an array. It registers the association of its (1-dimensional)
34793481
/// QualTypes and size expression's debug node, so that CGDebugInfo can
@@ -5180,7 +5182,8 @@ class CodeGenFunction : public CodeGenTypeCache {
51805182
void EmitBranchOnBoolExpr(const Expr *Cond, llvm::BasicBlock *TrueBlock,
51815183
llvm::BasicBlock *FalseBlock, uint64_t TrueCount,
51825184
Stmt::Likelihood LH = Stmt::LH_None,
5183-
const Expr *ConditionalOp = nullptr);
5185+
const Expr *ConditionalOp = nullptr,
5186+
const VarDecl *ConditionalDecl = nullptr);
51845187

51855188
/// Given an assignment `*LHS = RHS`, emit a test that checks if \p RHS is
51865189
/// nonnull, if \p LHS is marked _Nonnull.

clang/lib/Sema/SemaDecl.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7768,9 +7768,9 @@ NamedDecl *Sema::ActOnVariableDeclarator(
77687768
NewVD = cast<VarDecl>(Res.get());
77697769
AddToScope = false;
77707770
} else if (D.isDecompositionDeclarator()) {
7771-
NewVD = DecompositionDecl::Create(Context, DC, D.getBeginLoc(),
7772-
D.getIdentifierLoc(), R, TInfo, SC,
7773-
Bindings);
7771+
NewVD = DecompositionDecl::Create(
7772+
Context, DC, D.getBeginLoc(), D.getIdentifierLoc(), R, TInfo, SC,
7773+
Bindings, D.getContext() == DeclaratorContext::Condition);
77747774
} else
77757775
NewVD = VarDecl::Create(Context, DC, D.getBeginLoc(),
77767776
D.getIdentifierLoc(), II, R, TInfo, SC);

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -740,13 +740,16 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D,
740740
return nullptr;
741741
}
742742

743-
Diag(Decomp.getLSquareLoc(),
744-
!getLangOpts().CPlusPlus17
745-
? diag::ext_decomp_decl
746-
: D.getContext() == DeclaratorContext::Condition
747-
? diag::ext_decomp_decl_cond
748-
: diag::warn_cxx14_compat_decomp_decl)
749-
<< Decomp.getSourceRange();
743+
unsigned DiagID;
744+
if (!getLangOpts().CPlusPlus17)
745+
DiagID = diag::ext_decomp_decl;
746+
else if (D.getContext() == DeclaratorContext::Condition) {
747+
DiagID = getLangOpts().CPlusPlus26 ? diag::warn_cxx26_decomp_decl_cond
748+
: diag::ext_decomp_decl_cond;
749+
} else
750+
DiagID = diag::warn_cxx14_compat_decomp_decl;
751+
752+
Diag(Decomp.getLSquareLoc(), DiagID) << Decomp.getSourceRange();
750753

751754
// The semantic context is always just the current context.
752755
DeclContext *const DC = CurContext;

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "clang/AST/ASTContext.h"
1515
#include "clang/AST/ASTLambda.h"
1616
#include "clang/AST/ASTMutationListener.h"
17+
#include "clang/AST/DeclCXX.h"
1718
#include "clang/AST/DeclTemplate.h"
1819
#include "clang/AST/DependentDiagnostic.h"
1920
#include "clang/AST/Expr.h"
@@ -1473,9 +1474,10 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D,
14731474
// Build the instantiated declaration.
14741475
VarDecl *Var;
14751476
if (Bindings)
1476-
Var = DecompositionDecl::Create(SemaRef.Context, DC, D->getInnerLocStart(),
1477-
D->getLocation(), DI->getType(), DI,
1478-
D->getStorageClass(), *Bindings);
1477+
Var = DecompositionDecl::Create(
1478+
SemaRef.Context, DC, D->getInnerLocStart(), D->getLocation(),
1479+
DI->getType(), DI, D->getStorageClass(), *Bindings,
1480+
cast<DecompositionDecl>(D)->isDecisionVariable());
14791481
else
14801482
Var = VarDecl::Create(SemaRef.Context, DC, D->getInnerLocStart(),
14811483
D->getLocation(), D->getIdentifier(), DI->getType(),

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4115,9 +4115,12 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
41154115
case DECL_PARM_VAR:
41164116
D = ParmVarDecl::CreateDeserialized(Context, ID);
41174117
break;
4118-
case DECL_DECOMPOSITION:
4119-
D = DecompositionDecl::CreateDeserialized(Context, ID, Record.readInt());
4120-
break;
4118+
case DECL_DECOMPOSITION: {
4119+
unsigned NumBindings = Record.readInt();
4120+
bool IsDecisionVariable = Record.readBool();
4121+
D = DecompositionDecl::CreateDeserialized(Context, ID, NumBindings,
4122+
IsDecisionVariable);
4123+
} break;
41214124
case DECL_BINDING:
41224125
D = BindingDecl::CreateDeserialized(Context, ID);
41234126
break;

clang/lib/Serialization/ASTWriterDecl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,6 +1327,7 @@ void ASTDeclWriter::VisitParmVarDecl(ParmVarDecl *D) {
13271327
void ASTDeclWriter::VisitDecompositionDecl(DecompositionDecl *D) {
13281328
// Record the number of bindings first to simplify deserialization.
13291329
Record.push_back(D->bindings().size());
1330+
Record.push_back(D->isDecisionVariable());
13301331

13311332
VisitVarDecl(D);
13321333
for (auto *B : D->bindings())

clang/test/AST/ByteCode/if.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ namespace InitDecl {
5454
constexpr char g(char const (&x)[2]) {
5555
return 'x';
5656
if (auto [a, b] = x) // both-error {{an array type is not allowed here}} \
57-
// both-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
57+
// both-warning {{structured binding declaration in a condition is a C++2c extenstion}}
5858
;
5959
}
6060
static_assert(g("x") == 'x');

clang/test/Parser/cxx1z-decomposition.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ namespace OtherDecl {
3737
void g() {
3838
// A condition is allowed as a Clang extension.
3939
// See commentary in test/Parser/decomposed-condition.cpp
40-
for (; auto [a, b, c] = S(); ) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
41-
if (auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
42-
if (int n; auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
43-
switch (auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{statement requires expression of integer type ('S' invalid)}}
44-
switch (int n; auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{statement requires expression of integer type ('S' invalid)}}
45-
while (auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
40+
for (; auto [a, b, c] = S(); ) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
41+
if (auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
42+
if (int n; auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
43+
switch (auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{statement requires expression of integer type ('S' invalid)}}
44+
switch (int n; auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{statement requires expression of integer type ('S' invalid)}}
45+
while (auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
4646

4747
// An exception-declaration is not a simple-declaration.
4848
try {}

0 commit comments

Comments
 (0)