This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
C++ PATCHes for c++/43856
- From: Jason Merrill <jason at redhat dot com>
- To: gcc-patches List <gcc-patches at gcc dot gnu dot org>
- Date: Tue, 27 Apr 2010 19:01:04 -0400
- Subject: 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: