Skip to content
10 changes: 10 additions & 0 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2005,6 +2005,10 @@ class FunctionDecl : public DeclaratorDecl,

unsigned ODRHash;

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

/// End part of this FunctionDecl's source range.
///
/// We could compute the full range in getSourceRange(). However, when we're
Expand Down Expand Up @@ -2190,6 +2194,12 @@ class FunctionDecl : public DeclaratorDecl,
return hasBody(Definition);
}

void setThisDeclarationWasADefinition() {
ThisDeclarationWasADefinition = true;
}

bool wasThisDeclarationADefinition() { return ThisDeclarationWasADefinition; }

/// Returns whether the function has a trivial body that does not require any
/// specific codegen.
bool hasTrivialBody() const;
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3083,6 +3083,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
static_cast<unsigned char>(DeductionCandidate::Normal);
FunctionDeclBits.HasODRHash = false;
FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate = false;
ThisDeclarationWasADefinition = false;
if (TrailingRequiresClause)
setTrailingRequiresClause(TrailingRequiresClause);
}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2572,7 +2572,7 @@ 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) {
if (isFriend && D->wasThisDeclarationADefinition()) {
const FunctionDecl *Defn = nullptr;
if (D->hasBody(Defn)) {
D = const_cast<FunctionDecl *>(Defn);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10432,6 +10432,7 @@ void ASTReader::finishPendingActions() {
if (!getContext().getLangOpts().Modules || !FD->hasBody(Defn)) {
FD->setLazyBody(PB->second);
} else {
FD->setThisDeclarationWasADefinition();
auto *NonConstDefn = const_cast<FunctionDecl*>(Defn);
mergeDefinitionVisibility(NonConstDefn, FD);

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