r358881 - [Sema][NFC] Add more tests for the behavior of argument-dependent name lookup

Bruno Ricci via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 22 04:40:31 PDT 2019


Author: brunoricci
Date: Mon Apr 22 04:40:31 2019
New Revision: 358881

URL: http://llvm.org/viewvc/llvm-project?rev=358881&view=rev
Log:
[Sema][NFC] Add more tests for the behavior of argument-dependent name lookup

The goal here is to exercise each rule in [basic.lookup.argdep] at least once.
These new tests expose what I believe are 2 issues:

1. CWG 1691 needs to be implemented (p2:  [...] Its associated namespaces are
   the innermost enclosing namespaces of its associated classes [...]) The
   corresponding tests are adl_class_type::X2 and adl_class_type::X5.

2. The end of paragraph 2 ([...] 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.) is not implemented. Closely related, the
   restriction on non-dependent parameter types in this same paragraph needs
   to be removed. The corresponding tests are in adl_overload_set (both issues
   are from CWG 997).

Differential Revision: https://reviews.llvm.org/D60570

Reviewed By: riccibruno, Quuxplusone


Added:
    cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2-associated-namespaces-classes.cpp
    cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2-inline-namespace.cpp
Modified:
    cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp
    cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p3.cpp
    cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp

Added: cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2-associated-namespaces-classes.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2-associated-namespaces-classes.cpp?rev=358881&view=auto
==============================================================================
--- cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2-associated-namespaces-classes.cpp (added)
+++ cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2-associated-namespaces-classes.cpp Mon Apr 22 04:40:31 2019
@@ -0,0 +1,336 @@
+// 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); // expected-note {{'X2::f' declared here}}
+  }
+  void test2() {
+    f(X2::S{}); // FIXME: This is well-formed; X2 is the innermost enclosing namespace
+                // of the local struct S.
+                // expected-error at -2 {{use of undeclared identifier 'f'}}
+  }
+
+  // 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); // FIXME: This is well-formed. expected-error {{use of undeclared}}
+    }
+  }
+
+  // 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 at -2 {{use of undeclared identifier}}
+    X1_g(c); // ... nor is #2 ...
+             // expected-error at -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);
+    };
+  }
+
+  void test() {
+    N::E e;
+    f(e); // ok
+    N::S::F f;
+    g(f); // 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 at -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 at -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 at -1 {{use of undeclared}}
+  }
+}

Added: cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2-inline-namespace.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2-inline-namespace.cpp?rev=358881&view=auto
==============================================================================
--- cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2-inline-namespace.cpp (added)
+++ cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2-inline-namespace.cpp Mon Apr 22 04:40:31 2019
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+
+// C++11 [basic.lookup.argdep]p2
+//
+// [...] If an associated namespace is an inline namespace (10.3.1), its
+// enclosing namespace is also included in the set. If an associated
+// namespace directly contains inline namespaces, those inline namespaces
+// are also included in the set.
+
+namespace test1 {
+  namespace L {
+    namespace M {
+      inline namespace N {
+        inline namespace O {
+          struct S {};
+          void f1(S);
+        }
+        void f2(S);
+      }
+      void f3(S);
+    }
+    void f4(M::S); // expected-note {{declared here}}
+  }
+
+  void test() {
+    L::M::S s;
+    f1(s); // ok
+    f2(s); // ok
+    f3(s); // ok
+    f4(s); // expected-error {{use of undeclared}}
+  }
+}
+
+namespace test2 {
+  namespace L {
+    struct S {};
+    inline namespace M {
+      inline namespace N {
+        inline namespace O {
+          void f1(S);
+        }
+        void f2(S);
+      }
+      void f3(S);
+    }
+    void f4(S);
+  }
+
+  void test() {
+    L::S s;
+    f1(s); // ok
+    f2(s); // ok
+    f3(s); // ok
+    f4(s); // ok
+  }
+}

Modified: cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp?rev=358881&r1=358880&r2=358881&view=diff
==============================================================================
--- cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp (original)
+++ cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp Mon Apr 22 04:40:31 2019
@@ -132,3 +132,19 @@ namespace test8 {
     test8_function(ref);
   }
 }
+
+
+
+// [...] Typedef names and using-declarations used to specify the types
+// do not contribute to this set.
+namespace typedef_names_and_using_declarations {
+  namespace N { struct S {}; void f(S); }
+  namespace M { typedef N::S S; void g1(S); } // expected-note {{declared here}}
+  namespace L { using N::S; void g2(S); } // expected-note {{declared here}}
+  void test() {
+	  M::S s;
+    f(s);    // ok
+    g1(s);   // expected-error {{use of undeclared}}
+    g2(s);   // expected-error {{use of undeclared}}
+  }
+}

