Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1fd8db6
[Parser] Parse variadic friends
Sirraide Jul 31, 2024
ddb83c2
[Sema] Instantiate variadic friends
Sirraide Jul 31, 2024
5f5eb7e
[ASTPrinter] Group friend decls
Sirraide Jul 31, 2024
c603a5d
[Tests] Move some tests
Sirraide Aug 1, 2024
da65e3b
Feature-test macro + release notes
Sirraide Aug 1, 2024
070e5bc
clang-format
Sirraide Aug 1, 2024
70e7621
[NFC] auto -> auto*
Sirraide Aug 5, 2024
a520f33
[Clang] Add feature test macro and compat warnings
Sirraide Aug 5, 2024
38e6f09
Merge branch 'main' into variadic-friends
Sirraide Aug 5, 2024
99b85b4
Handle more invalid cases
Sirraide Aug 5, 2024
b6547be
clang-format
Sirraide Aug 5, 2024
4214b46
clang-format, again
Sirraide Aug 5, 2024
bf437ab
Remove feature test macro etc in preparation of splitting the impleme…
Sirraide Aug 5, 2024
e175ce6
Add test for cwg2917
Sirraide Aug 5, 2024
5f9d44a
Run make-cxx-dr-status
Sirraide Aug 5, 2024
f75da38
Update JSONNodeDumper
Sirraide Aug 5, 2024
36d46ff
Add more tests
Sirraide Aug 6, 2024
b3e3c2c
So long, FriendPackDecl
Sirraide Aug 6, 2024
bce98e6
Address most of Corentin’s feedback
Sirraide Aug 13, 2024
410d32b
Handle invalid pack expansions properly
Sirraide Aug 13, 2024
fdcac04
clang-format
Sirraide Aug 13, 2024
2ffff78
Update JSON/TextNodeDumper
Sirraide Aug 13, 2024
0d2a059
Revert "Remove feature test macro etc in preparation of splitting the…
Sirraide Aug 13, 2024
d689e73
Remove duplicate feature-test macro
Sirraide Aug 13, 2024
d311ff5
This is already tested elsewhere
Sirraide Aug 13, 2024
f668edc
Merge branch 'main' into variadic-friends
Sirraide Aug 15, 2024
10629bb
Mark decl as invalid
Sirraide Aug 15, 2024
2375ff5
Remove unrelated changes from cxx_dr_status
Sirraide Aug 15, 2024
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
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ C++2c Feature Support
- Add ``__builtin_is_virtual_base_of`` intrinsic, which supports
`P2985R0 A type trait for detecting virtual base classes <https://wg21.link/p2985r0>`_

- Implemented `P2893R3 Variadic Friends <https://wg21.link/P2893>`_

Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
83 changes: 71 additions & 12 deletions clang/include/clang/AST/DeclFriend.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ class FriendDecl final
// Location of the 'friend' specifier.
SourceLocation FriendLoc;

// Location of the '...', if present.
SourceLocation EllipsisLoc;

/// True if this 'friend' declaration is unsupported. Eventually we
/// will support every possible friend declaration, but for now we
/// silently ignore some and set this flag to authorize all access.
Expand All @@ -82,10 +85,11 @@ class FriendDecl final
unsigned NumTPLists : 31;

FriendDecl(DeclContext *DC, SourceLocation L, FriendUnion Friend,
SourceLocation FriendL,
SourceLocation FriendL, SourceLocation EllipsisLoc,
ArrayRef<TemplateParameterList *> FriendTypeTPLists)
: Decl(Decl::Friend, DC, L), Friend(Friend), FriendLoc(FriendL),
UnsupportedFriend(false), NumTPLists(FriendTypeTPLists.size()) {
EllipsisLoc(EllipsisLoc), UnsupportedFriend(false),
NumTPLists(FriendTypeTPLists.size()) {
for (unsigned i = 0; i < NumTPLists; ++i)
getTrailingObjects<TemplateParameterList *>()[i] = FriendTypeTPLists[i];
}
Expand All @@ -110,7 +114,7 @@ class FriendDecl final

static FriendDecl *
Create(ASTContext &C, DeclContext *DC, SourceLocation L, FriendUnion Friend_,
SourceLocation FriendL,
SourceLocation FriendL, SourceLocation EllipsisLoc = {},
ArrayRef<TemplateParameterList *> FriendTypeTPLists = std::nullopt);
static FriendDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
unsigned FriendTypeNumTPLists);
Expand Down Expand Up @@ -143,8 +147,24 @@ class FriendDecl final
return FriendLoc;
}

/// Retrieves the location of the '...', if present.
SourceLocation getEllipsisLoc() const { return EllipsisLoc; }

