Patchwork C++ PATCHes for c++/50545, 51222

login
register
mail settings
Submitter Jason Merrill
Date Aug. 31, 2012, 2:49 a.m.
Message ID <50402629.7050908@redhat.com>
Download mbox | patch
Permalink /patch/180895/
State New
Headers show

Comments

Jason Merrill - Aug. 31, 2012, 2:49 a.m.
I've been surprised at the number of issues that have come up while I've 
been working on implementing the notion of instantiation-dependent 
expressions, which aren't currently described in the standard other than 
as "expression involving a template parameter".  I checked in fixes for 
several of these issues last week, but now here's another batch.

The first patch fixes an issue with partial ordering whereby we weren't 
keeping processing_template_decl set when instantiating a function using 
dependent template arguments.

The second patch gives the error about using a parenthesized expression 
list to initialize a non-class variable even if the expressions are 
dependent, to avoid a diagnostic regression as more things become dependent.

The third patch uses coerce_template_parms to make sure that after we've 
substituted the deduced args into the partial specialization argument 
list, we do have arguments of the appropriate type and that constants 
have been folded the way we want.  Without this we could have unresolved 
overloads and variables instead of constants.

The fourth patch implements making a template template parameter a 
friend, which is tested in cpp0x/friend2.C and seems to have worked 
before entirely by accident.

The fifth patch moves the decision to build a SCOPE_REF to express a 
non-type-dependent qualified-id to a different place so that it is 
preserved in partial instantiations.

And finally, the implementation of instantiation_dependent_expression_p. 
  We really only need to check it in two places: deciding whether a 
decltype represents a dependent type, and in checking for 
value-dependence.  I've proposed to the committee that making 
value-dependent a superset of instantiation-dependent, but not doing the 
same for type-dependent, is the best way to handle 
instantiation-dependency, and that's what I've implemented here.  I also 
implemented something that has been a bit controversial on the 
committee: treating member references as instantiation-dependent even 
when they don't actually involve any template parameters, because access 
checking at instantiation time might vary between specializations; see 
my test decltype41.C for an example.

Tested x86_64-pc-linux-gnu, applying to trunk.

Patch

commit 121ddf0344d7cd0bee6eac5561339b82214f4fa5
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Jul 31 14:43:21 2012 -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.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 7ffc929..1b085bd 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 6506a67..5ce6e8a 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -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.  */
 
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 6d7004b..183a78a 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -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
index 0000000..7933c95
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/decltype40.C
@@ -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
index 0000000..1439e15
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/decltype41.C
@@ -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
index 0000000..6c1aa43
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/decltype42.C
@@ -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.