This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

C++ PATCHes for c++/43856


43856 is a problem whereby a call that relies on implicit capture of implicit 'this' in a lambda doesn't work properly because we weren't doing that capture for non-static member functions, only data members.

The first patch changes maybe_dummy_object to do implicit 'this' capture as appropriate, and also changes qualify_lookup to ignore op() from a closure type.

The second patch changes a couple other uses of current_class_ref to just use maybe_dummy_object instead, which makes calls with explicit scope work as well.

The third patch changes finish_non_static_data_member to take NULL_TREE to mean "relative to this" and call maybe_dummy_object rather than doing implicit 'this' capture directly. This change also lets us remove some duplicated diagnostic code and fixes a bug in DR 613 support (scoped5.C).

Tested x86_64-pc-linux-gnu, applied to trunk.
commit e97d2125b72beaac4d28f38a13114b2330d1bc6b
Author: jason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>
Date:   Tue Apr 27 21:26:25 2010 +0000

    	PR c++/43856
    	* name-lookup.c (qualify_lookup): Disqualify lambda op().
    	* class.c (current_nonlambda_class_type): New fn.
    	* semantics.c (nonlambda_method_basetype): New.
    	* cp-tree.h: Declare them.
    	* tree.c (maybe_dummy_object): Handle implicit 'this' capture.
    
    git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@158807 138bc75d-0d04-0410-961f-82ee72b054a4

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 07dfb1c..c640af4 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -5971,6 +5971,34 @@ currently_open_derived_class (tree t)
   return NULL_TREE;
 }
 
+/* Returns the innermost class type which is not a lambda closure type.  */
+
+tree
+current_nonlambda_class_type (void)
+{
+  int i;
+
+  /* We start looking from 1 because entry 0 is from global scope,
+     and has no type.  */
+  for (i = current_class_depth; i > 0; --i)
+    {
+      tree c;
+      if (i == current_class_depth)
+	c = current_class_type;
+      else
+	{
+	  if (current_class_stack[i].hidden)
+	    break;
+	  c = current_class_stack[i].type;
+	}
+      if (!c)
+	continue;
+      if (!LAMBDA_TYPE_P (c))
+	return c;
+    }
+  return NULL_TREE;
+}
+
 /* When entering a class scope, all enclosing class scopes' names with
    static meaning (static variables, static functions, types and
    enumerators) have to be visible.  This recursive function calls
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index dd89171..62e92cc 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4580,6 +4580,7 @@ extern void resort_type_method_vec		(void *, void *,
 extern bool add_method				(tree, tree, tree);
 extern bool currently_open_class		(tree);
 extern tree currently_open_derived_class	(tree);
+extern tree current_nonlambda_class_type	(void);
 extern tree finish_struct			(tree, tree);
 extern void finish_struct_1			(tree);
 extern int resolves_to_fixed_type_p		(tree, int *);
@@ -5212,6 +5213,7 @@ extern tree add_capture                         (tree, tree, tree, bool, bool);
 extern tree add_default_capture                 (tree, tree, tree);
 extern void register_capture_members		(tree);
 extern tree lambda_expr_this_capture            (tree);
+extern tree nonlambda_method_basetype		(void);
 extern void maybe_add_lambda_conv_op            (tree);
 
 /* in tree.c */
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index b4ac49f..5586bf7 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -3807,6 +3807,10 @@ qualify_lookup (tree val, int flags)
   if (cp_unevaluated_operand && TREE_CODE (val) == FIELD_DECL
       && DECL_NORMAL_CAPTURE_P (val))
     return false;
+  /* None of the lookups that use qualify_lookup want the op() from the
+     lambda; they want the one from the enclosing class.  */
+  if (TREE_CODE (val) == FUNCTION_DECL && LAMBDA_FUNCTION_P (val))
+    return false;
   return true;
 }
 
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 6bf33c7..7c03959 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -5869,6 +5869,32 @@ lambda_expr_this_capture (tree lambda)
   return result;
 }
 