/// Retrieves the source range for the friend declaration.
SourceRange getSourceRange() const override LLVM_READONLY {
if (TypeSourceInfo *TInfo = getFriendType()) {
SourceLocation StartL =
(NumTPLists == 0) ? getFriendLoc()
: getTrailingObjects<TemplateParameterList *>()[0]
->getTemplateLoc();
SourceLocation EndL =
isVariadic() ? getEllipsisLoc() : TInfo->getTypeLoc().getEndLoc();
return SourceRange(StartL, EndL);
}

if (isVariadic())
return SourceRange(getFriendLoc(), getEllipsisLoc());

if (NamedDecl *ND = getFriendDecl()) {
if (const auto *FD = dyn_cast<FunctionDecl>(ND))
return FD->getSourceRange();
Expand All @@ -158,15 +178,8 @@ class FriendDecl final
}
return SourceRange(getFriendLoc(), ND->getEndLoc());
}
else if (TypeSourceInfo *TInfo = getFriendType()) {
SourceLocation StartL =
(NumTPLists == 0) ? getFriendLoc()
: getTrailingObjects<TemplateParameterList *>()[0]
->getTemplateLoc();
return SourceRange(StartL, TInfo->getTypeLoc().getEndLoc());
}
else
return SourceRange(getFriendLoc(), getLocation());

return SourceRange(getFriendLoc(), getLocation());
}

/// Determines if this friend kind is unsupported.
Expand All @@ -177,11 +190,57 @@ class FriendDecl final
UnsupportedFriend = Unsupported;
}

bool isVariadic() const { return EllipsisLoc.isValid(); }

// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == Decl::Friend; }
};

class FriendPackDecl final
: public Decl,
private llvm::TrailingObjects<FriendPackDecl, FriendDecl *> {
FriendDecl *InstantiatedFrom;

/// The number of friend-declarations created by this pack expansion.
unsigned NumExpansions;

FriendPackDecl(DeclContext *DC, FriendDecl *InstantiatedFrom,
ArrayRef<FriendDecl *> FriendDecls)
: Decl(FriendPack, DC,
InstantiatedFrom ? InstantiatedFrom->getLocation()
: SourceLocation()),
InstantiatedFrom(InstantiatedFrom), NumExpansions(FriendDecls.size()) {
std::uninitialized_copy(FriendDecls.begin(), FriendDecls.end(),
getTrailingObjects<FriendDecl *>());
}

public:
friend class ASTDeclReader;
friend class ASTDeclWriter;
friend TrailingObjects;

FriendDecl *getInstantiatedFromFriendDecl() const { return InstantiatedFrom; }

ArrayRef<FriendDecl *> expansions() const {
return llvm::ArrayRef(getTrailingObjects<FriendDecl *>(), NumExpansions);
}

static FriendPackDecl *Create(ASTContext &C, DeclContext *DC,
FriendDecl *InstantiatedFrom,
ArrayRef<FriendDecl *> FriendDecls);

static FriendPackDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
unsigned NumExpansions);

SourceRange getSourceRange() const override LLVM_READONLY {
return InstantiatedFrom->getSourceRange();
}

static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == FriendPack; }
};

/// An iterator over the friend declarations of a class.
class CXXRecordDecl::friend_iterator {
friend class CXXRecordDecl;
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -1606,6 +1606,8 @@ DEF_TRAVERSE_DECL(FriendDecl, {
}
})

DEF_TRAVERSE_DECL(FriendPackDecl, {})

DEF_TRAVERSE_DECL(FriendTemplateDecl, {
if (D->getFriendType())
TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc()));
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DeclNodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ def FileScopeAsm : DeclNode<Decl>;
def TopLevelStmt : DeclNode<Decl>, DeclContext;
def AccessSpec : DeclNode<Decl>;
def Friend : DeclNode<Decl>;
def FriendPack : DeclNode<Decl>;
def FriendTemplate : DeclNode<Decl>;
def StaticAssert : DeclNode<Decl>;
def Block : DeclNode<Decl, "blocks">, DeclContext;
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1738,6 +1738,8 @@ def ext_friend_tag_redecl_outside_namespace : ExtWarn<
"enclosing namespace is a Microsoft extension; add a nested name specifier">,
InGroup<MicrosoftUnqualifiedFriend>;
def err_pure_friend : Error<"friend declaration cannot have a pure-specifier">;
def err_friend_template_decl_multiple_specifiers: Error<
"a friend declaration that befriends a template must contain exactly one type-specifier">;

def err_invalid_base_in_interface : Error<
"interface type cannot inherit from "
Expand Down
6 changes: 4 additions & 2 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -3800,7 +3800,8 @@ class Sema final : public SemaBase {
const ParsedAttributesView &DeclAttrs,
MultiTemplateParamsArg TemplateParams,
bool IsExplicitInstantiation,
RecordDecl *&AnonRecord);
RecordDecl *&AnonRecord,
SourceLocation FriendEllipsisLoc = {});

/// BuildAnonymousStructOrUnion - Handle the declaration of an
/// anonymous structure or union. Anonymous unions are a C++ feature
Expand Down Expand Up @@ -5538,7 +5539,8 @@ class Sema final : public SemaBase {
/// parameters present at all, require proper matching, i.e.
/// template <> template \<class T> friend class A<int>::B;
Decl *ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
MultiTemplateParamsArg TemplateParams);
MultiTemplateParamsArg TemplateParams,
SourceLocation FriendEllipsisLoc);
NamedDecl *ActOnFriendFunctionDecl(Scope *S, Declarator &D,
MultiTemplateParamsArg TemplateParams);

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1377,6 +1377,9 @@ enum DeclCode {
/// A FriendDecl record.
DECL_FRIEND,

/// A FriendPackDecl record.
DECL_FRIEND_PACK,

/// A FriendTemplateDecl record.
DECL_FRIEND_TEMPLATE,

Expand Down
33 changes: 31 additions & 2 deletions clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,7 @@ namespace clang {
ExpectedDecl VisitFieldDecl(FieldDecl *D);
ExpectedDecl VisitIndirectFieldDecl(IndirectFieldDecl *D);
ExpectedDecl VisitFriendDecl(FriendDecl *D);
ExpectedDecl VisitFriendPackDecl(FriendPackDecl *D);
ExpectedDecl VisitObjCIvarDecl(ObjCIvarDecl *D);
ExpectedDecl VisitVarDecl(VarDecl *D);
ExpectedDecl VisitImplicitParamDecl(ImplicitParamDecl *D);
Expand Down Expand Up @@ -4424,11 +4425,14 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
auto FriendLocOrErr = import(D->getFriendLoc());
if (!FriendLocOrErr)
return FriendLocOrErr.takeError();
auto EllipsisLocOrErr = import(D->getEllipsisLoc());
if (!EllipsisLocOrErr)
return EllipsisLocOrErr.takeError();

FriendDecl *FrD;
if (GetImportedOrCreateDecl(FrD, D, Importer.getToContext(), DC,
*LocationOrErr, ToFU,
*FriendLocOrErr, ToTPLists))
*LocationOrErr, ToFU, *FriendLocOrErr,
*EllipsisLocOrErr, ToTPLists))
return FrD;

FrD->setAccess(D->getAccess());
Expand All @@ -4437,6 +4441,31 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
return FrD;
}

ExpectedDecl ASTNodeImporter::VisitFriendPackDecl(FriendPackDecl *D) {
// Import the major distinguishing characteristics of a declaration.
DeclContext *DC, *LexicalDC;
if (Error Err = ImportDeclContext(D, DC, LexicalDC))
return std::move(Err);

auto ToInstantiatedFromFriendOrErr =
Importer.Import(D->getInstantiatedFromFriendDecl());
if (!ToInstantiatedFromFriendOrErr)
return ToInstantiatedFromFriendOrErr.takeError();
SmallVector<FriendDecl *, 4> Expansions(D->expansions().size());
if (Error Err = ImportArrayChecked(D->expansions(), Expansions.begin()))
return std::move(Err);

FriendPackDecl *ToFriendPack;
if (GetImportedOrCreateDecl(ToFriendPack, D, Importer.getToContext(), DC,
cast<FriendDecl>(*ToInstantiatedFromFriendOrErr),
Expansions))
return ToFriendPack;

addDeclToContexts(D, ToFriendPack);

return ToFriendPack;
}

