Patchwork C++ PATCH for various lambda bugs (51494, 51884, 56222, 51884)

login
register
mail settings
Submitter Jason Merrill
Date March 8, 2013, 4:03 p.m.
Message ID <513A0BE3.8000501@redhat.com>
Download mbox | patch
Permalink /patch/226161/
State New
Headers show

Comments

Jason Merrill - March 8, 2013, 4:03 p.m.
The first two patches deal with issues with 'this' capture; we were 
inappropriately capturing 'this' for uses of static members and forming 
pointers to members, and for uses in unevaluated context.  The last 
patch avoids a crash from recursive use of dfs_walk when instantiating a 
class with virtual functions using a local class whose scope involves a 
qualified-id.

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

Patch

commit 58322f8d0b0892ac66d184cc933655e135d324af
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Mar 5 12:09:52 2013 -0500

    	PR c++/51494
    	PR c++/51884
    	PR c++/56222
    	* tree.c (maybe_dummy_object): Don't capture 'this'.
    	* semantics.c (maybe_resolve_dummy): New.
    	(finish_non_static_data_member): Use it.
    	(finish_qualified_id_expr): Don't test is_dummy_object.
    	* cp-tree.h: Declare maybe_resolve_dummy.
    	* call.c (build_new_method_call_1): Use it.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 4eb38ec..530835b 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -7627,6 +7627,7 @@  build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,
       else
 	{
 	  fn = cand->fn;
+	  call = NULL_TREE;
 
 	  if (!(flags & LOOKUP_NONVIRTUAL)
 	      && DECL_PURE_VIRTUAL_P (fn)
@@ -7644,12 +7645,26 @@  build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,
 	  if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE
 	      && is_dummy_object (instance_ptr))
 	    {
-	      if (complain & tf_error)
-		error ("cannot call member function %qD without object",
-		       fn);
-	      call = error_mark_node;
+	      instance = maybe_resolve_dummy (instance);
+	      if (instance == error_mark_node)
+		call = error_mark_node;
+	      else if (!is_dummy_object (instance))
+		{
+		  /* We captured 'this' in the current lambda now that
+		     we know we really need it.  */
+		  instance_ptr = build_this (instance);
+		  cand->first_arg = instance_ptr;
+		}
+	      else
+		{
+		  if (complain & tf_error)
+		    error ("cannot call member function %qD without object",
+			   fn);
+		  call = error_mark_node;
+		}
 	    }
-	  else
+
+	  if (call != error_mark_node)
 	    {
 	      /* Optimize away vtable lookup if we know that this
 		 function can't be overridden.  We need to check if
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 4a597d8..c3b2aec 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5722,6 +5722,7 @@  extern bool is_capture_proxy			(tree);
 extern bool is_normal_capture_proxy             (tree);
 extern void register_capture_members		(tree);
 extern tree lambda_expr_this_capture            (tree);
+extern tree maybe_resolve_dummy			(tree);
 extern tree nonlambda_method_basetype		(void);
 extern void maybe_add_lambda_conv_op            (tree);
 extern bool is_lambda_ignored_entity            (tree);
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index d605de9..d11a4e4 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -1544,6 +1544,7 @@  finish_non_static_data_member (tree decl, tree object, tree qualifying_scope)
       object = maybe_dummy_object (scope, NULL);
     }
 
+  object = maybe_resolve_dummy (object);
   if (object == error_mark_node)
     return error_mark_node;
 
@@ -1778,15 +1779,14 @@  finish_qualified_id_expr (tree qualifying_class,
     }
   else if (BASELINK_P (expr) && !processing_template_decl)
     {
-      tree ob;
-
       /* See if any of the functions are non-static members.  */
       /* If so, the expression may be relative to 'this'.  */
       if (!shared_member_p (expr)
-	  && (ob = maybe_dummy_object (qualifying_class, NULL),
-	      !is_dummy_object (ob)))
+	  && current_class_ptr
+	  && DERIVED_FROM_P (qualifying_class,
+			     current_nonlambda_class_type ()))
 	expr = (build_class_member_access_expr
-		(ob,
+		(maybe_dummy_object (qualifying_class, NULL),
 		 expr,
 		 BASELINK_ACCESS_BINFO (expr),
 		 /*preserve_reference=*/false,
@@ -9534,6 +9534,34 @@  lambda_expr_this_capture (tree lambda)
   return result;
 }
 
+/* We don't want to capture 'this' until we know we need it, i.e. after
+   overload resolution has chosen a non-static member function.  At that
+   point we call this function to turn a dummy object into a use of the
+   'this' capture.  */
+
+tree
+maybe_resolve_dummy (tree object)
+{
+  if (!is_dummy_object (object))
+    return object;
+
+  tree type = TYPE_MAIN_VARIANT (TREE_TYPE (object));
+  gcc_assert (TREE_CODE (type) != POINTER_TYPE);
+
+  if (type != current_class_type
+      && current_class_type
+      && LAMBDA_TYPE_P (current_class_type))
+    {
+      /* In a lambda, need to go through 'this' capture.  */
+      tree lam = CLASSTYPE_LAMBDA_EXPR (current_class_type);
+      tree cap = lambda_expr_this_capture (lam);
+      object = build_x_indirect_ref (EXPR_LOCATION (object), cap,
+				     RO_NULL, tf_warning_or_error);
+    }
+
+  return object;
+}
+
 /* Returns the method basetype of the innermost non-lambda function, or
    NULL_TREE if none.  */
 
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index b57b44a..178b80a 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -2863,13 +2863,6 @@  maybe_dummy_object (tree type, tree* binfop)
       && (same_type_ignoring_top_level_qualifiers_p
 	  (TREE_TYPE (current_class_ref), context)))
     decl = current_class_ref;
-  else if (current != current_class_type
-	   && context == nonlambda_method_basetype ())
-    /* In a lambda, need to go through 'this' capture.  */
-    decl = (build_x_indirect_ref
-	    (input_location, (lambda_expr_this_capture
-			      (CLASSTYPE_LAMBDA_EXPR (current_class_type))),
-	     RO_NULL, tf_warning_or_error));
   else
     decl = build_dummy_object (context);
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-this11.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-this11.C
new file mode 100644
index 0000000..2618295
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-this11.C
@@ -0,0 +1,22 @@ 
+// PR c++/51494, c++/56222
+// Uses of static members and creating pointers to members aren't odr-uses
+// of 'this'.
+// { dg-do compile { target c++11 } }
+
+struct A
+{
+  static void f() {}
+  static int i;
+  int j;
+  void f(int);
+
+  void foo()
+  {
+    [] () {
+      ++i;
+      f();
+      &A::j;
+      (void(*)())&A::f;
+    };
+  }
+};

commit 977321fdf969a16e3546e044bc2c1404b0565804
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Mar 5 12:03:06 2013 -0500

    	* semantics.c (lambda_expr_this_capture): In unevaluated context,
    	just return the nearest 'this'.

diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index d11a4e4..233765a 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -9513,6 +9513,11 @@  lambda_expr_this_capture (tree lambda)
 
   if (!this_capture)
     {
+      /* In unevaluated context this isn't an odr-use, so just return the
+	 nearest 'this'.  */
+      if (cp_unevaluated_operand)
+	return lookup_name (this_identifier);
+
       error ("%<this%> was not captured for this lambda function");
       result = error_mark_node;
     }
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-this12.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-this12.C
new file mode 100644
index 0000000..ef573b1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-this12.C
@@ -0,0 +1,13 @@ 
+// Uses of 'this' in unevaluated context are not odr-uses.
+// { dg-do compile { target c++11 } }
+
+struct A
+{
+  int f() {}
+  int i;
+
+  void foo()
+  {
+    [] () { sizeof(i); sizeof(f()); };
+  }
+};

commit a5f8c7b4fb99d4f02201cd7eaa0bac518b2a4695
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Mar 6 09:27:22 2013 -0500

    	PR c++/51884
    	* class.c (modify_all_vtables): Mangle the vtable name before
    	entering dfs_walk.

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 2a0351f..746c29d 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -2541,6 +2541,10 @@  modify_all_vtables (tree t, tree virtuals)
   tree binfo = TYPE_BINFO (t);
   tree *fnsp;
 
+  /* Mangle the vtable name before entering dfs_walk (c++/51884).  */
+  if (TYPE_CONTAINS_VPTR_P (t))
+    get_vtable_decl (t, false);
+
   /* Update all of the vtables.  */
   dfs_walk_once (binfo, dfs_modify_vtables, NULL, t);
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/local-targ1.C b/gcc/testsuite/g++.dg/cpp0x/local-targ1.C
new file mode 100644
index 0000000..588149a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/local-targ1.C
@@ -0,0 +1,31 @@ 
+// PR c++/51884
+// { dg-do compile { target c++11 } }
+// { dg-final { scan-assembler "_ZN1BIZN3fooIivE3barILb1EEEvvE1CEC1ERKS4_" } }
+
+template<typename TT>
+  struct test { static const int value = 0; };
+template<int I>
+  struct enable_if { typedef void type; };
+
+struct A { virtual void f() {} };
+template<typename U> struct B : A { B(); B(const B&); };
+template<typename U> B<U>::B() { }
+template<typename U> B<U>::B(const B&) { }
+
+template<class T> void g(T) { }
+
+template<typename T, typename = void> struct foo;
+template<typename T>
+struct foo<T,typename enable_if<test<T>::value>::type>
+{
+  template <bool P> void bar() {
+    struct C { } c;
+    B<C> b;
+    g(b);
+  }
+};
+
+int main() {
+  foo<int> f;
+  f.bar<true>();
+}