// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 %s
// Attempt to test each rule for forming associated namespaces
// and classes as described in [basic.lookup.argdep]p2.
// fundamental type: no associated namespace and no associated class
namespace adl_fundamental_type {
constexpr int g(char) { return 1; } // #1
template <typename T> constexpr int foo(T t) { return g(t); }
constexpr int g(int) { return 2; } // #2 not found
void test() {
static_assert(foo(0) == 1); // ok, #1
}
}
// class type:
// associated classes: itself, the class of which it is a member (if any),
// direct and indirect base classes
// associated namespaces: innermost enclosing namespaces of associated classes
namespace adl_class_type {
// associated class: itself, simple case
namespace X1 {
namespace N {
struct S {};
void f(S); // found
}
void g(N::S); // not found
};
void test1() {
f(X1::N::S{}); // ok
g(X1::N::S{}); // expected-error {{use of undeclared identifier}}
}
// associated class: itself, local type
namespace X2 {
auto foo() {
struct S {} s;
return s;
}
using S = decltype(foo());
void f(S); // #1
}
void test2() {
f(X2::S{}); // This is well-formed; X2 is the innermost enclosing namespace
// of the local struct S. Calls #1.
}
// associated class: the parent class
namespace X3 {
struct S {
struct T {};
friend void f(T);
};
}
void test3() {
f(X3::S::T{}); // ok
}
// associated class: direct and indirect base classes
namespace X4 {
namespace IndirectBaseNamespace {
struct IndirectBase {};
void f(IndirectBase); // #1
}
namespace DirectBaseNamespace {
struct DirectBase : IndirectBaseNamespace::IndirectBase {};
void g(DirectBase); // #2
}
struct S : DirectBaseNamespace::DirectBase {};
}
void test4() {
f(X4::S{}); // ok, #1
g(X4::S{}); // ok, #2
}
// associated class: itself, lambda
namespace X5 {
namespace N {
auto get_lambda() { return [](){}; }
void f(decltype(get_lambda()));
}
void test5() {
auto lambda = N::get_lambda();
f(lambda); // ok
}
}
// The parameter types and return type of a lambda's operator() do not
// contribute to the associated namespaces and classes of the lambda itself.
namespace X6 {
namespace N {
struct A {};
template<class T> constexpr int f(T) { return 1; }
}
constexpr int f(N::A (*)()) { return 2; }
constexpr int f(void (*)(N::A)) { return 3; }
void test() {
constexpr auto lambda = []() -> N::A { return {}; };
static_assert(f(lambda) == 2);
constexpr auto lambda2 = [](N::A) {};
static_assert(f(lambda2) == 3);
}
}
} // namespace adl_class_type
// class template specialization: as for class type plus
// for non-type template arguments:
// - nothing
// for type template arguments:
// - associated namespaces and classes of the type template arguments
// for template template arguments:
// - namespaces of which template template arguments are member of
// - classes of which member template used as template template arguments
// are member of
namespace adl_class_template_specialization_type {
// non-type template argument
namespace X1 {
namespace BaseNamespace { struct Base {}; }
namespace N { struct S : BaseNamespace::Base {}; }
template <N::S *> struct C {};
namespace N {
template <S *p> void X1_f(C<p>); // #1
}
namespace BaseNamespace {
template <N::S *p> void X1_g(C<p>); // #2
}
template <N::S *p> void X1_h(C<p>); // #3
}
void test1() {
constexpr X1::N::S *p = nullptr;
X1::C<p> c;
X1_f(c); // N is not added to the set of associated namespaces
// and #1 is not found...
// expected-error@-2 {{use of undeclared identifier}}
X1_g(c); // ... nor is #2 ...
// expected-error@-1 {{use of undeclared identifier}}
X1_h(c); // ... but the namespace X1 is added and #3 is found.
}
// type template argument
namespace X2 {
template <typename T> struct C {};
namespace BaseNamespace { struct Base {}; }
namespace N { struct S : BaseNamespace::Base {}; }
namespace N {
template <typename T> void X2_f(C<T>); // #1
}
namespace BaseNamespace {
template <typename T> void X2_g(C<T>); // #2
}
template <typename T> void X2_h(C<T>); // #2
}
void test2() {
X2::C<X2::N::S> c;
X2_f(c); // N is added to the set of associated namespaces and #1 is found.
X2_g(c); // Similarly BaseNamespace is added and #2 is found.
X2_h(c); // As before, X2 is also added and #3 is found.
}
// template template argument
namespace X3 {
template <template <typename> class TT> struct C {};
namespace N {
template <typename T> struct Z {};
void X3_f(C<Z>); // #1
}
struct M {
template <typename T> struct Z {};
friend void X3_g(C<Z>); // #2
};
}
void test3() {
X3::C<X3::N::Z> c1;
X3::C<X3::M::Z> c2;
X3_f(c1); // ok, namespace N is added, #1
X3_g(c2); // ok, struct M is added, #2
}
}
// enumeration type:
// associated namespace: innermost enclosing namespace of its declaration.
// associated class: if the enumeration is a class member, the member's class.
namespace adl_enumeration_type {
namespace N {
enum E : int;
void f(E);
struct S {
enum F : int;
friend void g(F);
};
auto foo() {
enum G {} g;
return g;
}
using G = decltype(foo());
void h(G);
}
void test() {
N::E e;
f(e); // ok
N::S::F f;
g(f); // ok
N::G g;
h(g); // ok
}
}
// pointer and reference type:
// associated namespaces and classes of the pointee type
// array type:
// associated namespaces and classes of the base type
namespace adl_point_array_reference_type {
namespace N {
struct S {};
void f(S *);
void f(S &);
}
void test() {
N::S *p;
f(p); // ok
extern N::S &r;
f(r); // ok
N::S a[2];
f(a); // ok
}
}
// function type:
// associated namespaces and classes of the function parameter types
// and the return type.
namespace adl_function_type {
namespace M { struct T; }
namespace N {
struct S {};
void f(S (*)(M::T));
};
namespace M {
struct T {};
void g(N::S (*)(T));
}
void test() {
extern N::S x(M::T);
f(x); // ok
g(x); // ok
}
}
// pointer to member function:
// associated namespaces and classes of the class, parameter types
// and return type.
namespace adl_pointer_to_member_function {
namespace M { struct C; }
namespace L { struct T; }
namespace N {
struct S {};
void f(N::S (M::C::*)(L::T));
}
namespace L {
struct T {};
void g(N::S (M::C::*)(L::T));
}
namespace M {
struct C {};
void h(N::S (M::C::*)(L::T));
}
void test() {
N::S (M::C::*p)(L::T);
f(p); // ok
g(p); // ok
h(p); // ok
}
}
// pointer to member:
// associated namespaces and classes of the class and of the member type.
namespace adl_pointer_to_member {
namespace M { struct C; }
namespace N {
struct S {};
void f(N::S (M::C::*));
}
namespace M {
struct C {};
void g(N::S (M::C::*));
}
void test() {
N::S (M::C::*p);
f(p); // ok
g(p); // ok
}
}
// [...] if the argument is the name or address of a set of overloaded
// functions and/or function templates, its associated classes and namespaces
// are the union of those associated with each of the members of the set,
// i.e., the classes and namespaces associated with its parameter types and
// return type.
//
// Additionally, if the aforementioned set of overloaded functions is named
// with a template-id, its associated classes and namespaces also include
// those of its type template-arguments and its template template-arguments.
//
// CWG 33 for the union rule. CWG 997 for the template-id rule.
namespace adl_overload_set {
namespace N {
struct S {};
constexpr int f(int (*g)()) { return g(); }
// expected-note@-1 2{{'N::f' declared here}}
template <typename T> struct Q;
}
constexpr int g1() { return 1; }
constexpr int g1(N::S) { return 2; }
template <typename T> constexpr int g2() { return 3; }
// Inspired from CWG 997.
constexpr int g3() { return 4; }
template <typename T> constexpr int g3(T, N::Q<T>) { return 5; }
void test() {
static_assert(f(g1) == 1, ""); // Well-formed from the union rule above
static_assert(f(g2<N::S>) == 3, ""); // FIXME: Well-formed from the template-id rule above.
// expected-error@-1 {{use of undeclared}}
// A objection was raised during review against implementing the
// template-id rule. Currently only GCC implements it. Implementing
// it would weaken the argument to remove it in the future since
// actual real code might start to depend on it.
static_assert(f(g3) == 4, ""); // FIXME: Also well-formed from the union rule.
// expected-error@-1 {{use of undeclared}}
}
}