diff mbox

C++ PATCH for c++/44587 (qualified-ids as template arguments)

Message ID 4C2A4883.1070603@redhat.com
State New
Headers show

Commit Message

Jason Merrill June 29, 2010, 7:24 p.m. UTC
In 44587, using a qualified-id naming an array static data member as a 
pointer template argument was causing an ICE.  When I investigated this, 
it turned out that we weren't doing anything about 14.6.2.4/3:

A non-integral non-type template-argument is dependent if its type is 
dependent or it has either of the following forms
         qualified-id
         & qualified-id

which is somewhat odd because this text was there in C++98.

Then I noticed that the equivalent testcase using an unqualified-id was 
being miscompiled as well--and that it seems to be a hole in the 
standard, as the address of a static data member is clearly 
value-dependent, but the standard doesn't say that.

These testcases worked in 4.2, but started going wrong in 4.3.

So, this patch clarifies that such an address is value-dependent, makes 
sure that we check that before deciding to fold it, and treats a 
qualified-id that names a member of the current instantiation the same 
as an unqualified-id to avoid crashing in lvalue_p.

Tested x86_64-pc-linux-gnu, applying to 4.4, 4.5 and trunk.

Comments

H.J. Lu Nov. 12, 2010, 3:45 p.m. UTC | #1
On Tue, Jun 29, 2010 at 12:24 PM, Jason Merrill <jason@redhat.com> wrote:
> In 44587, using a qualified-id naming an array static data member as a
> pointer template argument was causing an ICE.  When I investigated this, it
> turned out that we weren't doing anything about 14.6.2.4/3:
>
> A non-integral non-type template-argument is dependent if its type is
> dependent or it has either of the following forms
>        qualified-id
>        & qualified-id
>
> which is somewhat odd because this text was there in C++98.
>
> Then I noticed that the equivalent testcase using an unqualified-id was
> being miscompiled as well--and that it seems to be a hole in the standard,
> as the address of a static data member is clearly value-dependent, but the
> standard doesn't say that.
>
> These testcases worked in 4.2, but started going wrong in 4.3.
>
> So, this patch clarifies that such an address is value-dependent, makes sure
> that we check that before deciding to fold it, and treats a qualified-id
> that names a member of the current instantiation the same as an
> unqualified-id to avoid crashing in lvalue_p.
>
> Tested x86_64-pc-linux-gnu, applying to 4.4, 4.5 and trunk.
>

This caused:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46058
diff mbox

Patch

commit af6c6141ae0833fe8cb7bebda2196e9c382cd074
Author: Jason Merrill <jason@redhat.com>
Date:   Mon Jun 28 18:11:13 2010 -0400

    	PR c++/44587
    	* pt.c (has_value_dependent_address): New.
    	(value_dependent_expression_p): Check it.
    	(convert_nontype_argument): Likewise.  Call decay_conversion before
    	folding if we want a pointer.
    	* semantics.c (finish_id_expression): Don't add SCOPE_REF if the
    	scope is the current instantiation.

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 863218d..8a447ec 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -4880,6 +4880,36 @@  check_valid_ptrmem_cst_expr (tree type, tree expr)
   return false;
 }
 
+/* Returns TRUE iff the address of OP is value-dependent.
+
+   14.6.2.4 [temp.dep.temp]:
+   A non-integral non-type template-argument is dependent if its type is
+   dependent or it has either of the following forms
+     qualified-id
+     & qualified-id
+   and contains a nested-name-specifier which specifies a class-name that
+   names a dependent type.
+
+   We generalize this to just say that the address of a member of a
+   dependent class is value-dependent; the above doesn't cover the
+   address of a static data member named with an unqualified-id.  */
+
+static bool
+has_value_dependent_address (tree op)
+{
+  /* We could use get_inner_reference here, but there's no need;
+     this is only relevant for template non-type arguments, which
+     can only be expressed as &id-expression.  */
+  if (DECL_P (op))
+    {
+      tree ctx = CP_DECL_CONTEXT (op);
+      if (TYPE_P (ctx) && dependent_type_p (ctx))
+	return true;
+    }
+
+  return false;
+}
+
 /* Attempt to convert the non-type template parameter EXPR to the
    indicated TYPE.  If the conversion is successful, return the
    converted value.  If the conversion is unsuccessful, return
@@ -4918,6 +4948,11 @@  convert_nontype_argument (tree type, tree expr)
       return NULL_TREE;
     }
 
+  /* Add the ADDR_EXPR now for the benefit of
+     value_dependent_expression_p.  */
+  if (TYPE_PTROBV_P (type))
+    expr = decay_conversion (expr);
+
   /* If we are in a template, EXPR may be non-dependent, but still
      have a syntactic, rather than semantic, form.  For example, EXPR
      might be a SCOPE_REF, rather than the VAR_DECL to which the
@@ -4925,7 +4960,11 @@  convert_nontype_argument (tree type, tree expr)
      so that access checking can be performed when the template is
      instantiated -- but here we need the resolved form so that we can
      convert the argument.  */
