Skip to content

Commit

Permalink
Part of P1091R3: permit structured bindings to be declared 'static' and
Browse files Browse the repository at this point in the history
'thread_local' in C++20.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@361424 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
zygoloid committed May 22, 2019
1 parent 7566666 commit afcbd61
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 11 deletions.
9 changes: 9 additions & 0 deletions include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,15 @@ def ext_decomp_decl_cond : ExtWarn<
def err_decomp_decl_spec : Error<
"decomposition declaration cannot be declared "
"%plural{1:'%1'|:with '%1' specifiers}0">;
def ext_decomp_decl_spec : ExtWarn<
"decomposition declaration declared "
"%plural{1:'%1'|:with '%1' specifiers}0 is a C++2a extension">,
InGroup<CXX2a>;
def warn_cxx17_compat_decomp_decl_spec : Warning<
"decomposition declaration declared "
"%plural{1:'%1'|:with '%1' specifiers}0 "
"is incompatible with C++ standards before C++2a">,
InGroup<CXXPre2aCompat>, DefaultIgnore;
def err_decomp_decl_type : Error<
"decomposition declaration cannot be declared with type %0; "
"declared type must be 'auto' or reference to 'auto'">;
Expand Down
30 changes: 25 additions & 5 deletions lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -715,20 +715,30 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D,
// The semantic context is always just the current context.
DeclContext *const DC = CurContext;

// C++1z [dcl.dcl]/8:
// C++17 [dcl.dcl]/8:
// The decl-specifier-seq shall contain only the type-specifier auto
// and cv-qualifiers.
// C++2a [dcl.dcl]/8:
// If decl-specifier-seq contains any decl-specifier other than static,
// thread_local, auto, or cv-qualifiers, the program is ill-formed.
auto &DS = D.getDeclSpec();
{
SmallVector<StringRef, 8> BadSpecifiers;
SmallVector<SourceLocation, 8> BadSpecifierLocs;
SmallVector<StringRef, 8> CPlusPlus20Specifiers;
SmallVector<SourceLocation, 8> CPlusPlus20SpecifierLocs;
if (auto SCS = DS.getStorageClassSpec()) {
BadSpecifiers.push_back(DeclSpec::getSpecifierName(SCS));
BadSpecifierLocs.push_back(DS.getStorageClassSpecLoc());
if (SCS == DeclSpec::SCS_static) {
CPlusPlus20Specifiers.push_back(DeclSpec::getSpecifierName(SCS));
CPlusPlus20SpecifierLocs.push_back(DS.getStorageClassSpecLoc());
} else {
BadSpecifiers.push_back(DeclSpec::getSpecifierName(SCS));
BadSpecifierLocs.push_back(DS.getStorageClassSpecLoc());
}
}
if (auto TSCS = DS.getThreadStorageClassSpec()) {
BadSpecifiers.push_back(DeclSpec::getSpecifierName(TSCS));
BadSpecifierLocs.push_back(DS.getThreadStorageClassSpecLoc());
CPlusPlus20Specifiers.push_back(DeclSpec::getSpecifierName(TSCS));
CPlusPlus20SpecifierLocs.push_back(DS.getThreadStorageClassSpecLoc());
}
if (DS.isConstexprSpecified()) {
BadSpecifiers.push_back("constexpr");
Expand All @@ -746,6 +756,16 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D,
// them when building the underlying variable.
for (auto Loc : BadSpecifierLocs)
Err << SourceRange(Loc, Loc);
} else if (!CPlusPlus20Specifiers.empty()) {
auto &&Warn = Diag(CPlusPlus20SpecifierLocs.front(),
getLangOpts().CPlusPlus2a
? diag::warn_cxx17_compat_decomp_decl_spec
: diag::ext_decomp_decl_spec);
Warn << (int)CPlusPlus20Specifiers.size()
<< llvm::join(CPlusPlus20Specifiers.begin(),
CPlusPlus20Specifiers.end(), " ");
for (auto Loc : CPlusPlus20SpecifierLocs)
Warn << SourceRange(Loc, Loc);
}
// We can't recover from it being declared as a typedef.
if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef)
Expand Down
47 changes: 47 additions & 0 deletions test/CodeGenCXX/cxx1z-decomposition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,50 @@ void test_bitfield(A &a) {
// CHECK: or i16 %{{.*}}, 5
// CHECK: store i16 %{{.*}}, i16* %[[BITFIELD]],
}

// CHECK-LABEL: define {{.*}}@_Z18test_static_simple
void test_static_simple() {
static auto [x1, x2] = make<A>();
// CHECK: load atomic {{.*}}i64* @_ZGVZ18test_static_simplevEDC2x12x2E
// CHECK: br i1
// CHECK: @__cxa_guard_acquire(
// CHECK: call {{.*}} @_Z4makeI1AERT_v(
// CHECK: memcpy{{.*}} @_ZZ18test_static_simplevEDC2x12x2E
// CHECK: @__cxa_guard_release(
}

