forked from OSchip/llvm-project
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 llvm-svn: 320011
This commit is contained in:
parent
b5deac444d
commit
c81f4538ec
|
@ -414,6 +414,9 @@ def warn_cxx14_compat_decomp_decl : Warning<
|
|||
"C++ standards before C++17">, DefaultIgnore, InGroup<CXXPre17Compat>;
|
||||
def ext_decomp_decl : ExtWarn<
|
||||
"decomposition declarations are a C++17 extension">, InGroup<CXX17>;
|
||||
def ext_decomp_decl_cond : ExtWarn<
|
||||
"ISO C++17 does not permit structured binding declaration in a condition">,
|
||||
InGroup<DiagGroup<"binding-in-condition">>;
|
||||
def err_decomp_decl_spec : Error<
|
||||
"decomposition declaration cannot be declared "
|
||||
"%plural{1:'%1'|:with '%1' specifiers}0">;
|
||||
|
|
|
@ -1995,9 +1995,9 @@ public:
|
|||
case BlockContext:
|
||||
case ForContext:
|
||||
case InitStmtContext:
|
||||
case ConditionContext:
|
||||
return true;
|
||||
|
||||
case ConditionContext:
|
||||
case MemberContext:
|
||||
case PrototypeContext:
|
||||
case TemplateParamContext:
|
||||
|
|
|
@ -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
|
||||
///
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue