Skip to content

release/20.x: [modules] Handle friend function that was a definition but became only a declaration during AST deserialization (#132214) #134232

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,7 @@ Bug Fixes to C++ Support
- Fixed an incorrect pointer access when checking access-control on concepts. (#GH131530)
- Fixed various alias CTAD bugs involving variadic template arguments. (#GH123591), (#GH127539), (#GH129077),
(#GH129620), and (#GH129998).
- Fixed the false compilation error "redefinition of default argument" for friend functions with default parameters. (#GH130917)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/ExternalASTSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ class ExternalASTSource : public RefCountedBase<ExternalASTSource> {

virtual ExtKind hasExternalDefinitions(const Decl *D);

/// True if this function declaration was a definition before in its own
/// module.
virtual bool wasThisDeclarationADefinition(const FunctionDecl *FD);

/// Finds all declarations lexically contained within the given
/// DeclContext, after applying an optional filter predicate.
///
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Sema/MultiplexExternalSemaSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ class MultiplexExternalSemaSource : public ExternalSemaSource {

ExtKind hasExternalDefinitions(const Decl *D) override;

bool wasThisDeclarationADefinition(const FunctionDecl *FD) override;

/// Find all declarations with the given name in the
/// given context.
bool FindExternalVisibleDeclsByName(const DeclContext *DC,
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Serialization/ASTReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -1392,6 +1392,10 @@ class ASTReader

llvm::DenseMap<const Decl *, bool> DefinitionSource;

/// Friend functions that were defined but might have had their bodies
/// removed.
llvm::DenseSet<const FunctionDecl *> ThisDeclarationWasADefinitionSet;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an ABI break.


bool shouldDisableValidationForFile(const serialization::ModuleFile &M) const;

/// Reads a statement from the specified cursor.
Expand Down Expand Up @@ -2375,6 +2379,8 @@ class ASTReader

ExtKind hasExternalDefinitions(const Decl *D) override;

bool wasThisDeclarationADefinition(const FunctionDecl *FD) override;

/// Retrieve a selector from the given module with its local ID
/// number.
Selector getLocalSelector(ModuleFile &M, unsigned LocalID);
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/ExternalASTSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ ExternalASTSource::hasExternalDefinitions(const Decl *D) {
return EK_ReplyHazy;
}

bool ExternalASTSource::wasThisDeclarationADefinition(const FunctionDecl *FD) {
return false;
}

void ExternalASTSource::FindFileRegionDecls(FileID File, unsigned Offset,
unsigned Length,
SmallVectorImpl<Decl *> &Decls) {}
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Sema/MultiplexExternalSemaSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ MultiplexExternalSemaSource::hasExternalDefinitions(const Decl *D) {
return EK_ReplyHazy;
}

bool MultiplexExternalSemaSource::wasThisDeclarationADefinition(
const FunctionDecl *FD) {
for (const auto &S : Sources)
if (S->wasThisDeclarationADefinition(FD))
return true;
return false;
}

bool MultiplexExternalSemaSource::FindExternalVisibleDeclsByName(
const DeclContext *DC, DeclarationName Name,
const DeclContext *OriginalDC) {
Expand Down
12 changes: 7 additions & 5 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2175,11 +2175,13 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
// Friend function defined withing class template may stop being function
// definition during AST merges from different modules, in this case decl
// with function body should be used for instantiation.
if (isFriend) {
const FunctionDecl *Defn = nullptr;
if (D->hasBody(Defn)) {
D = const_cast<FunctionDecl *>(Defn);
FunctionTemplate = Defn->getDescribedFunctionTemplate();
if (ExternalASTSource *Source = SemaRef.Context.getExternalSource()) {
if (isFriend && Source->wasThisDeclarationADefinition(D)) {
const FunctionDecl *Defn = nullptr;
if (D->hasBody(Defn)) {
D = const_cast<FunctionDecl *>(Defn);
FunctionTemplate = Defn->getDescribedFunctionTemplate();
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9664,6 +9664,10 @@ ExternalASTSource::ExtKind ASTReader::hasExternalDefinitions(const Decl *FD) {
return I->second ? EK_Never : EK_Always;
}

bool ASTReader::wasThisDeclarationADefinition(const FunctionDecl *FD) {
return ThisDeclarationWasADefinitionSet.contains(FD);
}

Selector ASTReader::getLocalSelector(ModuleFile &M, unsigned LocalID) {
return DecodeSelector(getGlobalSelectorID(M, LocalID));
}
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,9 @@ void ASTDeclReader::ReadFunctionDefinition(FunctionDecl *FD) {
}
// Store the offset of the body so we can lazily load it later.
Reader.PendingBodies[FD] = GetCurrentCursorOffset();
// For now remember ThisDeclarationWasADefinition only for friend functions.
if (FD->getFriendObjectKind())
Reader.ThisDeclarationWasADefinitionSet.insert(FD);
}

void ASTDeclReader::Visit(Decl *D) {
Expand Down
39 changes: 39 additions & 0 deletions clang/test/SemaCXX/friend-default-parameters-modules.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// RUN: rm -fR %t
// RUN: split-file %s %t
// RUN: cd %t
// RUN: %clang_cc1 -std=c++20 -fmodule-map-file=modules.map -xc++ -emit-module -fmodule-name=foo modules.map -o foo.pcm
// RUN: %clang_cc1 -std=c++20 -fmodule-map-file=modules.map -O1 -emit-obj main.cc -verify -fmodule-file=foo.pcm

//--- modules.map
module "foo" {
export *
module "foo.h" {
export *
header "foo.h"
}
}

//--- foo.h
#pragma once

template <int>
void Create(const void* = nullptr);

template <int>
struct ObjImpl {
template <int>
friend void ::Create(const void*);
};

template <int I>
void Create(const void*) {
(void) ObjImpl<I>{};
}

//--- main.cc
// expected-no-diagnostics
#include "foo.h"

int main() {
Create<42>();
}
21 changes: 21 additions & 0 deletions clang/test/SemaCXX/friend-default-parameters.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// RUN: %clang_cc1 -std=c++20 -verify -emit-llvm-only %s

template <int>
void Create(const void* = nullptr);

template <int>
struct ObjImpl {
template <int>
friend void ::Create(const void*);
};

template <int I>
void Create(const void*) {
(void) ObjImpl<I>{};
}

int main() {
Create<42>();
}

// expected-no-diagnostics
Loading