Skip to content

Commit 6dd9061

Browse files
authored
[Clang] Implement C++26 Attributes for Structured Bindings (P0609R3) (#89906)
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p0609r3.pdf We support this feature in all language mode. maybe_unused applied to a binding makes the whole declaration unused.
1 parent 216787c commit 6dd9061

File tree

16 files changed

+127
-32
lines changed

16 files changed

+127
-32
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,6 +1493,7 @@ Conditional ``explicit`` __cpp_conditional_explicit C+
14931493
``if consteval`` __cpp_if_consteval C++23 C++20
14941494
``static operator()`` __cpp_static_call_operator C++23 C++03
14951495
Attributes on Lambda-Expressions C++23 C++11
1496+
Attributes on Structured Bindings __cpp_structured_bindings C++26 C++03
14961497
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
14971498
-------------------------------------------- -------------------------------- ------------- -------------
14981499
Designated initializers (N494) C99 C89

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ C++2c Feature Support
143143

144144
- Implemented `P2573R2: = delete("should have a reason"); <https://wg21.link/P2573R2>`_
145145

146+
- Implemented `P0609R3: Attributes for Structured Bindings <https://wg21.link/P0609R3>`_
147+
146148

147149
Resolutions to C++ Defect Reports
148150
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Basic/Attr.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3211,7 +3211,7 @@ def ObjCRequiresPropertyDefs : InheritableAttr {
32113211
def Unused : InheritableAttr {
32123212
let Spellings = [CXX11<"", "maybe_unused", 201603>, GCC<"unused">,
32133213
C23<"", "maybe_unused", 202106>];
3214-
let Subjects = SubjectList<[Var, ObjCIvar, Type, Enum, EnumConstant, Label,
3214+
let Subjects = SubjectList<[Var, Binding, ObjCIvar, Type, Enum, EnumConstant, Label,
32153215
Field, ObjCMethod, FunctionLike]>;
32163216
let Documentation = [WarnMaybeUnusedDocs];
32173217
}

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,15 @@ def ext_decomp_decl_empty : ExtWarn<
478478
"ISO C++17 does not allow a decomposition group to be empty">,
479479
InGroup<DiagGroup<"empty-decomposition">>;
480480

481+
// C++26 structured bindings
482+
def ext_decl_attrs_on_binding : ExtWarn<
483+
"an attribute specifier sequence attached to a structured binding declaration "
484+
"is a C++2c extension">, InGroup<CXX26>;
485+
def warn_cxx23_compat_decl_attrs_on_binding : Warning<
486+
"an attribute specifier sequence attached to a structured binding declaration "
487+
"is incompatible with C++ standards before C++2c">,
488+
InGroup<CXXPre26Compat>, DefaultIgnore;
489+
481490
/// Objective-C parser diagnostics
482491
def err_expected_minus_or_plus : Error<
483492
"method type specifier must start with '-' or '+'">;

clang/include/clang/Sema/DeclSpec.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "llvm/ADT/SmallVector.h"
3737
#include "llvm/Support/Compiler.h"
3838
#include "llvm/Support/ErrorHandling.h"
39+
#include <optional>
3940

4041
namespace clang {
4142
class ASTContext;
@@ -1790,6 +1791,7 @@ class DecompositionDeclarator {
17901791
struct Binding {
17911792
IdentifierInfo *Name;
17921793
SourceLocation NameLoc;
1794+
std::optional<ParsedAttributes> Attrs;
17931795
};
17941796

17951797
private:
@@ -2339,10 +2341,10 @@ class Declarator {
23392341
}
23402342

23412343
/// Set the decomposition bindings for this declarator.
2342-
void
2343-
setDecompositionBindings(SourceLocation LSquareLoc,
2344-
ArrayRef<DecompositionDeclarator::Binding> Bindings,
2345-
SourceLocation RSquareLoc);
2344+
void setDecompositionBindings(
2345+
SourceLocation LSquareLoc,
2346+
MutableArrayRef<DecompositionDeclarator::Binding> Bindings,
2347+
SourceLocation RSquareLoc);
23462348

23472349
/// AddTypeInfo - Add a chunk to this declarator. Also extend the range to
23482350
/// EndLoc, which should be the last token of the chunk.

clang/include/clang/Sema/ParsedAttr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,7 @@ class ParsedAttributes : public ParsedAttributesView {
948948
ParsedAttributes(AttributeFactory &factory) : pool(factory) {}
949949
ParsedAttributes(const ParsedAttributes &) = delete;
950950
ParsedAttributes &operator=(const ParsedAttributes &) = delete;
951+
ParsedAttributes(ParsedAttributes &&G) = default;
951952

952953
AttributePool &getPool() const { return pool; }
953954

clang/lib/AST/DeclBase.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1115,7 +1115,9 @@ int64_t Decl::getID() const {
11151115

11161116
const FunctionType *Decl::getFunctionType(bool BlocksToo) const {
11171117
QualType Ty;
1118-
if (const auto *D = dyn_cast<ValueDecl>(this))
1118+
if (const auto *D = dyn_cast<BindingDecl>(this))
1119+
return nullptr;
1120+
else if (const auto *D = dyn_cast<ValueDecl>(this))
11191121
Ty = D->getType();
11201122
else if (const auto *D = dyn_cast<TypedefNameDecl>(this))
11211123
Ty = D->getUnderlyingType();

clang/lib/Frontend/InitPreprocessor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
703703
Builder.defineMacro("__cpp_nested_namespace_definitions", "201411L");
704704
Builder.defineMacro("__cpp_variadic_using", "201611L");
705705
Builder.defineMacro("__cpp_aggregate_bases", "201603L");
706-
Builder.defineMacro("__cpp_structured_bindings", "201606L");
706+
Builder.defineMacro("__cpp_structured_bindings", "202403L");
707707
Builder.defineMacro("__cpp_nontype_template_args",
708708
"201411L"); // (not latest)
709709
Builder.defineMacro("__cpp_fold_expressions", "201603L");

clang/lib/Parse/ParseDecl.cpp

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7038,18 +7038,23 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
70387038
void Parser::ParseDecompositionDeclarator(Declarator &D) {
70397039
assert(Tok.is(tok::l_square));
70407040

7041+
TentativeParsingAction PA(*this);
7042+
BalancedDelimiterTracker T(*this, tok::l_square);
7043+
T.consumeOpen();
7044+
7045+
if (isCXX11AttributeSpecifier())
7046+
DiagnoseAndSkipCXX11Attributes();
7047+
70417048
// If this doesn't look like a structured binding, maybe it's a misplaced
70427049
// array declarator.
7043-
// FIXME: Consume the l_square first so we don't need extra lookahead for
7044-
// this.
7045-
if (!(NextToken().is(tok::identifier) &&
7046-
GetLookAheadToken(2).isOneOf(tok::comma, tok::r_square)) &&
7047-
!(NextToken().is(tok::r_square) &&
7048-
GetLookAheadToken(2).isOneOf(tok::equal, tok::l_brace)))
7050+
if (!(Tok.is(tok::identifier) &&
7051+
NextToken().isOneOf(tok::comma, tok::r_square, tok::kw_alignas,
7052+
tok::l_square)) &&
7053+
!(Tok.is(tok::r_square) &&
7054+
NextToken().isOneOf(tok::equal, tok::l_brace))) {
7055+
PA.Revert();
70497056
return ParseMisplacedBracketDeclarator(D);
7050-
7051-
BalancedDelimiterTracker T(*this, tok::l_square);
7052-
T.consumeOpen();
7057+
}
70537058

70547059
SmallVector<DecompositionDeclarator::Binding, 32> Bindings;
70557060
while (Tok.isNot(tok::r_square)) {
@@ -7074,13 +7079,27 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) {
70747079
}
70757080
}
70767081

7082+
if (isCXX11AttributeSpecifier())
7083+
DiagnoseAndSkipCXX11Attributes();
7084+
70777085
if (Tok.isNot(tok::identifier)) {
70787086
Diag(Tok, diag::err_expected) << tok::identifier;
70797087
break;
70807088
}
70817089

7082-
Bindings.push_back({Tok.getIdentifierInfo(), Tok.getLocation()});
7090+
IdentifierInfo *II = Tok.getIdentifierInfo();
7091+
SourceLocation Loc = Tok.getLocation();
70837092
ConsumeToken();
7093+
7094+
ParsedAttributes Attrs(AttrFactory);
7095+
if (isCXX11AttributeSpecifier()) {
7096+
Diag(Tok, getLangOpts().CPlusPlus26
7097+
? diag::warn_cxx23_compat_decl_attrs_on_binding
7098+
: diag::ext_decl_attrs_on_binding);
7099+
MaybeParseCXX11Attributes(Attrs);
7100+
}
7101+
7102+
Bindings.push_back({II, Loc, std::move(Attrs)});
70847103
}
70857104

70867105
if (Tok.isNot(tok::r_square))
@@ -7095,6 +7114,8 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) {
70957114
T.consumeClose();
70967115
}
70977116

7117+
PA.Commit();
7118+
70987119
return D.setDecompositionBindings(T.getOpenLocation(), Bindings,
70997120
T.getCloseLocation());
71007121
}

clang/lib/Sema/DeclSpec.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto,
293293

294294
void Declarator::setDecompositionBindings(
295295
SourceLocation LSquareLoc,
296-
ArrayRef<DecompositionDeclarator::Binding> Bindings,
296+
MutableArrayRef<DecompositionDeclarator::Binding> Bindings,
297297
SourceLocation RSquareLoc) {
298298
assert(!hasName() && "declarator given multiple names!");
299299

@@ -317,7 +317,7 @@ void Declarator::setDecompositionBindings(
317317
new DecompositionDeclarator::Binding[Bindings.size()];
318318
BindingGroup.DeleteBindings = true;
319319
}
320-
std::uninitialized_copy(Bindings.begin(), Bindings.end(),
320+
std::uninitialized_move(Bindings.begin(), Bindings.end(),
321321
BindingGroup.Bindings);
322322
}
323323
}

0 commit comments

Comments
 (0)