Patchwork C++ PATCH for c++/56247 (ICE with PMF in two template member functions)

login
register
mail settings
Submitter Jason Merrill
Date Feb. 9, 2013, 8:37 p.m.
Message ID <5116B36F.2050202@redhat.com>
Download mbox | patch
Permalink /patch/219446/
State New
Headers show

Comments

Jason Merrill - Feb. 9, 2013, 8:37 p.m.
Here, the internal representation of &Base::method in the templates 
involves an OFFSET_REF around 'this', and we were treating the two 
instances of Wrapper as equivalent.  But they involve different 'this' 
parameters, which makes a difference when we go to look up the local 
specialization.  So we need to be stricter when comparing hash table 
entries so that they aren't shared.

Tested x86_64-pc-linux-gnu, applying to trunk, 4.7 and 4.6.

Patch

commit 504f38b8ed59af62c0226372837f8cb56765905c
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Feb 8 22:00:02 2013 -0500

    	PR c++/56247
    	* pt.c (eq_specializations): Set comparing_specializations.
    	* tree.c (cp_tree_equal): Check it.
    	* cp-tree.h: Declare it.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 303f5f6..d9270e2 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4247,6 +4247,10 @@  extern GTY(()) tree integer_two_node;
    function, two inside the body of a function in a local class, etc.)  */
 extern int function_depth;
 
+/* Nonzero if we are inside eq_specializations, which affects comparison of
+   PARM_DECLs in cp_tree_equal.  */
+extern int comparing_specializations;
+
 /* In parser.c.  */
 
 /* Nonzero if we are parsing an unevaluated operand: an operand to
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 29664ea..a3359ad 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -1461,14 +1461,21 @@  register_specialization (tree spec, tree tmpl, tree args, bool is_friend,
 /* Returns true iff two spec_entry nodes are equivalent.  Only compares the
    TMPL and ARGS members, ignores SPEC.  */
 
+int comparing_specializations;
+
 static int
 eq_specializations (const void *p1, const void *p2)
 {
   const spec_entry *e1 = (const spec_entry *)p1;
   const spec_entry *e2 = (const spec_entry *)p2;
+  int equal;
 
-  return (e1->tmpl == e2->tmpl
-	  && comp_template_args (e1->args, e2->args));
+  ++comparing_specializations;
+  equal = (e1->tmpl == e2->tmpl
+	   && comp_template_args (e1->args, e2->args));
+  --comparing_specializations;
+
+  return equal;
 }
 
 /* Returns a hash for a template TMPL and template arguments ARGS.  */
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 18d9a98..0b033c2 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -2580,6 +2580,13 @@  cp_tree_equal (tree t1, tree t2)
 	 with an out-of-class definition of the function, but can also come
 	 up for expressions that involve 'this' in a member function
 	 template.  */
+
+      if (comparing_specializations)
+	/* When comparing hash table entries, only an exact match is
+	   good enough; we don't want to replace 'this' with the
+	   version from another function.  */
+	return false;
+
       if (same_type_p (TREE_TYPE (t1), TREE_TYPE (t2)))
 	{
 	  if (DECL_ARTIFICIAL (t1) ^ DECL_ARTIFICIAL (t2))
diff --git a/gcc/testsuite/g++.dg/template/ptrmem23.C b/gcc/testsuite/g++.dg/template/ptrmem23.C
new file mode 100644
index 0000000..28c0a63
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/ptrmem23.C
@@ -0,0 +1,22 @@ 
+// PR c++/56247
+
+struct Base {
+    void method() {}
+};
+
+typedef void (Base::*MemPtr)();
+
+// Template with a member function pointer "non-type parameter".
+template<MemPtr func>
+struct Wrapper {};
+
+template<class C>
+struct Child : public Base {
+    // Templated derived class instantiates the Wrapper with the same parameter
+    // in two different virtual methods.
+    void foo() { typedef Wrapper<&Base::method> W; }
+    void bar() { typedef Wrapper<&Base::method> W; }
+};
+
+// Instantiate Child with some type.
+template class Child<int>;