// CHECK-LABEL: define {{.*}}@_Z17test_static_tuple
void test_static_tuple() {
// Note that the desugaring specified for this construct requires three
// separate guarded initializations. It is possible for an exception to be
// thrown after the first initialization and before the second, and if that
// happens, we are not permitted to rerun the first initialization, so we
// can't combine these into a single guarded initialization in general.
static auto [x1, x2] = make<B>();

// Initialization of the implied variable.
// CHECK: load atomic {{.*}} @_ZGVZ17test_static_tuplevEDC2x12x2E
// CHECK: br i1
// CHECK: @__cxa_guard_acquire({{.*}} @_ZGVZ17test_static_tuplevEDC2x12x2E)
// CHECK: call {{.*}} @_Z4makeI1BERT_v(
// CHECK: @__cxa_guard_release({{.*}} @_ZGVZ17test_static_tuplevEDC2x12x2E)

// Initialization of the secret 'x1' variable.
// CHECK: load atomic {{.*}} @_ZGVZ17test_static_tuplevE2x1
// CHECK: br i1
// CHECK: @__cxa_guard_acquire({{.*}} @_ZGVZ17test_static_tuplevE2x1)
// CHECK: call {{.*}} @_Z3getILi0EEDa1B(
// CHECK: call {{.*}} @_ZN1XC1E1Y({{.*}} @_ZGRZ17test_static_tuplevE2x1_,
// CHECK: call {{.*}} @__cxa_atexit({{.*}} @_ZN1XD1Ev {{.*}} @_ZGRZ17test_static_tuplevE2x1_
// CHECK: store {{.*}} @_ZGRZ17test_static_tuplevE2x1_, {{.*}} @_ZZ17test_static_tuplevE2x1
// CHECK: call void @__cxa_guard_release({{.*}} @_ZGVZ17test_static_tuplevE2x1)

// Initialization of the secret 'x2' variable.
// CHECK: load atomic {{.*}} @_ZGVZ17test_static_tuplevE2x2
// CHECK: br i1
// CHECK: @__cxa_guard_acquire({{.*}} @_ZGVZ17test_static_tuplevE2x2)
// CHECK: call {{.*}} @_Z3getILi1EEDa1B(
// CHECK: store {{.*}}, {{.*}} @_ZGRZ17test_static_tuplevE2x2_
// CHECK: store {{.*}} @_ZGRZ17test_static_tuplevE2x2_, {{.*}} @_ZZ17test_static_tuplevE2x2
// CHECK: call void @__cxa_guard_release({{.*}} @_ZGVZ17test_static_tuplevE2x2)
}
10 changes: 6 additions & 4 deletions test/Parser/cxx1z-decomposition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ namespace BadSpecifiers {
struct S { int n; } s;
void f() {
// storage-class-specifiers
static auto &[a] = n; // expected-error {{cannot be declared 'static'}}
thread_local auto &[b] = n; // expected-error {{cannot be declared 'thread_local'}}
static auto &[a] = n; // expected-warning {{declared 'static' is a C++2a extension}}
thread_local auto &[b] = n; // expected-warning {{declared 'thread_local' is a C++2a extension}}
extern auto &[c] = n; // expected-error {{cannot be declared 'extern'}} expected-error {{cannot have an initializer}}
struct S {
mutable auto &[d] = n; // expected-error {{not permitted in this context}}
Expand All @@ -82,9 +82,11 @@ namespace BadSpecifiers {
};
typedef auto &[h] = n; // expected-error {{cannot be declared 'typedef'}}
constexpr auto &[i] = n; // expected-error {{cannot be declared 'constexpr'}}

static constexpr thread_local auto &[j] = n; // expected-error {{cannot be declared with 'static thread_local constexpr' specifiers}}
}

static constexpr inline thread_local auto &[j1] = n; // expected-error {{cannot be declared with 'constexpr inline' specifiers}}
static thread_local auto &[j2] = n; // expected-warning {{declared with 'static thread_local' specifiers is a C++2a extension}}

inline auto &[k] = n; // expected-error {{cannot be declared 'inline'}}

const int K = 5;
Expand Down
16 changes: 16 additions & 0 deletions test/SemaCXX/cxx17-compat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,19 @@ struct ConstexprVirtual {
// expected-warning@-4 {{virtual constexpr functions are incompatible with C++ standards before C++2a}}
#endif
};

struct C { int x, y, z; };
static auto [cx, cy, cz] = C();
#if __cplusplus <= 201703L
// expected-warning@-2 {{decomposition declaration declared 'static' is a C++2a extension}}
#else
// expected-warning@-4 {{decomposition declaration declared 'static' is incompatible with C++ standards before C++2a}}
#endif
void f() {
static thread_local auto [cx, cy, cz] = C();
#if __cplusplus <= 201703L
// expected-warning@-2 {{decomposition declaration declared with 'static thread_local' specifiers is a C++2a extension}}
#else
// expected-warning@-4 {{decomposition declaration declared with 'static thread_local' specifiers is incompatible with C++ standards before C++2a}}
#endif
}
2 changes: 1 addition & 1 deletion test/SemaCXX/cxx1z-decomposition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ template <class T> void dependent_foreach(T t) {

struct PR37352 {
int n;
void f() { static auto [a] = *this; } // expected-error {{cannot be declared 'static'}}
void f() { static auto [a] = *this; } // expected-warning {{C++2a extension}}
};

namespace instantiate_template {
Expand Down
2 changes: 1 addition & 1 deletion www/cxx_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -1034,7 +1034,7 @@ <h2 id="cxx20">C++2a implementation status</h2>
<tr>
<td rowspan="2">Structured binding extensions</td>
<td><a href="http://wg21.link/p1091r3">P1091R3</a></td>
<td rowspan="2" class="none" align="center">No</td>
<td rowspan="2" class="partial" align="center">Partial</td>
</tr>
<tr>
<td><a href="http://wg21.link/p1381r1">P1381R1</a></td>
Expand Down

0 comments on commit afcbd61

Please sign in to comment.