[clang] 4a505e1 - [clang] Add tests for CWG issues about friend declaration matching (#106117)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 2 07:20:48 PDT 2024
Author: Vlad Serebrennikov
Date: 2024-09-02T18:20:44+04:00
New Revision: 4a505e15e768011e47101cf9aeb0c7787fdc9acf
URL: https://github.com/llvm/llvm-project/commit/4a505e15e768011e47101cf9aeb0c7787fdc9acf
DIFF: https://github.com/llvm/llvm-project/commit/4a505e15e768011e47101cf9aeb0c7787fdc9acf.diff
LOG: [clang] Add tests for CWG issues about friend declaration matching (#106117)
This patch covers CWG issues regarding declaration matching when
`friend` declarations are involved:
[CWG138](https://cplusplus.github.io/CWG/issues/138.html),
[CWG386](https://cplusplus.github.io/CWG/issues/386.html),
[CWG1477](https://cplusplus.github.io/CWG/issues/1477.html), and
[CWG1900](https://cplusplus.github.io/CWG/issues/1900.html). Atypical
for our CWG tests, the ones in this patch are quite extensively
commented in-line, explaining the mechanics. PR description focuses on
high-level concerns and references.
[CWG138](https://cplusplus.github.io/CWG/issues/138.html) "Friend
declaration name lookup"
-----------
[P1787R6](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1787r6.html):
> [CWG138](https://cplusplus.github.io/CWG/issues/138.html) is resolved
according to [N1229](http://wg21.link/n1229), except that
using-directives that nominate nested namespaces are considered.
I find it hard to pin down the scope of this issue, so I'm relying on
three examples from the filing to define it. Because of that, it's also
hard to pinpoint exact wording changes that resolve it. Relevant
references are:
[[dcl.meaning.general]/2](http://eel.is/c++draft/dcl.meaning#general-2),
[[namespace.udecl]/10](https://eel.is/c++draft/namespace.udecl#10),
[[dcl.type.elab]/3](https://eel.is/c++draft/dcl.type.elab#3),
[[basic.lookup.elab]/1](https://eel.is/c++draft/basic.lookup.elab#1).
[CWG386](https://cplusplus.github.io/CWG/issues/386.html) "Friend
declaration of name brought in by _using-declaration_"
-----------
[P1787R6](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1787r6.html):
> [CWG386](https://cplusplus.github.io/CWG/issues/386.html),
[CWG1839](https://cplusplus.github.io/CWG/issues/1839.html),
[CWG1818](https://cplusplus.github.io/CWG/issues/1818.html),
[CWG2058](https://cplusplus.github.io/CWG/issues/2058.html),
[CWG1900](https://cplusplus.github.io/CWG/issues/1900.html), and
Richard’s observation in [“are non-type names ignored in a
class-head-name or
enum-head-name?”](http://lists.isocpp.org/core/2017/01/1604.php) are
resolved by describing the limited lookup that occurs for a
declarator-id, including the changes in Richard’s [proposed resolution
for
CWG1839](http://wiki.edg.com/pub/Wg21cologne2019/CoreWorkingGroup/cwg1839.html)
(which also resolves CWG1818 and what of CWG2058 was not resolved along
with CWG2059) and rejecting the example from
[CWG1477](https://cplusplus.github.io/CWG/issues/1477.html).
Wording
([[dcl.meaning.general]/2](http://eel.is/c++draft/dcl.meaning#general-2)):
> — If the
[id-expression](http://eel.is/c++draft/expr.prim.id.general#nt:id-expression)
E in the
[declarator-id](http://eel.is/c++draft/dcl.decl.general#nt:declarator-id)
of the
[declarator](http://eel.is/c++draft/dcl.decl.general#nt:declarator) is a
[qualified-id](http://eel.is/c++draft/expr.prim.id.qual#nt:qualified-id)
or a [template-id](http://eel.is/c++draft/temp.names#nt:template-id):
> — [...]
> — The
[declarator](http://eel.is/c++draft/dcl.decl.general#nt:declarator)
shall correspond to one or more declarations found by the lookup; they
shall all have the same target scope, and the target scope of the
[declarator](http://eel.is/c++draft/dcl.decl.general#nt:declarator) is
that
scope[.](http://eel.is/c++draft/dcl.meaning#general-2.2.2.sentence-1)
This issue focuses on interaction of `friend` declarations with
template-id and qualified-id with using-declarations. The short answer
is that terminal name in such declarations undergo lookup, and
using-declarations do what they usually do helping that lookup. Target
scope of such friend declaration is the target scope of lookup result,
so no conflicts arise with the using-declarations.
[CWG1477](https://cplusplus.github.io/CWG/issues/1477.html) "Definition
of a `friend` outside its namespace"
-----------
[P1787R6](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1787r6.html):
> [...] and rejecting the example from
[CWG1477](https://cplusplus.github.io/CWG/issues/1477.html).
Wording
([[dcl.meaning.general]/3.4](http://eel.is/c++draft/dcl.meaning#general-3.4)):
> Otherwise, the terminal name of the
[declarator-id](http://eel.is/c++draft/dcl.decl.general#nt:declarator-id)
is not looked
up[.](http://eel.is/c++draft/dcl.meaning#general-3.4.sentence-1)
If it is a qualified name, the
[declarator](http://eel.is/c++draft/dcl.decl.general#nt:declarator)
shall correspond to one or more declarations nominable in S; all the
declarations shall have the same target scope and the target scope of
the [declarator](http://eel.is/c++draft/dcl.decl.general#nt:declarator)
is that
scope[.](http://eel.is/c++draft/dcl.meaning#general-3.4.sentence-2)
This issue focuses on befriending a function in one scope, then defining
it from other scope using qualified-id. Contrary to what P1787R6 says in
prose, this example is accepted by the wording in that paper. In the
wording quote above, note the absence of a statement like "terminal name
of the declarator-id is not bound", contrary to similar statements made
before that in [dcl.meaning.general] about friend declarations and
template-ids.
There's also a note in [basic.scope.scope] that supports the rejection,
but it's considered incorrect and expected to be removed in the future.
This is tracked in https://github.com/cplusplus/draft/pull/7238.
[CWG1900](https://cplusplus.github.io/CWG/issues/1900.html) "Do `friend`
declarations count as “previous declarations”?"
------------------
[P1787R6](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1787r6.html):
> [CWG386](https://cplusplus.github.io/CWG/issues/386.html),
[CWG1839](https://cplusplus.github.io/CWG/issues/1839.html),
[CWG1818](https://cplusplus.github.io/CWG/issues/1818.html),
[CWG2058](https://cplusplus.github.io/CWG/issues/2058.html),
[CWG1900](https://cplusplus.github.io/CWG/issues/1900.html), and
Richard’s observation in [“are non-type names ignored in a
class-head-name or
enum-head-name?”](http://lists.isocpp.org/core/2017/01/1604.php) are
resolved by describing the limited lookup that occurs for a
declarator-id, including the changes in Richard’s [proposed resolution
for
CWG1839](http://wiki.edg.com/pub/Wg21cologne2019/CoreWorkingGroup/cwg1839.html)
(which also resolves CWG1818 and what of CWG2058 was not resolved along
with CWG2059) and rejecting the example from
[CWG1477](https://cplusplus.github.io/CWG/issues/1477.html).
Wording
([[dcl.meaning.general]/2.3](http://eel.is/c++draft/dcl.meaning#general-2.3)):
> The declaration's target scope is the innermost enclosing namespace
scope; if the declaration is contained by a block scope, the declaration
shall correspond to a reachable
([[module.reach]](http://eel.is/c++draft/module.reach)) declaration that
inhabits the innermost block
scope[.](http://eel.is/c++draft/dcl.meaning#general-2.3.sentence-2)
Wording
([[basic.scope.scope]/7](http://eel.is/c++draft/basic.scope#scope-7)):
> A declaration is
[nominable](http://eel.is/c++draft/basic.scope#def:nominable) in a
class, class template, or namespace E at a point P if it precedes P, it
does not inhabit a block scope, and its target scope is the scope
associated with E or, if E is a namespace, any element of the inline
namespace set of E
([[namespace.def]](http://eel.is/c++draft/namespace.def))[.](http://eel.is/c++draft/basic.scope#scope-7.sentence-1)
Wording
([[dcl.meaning.general]/3.4](http://eel.is/c++draft/dcl.meaning#general-3.4)):
> If it is a qualified name, the
[declarator](http://eel.is/c++draft/dcl.decl.general#nt:declarator)
shall correspond to one or more declarations nominable in S; [...]
In the new wording it's clear that while `friend` declarations of
functions do not bind names, declaration is still introduced, and is
nominable, making it eligible for a later definition by qualified-id.
Added:
Modified:
clang/test/CXX/drs/cwg14xx.cpp
clang/test/CXX/drs/cwg19xx.cpp
clang/test/CXX/drs/cwg1xx.cpp
clang/test/CXX/drs/cwg3xx.cpp
clang/www/cxx_dr_status.html
Removed:
################################################################################
diff --git a/clang/test/CXX/drs/cwg14xx.cpp b/clang/test/CXX/drs/cwg14xx.cpp
index a23ac744436331..5301185d046982 100644
--- a/clang/test/CXX/drs/cwg14xx.cpp
+++ b/clang/test/CXX/drs/cwg14xx.cpp
@@ -603,6 +603,28 @@ namespace cwg1467 { // cwg1467: 3.7 c++11
#endif
} // cwg1467
+namespace cwg1477 { // cwg1477: 2.7
+namespace N {
+struct A {
+ // Name "f" is not bound in N,
+ // so single searches of 'f' in N won't find it,
+ // but the targets scope of this declaration is N,
+ // making it nominable in N.
+ // (_N4988_.[dcl.meaning]/2.1, [basic.scope.scope]/7,
+ // [basic.lookup.general]/3)
+ friend int f();
+};
+}
+// Corresponds to the friend declaration,
+// because it's nominable in N,
+// and binds name 'f' in N.
+// (_N4988_.[dcl.meaning]/3.4, [basic.scope.scope]/2.5)
+int N::f() { return 0; }
+// Name 'f' is bound in N,
+// so the search performed by qualified lookup finds it.
+int i = N::f();
+} // namespace cwg1477
+
namespace cwg1479 { // cwg1479: 3.1
#if __cplusplus >= 201103L
int operator"" _a(const char*, std::size_t = 0);
diff --git a/clang/test/CXX/drs/cwg19xx.cpp b/clang/test/CXX/drs/cwg19xx.cpp
index 762787a4368e62..2fe46909eaacb1 100644
--- a/clang/test/CXX/drs/cwg19xx.cpp
+++ b/clang/test/CXX/drs/cwg19xx.cpp
@@ -8,6 +8,18 @@
namespace std { struct type_info; }
+namespace cwg1900 { // cwg1900: 2.7
+// See the test for CWG1477 for detailed analysis
+namespace N {
+struct A {
+ friend int f();
+};
+}
+int N::f() { return 0; }
+int N::g() { return 0; }
+// expected-error at -1 {{out-of-line definition of 'g' does not match any declaration in namespace 'cwg1900::N'}}
+} // namespace cwg1900
+
namespace cwg1902 { // cwg1902: 3.7
struct A {};
struct B {
diff --git a/clang/test/CXX/drs/cwg1xx.cpp b/clang/test/CXX/drs/cwg1xx.cpp
index e7dddd1ea9278f..d6ee0844458b1d 100644
--- a/clang/test/CXX/drs/cwg1xx.cpp
+++ b/clang/test/CXX/drs/cwg1xx.cpp
@@ -568,6 +568,80 @@ namespace cwg137 { // cwg137: yes
const volatile int *cvqcv = static_cast<const volatile int*>(cvp);
}
+namespace cwg138 { // cwg138: partial
+namespace example1 {
+void foo(); // #cwg138-ex1-foo
+namespace A {
+ using example1::foo; // #cwg138-ex1-using
+ class X {
+ static const int i = 10;
+ // This friend declaration is using neither qualified-id nor template-id,
+ // so name 'foo' is not looked up, which means the using-declaration has no effect.
+ // Target scope of this declaration is A, so this is grating friendship to
+ // (hypothetical) A::foo instead of 'example1::foo' using declaration refers to.
+ // A::foo corresponds to example1::foo named by the using declaration,
+ // and since A::foo is a
diff erent entity, they potentially conflict.
+ // FIXME: This is ill-formed, but not for the reason diagnostic says.
+ friend void foo();
+ // expected-error at -1 {{cannot befriend target of using declaration}}
+ // expected-note@#cwg138-ex1-foo {{target of using declaration}}
+ // expected-note@#cwg138-ex1-using {{using declaration}}
+ };
+}
+} // namespace example1
+
+namespace example2 {
+void f();
+void g();
+class B {
+ void g();
+};
+class A : public B {
+ static const int i = 10;
+ void f();
+ // Both friend declaration are not using qualified-ids or template-ids,
+ // so 'f' and 'g' are not looked up, which means that presence of A::f
+ // and base B have no effect.
+ // Both target scope of namespace 'example2', and grant friendship to
+ // example2::f and example2::g respectively.
+ friend void f();
+ friend void g();
+};
+void f() {
+ int i2 = A::i;
+}
+void g() {
+ int i3 = A::i;
+}
+} // namespace example2
+
+namespace example3 {
+struct Base {
+private:
+ static const int i = 10; // #cwg138-ex3-Base-i
+
+public:
+ struct Data;
+ // Elaborated type specifier is not the sole constituent of declaration,
+ // so 'Data' undergoes unqualified type-only lookup, which finds Base::Data.
+ friend class Data;
+
+ struct Data {
+ void f() {
+ int i2 = Base::i;
+ }
+ };
+};
+struct Data {
+ void f() {
+ int i2 = Base::i;
+ // expected-error at -1 {{'i' is a private member of 'cwg138::example3::Base'}}
+ // expected-note@#cwg138-ex3-Base-i {{declared private here}}
+ }
+};
+} // namespace example3
+} // namespace cwg138
+
namespace cwg139 { // cwg139: yes
namespace example1 {
typedef int f; // #cwg139-typedef-f
diff --git a/clang/test/CXX/drs/cwg3xx.cpp b/clang/test/CXX/drs/cwg3xx.cpp
index a10ed95941ba4a..7157feed3f7626 100644
--- a/clang/test/CXX/drs/cwg3xx.cpp
+++ b/clang/test/CXX/drs/cwg3xx.cpp
@@ -1369,6 +1369,78 @@ namespace cwg385 { // cwg385: 2.8
// expected-note@#cwg385-n {{member is declared here}}
}
+namespace cwg386 { // cwg386: no
+namespace example1 {
+namespace N1 {
+// Binds name 'f' in N1. Target scope is N1.
+template<typename T> void f( T* x ) {
+ // ... other stuff ...
+ delete x;
+}
+}
+
+namespace N2 {
+// Bind name 'f' in N2. When a single search find this declaration,
+// it's replaced with N1::f declaration.
+using N1::f;
+
+// According to _N4988_.[dcl.meaning]/3.3:
+// `f<int>` is not a qualified-id, so its target scope is N2.
+// `f<int>` is a template-id, so 'f' undergoes (unqualified) lookup.
+// Search performed by unqualified lookup finds N1::f via using-declaration,
+// but this result is not considered, because it's not nominable in N2,
+// which is because its target scope is N1.
+// So unqualified lookup doesn't find anything, making this declaration ill-formed.
+template<> void f<int>( int* );
+// expected-error at -1 {{no function template matches function template specialization 'f'}}
+
+class Test {
+ ~Test() { }
+ // According to _N4988_.[dcl.meaning]/2.2:
+ // `f<>` is a template-id and not a template declaration,
+ // so its terminal name 'f' undergoes (unqualified) lookup.
+ // Search in N2 performed by unqualified lookup finds
+ // (single) N1::f declaration via using-declaration.
+ // N1::f is replaced with N1::f<> specialization after deduction,
+ // and this is the result of the unqualified lookup.
+ // This friend declaration correspond to the result of the lookup.
+ // All lookup results target the same scope, which is N1,
+ // so target scope of this friend declaration is also N1.
+ // FIXME: This is well-formed.
+ friend void f<>( Test* x );
+ // expected-error at -1 {{no function template matches function template specialization 'f'}}
+};
+}
+} // namespace example1
+
+namespace example2 {
+namespace N1 {
+// Binds name 'f' in N1. Target scope is N1.
+void f(); // #cwg386-ex2-N1-f
+}
+
+namespace N2 {
+// Bind name 'f' in N2. When a single search finds this declaration,
+// it's replaced with N1::f declaration.
+using N1::f; // #cwg386-ex2-using
+class A {
+ // According to _N4988_.[dcl.meaning]/2.2:
+ // `N2::f` is a qualified-id, so its terminal name 'f' undergoes (qualified) lookup.
+ // Search in N2 performed by qualified lookup finds N1::f via using-declaration,
+ // which is the (only) result of qualified lookup.
+ // This friend declaration corresponds to the result of the lookup.
+ // All lookup results target the same scope, which is N1,
+ // so target scope of this friend declaration is also N1.
+ // FIXME: This is well-formed.
+ friend void N2::f();
+ // expected-error at -1 {{cannot befriend target of using declaration}}
+ // expected-note@#cwg386-ex2-N1-f {{target of using declaration}}
+ // expected-note@#cwg386-ex2-using {{using declaration}}
+};
+}
+} // namespace example2
+} // namespace cwg386
+
namespace cwg387 { // cwg387: 2.8
namespace old {
template<typename T> class number {
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 9c4aed35012bf0..4f4d8d0a97d43f 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -873,7 +873,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/138.html">138</a></td>
<td>CD6</td>
<td>Friend declaration name lookup</td>
- <td class="unknown" align="center">Unknown</td>
+ <td class="partial" align="center">Partial</td>
</tr>
<tr id="139">
<td><a href="https://cplusplus.github.io/CWG/issues/139.html">139</a></td>
@@ -2363,7 +2363,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/386.html">386</a></td>
<td>CD6</td>
<td>Friend declaration of name brought in by <I>using-declaration</I></td>
- <td class="unknown" align="center">Unknown</td>
+ <td class="none" align="center">No</td>
</tr>
<tr id="387">
<td><a href="https://cplusplus.github.io/CWG/issues/387.html">387</a></td>
@@ -8685,7 +8685,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/1477.html">1477</a></td>
<td>CD3</td>
<td>Definition of a <TT>friend</TT> outside its namespace</td>
- <td class="unknown" align="center">Unknown</td>
+ <td class="full" align="center">Clang 2.7</td>
</tr>
<tr id="1478">
<td><a href="https://cplusplus.github.io/CWG/issues/1478.html">1478</a></td>
@@ -11231,7 +11231,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/1900.html">1900</a></td>
<td>CD6</td>
<td>Do <TT>friend</TT> declarations count as “previous declarations”?</td>
- <td class="unknown" align="center">Unknown</td>
+ <td class="full" align="center">Clang 2.7</td>
</tr>
<tr class="open" id="1901">
<td><a href="https://cplusplus.github.io/CWG/issues/1901.html">1901</a></td>
More information about the cfe-commits
mailing list