+/* Returns the method basetype of the innermost non-lambda function, or
+   NULL_TREE if none.  */
+
+tree
+nonlambda_method_basetype (void)
+{
+  tree fn, type;
+  if (!current_class_ref)
+    return NULL_TREE;
+
+  type = current_class_type;
+  if (!LAMBDA_TYPE_P (type))
+    return type;
+
+  /* Find the nearest enclosing non-lambda function.  */
+  fn = TYPE_NAME (type);
+  do
+    fn = decl_function_context (fn);
+  while (fn && LAMBDA_FUNCTION_P (fn));
+
+  if (!fn || !DECL_NONSTATIC_MEMBER_FUNCTION_P (fn))
+    return NULL_TREE;
+
+  return TYPE_METHOD_BASETYPE (TREE_TYPE (fn));
+}
+
 /* If the closure TYPE has a static op(), also add a conversion to function
    pointer.  */
 
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 0abc12c..f8b2c40 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -2293,11 +2293,11 @@ maybe_dummy_object (tree type, tree* binfop)
 {
   tree decl, context;
   tree binfo;
+  tree current = current_nonlambda_class_type ();
 
-  if (current_class_type
-      && (binfo = lookup_base (current_class_type, type,
-			       ba_unique | ba_quiet, NULL)))
-    context = current_class_type;
+  if (current
+      && (binfo = lookup_base (current, type, ba_any, NULL)))
+    context = current;
   else
     {
       /* Reference from a nested class member function.  */
@@ -2315,6 +2315,13 @@ maybe_dummy_object (tree type, tree* binfop)
       && same_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (current_class_ref)),
 		      current_class_type))
     decl = current_class_ref;