ExpectedDecl ASTNodeImporter::VisitObjCIvarDecl(ObjCIvarDecl *D) {
// Import the major distinguishing characteristics of an ivar.
DeclContext *DC, *LexicalDC;
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/DeclBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {

// Never have names.
case Friend:
case FriendPack:
case FriendTemplate:
case AccessSpec:
case LinkageSpec:
Expand Down
34 changes: 27 additions & 7 deletions clang/lib/AST/DeclFriend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ FriendDecl *FriendDecl::getNextFriendSlowCase() {
NextFriend.get(getASTContext().getExternalSource()));
}

FriendDecl *FriendDecl::Create(ASTContext &C, DeclContext *DC,
SourceLocation L,
FriendUnion Friend,
SourceLocation FriendL,
ArrayRef<TemplateParameterList *> FriendTypeTPLists) {
FriendDecl *
FriendDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L,
FriendUnion Friend, SourceLocation FriendL,
SourceLocation EllipsisLoc,
ArrayRef<TemplateParameterList *> FriendTypeTPLists) {
#ifndef NDEBUG
if (Friend.is<NamedDecl *>()) {
const auto *D = Friend.get<NamedDecl*>();
Expand All @@ -56,8 +56,8 @@ FriendDecl *FriendDecl::Create(ASTContext &C, DeclContext *DC,
std::size_t Extra =
FriendDecl::additionalSizeToAlloc<TemplateParameterList *>(
FriendTypeTPLists.size());
auto *FD = new (C, DC, Extra) FriendDecl(DC, L, Friend, FriendL,
FriendTypeTPLists);
auto *FD = new (C, DC, Extra)
FriendDecl(DC, L, Friend, FriendL, EllipsisLoc, FriendTypeTPLists);
cast<CXXRecordDecl>(DC)->pushFriendDecl(FD);
return FD;
}
Expand All @@ -69,6 +69,26 @@ FriendDecl *FriendDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
return new (C, ID, Extra) FriendDecl(EmptyShell(), FriendTypeNumTPLists);
}

FriendPackDecl *FriendPackDecl::Create(ASTContext &C, DeclContext *DC,
FriendDecl *InstantiatedFrom,
ArrayRef<FriendDecl *> FriendDecls) {
size_t Extra = additionalSizeToAlloc<FriendDecl *>(FriendDecls.size());
return new (C, DC, Extra) FriendPackDecl(DC, InstantiatedFrom, FriendDecls);
}

FriendPackDecl *FriendPackDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID,
unsigned NumExpansions) {
size_t Extra = additionalSizeToAlloc<FriendDecl *>(NumExpansions);
auto *Result =
new (C, ID, Extra) FriendPackDecl(nullptr, nullptr, std::nullopt);
Result->NumExpansions = NumExpansions;
auto *Trail = Result->getTrailingObjects<FriendDecl *>();
for (unsigned I = 0; I != NumExpansions; ++I)
new (Trail + I) FriendDecl *(nullptr);
return Result;
}

FriendDecl *CXXRecordDecl::getFirstFriend() const {
ExternalASTSource *Source = getParentASTContext().getExternalSource();
Decl *First = data().FirstFriend.get(Source);
Expand Down
40 changes: 35 additions & 5 deletions clang/lib/AST/DeclPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ namespace {
void VisitEnumConstantDecl(EnumConstantDecl *D);
void VisitEmptyDecl(EmptyDecl *D);
void VisitFunctionDecl(FunctionDecl *D);
void VisitFriendDecl(FriendDecl *D);
void VisitFriendDecl(FriendDecl *D, bool FirstInGroup = true);
void VisitFieldDecl(FieldDecl *D);
void VisitVarDecl(VarDecl *D);
void VisitLabelDecl(LabelDecl *D);
Expand Down Expand Up @@ -487,7 +487,26 @@ void DeclPrinter::VisitDeclContext(DeclContext *DC, bool Indent) {
}

this->Indent();
Visit(*D);

// Group friend declarations if need be.
if (isa<FriendDecl>(*D)) {
auto *FD = cast<FriendDecl>(*D);
VisitFriendDecl(FD);
SourceLocation FriendLoc = FD->getFriendLoc();

// Use a separate iterator; 'D' is always one declaration 'behind' in
// this loop; the last friend printed here (or the first printed just
// now before this loop if there are no subsequent friends) will be
// skipped by the '++D' of the outer loop.
for (DeclContext::decl_iterator It; It = std::next(D), It != DEnd; ++D) {
auto NextFriend = dyn_cast<FriendDecl>(*It);
if (!NextFriend || NextFriend->getFriendLoc() != FriendLoc)
break;
VisitFriendDecl(NextFriend, false);
}
} else {
Visit(*D);
}

// FIXME: Need to be able to tell the DeclPrinter when
const char *Terminator = nullptr;
Expand Down Expand Up @@ -862,13 +881,21 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
}
}

void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
void DeclPrinter::VisitFriendDecl(FriendDecl *D, bool FirstInGroup) {
if (TypeSourceInfo *TSI = D->getFriendType()) {
unsigned NumTPLists = D->getFriendTypeNumTemplateParameterLists();
for (unsigned i = 0; i < NumTPLists; ++i)
printTemplateParameters(D->getFriendTypeTemplateParameterList(i));
Out << "friend ";
Out << " " << TSI->getType().getAsString(Policy);

// Hack to print friend declarations declared as a group, e.g.
// 'friend int, long;', instead of printing them as two separate
// FriendDecls, which they are in the AST.
if (FirstInGroup)
Out << "friend ";
else
Out << ", ";

Out << TSI->getType().getAsString(Policy);
}
else if (FunctionDecl *FD =
dyn_cast<FunctionDecl>(D->getFriendDecl())) {
Expand All @@ -885,6 +912,9 @@ void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
Out << "friend ";
VisitRedeclarableTemplateDecl(CTD);
}

if (D->isVariadic())
Out << "...";
}

void DeclPrinter::VisitFieldDecl(FieldDecl *D) {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/ODRHash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {
} else {
AddDecl(D->getFriendDecl());
}
Hash.AddBoolean(D->isVariadic());
}

void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) {
Expand Down
Loading