re PR c++/50545 ([C++0x][DR 1172] SFINAE does not handle an explicit type conversion...
authorJason Merrill <jason@redhat.com>
Fri, 31 Aug 2012 02:50:28 +0000 (22:50 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Fri, 31 Aug 2012 02:50:28 +0000 (22:50 -0400)
PR c++/50545
PR c++/51222
* pt.c (instantiation_dependent_r): New.
(instantiation_dependent_expression_p): New.
(value_dependent_expression_p): Use it.  SCOPE_REF is always dependent.
* semantics.c (finish_decltype_type): Use it.
* cp-tree.h: Declare it.

From-SVN: r190830

gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/pt.c
gcc/cp/semantics.c
gcc/testsuite/g++.dg/cpp0x/decltype40.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/decltype41.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/decltype42.C [new file with mode: 0644]

index c202dc4..0bfcde7 100644 (file)
@@ -1,5 +1,13 @@
 2012-08-30  Jason Merrill  <jason@redhat.com>
 
+       PR c++/50545
+       PR c++/51222
+       * pt.c (instantiation_dependent_r): New.
+       (instantiation_dependent_expression_p): New.
+       (value_dependent_expression_p): Use it.  SCOPE_REF is always dependent.
+       * semantics.c (finish_decltype_type): Use it.
+       * cp-tree.h: Declare it.
+
        * semantics.c (finish_qualified_id_expr): Handle building up a
        non-dependent SCOPE_REF here.
        (finish_id_expression): Not here.
index 7ffc929..1b085bd 100644 (file)
@@ -5391,6 +5391,7 @@ extern bool any_type_dependent_arguments_p      (const VEC(tree,gc) *);
 extern bool any_type_dependent_elements_p       (const_tree);
 extern bool type_dependent_expression_p_push   (tree);
 extern bool value_dependent_expression_p       (tree);
+extern bool instantiation_dependent_expression_p (tree);
 extern bool any_value_dependent_elements_p      (const_tree);
 extern bool dependent_omp_for_p                        (tree, tree, tree, tree);
 extern tree resolve_typename_type              (tree, bool);
index 6506a67..5ce6e8a 100644 (file)
@@ -19100,20 +19100,7 @@ dependent_scope_p (tree scope)
 
 /* Note that this predicate is not appropriate for general expressions;
    only constant expressions (that satisfy potential_constant_expression)
-   can be tested for value dependence.
-
-   We should really also have a predicate for "instantiation-dependent".
-
-   fold_non_dependent_expr: fold if constant and not type-dependent and not value-dependent.
-     (what about instantiation-dependent constant-expressions?)
-   is_late_template_attribute: defer if instantiation-dependent.
-   compute_array_index_type: proceed if constant and not t- or v-dependent
-     if instantiation-dependent, need to remember full expression
-   uses_template_parms: FIXME - need to audit callers
-   tsubst_decl [function_decl]: Why is this using value_dependent_expression_p?
-   dependent_type_p [array_type]: dependent if index type is dependent
-     (or non-constant?)
-   static_assert - instantiation-dependent */
+   can be tested for value dependence.  */
 
 bool
 value_dependent_expression_p (tree expression)
@@ -19193,7 +19180,7 @@ value_dependent_expression_p (tree expression)
         return true;
       else if (TYPE_P (expression))
        return dependent_type_p (expression);
-      return type_dependent_expression_p (expression);
+      return instantiation_dependent_expression_p (expression);
 
     case AT_ENCODE_EXPR:
       /* An 'encode' expression is value-dependent if the operand is
@@ -19203,13 +19190,13 @@ value_dependent_expression_p (tree expression)
 
     case NOEXCEPT_EXPR:
       expression = TREE_OPERAND (expression, 0);
-      return type_dependent_expression_p (expression);
+      return instantiation_dependent_expression_p (expression);
 
     case SCOPE_REF:
-      {
-       tree name = TREE_OPERAND (expression, 1);
-       return value_dependent_expression_p (name);
-      }
+      /* instantiation_dependent_r treats this as dependent so that we
+        check access at instantiation time, and all instantiation-dependent
+        expressions should also be considered value-dependent.  */
+      return true;
 
     case COMPONENT_REF:
       return (value_dependent_expression_p (TREE_OPERAND (expression, 0))
@@ -19488,6 +19475,104 @@ type_dependent_expression_p (tree expression)
   return (dependent_type_p (TREE_TYPE (expression)));
 }
 
+/* walk_tree callback function for instantiation_dependent_expression_p,
+   below.  Returns non-zero if a dependent subexpression is found.  */
+
+static tree
+instantiation_dependent_r (tree *tp, int *walk_subtrees,
+                          void *data ATTRIBUTE_UNUSED)
+{
+  if (TYPE_P (*tp))
+    {
+      /* We don't have to worry about decltype currently because decltype
+        of an instantiation-dependent expr is a dependent type.  This
+        might change depending on the resolution of DR 1172.  */
+      *walk_subtrees = false;
+      return NULL_TREE;
+    }
+  enum tree_code code = TREE_CODE (*tp);
+  switch (code)
+    {
+      /* Don't treat an argument list as dependent just because it has no
+        TREE_TYPE.  */
+    case TREE_LIST:
+    case TREE_VEC:
+      return NULL_TREE;
+
+    case TEMPLATE_PARM_INDEX:
+      return *tp;
+
+      /* Handle expressions with type operands.  */
+    case SIZEOF_EXPR:
+    case ALIGNOF_EXPR:
+    case TYPEID_EXPR:
+    case AT_ENCODE_EXPR:
+    case TRAIT_EXPR:
+      {
+       tree op = TREE_OPERAND (*tp, 0);
+       if (TYPE_P (op))
+         {
+           if (dependent_type_p (op)
+               || (code == TRAIT_EXPR
+                   && dependent_type_p (TREE_OPERAND (*tp, 1))))
+             return *tp;
+           else
+             {
+               *walk_subtrees = false;
+               return NULL_TREE;
+             }
+         }
+       break;
+      }
+
+    case COMPONENT_REF:
+      if (TREE_CODE (TREE_OPERAND (*tp, 1)) == IDENTIFIER_NODE)
+       /* In a template, finish_class_member_access_expr creates a
+          COMPONENT_REF with an IDENTIFIER_NODE for op1 even if it isn't
+          type-dependent, so that we can check access control at
+          instantiation time (PR 42277).  See also Core issue 1273.  */
+       return *tp;
+      break;
+
+    case SCOPE_REF:
+      /* Similarly, finish_qualified_id_expr builds up a SCOPE_REF in a
+        template so that we can check access at instantiation time even
+        though we know which member it resolves to.  */
+      return *tp;
+
+    default:
+      break;
+    }
+
+  if (type_dependent_expression_p (*tp))
+    return *tp;
+  else
+    return NULL_TREE;
+}
+
+/* Returns TRUE if the EXPRESSION is instantiation-dependent, in the
+   sense defined by the ABI:
+
+   "An expression is instantiation-dependent if it is type-dependent
+   or value-dependent, or it has a subexpression that is type-dependent
+   or value-dependent."  */
+
+bool
+instantiation_dependent_expression_p (tree expression)
+{
+  tree result;
+
+  if (!processing_template_decl)
+    return false;
+
+  if (expression == error_mark_node)
+    return false;
+
+  result = cp_walk_tree_without_duplicates (&expression,
+                                           instantiation_dependent_r, NULL);
+  return result != NULL_TREE;
+}
+
 /* Like type_dependent_expression_p, but it also works while not processing
    a template definition, i.e. during substitution or mangling.  */
 
index 6d7004b..183a78a 100644 (file)
@@ -5187,14 +5187,10 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
       return error_mark_node;
     }
 
-  /* FIXME instantiation-dependent  */
-  if (type_dependent_expression_p (expr)
-      /* In a template, a COMPONENT_REF has an IDENTIFIER_NODE for op1 even
-        if it isn't dependent, so that we can check access control at
-        instantiation time, so defer the decltype as well (PR 42277).  */
-      || (id_expression_or_member_access_p
-         && processing_template_decl
-         && TREE_CODE (expr) == COMPONENT_REF))
+  /* Depending on the resolution of DR 1172, we may later need to distinguish
+     instantiation-dependent but not type-dependent expressions so that, say,
+     A<decltype(sizeof(T))>::U doesn't require 'typename'.  */
+  if (instantiation_dependent_expression_p (expr))
     {
       type = cxx_make_type (DECLTYPE_TYPE);
       DECLTYPE_TYPE_EXPR (type) = expr;
diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype40.C b/gcc/testsuite/g++.dg/cpp0x/decltype40.C
new file mode 100644 (file)
index 0000000..7933c95
--- /dev/null
@@ -0,0 +1,101 @@
+// PR c++/51222
+// { dg-options -std=c++11 }
+
+template<class T>
+struct add_rref {
+  typedef T&& type;
+};
+
+template<>
+struct add_rref<void> {
+  typedef void type;
+};
+
+template<class T>
+typename add_rref<T>::type declval();
+
+template<class T, class U, class =
+  decltype(::delete ::new T(declval<U>()))
+>
+auto f(int) -> char;
+
+template<class, class>
+auto f(...) -> char(&)[2];
+
+template<class T, class =
+  decltype(::delete ::new T())
+>
+auto g(int) -> char;
+
+template<class>
+auto g(...) -> char(&)[2];
+
+template<class T, class U>
+auto f2(int) -> decltype(::delete ::new T(declval<U>()), char());
+
+template<class, class>
+auto f2(...) -> char(&)[2];
+
+template<class T>
+auto g2(int) -> decltype(::delete ::new T(), char());
+
+template<class>
+auto g2(...) -> char(&)[2];
+
+struct C { };
+
+struct A {
+  virtual ~A() = 0;
+};
+
+struct D1 {
+  D1() = delete;
+};
+
+struct D2 {
+  ~D2() = delete;
+};
+
+static_assert(sizeof(g<void>(0)) == 2, "Ouch");
+static_assert(sizeof(g<void()>(0)) == 2, "Ouch");
+static_assert(sizeof(g<void() const>(0)) == 2, "Ouch");
+static_assert(sizeof(g<A>(0)) == 2, "Ouch");
+static_assert(sizeof(g<D1>(0)) == 2, "Ouch");
+static_assert(sizeof(g<D2>(0)) == 2, "Ouch");
+static_assert(sizeof(g<int&>(0)) == 2, "Ouch");
+static_assert(sizeof(g<int&&>(0)) == 2, "Ouch");
+static_assert(sizeof(g<void(&)()>(0)) == 2, "Ouch");
+static_assert(sizeof(g<void(&&)()>(0)) == 2, "Ouch");
+static_assert(sizeof(f<void, void>(0)) == 2, "Ouch");
+static_assert(sizeof(f<void(), void()>(0)) == 2, "Ouch");
+static_assert(sizeof(f<void() const, void() const>(0)) == 2, "Ouch");
+static_assert(sizeof(f<int, void>(0)) == 2, "Ouch");
+static_assert(sizeof(f<void, int>(0)) == 2, "Ouch");
+static_assert(sizeof(f<C, void>(0)) == 2, "Ouch");
+static_assert(sizeof(f<C, int>(0)) == 2, "Ouch");
+static_assert(sizeof(f<int&, int&>(0)) == 2, "Ouch");
+static_assert(sizeof(f<int&&, int&&>(0)) == 2, "Ouch");
+static_assert(sizeof(f<void(&)(), void(&)()>(0)) == 2, "Ouch");
+static_assert(sizeof(f<void(&&)(), void(&&)()>(0)) == 2, "Ouch");
+
+static_assert(sizeof(g2<void>(0)) == 2, "Ouch");
+static_assert(sizeof(g2<void()>(0)) == 2, "Ouch");
+static_assert(sizeof(g2<void() const>(0)) == 2, "Ouch");
+static_assert(sizeof(g2<A>(0)) == 2, "Ouch");
+static_assert(sizeof(g2<D1>(0)) == 2, "Ouch");
+static_assert(sizeof(g2<D2>(0)) == 2, "Ouch");
+static_assert(sizeof(g2<int&>(0)) == 2, "Ouch");
+static_assert(sizeof(g2<int&&>(0)) == 2, "Ouch");
+static_assert(sizeof(g2<void(&)()>(0)) == 2, "Ouch");
+static_assert(sizeof(g2<void(&&)()>(0)) == 2, "Ouch");
+static_assert(sizeof(f2<void, void>(0)) == 2, "Ouch");
+static_assert(sizeof(f2<void(), void()>(0)) == 2, "Ouch");
+static_assert(sizeof(f2<void() const, void() const>(0)) == 2, "Ouch");
+static_assert(sizeof(f2<int, void>(0)) == 2, "Ouch");
+static_assert(sizeof(f2<void, int>(0)) == 2, "Ouch");
+static_assert(sizeof(f2<C, void>(0)) == 2, "Ouch");
+static_assert(sizeof(f2<C, int>(0)) == 2, "Ouch");
+static_assert(sizeof(f2<int&, int&>(0)) == 2, "Ouch");
+static_assert(sizeof(f2<int&&, int&&>(0)) == 2, "Ouch");
+static_assert(sizeof(f2<void(&)(), void(&)()>(0)) == 2, "Ouch");
+static_assert(sizeof(f2<void(&&)(), void(&&)()>(0)) == 2, "Ouch");
diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype41.C b/gcc/testsuite/g++.dg/cpp0x/decltype41.C
new file mode 100644 (file)
index 0000000..1439e15
--- /dev/null
@@ -0,0 +1,43 @@
+// Core 1273
+// { dg-do compile { target c++11 } }
+
+template <class T> struct C;
+template <class T> struct D;
+
+class A
+{
+  int i;
+  static int j;
+  friend struct C<int>;
+  friend struct D<int>;
+} a;
+
+class B
+{
+  int i;
+  static int j;
+  friend struct C<float>;
+  friend struct D<float>;
+} b;
+
+template <class T>
+struct C
+{
+  template <class U> decltype (a.i) f() { } // #1
+  template <class U> decltype (b.i) f() { } // #2
+};
+
+template <class T>
+struct D
+{
+  template <class U> decltype (A::j) f() { } // #1
+  template <class U> decltype (B::j) f() { } // #2
+};
+
+int main()
+{
+  C<int>().f<int>();     // calls #1
+  C<float>().f<float>(); // calls #2
+  D<int>().f<int>();     // calls #1
+  D<float>().f<float>(); // calls #2
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype42.C b/gcc/testsuite/g++.dg/cpp0x/decltype42.C
new file mode 100644 (file)
index 0000000..6c1aa43
--- /dev/null
@@ -0,0 +1,31 @@
+// PR c++/50545
+// { dg-do compile { target c++11 } }
+
+template< class T >
+T&& declval();
+
+// #1
+template< class T >
+auto f( int )
+  -> decltype( int{ declval<T>() } );
+
+// #2
+template< class >
+void f( ... );
+
+
+#define STATIC_ASSERT( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ )
+
+template< class T, class U >
+struct is_same {
+  static constexpr bool value = false;
+};
+
+template< class T >
+struct is_same<T, T> {
+  static constexpr bool value = true;
+};
+
+
+STATIC_ASSERT( is_same< decltype( f<int>(0) ),  int >::value );  // OK; f<int>(0) calls #1.
+STATIC_ASSERT( is_same< decltype( f<int*>(0) ), void >::value ); // static assertion fails; f<int*>(0) should call #2, because int{ (int*)0 } is ill-formed, but calls #1.