Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,8 @@ defm decomp_decl : CXX17Compat<"decomposition declarations are">;
defm inline_variable : CXX17Compat<"inline variables are">;

// C++20 compatibility with C++17 and earlier.
defm decomp_decl_spec : CXX20Compat<
"decomposition declaration declared "
"%plural{1:'%1'|:with '%1' specifiers}0 is">;
defm decomp_decl_spec
: CXX20Compat<"decomposition declaration declared '%0' is">;
defm constexpr_local_var_no_init : CXX20Compat<
"uninitialized variable in a constexpr %select{function|constructor}0 is">;
defm constexpr_function_try_block : CXX20Compat<
Expand Down Expand Up @@ -593,9 +592,8 @@ def warn_modifying_shadowing_decl :
// C++ decomposition declarations
def err_decomp_decl_context : Error<
"decomposition declaration not permitted in this context">;
def err_decomp_decl_spec : Error<
"decomposition declaration cannot be declared "
"%plural{1:'%1'|:with '%1' specifiers}0">;
def err_decomp_decl_spec
: Error<"decomposition declaration cannot be declared '%0'">;
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
86 changes: 36 additions & 50 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -768,58 +768,44 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D,
// C++23 [dcl.pre]/6:
// Each decl-specifier in the decl-specifier-seq shall be static,
// thread_local, auto (9.2.9.6 [dcl.spec.auto]), or a cv-qualifier.
// C++23 [dcl.pre]/7:
// Each decl-specifier in the decl-specifier-seq shall be constexpr,
// constinit, static, thread_local, auto, or a cv-qualifier
auto &DS = D.getDeclSpec();
{
// Note: While constrained-auto needs to be checked, we do so separately so
// we can emit a better diagnostic.
SmallVector<StringRef, 8> BadSpecifiers;
SmallVector<SourceLocation, 8> BadSpecifierLocs;
SmallVector<StringRef, 8> CPlusPlus20Specifiers;
SmallVector<SourceLocation, 8> CPlusPlus20SpecifierLocs;
if (auto SCS = DS.getStorageClassSpec()) {
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()) {
CPlusPlus20Specifiers.push_back(DeclSpec::getSpecifierName(TSCS));
CPlusPlus20SpecifierLocs.push_back(DS.getThreadStorageClassSpecLoc());
}
if (DS.hasConstexprSpecifier()) {
BadSpecifiers.push_back(
DeclSpec::getSpecifierName(DS.getConstexprSpecifier()));
BadSpecifierLocs.push_back(DS.getConstexprSpecLoc());
}
if (DS.isInlineSpecified()) {
BadSpecifiers.push_back("inline");
BadSpecifierLocs.push_back(DS.getInlineSpecLoc());
}

if (!BadSpecifiers.empty()) {
auto &&Err = Diag(BadSpecifierLocs.front(), diag::err_decomp_decl_spec);
Err << (int)BadSpecifiers.size()
<< llvm::join(BadSpecifiers.begin(), BadSpecifiers.end(), " ");
// Don't add FixItHints to remove the specifiers; we do still respect
// them when building the underlying variable.
for (auto Loc : BadSpecifierLocs)
Err << SourceRange(Loc, Loc);
} else if (!CPlusPlus20Specifiers.empty()) {
auto &&Warn = DiagCompat(CPlusPlus20SpecifierLocs.front(),
diag_compat::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)
return nullptr;
auto DiagBadSpecifier = [&](StringRef Name, SourceLocation Loc) {
Diag(Loc, diag::err_decomp_decl_spec) << Name;
};

auto DiagCpp20Specifier = [&](StringRef Name, SourceLocation Loc) {
DiagCompat(Loc, diag_compat::decomp_decl_spec) << Name;
};

if (auto SCS = DS.getStorageClassSpec()) {
if (SCS == DeclSpec::SCS_static)
DiagCpp20Specifier(DeclSpec::getSpecifierName(SCS),
DS.getStorageClassSpecLoc());
else
DiagBadSpecifier(DeclSpec::getSpecifierName(SCS),
DS.getStorageClassSpecLoc());
}
if (auto TSCS = DS.getThreadStorageClassSpec())
DiagCpp20Specifier(DeclSpec::getSpecifierName(TSCS),
DS.getThreadStorageClassSpecLoc());

if (DS.isInlineSpecified())
DiagBadSpecifier("inline", DS.getInlineSpecLoc());

if (ConstexprSpecKind ConstexprSpec = DS.getConstexprSpecifier();
ConstexprSpec != ConstexprSpecKind::Unspecified) {
if (ConstexprSpec == ConstexprSpecKind::Consteval ||
!getLangOpts().CPlusPlus26)
DiagBadSpecifier(DeclSpec::getSpecifierName(ConstexprSpec),
DS.getConstexprSpecLoc());
}

// We can't recover from it being declared as a typedef.
if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef)
return nullptr;

// C++2a [dcl.struct.bind]p1:
// A cv that includes volatile is deprecated
Expand Down
14 changes: 11 additions & 3 deletions clang/test/Parser/cxx1z-decomposition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,19 @@ namespace BadSpecifiers {
friend auto &[g] = n; // expected-error {{'auto' not allowed}} expected-error {{friends can only be classes or functions}}
};
typedef auto &[h] = n; // expected-error {{cannot be declared 'typedef'}}
constexpr auto &[i] = n; // expected-error {{cannot be declared 'constexpr'}}
constexpr auto &[i] = n; // pre2c-error {{cannot be declared 'constexpr'}}
}

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

