Skip to content

[Modules] "Cannot befriend target of using declaration" #61125

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
davidstone opened this issue Mar 2, 2023 · 9 comments
Closed

[Modules] "Cannot befriend target of using declaration" #61125

davidstone opened this issue Mar 2, 2023 · 9 comments
Labels
c++ clang:modules C++20 modules and Clang Header Modules duplicate Resolved as duplicate

Comments

@davidstone
Copy link
Contributor

Given the following two translation units

module;

namespace n
{

template<typename>
struct a {
	template<typename T>
	friend void aa(a<T>);
};

template<typename T>
inline void aa(a<T>) {
}


} //namespace n

export module a;

namespace n {

export using n::a;
export using n::aa;

}
import a;

void b() {
	aa(n::a<int>());
}

And compiling with

clang++ -std=c++20 -x c++-module --precompile -c a.cpp -o a.pcm
clang++ -w -std=c++20 -fmodule-file=a=a.pcm -c b.cpp -o /dev/null

Causes clang to error with

In file included from b.cpp:1:
a.cpp:9:14: error: cannot befriend target of using declaration
        friend void aa(a<T>);
                    ^
b.cpp:4:5: note: in instantiation of template class 'n::a<int>' requested here
        aa(n::a<int>());
           ^
a.cpp:13:13: note: target of using declaration
inline void aa(a<T>) {
            ^
a.cpp:24:17: note: using declaration
export using n::aa;
                ^
1 error generated.

I believe this code should be accepted, but I'm not 100% sure.

@EugeneZelenko EugeneZelenko added clang:modules C++20 modules and Clang Header Modules and removed new issue labels Mar 2, 2023
@llvmbot
Copy link
Member

llvmbot commented Mar 2, 2023

@llvm/issue-subscribers-clang-modules

@llvmbot
Copy link
Member

llvmbot commented May 6, 2025

@llvm/issue-subscribers-c-1

Author: David Stone (davidstone)

Given the following two translation units
module;

namespace n
{

template&lt;typename&gt;
struct a {
	template&lt;typename T&gt;
	friend void aa(a&lt;T&gt;);
};

template&lt;typename T&gt;
inline void aa(a&lt;T&gt;) {
}


} //namespace n

export module a;

namespace n {

export using n::a;
export using n::aa;

}
import a;

void b() {
	aa(n::a&lt;int&gt;());
}

And compiling with

clang++ -std=c++20 -x c++-module --precompile -c a.cpp -o a.pcm
clang++ -w -std=c++20 -fmodule-file=a=a.pcm -c b.cpp -o /dev/null

Causes clang to error with

In file included from b.cpp:1:
a.cpp:9:14: error: cannot befriend target of using declaration
        friend void aa(a&lt;T&gt;);
                    ^
b.cpp:4:5: note: in instantiation of template class 'n::a&lt;int&gt;' requested here
        aa(n::a&lt;int&gt;());
           ^
a.cpp:13:13: note: target of using declaration
inline void aa(a&lt;T&gt;) {
            ^
a.cpp:24:17: note: using declaration
export using n::aa;
                ^
1 error generated.

I believe this code should be accepted, but I'm not 100% sure.

@ChuanqiXu9
Copy link
Member

@davidstone @cor3ntin @AaronBallman @shafik I am not sure about the root reason for the error. (which seems not related to modules) So I tried to CC other guys.

What's the purpose of cannot befriend target of using declaration? I can't find it in https://eel.is/c++draft/namespace.udecl#14 and https://eel.is/c++draft/class.friend ? Is this an oversight?

@AaronBallman
Copy link
Collaborator

Those changes came in from c70fca60dab47 back in 2013. The explanation in https://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20130325/076977.html is that [dcl.meaning]p1 is the reason:
C++11 [dcl.meaning]p1:

... When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers (or, in the case of a namespace, of an element of the inline namespace set of that namespace (7.3.1)) or to a specialization thereof; the member shall not merely have been introduced by a using-declaration in the scope of the class or namespace nominated by the nested-name-specifier of the declarator-id. ...

Those words are no longer present, I think we hit this case instead: https://eel.is/c++draft/dcl.meaning#general-2.3. I didn't track down what changed the wording, but that wording is present in C++20, but not C++23. If I had to hazard a guess, I would guess the words changed thanks to "declarations and where to find them" (https://wg21.link/P1787) which @Endilll is quite familiar with.

@ChuanqiXu9
Copy link
Member

Thanks!

I took another look and now I feel this is more or less a mismatch.

The code is:

if (!Old) {
if (UsingShadowDecl *Shadow = dyn_cast<UsingShadowDecl>(OldD)) {
if (New->getFriendObjectKind()) {
Diag(New->getLocation(), diag::err_using_decl_friend);
Diag(Shadow->getTargetDecl()->getLocation(),
diag::note_using_decl_target);
Diag(Shadow->getIntroducer()->getLocation(), diag::note_using_decl)
<< 0;
return true;
}

Here the story is, during the compilation of b.cpp, n::aa is looked up, but the templated function decl n::aa is not exported so we can't find it. We find the using decl n::aa. So we find the using decl n::aa before the actual function decl n::aa. Then we saw the friend decl, and we matched the above diagnostics.

This is the reason why it won't happen in normal codes, since in normal codes, in the reproducer, we will always see the actual function decl first and later the using decl.

Let me see how can we fix this.

@ChuanqiXu9
Copy link
Member

Oh, when I try to work on this, I surprisingly find the reproducer is accepted now... I commit it directly to avoid regression at 2d81994

Feel free to ask for reopening this if I observe anything wrong.

@bangerth
Copy link

bangerth commented May 7, 2025

@ChuanqiXu9 Does whatever change cause this issue to be fixed also fix #138558?

@ChuanqiXu9
Copy link
Member

@ChuanqiXu9 Does whatever change cause this issue to be fixed also fix #138558?

I didn't search the commit that makes this issue closed. But #138558 is different than the current one. I feel from what @AaronBallman said, the friend declaration won't be looked up. Since in the current issue, there is a declaration n::aa in the namespace (out of the friend declaration), but in your example, the friend declaration is defined inside the class. This is the difference.

We can see it clearly from the non modular example: https://godbolt.org/z/45jsr61WM

So... we can't fix it since it is not an "issue"

@bangerth
Copy link

bangerth commented May 8, 2025

Let me move the discussion to #138558.

@EugeneZelenko EugeneZelenko added the duplicate Resolved as duplicate label May 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ clang:modules C++20 modules and Clang Header Modules duplicate Resolved as duplicate
Projects
None yet
Development

No branches or pull requests

6 participants