+  else if (current != current_class_type
+	   && context == nonlambda_method_basetype ())
+    /* In a lambda, need to go through 'this' capture.  */
+    decl = (cp_build_indirect_ref
+	    ((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-this2.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-this2.C
new file mode 100644
index 0000000..ce4bda4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-this2.C
@@ -0,0 +1,16 @@
+// PR c++/43856
+// Test for implicit 'this' capture via rewriting.
+// { dg-options "-std=c++0x" }
+
+struct S1 {
+  int operator()(int);
+  int i;
+  void g();
+  void f() {
+    [=]() {
+      i;
+      g();
+      operator()(42);
+    };
+  }
+};

commit f765949633861a91dbe3356ad50c8a649effc856
Author: jason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>
Date:   Tue Apr 27 21:27:04 2010 +0000

    	* semantics.c (finish_qualified_id_expr): Use maybe_dummy_object
    	rather than checking current_class_ref directly.
    	(finish_call_expr): Likewise.
    
    git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@158808 138bc75d-0d04-0410-961f-82ee72b054a4

diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 7c03959..7bcd756 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -1690,6 +1690,7 @@ finish_qualified_id_expr (tree qualifying_class,
   else if (BASELINK_P (expr) && !processing_template_decl)
     {
       tree fns;
+      tree ob;
 
       /* See if any of the functions are non-static members.  */
       fns = BASELINK_FUNCTIONS (expr);
@@ -1697,10 +1698,10 @@ finish_qualified_id_expr (tree qualifying_class,
 	fns = TREE_OPERAND (fns, 0);
       /* If so, the expression may be relative to 'this'.  */
       if (!shared_member_p (fns)
-	  && current_class_ref
-	  && DERIVED_FROM_P (qualifying_class, TREE_TYPE (current_class_ref)))
+	  && (ob = maybe_dummy_object (qualifying_class, NULL),
+	      !is_dummy_object (ob)))
 	expr = (build_class_member_access_expr
-		(maybe_dummy_object (qualifying_class, NULL),
+		(ob,
 		 expr,
 		 BASELINK_ACCESS_BINFO (expr),
 		 /*preserve_reference=*/false,
@@ -2002,31 +2003,18 @@ finish_call_expr (tree fn, VEC(tree,gc) **args, bool disallow_virtual,
 	   . operator.... [Otherwise] a contrived object of type T
 	   becomes the implied object argument.
 
-	This paragraph is unclear about this situation:
+	In this situation:
 
 	  struct A { void f(); };
 	  struct B : public A {};
 	  struct C : public A { void g() { B::f(); }};
 
-	In particular, for `B::f', this paragraph does not make clear
-	whether "the class of that member function" refers to `A' or
-	to `B'.  We believe it refers to `B'.  */
-      if (current_class_type
-	  && DERIVED_FROM_P (BINFO_TYPE (BASELINK_ACCESS_BINFO (fn)),
-			     current_class_type)
-	  && current_class_ref)
-	object = maybe_dummy_object (BINFO_TYPE (BASELINK_ACCESS_BINFO (fn)),
-				     NULL);
-      else
-	{
-	  tree representative_fn;
+	"the class of that member function" refers to `A'.  But 11.2
+	[class.access.base] says that we need to convert 'this' to B* as
+	part of the access, so we pass 'B' to maybe_dummy_object.  */
 
-	  representative_fn = BASELINK_FUNCTIONS (fn);
-	  if (TREE_CODE (representative_fn) == TEMPLATE_ID_EXPR)
-	    representative_fn = TREE_OPERAND (representative_fn, 0);
-	  representative_fn = get_first_fn (representative_fn);
-	  object = build_dummy_object (DECL_CONTEXT (representative_fn));
-	}
+      object = maybe_dummy_object (BINFO_TYPE (BASELINK_ACCESS_BINFO (fn)),
+				   NULL);
 
       if (processing_template_decl)
 	{
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-this2.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-this2.C
index ce4bda4..04fe474 100644
--- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-this2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-this2.C
@@ -10,6 +10,7 @@ struct S1 {
     [=]() {
       i;
       g();
+      S1::g();
       operator()(42);
     };
   }

commit 70269a57a8e4b7428eff50077451d4a9648afe70
Author: jason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>
Date:   Tue Apr 27 21:30:56 2010 +0000

    	* semantics.c (finish_non_static_data_member): Call maybe_dummy_object
    	whenever object is NULL_TREE.  Don't do 'this' capture here.
    	(finish_qualified_id_expr): Pass NULL_TREE.
    	(finish_id_expression): Likewise.
    	(lambda_expr_this_capture): Likewise.
    
    git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@158809 138bc75d-0d04-0410-961f-82ee72b054a4

diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 7bcd756..73fed15 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -1424,17 +1424,18 @@ finish_non_static_data_member (tree decl, tree object, tree qualifying_scope)
 {
   gcc_assert (TREE_CODE (decl) == FIELD_DECL);
 
-  if (!object && cp_unevaluated_operand != 0)
+  if (!object)
     {
-      /* DR 613: Can use non-static data members without an associated
-         object in sizeof/decltype/alignof.  */
       tree scope = qualifying_scope;
       if (scope == NULL_TREE)
 	scope = context_for_name_lookup (decl);
       object = maybe_dummy_object (scope, NULL);
     }
 
-  if (!object)
+  /* DR 613: Can use non-static data members without an associated
+     object in sizeof/decltype/alignof.  */
+  if (is_dummy_object (object) && cp_unevaluated_operand == 0
+      && (!processing_template_decl || !current_class_ref))
     {
       if (current_function_decl
 	  && DECL_STATIC_FUNCTION_P (current_function_decl))
@@ -1446,19 +1447,6 @@ finish_non_static_data_member (tree decl, tree object, tree qualifying_scope)
       return error_mark_node;
     }
 
-  /* If decl is a non-capture field and object has a lambda type,
-     then we have a reference to a member of 'this' from a
-     lambda inside a non-static member function, and we must get to decl
-     through the 'this' capture.  If decl is not a member of that object,
-     either, then its access will still fail later.  */
-  if (LAMBDA_TYPE_P (TREE_TYPE (object))
-      && !LAMBDA_TYPE_P (DECL_CONTEXT (decl)))
-    object = cp_build_indirect_ref (lambda_expr_this_capture
-				    (CLASSTYPE_LAMBDA_EXPR
-				     (TREE_TYPE (object))),
-                                    RO_NULL,
-                                    /*complain=*/tf_warning_or_error);
-
   if (current_class_ptr)
     TREE_USED (current_class_ptr) = 1;
   if (processing_template_decl && !qualifying_scope)
@@ -1494,21 +1482,6 @@ finish_non_static_data_member (tree decl, tree object, tree qualifying_scope)
   else
     {
       tree access_type = TREE_TYPE (object);
-      tree lookup_context = context_for_name_lookup (decl);
-
-      while (!DERIVED_FROM_P (lookup_context, access_type))
-	{
-	  access_type = TYPE_CONTEXT (access_type);
-	  while (access_type && DECL_P (access_type))
-	    access_type = DECL_CONTEXT (access_type);
-
-	  if (!access_type)
-	    {
-	      error ("object missing in reference to %q+D", decl);
-	      error ("from this location");
-	      return error_mark_node;
-	    }
-	}
 
       perform_or_defer_access_check (TYPE_BINFO (access_type), decl,
 				     decl);
@@ -1683,7 +1656,7 @@ finish_qualified_id_expr (tree qualifying_class,
   else if (TREE_CODE (expr) == FIELD_DECL)
     {
       push_deferring_access_checks (dk_no_check);
-      expr = finish_non_static_data_member (expr, current_class_ref,
+      expr = finish_non_static_data_member (expr, NULL_TREE,
 					    qualifying_class);
       pop_deferring_access_checks ();
     }
@@ -3062,7 +3035,7 @@ finish_id_expression (tree id_expression,
 		 already.  Turn off checking to avoid duplicate errors.  */
 	      push_deferring_access_checks (dk_no_check);
 	      decl = finish_non_static_data_member
-		       (decl, current_class_ref,
+		       (decl, NULL_TREE,
 			/*qualifying_scope=*/NULL_TREE);
 	      pop_deferring_access_checks ();
 	      return decl;
@@ -3143,7 +3116,7 @@ finish_id_expression (tree id_expression,
 	     Access checking has been performed during name lookup
 	     already.  Turn off checking to avoid duplicate errors.  */
 	  push_deferring_access_checks (dk_no_check);
-	  decl = finish_non_static_data_member (decl, current_class_ref,
+	  decl = finish_non_static_data_member (decl, NULL_TREE,
 						/*qualifying_scope=*/NULL_TREE);
 	  pop_deferring_access_checks ();
 	}
@@ -5844,7 +5817,7 @@ lambda_expr_this_capture (tree lambda)
       gcc_assert (TYPE_MAIN_VARIANT (TREE_TYPE (current_class_ref)) == TREE_TYPE (lambda));
 
       result = finish_non_static_data_member (this_capture,
-                                              current_class_ref,
+                                              NULL_TREE,
                                               /*qualifying_scope=*/NULL_TREE);
 
       /* If 'this' is captured, each use of 'this' is transformed into an
diff --git a/gcc/testsuite/g++.dg/lookup/scoped5.C b/gcc/testsuite/g++.dg/lookup/scoped5.C
index 37ac164..a4aa729 100644
--- a/gcc/testsuite/g++.dg/lookup/scoped5.C
+++ b/gcc/testsuite/g++.dg/lookup/scoped5.C
@@ -9,11 +9,11 @@ class A {
 public:
   class B {
   public:
-    int a;			// { dg-error "object missing" }
+    int a;
   };
 };
 
 class C {
 public:
-  void f(void) { sizeof(A::B::a); } // { dg-error "this location" }
+  void f(void) { sizeof(A::B::a); }
 };
diff --git a/gcc/testsuite/g++.dg/lookup/scoped8.C b/gcc/testsuite/g++.dg/lookup/scoped8.C
index 2ba28a6..2764f75 100644
--- a/gcc/testsuite/g++.dg/lookup/scoped8.C
+++ b/gcc/testsuite/g++.dg/lookup/scoped8.C
@@ -7,7 +7,7 @@
 
 struct A
 {
-    int i;			// { dg-error "object missing" }
+    int i;			// { dg-error "non-static" }
 };
 
 template <int> struct B
diff --git a/gcc/testsuite/g++.dg/template/dependent-expr5.C b/gcc/testsuite/g++.dg/template/dependent-expr5.C
index 64e86c7..db67273 100644
--- a/gcc/testsuite/g++.dg/template/dependent-expr5.C
+++ b/gcc/testsuite/g++.dg/template/dependent-expr5.C
@@ -18,7 +18,7 @@ template<class F, class T> void bindb(F (T::*f)(void)) {} // { dg-message "note"
 
 struct foo {
   static int baist;
-  int bait;
+  int bait;			// { dg-error "non-static data member" }
   void barf ();
   static void barf (int);
 
@@ -31,7 +31,7 @@ struct foo {
     bar() {
       bind (&baist);
       bind (&foo::baist);
-      bind (&bait); // { dg-error "nonstatic data member" }
+      bind (&bait); // { dg-error "from this location" }
       bind (&foo::bait);
 
       bind (&baikst);
@@ -75,7 +75,7 @@ struct foo {
     barT() {
       bind (&baist);
       bind (&foo::baist);
-      bind (&bait); // { dg-error "nonstatic data member" }
+      bind (&bait); // { dg-error "from this location" }
       bind (&foo::bait);
 
       bind (&baikst);
diff --git a/gcc/testsuite/g++.old-deja/g++.brendan/nest1.C b/gcc/testsuite/g++.old-deja/g++.brendan/nest1.C
index 7763538..842b2f6 100644
--- a/gcc/testsuite/g++.old-deja/g++.brendan/nest1.C
+++ b/gcc/testsuite/g++.old-deja/g++.brendan/nest1.C
@@ -3,7 +3,7 @@
 int x;
 class enclose {
 public:
-  int x;
+  int x;			// { dg-error "non-static" }
 
   class inner {
   public:


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]