Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -410,6 +410,8 @@ "C++ standards before C++17">, DefaultIgnore, InGroup; def ext_decomp_decl : ExtWarn< "decomposition declarations are a C++17 extension">, InGroup; +def ext_decomp_decl_cond : ExtWarn< + "decomposed condition is a Clang extension">; def err_decomp_decl_spec : Error< "decomposition declaration cannot be declared " "%plural{1:'%1'|:with '%1' specifiers}0">; Index: include/clang/Sema/DeclSpec.h =================================================================== --- include/clang/Sema/DeclSpec.h +++ include/clang/Sema/DeclSpec.h @@ -2009,9 +2009,9 @@ case BlockContext: case ForContext: case InitStmtContext: + case ConditionContext: return true; - case ConditionContext: case MemberContext: case PrototypeContext: case TemplateParamContext: Index: lib/Parse/ParseExprCXX.cpp =================================================================== --- lib/Parse/ParseExprCXX.cpp +++ lib/Parse/ParseExprCXX.cpp @@ -1708,6 +1708,8 @@ /// type-specifier-seq declarator '=' assignment-expression /// [C++11] type-specifier-seq declarator '=' initializer-clause /// [C++11] type-specifier-seq declarator braced-init-list +/// [Clang] type-specifier-seq ref-qualifier[opt] '[' identifier-list ']' +/// brace-or-equal-initializer /// [GNU] type-specifier-seq declarator simple-asm-expr[opt] attributes[opt] /// '=' assignment-expression /// Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -692,8 +692,9 @@ assert(D.isDecompositionDeclarator()); const DecompositionDeclarator &Decomp = D.getDecompositionDeclarator(); - // The syntax only allows a decomposition declarator as a simple-declaration - // or a for-range-declaration, but we parse it in more cases than that. + // The syntax only allows a decomposition declarator as a simple-declaration, + // a for-range-declaration, or a condition in Clang, but we parse it in more + // cases than that. if (!D.mayHaveDecompositionDeclarator()) { Diag(Decomp.getLSquareLoc(), diag::err_decomp_decl_context) << Decomp.getSourceRange(); @@ -708,10 +709,15 @@ return nullptr; } - Diag(Decomp.getLSquareLoc(), getLangOpts().CPlusPlus1z - ? diag::warn_cxx14_compat_decomp_decl - : diag::ext_decomp_decl) - << Decomp.getSourceRange(); + Diag(Decomp.getLSquareLoc(), [&] { + if (getLangOpts().CPlusPlus1z) { + if (D.getContext() == Declarator::ConditionContext) + return diag::ext_decomp_decl_cond; + else + return diag::warn_cxx14_compat_decomp_decl; + } else + return diag::ext_decomp_decl; + }()) << Decomp.getSourceRange(); // The semantic context is always just the current context. DeclContext *const DC = CurContext; Index: test/Misc/warning-flags.c =================================================================== --- test/Misc/warning-flags.c +++ test/Misc/warning-flags.c @@ -18,7 +18,8 @@ The list of warnings below should NEVER grow. It should gradually shrink to 0. -CHECK: Warnings without flags (77): +CHECK: Warnings without flags (78): +CHECK-NEXT: ext_decomp_decl_cond CHECK-NEXT: ext_excess_initializers CHECK-NEXT: ext_excess_initializers_in_char_array_initializer CHECK-NEXT: ext_expected_semi_decl_list Index: test/Parser/cxx1z-decomposition.cpp =================================================================== --- test/Parser/cxx1z-decomposition.cpp +++ test/Parser/cxx1z-decomposition.cpp @@ -32,13 +32,8 @@ void f(auto [a, b, c]); // expected-error {{'auto' not allowed in function prototype}} expected-error {{'a'}} void g() { - // A condition is not a simple-declaration. - for (; auto [a, b, c] = S(); ) {} // expected-error {{not permitted in this context}} - if (auto [a, b, c] = S()) {} // expected-error {{not permitted in this context}} - if (int n; auto [a, b, c] = S()) {} // expected-error {{not permitted in this context}} - switch (auto [a, b, c] = S()) {} // expected-error {{not permitted in this context}} - switch (int n; auto [a, b, c] = S()) {} // expected-error {{not permitted in this context}} - while (auto [a, b, c] = S()) {} // expected-error {{not permitted in this context}} + // A condition is allowed as a Clang extension. + // See commentary in test/Parser/decomposed-condition.cpp // An exception-declaration is not a simple-declaration. try {} Index: test/Parser/decomposed-condition.cpp =================================================================== --- /dev/null +++ test/Parser/decomposed-condition.cpp @@ -0,0 +1,61 @@ +// RUN: %clang_cc1 -std=c++1z %s -verify + +struct Na { + bool flag; + float data; +}; + +struct Rst { + bool flag; + float data; + explicit operator bool() const { + return flag; + } +}; + +Rst f(); +Na g(); + +namespace CondInIf { +void h() { + if (auto [ok, d] = f()) // expected-warning {{decomposed condition is a Clang extension}} + ; + if (auto [ok, d] = g()) // expected-warning {{decomposed condition is a Clang extension}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}} + ; +} +} // namespace CondInIf + +namespace CondInWhile { +void h() { + while (auto [ok, d] = f()) // expected-warning {{decomposed condition is a Clang extension}} + ; + while (auto [ok, d] = g()) // expected-warning {{decomposed condition is a Clang extension}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}} + ; +} +} // namespace CondInWhile + +namespace CondInFor { +void h() { + for (; auto [ok, d] = f();) // expected-warning {{decomposed condition is a Clang extension}} + ; + for (; auto [ok, d] = g();) // expected-warning {{decomposed condition is a Clang extension}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}} + ; +} +} // namespace CondInFor + +struct IntegerLike { + bool flag; + float data; + operator int() const { + return int(data); + } +}; + +namespace CondInSwitch { +void h(IntegerLike x) { + switch (auto [ok, d] = x) // expected-warning {{decomposed condition is a Clang extension}} + ; + switch (auto [ok, d] = g()) // expected-warning {{decomposed condition is a Clang extension}} expected-error {{statement requires expression of integer type ('Na' invalid)}} + ; +} +} // namespace CondInSwitch Index: test/SemaCXX/decomposed-condition.cpp =================================================================== --- /dev/null +++ test/SemaCXX/decomposed-condition.cpp @@ -0,0 +1,99 @@ +// RUN: %clang_cc1 -std=c++1z -w -verify %s + +struct X { + bool flag; + int data; + constexpr explicit operator bool() const { + return flag; + } + constexpr operator int() const { + return data; + } +}; + +namespace CondInIf { +constexpr int f(X x) { + if (auto [ok, d] = x) + return d + int(ok); + else + return d * int(ok); + ok = {}; // expected-error {{use of undeclared identifier 'ok'}} + d = {}; // expected-error {{use of undeclared identifier 'd'}} +} + +static_assert(f({true, 2}) == 3); +static_assert(f({false, 2}) == 0); + +constexpr char g(char const (&x)[2]) { + if (auto &[a, b] = x) + return a; + else + return b; + + if (auto [a, b] = x) // expected-error {{an array type is not allowed here}} + ; +} + +static_assert(g("x") == 'x'); +} // namespace CondInIf + +namespace CondInSwitch { +constexpr int f(int n) { + switch (X s = {true, n}; auto [ok, d] = s) { + s = {}; + case 0: + return int(ok); + case 1: + return d * 10; + case 2: + return d * 40; + default: + return 0; + } + ok = {}; // expected-error {{use of undeclared identifier 'ok'}} + d = {}; // expected-error {{use of undeclared identifier 'd'}} + s = {}; // expected-error {{use of undeclared identifier 's'}} +} + +static_assert(f(0) == 1); +static_assert(f(1) == 10); +static_assert(f(2) == 80); +} // namespace CondInSwitch + +namespace CondInWhile { +constexpr int f(int n) { + int m = 1; + while (auto [ok, d] = X{n > 1, n}) { + m *= d; + --n; + } + return m; + return ok; // expected-error {{use of undeclared identifier 'ok'}} +} + +static_assert(f(0) == 1); +static_assert(f(1) == 1); +static_assert(f(4) == 24); +} // namespace CondInWhile + +namespace CondInFor { +constexpr int f(int n) { + int a = 1, b = 1; + for (X x = {true, n}; auto &[ok, d] = x; --d) { + if (d < 2) + ok = false; + else { + int x = b; + b += a; + a = x; + } + } + return b; + return d; // expected-error {{use of undeclared identifier 'd'}} +} + +static_assert(f(0) == 1); +static_assert(f(1) == 1); +static_assert(f(2) == 2); +static_assert(f(5) == 8); +} // namespace CondInFor