Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -414,6 +414,9 @@ "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< + "ISO C++17 does not permit structured binding declaration in a condition">, + InGroup>; 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 @@ -1995,9 +1995,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 @@ -1715,6 +1715,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,9 +709,12 @@ return nullptr; } - Diag(Decomp.getLSquareLoc(), getLangOpts().CPlusPlus17 - ? diag::warn_cxx14_compat_decomp_decl - : diag::ext_decomp_decl) + Diag(Decomp.getLSquareLoc(), + !getLangOpts().CPlusPlus17 + ? diag::ext_decomp_decl + : D.getContext() == Declarator::ConditionContext + ? diag::ext_decomp_decl_cond + : diag::warn_cxx14_compat_decomp_decl) << Decomp.getSourceRange(); // The semantic context is always just the current context. Index: test/Parser/cxx1z-decomposition.cpp =================================================================== --- test/Parser/cxx1z-decomposition.cpp +++ test/Parser/cxx1z-decomposition.cpp @@ -32,13 +32,14 @@ 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 + 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'}} + 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'}} + 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'}} + 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)}} + 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)}} + 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'}} // An exception-declaration is not a simple-declaration. try {} Index: test/Parser/decomposed-condition.cpp =================================================================== --- test/Parser/decomposed-condition.cpp +++ 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 {{ISO C++17 does not permit structured binding declaration in a condition}} + ; + if (auto [ok, d] = g()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} 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 {{ISO C++17 does not permit structured binding declaration in a condition}} + ; + while (auto [ok, d] = g()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} 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 {{ISO C++17 does not permit structured binding declaration in a condition}} + ; + for (; auto [ok, d] = g();) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} 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 {{ISO C++17 does not permit structured binding declaration in a condition}} + ; + switch (auto [ok, d] = g()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{statement requires expression of integer type ('Na' invalid)}} + ; +} +} // namespace CondInSwitch Index: test/SemaCXX/decomposed-condition.cpp =================================================================== --- test/SemaCXX/decomposed-condition.cpp +++ test/SemaCXX/decomposed-condition.cpp @@ -0,0 +1,99 @@ +// RUN: %clang_cc1 -std=c++1z -Wno-binding-in-condition -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