static thread_local auto &[j2] = n;
// cxx17-warning@-1 {{declared 'static' is a C++20 extension}}\
// cxx17-warning@-1 {{declared 'thread_local' is a C++20 extension}}


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

Expand Down
6 changes: 4 additions & 2 deletions clang/test/SemaCXX/cxx17-compat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,11 @@ static auto [cx, cy, cz] = C();
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++20 extension}}
// expected-warning@-2 {{decomposition declaration declared 'static' is a C++20 extension}}
// expected-warning@-3 {{decomposition declaration declared 'thread_local' is a C++20 extension}}
#else
// expected-warning@-4 {{decomposition declaration declared with 'static thread_local' specifiers is incompatible with C++ standards before C++20}}
// expected-warning@-5 {{decomposition declaration declared 'static' is incompatible with C++ standards before C++20}}
// expected-warning@-6 {{decomposition declaration declared 'thread_local' is incompatible with C++ standards before C++20}}
#endif
}

Expand Down
4 changes: 2 additions & 2 deletions clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ void decompose_array() {
auto [x, ...rest, y] = arr;

// cxx26-warning@+4 {{structured binding packs are incompatible with C++ standards before C++2c}}
// cxx23-warning@+3 {{structured binding packs are a C++2c extension}}
// nontemplate-error@+2 {{decomposition declaration cannot be declared 'constexpr'}}
// cxx23-error@+3 {{decomposition declaration cannot be declared 'constexpr'}}
// cxx23-warning@+2 {{structured binding packs are a C++2c extension}}
// nontemplate-error@+1 {{pack declaration outside of template}}
constexpr auto [x_c, ...rest_c, y_c] = arr;
}
76 changes: 76 additions & 0 deletions clang/test/SemaCXX/cxx2c-decomposition.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// RUN: %clang_cc1 -std=c++2c %s -triple x86_64-unknown-linux-gnu -verify=expected
// RUN: %clang_cc1 -std=c++2c %s -triple x86_64-unknown-linux-gnu -verify=expected -fexperimental-new-constant-interpreter

namespace std {
using size_t = decltype(sizeof(0));
template<typename> struct tuple_size;
template<size_t, typename> struct tuple_element;
}

struct Y { int n = 0; };
struct X { X(); X(Y); X(const X&); ~X(); int k = 42;}; // #X-decl
struct Z { constexpr Z(): i (43){}; int i;}; // #Z-decl
struct Z2 { constexpr Z2(): i (0){}; int i; ~Z2();}; // #Z2-decl

struct Bit { constexpr Bit(): i(1), j(1){}; int i: 2; int j:2;};

struct A { int a : 13; bool b; };

struct B {};
template<> struct std::tuple_size<B> { enum { value = 2 }; };
template<> struct std::tuple_size<const B> { enum { value = 2 }; };
template<> struct std::tuple_element<0, const B> { using type = Y; };
template<> struct std::tuple_element<1, const B> { using type = const int&; };
template<int N>
constexpr auto get(B) {
if constexpr (N == 0)
return Y();
else
return 0.0;
}


constexpr auto [t1] = Y {42};
static_assert(t1 == 42);

constexpr int i[] = {1, 2};
constexpr auto [t2, t3] = i;
static_assert(t2 == 1);
static_assert(t3 == 2);

constexpr auto [t4] = X();
// expected-error@-1 {{constexpr variable cannot have non-literal type 'const X'}} \
// expected-note@#X-decl {{'X' is not literal because it is not an aggregate and has no constexpr constructors other than copy or move constructors}}

constexpr auto [t5] = Z();
static_assert(t5 == 43);

constexpr auto [t6] = Z2();
//expected-error@-1 {{constexpr variable cannot have non-literal type 'const Z2'}}
// expected-note@#Z2-decl {{'Z2' is not literal because its destructor is not constexpr}}

constexpr auto [t7, t8] = Bit();
static_assert(t7 == 1);
static_assert(t8 == 1);

void test_tpl(auto) {
constexpr auto [...p] = Bit();
static_assert(((p == 1) && ...));
}

void test() {
test_tpl(0);
}

// FIXME : support tuple
constexpr auto [a, b] = B{};
static_assert(a.n == 0);
// expected-error@-1 {{static assertion expression is not an integral constant expression}} \
// expected-note@-1 {{read of temporary is not allowed in a constant expression outside the expression that created the temporary}}\
// expected-note@-2 {{temporary created here}}

constinit auto [init1] = Y {42};
constinit auto [init2] = X {}; // expected-error {{variable does not have a constant initializer}} \
// expected-note {{required by 'constinit' specifier here}} \
// expected-note {{non-constexpr constructor 'X' cannot be used in a constant expression}} \
// expected-note@#X-decl {{declared here}}