Skip to content
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
16 changes: 15 additions & 1 deletion clang/include/clang/Serialization/ASTReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -1390,7 +1390,19 @@ class ASTReader
/// predefines buffer may contain additional definitions.
std::string SuggestedPredefines;

llvm::DenseMap<const Decl *, bool> DefinitionSource;
struct DefinitionSourceFlags {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
struct DefinitionSourceFlags {
struct DefinitionSourceBits {

nit: Personal taste

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

ExtKind HasExternalDefinitions : 2;

/// Indicates if given function declaration was a definition but its body
/// was removed due to declaration merging.
bool ThisDeclarationWasADefinition : 1;

DefinitionSourceFlags()
: HasExternalDefinitions(EK_ReplyHazy),
ThisDeclarationWasADefinition(false) {}
};

llvm::DenseMap<const Decl *, DefinitionSourceFlags> DefinitionSource;
Copy link
Member

Choose a reason for hiding this comment

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

It might be better to add another new map for it. e.g., Decl is not a function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think memory overhead for another map will be higher. The value_type was bool but in reality at least 4-8 bytes were allocated for it so converting it to struct with 3 bits only shouldn't take extra space.

Copy link
Member

Choose a reason for hiding this comment

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

But it may introduce unnecessary insertions. I still feel it is better to add another map. And this practice seems to be more scalarble to me, e.g, if we want to add other similar information, it may not be good to add all the information to the struct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

IMHO it will only increase memory consumption but I don't have strong opinion here so moved to a new map. PTAL


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

Expand Down Expand Up @@ -2374,6 +2386,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
12 changes: 7 additions & 5 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2572,11 +2572,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
9 changes: 8 additions & 1 deletion clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9657,7 +9657,14 @@ ExternalASTSource::ExtKind ASTReader::hasExternalDefinitions(const Decl *FD) {
auto I = DefinitionSource.find(FD);
if (I == DefinitionSource.end())
return EK_ReplyHazy;
return I->second ? EK_Never : EK_Always;
return I->second.HasExternalDefinitions;
}

bool ASTReader::wasThisDeclarationADefinition(const FunctionDecl *FD) {
auto I = DefinitionSource.find(FD);
if (I == DefinitionSource.end())
return false;
return I->second.ThisDeclarationWasADefinition;
}

Selector ASTReader::getLocalSelector(ModuleFile &M, unsigned LocalID) {
Expand Down
25 changes: 16 additions & 9 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -512,9 +512,11 @@ uint64_t ASTDeclReader::GetCurrentCursorOffset() {

void ASTDeclReader::ReadFunctionDefinition(FunctionDecl *FD) {
if (Record.readInt()) {
Reader.DefinitionSource[FD] =
Loc.F->Kind == ModuleKind::MK_MainFile ||
Reader.getContext().getLangOpts().BuildingPCHWithObjectFile;
Reader.DefinitionSource[FD].HasExternalDefinitions =
(Loc.F->Kind == ModuleKind::MK_MainFile ||
Reader.getContext().getLangOpts().BuildingPCHWithObjectFile)
? ExternalASTSource::EK_Never
: ExternalASTSource::EK_Always;
}
if (auto *CD = dyn_cast<CXXConstructorDecl>(FD)) {
CD->setNumCtorInitializers(Record.readInt());
Expand All @@ -523,6 +525,7 @@ void ASTDeclReader::ReadFunctionDefinition(FunctionDecl *FD) {
}
// Store the offset of the body so we can lazily load it later.
Reader.PendingBodies[FD] = GetCurrentCursorOffset();
Reader.DefinitionSource[FD].ThisDeclarationWasADefinition = true;
}

void ASTDeclReader::Visit(Decl *D) {
Expand Down Expand Up @@ -1652,9 +1655,11 @@ RedeclarableResult ASTDeclReader::VisitVarDeclImpl(VarDecl *VD) {
VD->setLocalExternDecl();

if (DefGeneratedInModule) {
Reader.DefinitionSource[VD] =
Loc.F->Kind == ModuleKind::MK_MainFile ||
Reader.getContext().getLangOpts().BuildingPCHWithObjectFile;
Reader.DefinitionSource[VD].HasExternalDefinitions =
(Loc.F->Kind == ModuleKind::MK_MainFile ||
Reader.getContext().getLangOpts().BuildingPCHWithObjectFile)
? ExternalASTSource::EK_Never
: ExternalASTSource::EK_Always;
}

if (VD->hasAttr<BlocksAttr>()) {
Expand Down Expand Up @@ -1996,9 +2001,11 @@ void ASTDeclReader::ReadCXXDefinitionData(
Data.HasODRHash = true;

if (Record.readInt()) {
Reader.DefinitionSource[D] =
Loc.F->Kind == ModuleKind::MK_MainFile ||
Reader.getContext().getLangOpts().BuildingPCHWithObjectFile;
Reader.DefinitionSource[D].HasExternalDefinitions =
(Loc.F->Kind == ModuleKind::MK_MainFile ||
Reader.getContext().getLangOpts().BuildingPCHWithObjectFile)
? ExternalASTSource::EK_Never
: ExternalASTSource::EK_Always;
}

Record.readUnresolvedSet(Data.Conversions);
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