Skip to content

Commit ab5a5a9

Browse files
committed
[C++20] [Modules] Fix incorrect diagnostic for using befriend target
Close #138558 The compiler failed to understand the redeclaration-relationship when performing checks when MergeFunctionDecl. This seemed to be a complex circular problem (how can we know the redeclaration relationship before performing merging?). But the fix seems to be easy and safe. It is fine to only perform the check only if the using decl is a local decl.
1 parent 23b65ed commit ab5a5a9

File tree

4 files changed

+141
-1
lines changed

4 files changed

+141
-1
lines changed

clang/lib/Sema/SemaDecl.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3653,7 +3653,9 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S,
36533653
FunctionDecl *Old = OldD->getAsFunction();
36543654
if (!Old) {
36553655
if (UsingShadowDecl *Shadow = dyn_cast<UsingShadowDecl>(OldD)) {
3656-
if (New->getFriendObjectKind()) {
3656+
// We don't need to check the using friend pattern from other module unit
3657+
// since we should have diagnosed such cases in its unit already.
3658+
if (New->getFriendObjectKind() && !OldD->isInAnotherModuleUnit()) {
36573659
Diag(New->getLocation(), diag::err_using_decl_friend);
36583660
Diag(Shadow->getTargetDecl()->getLocation(),
36593661
diag::note_using_decl_target);

clang/test/Modules/befriend-2.cppm

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: split-file %s %t
4+
//
5+
// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-reduced-module-interface -o %t/test-A.pcm
6+
// RUN: %clang_cc1 -std=c++20 %t/N.cppm -emit-reduced-module-interface -o %t/test-N.pcm
7+
// RUN: %clang_cc1 -std=c++20 %t/B.cppm -verify -fsyntax-only -fprebuilt-module-path=%t
8+
9+
//--- a.h
10+
namespace N {
11+
12+
template <typename>
13+
class C {
14+
template <typename> friend void foo();
15+
};
16+
17+
template <typename> void foo() {}
18+
} // namespace N
19+
20+
//--- a.cppm
21+
// This is some unrelated file. It also #includes system headers, but
22+
// here does not even export anything.
23+
module;
24+
#include "a.h"
25+
export module test:A;
26+
export {
27+
using N::C;
28+
using N::foo;
29+
}
30+
31+
//--- std.h
32+
// Declarations typically #included from C++ header files:
33+
namespace N { // In practice, this would be namespace std
34+
inline namespace impl { // In practice, this would be namespace __1
35+
template <typename>
36+
class C {
37+
template <typename> friend void foo();
38+
};
39+
40+
template <typename> void foo() {}
41+
} // namespace impl
42+
} // namespace N
43+
44+
//--- N.cppm
45+
module;
46+
#include "std.h"
47+
export module test:N;
48+
49+
// Now wrap these names into a module and export them:
50+
export {
51+
namespace N {
52+
using N::C;
53+
using N::foo;
54+
}
55+
}
56+
57+
//--- B.cppm
58+
// expected-no-diagnostics
59+
// A file that consumes the partitions from the other two files,
60+
// including the exported N::C name.
61+
module test:B;
62+
import :N;
63+
import :A;
64+
65+
N::C<int> x;

clang/test/Modules/befriend-3.cppm

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %clang_cc1 -std=c++20 %s -fsyntax-only -verify
2+
export module m;
3+
4+
namespace test {
5+
namespace ns1 {
6+
namespace ns2 {
7+
template<class T> void f(T t); // expected-note {{target of using declaration}}
8+
}
9+
using ns2::f; // expected-note {{using declaration}}
10+
}
11+
struct A { void f(); }; // expected-note 2{{target of using declaration}}
12+
struct B : public A { using A::f; }; // expected-note {{using declaration}}
13+
template<typename T> struct C : A { using A::f; }; // expected-note {{using declaration}}
14+
struct X {
15+
template<class T> friend void ns1::f(T t); // expected-error {{cannot befriend target of using declaration}}
16+
friend void B::f(); // expected-error {{cannot befriend target of using declaration}}
17+
friend void C<int>::f(); // expected-error {{cannot befriend target of using declaration}}
18+
};
19+
}

clang/test/Modules/pr138558.cppm

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: split-file %s %t
4+
//
5+
// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-reduced-module-interface -o %t/test-A.pcm
6+
// RUN: %clang_cc1 -std=c++20 %t/N.cppm -emit-reduced-module-interface -o %t/test-N.pcm
7+
// RUN: %clang_cc1 -std=c++20 %t/B.cppm -verify -fsyntax-only -fprebuilt-module-path=%t
8+
9+
//--- a.h
10+
namespace N {
11+
inline namespace impl {
12+
template <typename>
13+
class C {
14+
template <typename> friend void foo();
15+
};
16+
17+
template <typename> void foo() {}
18+
} // namespace impl
19+
} // namespace N
20+
21+
//--- a.cppm
22+
// This is some unrelated file. It also #includes system headers, but
23+
// here does not even export anything.
24+
module;
25+
#include "a.h"
26+
export module test:A;
27+
// To make sure they won't elided.
28+
using N::C;
29+
using N::foo;
30+
31+
//--- N.cppm
32+
module;
33+
#include "a.h"
34+
export module test:N;
35+
36+
// Now wrap these names into a module and export them:
37+
export {
38+
namespace N {
39+
inline namespace impl {
40+
using N::impl::C;
41+
using N::impl::foo;
42+
}
43+
}
44+
}
45+
46+
//--- B.cppm
47+
// expected-no-diagnostics
48+
// A file that consumes the partitions from the other two files,
49+
// including the exported N::C name.
50+
module test:B;
51+
import :N;
52+
import :A;
53+
54+
N::C<int> x;

0 commit comments

Comments
 (0)