Modified: cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p3.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p3.cpp?rev=358881&r1=358880&r2=358881&view=diff
==============================================================================
--- cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p3.cpp (original)
+++ cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p3.cpp Mon Apr 22 04:40:31 2019
@@ -18,3 +18,67 @@ namespace test0 {
     }
   };
 }
+
+// If X contains [...] then Y is empty.
+// - a declaration of a class member
+namespace test_adl_suppression_by_class_member {
+  namespace N {
+    struct T {};
+    void f(T); // expected-note {{declared here}}
+  }
+  struct S {
+    void f();
+    void test() {
+      N::T t;
+      f(t); // expected-error {{too many arguments}}
+    }
+  };
+}
+
+// - a block-scope function declaration that is not a using-declaration
+namespace test_adl_suppression_by_block_scope {
+  namespace N {
+    struct S {};
+    void f(S);
+  }
+  namespace M { void f(int); } // expected-note 2{{candidate}}
+  void test1() {
+    N::S s;
+    using M::f;
+    f(s); // ok
+  }
+
+  void test2() {
+    N::S s;
+    extern void f(char); // expected-note {{passing argument to parameter here}}
+    f(s); // expected-error {{no viable conversion from 'N::S' to 'char'}}
+  }
+
+  void test3() {
+    N::S s;
+    extern void f(char); // expected-note {{candidate}}
+    using M::f;
+    f(s); // expected-error {{no matching function}}
+  }
+
+  void test4() {
+    N::S s;
+    using M::f;
+    extern void f(char); // expected-note {{candidate}}
+    f(s); // expected-error {{no matching function}}
+  }
+
+}
+
+// - a declaration that is neither a function nor a function template
+namespace test_adl_suppression_by_non_function {
+  namespace N {
+    struct S {};
+    void f(S);
+  }
+  void test() {
+    extern void (*f)();
+    N::S s;
+    f(s); // expected-error {{too many arguments}}
+  }
+}

Modified: cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp?rev=358881&r1=358880&r2=358881&view=diff
==============================================================================
--- cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp (original)
+++ cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp Mon Apr 22 04:40:31 2019
@@ -67,3 +67,96 @@ namespace test1 {
     foo(a, 10); // expected-error {{no matching function for call to 'foo'}}
   }
 }
+
+
+// Check the rules described in p4:
+//  When considering an associated namespace, the lookup is the same as the lookup
+//  performed when the associated namespace is used as a qualifier (6.4.3.2) except that:
+
+//  - Any using-directives in the associated namespace are ignored.
+namespace test_using_directives {
+  namespace M { struct S; }
+  namespace N {
+    void f(M::S); // expected-note {{declared here}}
+  }
+  namespace M {
+    using namespace N;
+    struct S {};
+  }
+  void test() {
+    M::S s;
+    f(s); // expected-error {{use of undeclared}}
+    M::f(s); // ok
+  }
+}
+
+//  - Any namespace-scope friend functions or friend function templates declared in
+//    associated classes are visible within their respective namespaces even if
+//    they are not visible during an ordinary lookup
+// (Note: For the friend declaration to be visible, the corresponding class must be
+//  included in the set of associated classes. Merely including the namespace in
+//  the set of associated namespaces is not enough.)
+namespace test_friend1 {
+  namespace N {
+    struct S;
+    struct T {
+      friend void f(S); // #1
+    };
+    struct S { S(); S(T); };
+  }
+
+  void test() {
+    N::S s;
+    N::T t;
+    f(s); // expected-error {{use of undeclared}}
+    f(t); // ok, #1
+  }
+}
+
+// credit: Arthur O’Dwyer
+namespace test_friend2 {
+  struct A {
+    struct B {
+        struct C {};
+    };
+    friend void foo(...); // #1
+  };
+
+  struct D {
+    friend void foo(...); // #2
+  };
+  template<class> struct E {
+    struct F {};
+  };
+
+  template<class> struct G {};
+  template<class> struct H {};
+  template<class> struct I {};
+  struct J { friend void foo(...) {} }; // #3
+
+  void test() {
+    A::B::C c;
+    foo(c); // #1 is not visible since A is not an associated class
+            // expected-error at -1 {{use of undeclared}}
+    E<D>::F f;
+    foo(f); // #2 is not visible since D is not an associated class
+            // expected-error at -1 {{use of undeclared}}
+    G<H<I<J> > > j;
+    foo(j);  // ok, #3.
+  }
+}
+
+//  - All names except those of (possibly overloaded) functions and
+//    function templates are ignored.
+namespace test_other_names {
+  namespace N {
+    struct S {};
+    struct Callable { void operator()(S); };
+    static struct Callable Callable;
+  }
+
+  void test() {
+    N::S s;
+    Callable(s); // expected-error {{use of undeclared}}
+  }
+}




More information about the cfe-commits mailing list