-  expr = fold_non_dependent_expr (expr);
+  if (TYPE_REF_OBJ_P (type)
+      && has_value_dependent_address (expr))
+    /* If we want the address and it's value-dependent, don't fold.  */;
+  else
+    expr = fold_non_dependent_expr (expr);
   if (error_operand_p (expr))
     return error_mark_node;
   expr_type = TREE_TYPE (expr);
@@ -17638,6 +17677,13 @@  value_dependent_expression_p (tree expression)
       return ((value_dependent_expression_p (TREE_OPERAND (expression, 0)))
 	      || (value_dependent_expression_p (TREE_OPERAND (expression, 2))));
 
+    case ADDR_EXPR:
+      {
+	tree op = TREE_OPERAND (expression, 0);
+	return (value_dependent_expression_p (op)
+		|| has_value_dependent_address (op));
+      }
+
     default:
       /* A constant expression is value-dependent if any subexpression is
 	 value-dependent.  */
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index e750937..8baf76a 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -3097,7 +3097,13 @@  finish_id_expression (tree id_expression,
 	    {
 	      tree r = convert_from_reference (decl);
 
-	      if (processing_template_decl && TYPE_P (scope))
+	      /* In a template, return a SCOPE_REF for most qualified-ids
+		 so that we can check access at instantiation time.  But if
+		 we're looking at a member of the current instantiation, we
+		 know we have access and building up the SCOPE_REF confuses
+		 non-type template argument handling.  */
+	      if (processing_template_decl && TYPE_P (scope)
+		  && !currently_open_class (scope))
 		r = build_qualified_name (TREE_TYPE (r),
 					  scope, decl,
 					  template_p);
diff --git a/gcc/testsuite/g++.dg/template/qualified-id2.C b/gcc/testsuite/g++.dg/template/qualified-id2.C
new file mode 100644
index 0000000..e88e854
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/qualified-id2.C
@@ -0,0 +1,27 @@ 
+// PR c++/44587
+// { dg-do run }
+
+template <const char *N> struct A { static const char *p; };
+template <const char *N> const char *A<N>::p = N;
+template <class T> struct B { static const char c[1]; typedef A<B<T>::c> C; };
+template <class T> const char B<T>::c[1] = "";
+template <class T> struct D { static const char c[1]; typedef A<c> C; };
+template <class T> const char D<T>::c[1] = "";
+
+template <int& I> struct E { static int *ip; };
+template <int& I> int* E<I>::ip = &I;
+template <class T> struct F { static int i; typedef E<F<T>::i> C; };
+template <class T> int F<T>::i;
+template <class T> struct G { static int i; typedef E<i> C; };
+template <class T> int G<T>::i;
+
+#define AS(X) if (!(X)) return 1;
+int main()
+{
+  AS(B<int>::C::p == B<int>::c);
+  AS(B<float>::C::p == B<float>::c);
+  AS(B<float>::C::p != B<int>::c);
+  AS(D<int>::C::p == D<int>::c);
+  AS(D<float>::C::p == D<float>::c);
+  AS(D<float>::C::p != D<int>::c);
+}
diff --git a/gcc/testsuite/g++.dg/template/qualified-id3.C b/gcc/testsuite/g++.dg/template/qualified-id3.C
new file mode 100644
index 0000000..d97ef5c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/qualified-id3.C
@@ -0,0 +1,14 @@ 
+// PR c++/44587
+
+template <const int N> struct A { };
+template <class T> struct B {
+  static const int c;
+  typedef A<B<T>::c> C;		// { dg-error "non-constant" }
+};
+template <class T> const int B<T>::c = sizeof (T);
+
+template <const int N> struct D { };
+template <class T> struct E {
+  static const int c = sizeof (T);
+  typedef D<E<T>::c> F;		// OK
+};