From: Zhihao Yuan Date: Thu, 7 Dec 2017 07:03:15 +0000 (+0000) Subject: Allow conditions to be decomposed with structured bindings X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=879a8f700a461afdfe57e2589997410543403337;p=clang Allow conditions to be decomposed with structured bindings Summary: This feature was discussed but not yet proposed. It allows a structured binding to appear as a //condition// if (auto [ok, val] = f(...)) So the user can save an extra //condition// if the statement can test the value to-be-decomposed instead. Formally, it makes the value of the underlying object of the structured binding declaration also the value of a //condition// that is an initialized declaration. Considering its logicality which is entirely evident from its trivial implementation, I think it might be acceptable to land it as an extension for now before I write the paper. Reviewers: rsmith, faisalv, aaron.ballman Reviewed By: rsmith Subscribers: aaron.ballman, cfe-commits Differential Revision: https://reviews.llvm.org/D39284 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@320011 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 8f6a80f79e..b5e1b09666 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -414,6 +414,9 @@ def warn_cxx14_compat_decomp_decl : Warning< "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">; diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index dd06582f75..760a04d3c8 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -1995,9 +1995,9 @@ public: case BlockContext: case ForContext: case InitStmtContext: + case ConditionContext: return true; - case ConditionContext: case MemberContext: case PrototypeContext: case TemplateParamContext: diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index ade065572d..959cb7a61d 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -1715,6 +1715,8 @@ Parser::ParseCXXTypeConstructExpression(const DeclSpec &DS) { /// 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 /// diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 00d2319e5c..5a5874ee7a 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -692,8 +692,9 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D, 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 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D, 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. diff --git a/test/Parser/cxx1z-decomposition.cpp b/test/Parser/cxx1z-decomposition.cpp index 9cbe70e3c6..cf4ba77723 100644 --- a/test/Parser/cxx1z-decomposition.cpp +++ b/test/Parser/cxx1z-decomposition.cpp @@ -32,13 +32,14 @@ namespace OtherDecl { 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 {} diff --git a/test/Parser/decomposed-condition.cpp b/test/Parser/decomposed-condition.cpp new file mode 100644 index 0000000000..c41c82292e --- /dev/null +++ b/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 diff --git a/test/SemaCXX/decomposed-condition.cpp b/test/SemaCXX/decomposed-condition.cpp new file mode 100644 index 0000000000..ab011f6ae4 --- /dev/null +++ b/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