re PR c++/61636 (generic lambda: segfault / "cannot call member function without...
authorNathan Sidwell <nathan@acm.org>
Tue, 17 Jan 2017 18:22:34 +0000 (18:22 +0000)
committerNathan Sidwell <nathan@gcc.gnu.org>
Tue, 17 Jan 2017 18:22:34 +0000 (18:22 +0000)
PR c++/61636
* cp-tree.h (maybe_generic_this_capture): Declare.
* lambda.c (resolvable_dummy_lambda): New, broken out of ...
(maybe_resolve_dummy): ... here.  Call it.
(maybe_generic_this_capture): New.
* parser.c (cp_parser_postfix_expression): Speculatively capture
this in generic lambda in unresolved member function call.
* pt.c (tsubst_copy_and_build): Force hard error from failed
member function lookup in generic lambda.

PR c++/61636
* g++.dg/cpp1y/pr61636-1.C: New.
* g++.dg/cpp1y/pr61636-2.C: New.
* g++.dg/cpp1y/pr61636-3.C: New.

From-SVN: r244544

gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/lambda.c
gcc/cp/parser.c
gcc/cp/pt.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp1y/pr61636-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/pr61636-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/pr61636-3.C [new file with mode: 0644]

index 1ea7b28454f11ed0a69729600f46ca736e46a67c..d24b2ae23ad39d6d2c33724be6c618d8cfe30ac5 100644 (file)
@@ -1,3 +1,15 @@
+2017-01-17  Nathan Sidwell  <nathan@acm.org>
+
+       PR c++/61636
+       * cp-tree.h (maybe_generic_this_capture): Declare.
+       * lambda.c (resolvable_dummy_lambda): New, broken out of ...
+       (maybe_resolve_dummy): ... here.  Call it.
+       (maybe_generic_this_capture): New.
+       * parser.c (cp_parser_postfix_expression): Speculatively capture
+       this in generic lambda in unresolved member function call.
+       * pt.c (tsubst_copy_and_build): Force hard error from failed
+       member function lookup in generic lambda.
+
 2017-01-17  Aldy Hernandez  <aldyh@redhat.com>
 
        PR c++/70565
index 24de3462bb7711ba0880038e627561bc01a32c6d..0c8f147c6b24ca6bd1e9ec6cbd3ffe3b64bc3eb3 100644 (file)
@@ -6551,6 +6551,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, bool);
+extern void maybe_generic_this_capture         (tree, tree);
 extern tree maybe_resolve_dummy                        (tree, bool);
 extern tree current_nonlambda_function         (void);
 extern tree nonlambda_method_basetype          (void);
index 98fdb740a742f3366d95fe3db5555413586c5359..4d22c3d37d98c365e45500f17aab1a64aefa5ae1 100644 (file)
@@ -793,16 +793,14 @@ lambda_expr_this_capture (tree lambda, bool add_capture_p)
   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.  */
+/* Return the current LAMBDA_EXPR, if this is a resolvable dummy
+   object.  NULL otherwise..  */
 
-tree
-maybe_resolve_dummy (tree object, bool add_capture_p)
+static tree
+resolvable_dummy_lambda (tree object)
 {
   if (!is_dummy_object (object))
-    return object;
+    return NULL_TREE;
 
   tree type = TYPE_MAIN_VARIANT (TREE_TYPE (object));
   gcc_assert (!TYPE_PTR_P (type));
@@ -812,18 +810,55 @@ maybe_resolve_dummy (tree object, bool add_capture_p)
       && LAMBDA_TYPE_P (current_class_type)
       && lambda_function (current_class_type)
       && DERIVED_FROM_P (type, current_nonlambda_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, add_capture_p);
-      if (cap && cap != error_mark_node)
+    return CLASSTYPE_LAMBDA_EXPR (current_class_type);
+
+  return NULL_TREE;
+}
+
+/* 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, bool add_capture_p)
+{
+  if (tree lam = resolvable_dummy_lambda (object))
+    if (tree cap = lambda_expr_this_capture (lam, add_capture_p))
+      if (cap != error_mark_node)
        object = build_x_indirect_ref (EXPR_LOCATION (object), cap,
                                       RO_NULL, tf_warning_or_error);
-    }
 
   return object;
 }
 
+/* When parsing a generic lambda containing an argument-dependent
+   member function call we defer overload resolution to instantiation
+   time.  But we have to know now whether to capture this or not.
+   Do that if FNS contains any non-static fns.
+   The std doesn't anticipate this case, but I expect this to be the
+   outcome of discussion.  */
+
+void
+maybe_generic_this_capture (tree object, tree fns)
+{
+  if (tree lam = resolvable_dummy_lambda (object))
+    if (!LAMBDA_EXPR_THIS_CAPTURE (lam))
+      {
+       /* We've not yet captured, so look at the function set of
+          interest.  */
+       if (BASELINK_P (fns))
+         fns = BASELINK_FUNCTIONS (fns);
+       for (; fns; fns = OVL_NEXT (fns))
+         if (DECL_NONSTATIC_MEMBER_FUNCTION_P (OVL_CURRENT (fns)))
+           {
+             /* Found a non-static member.  Capture this.  */
+             lambda_expr_this_capture (lam, true);
+             break;
+           }
+      }
+}
+
 /* Returns the innermost non-lambda function.  */
 
 tree
index 295c450507d0082b77e89ef1841bd38525417129..6d3b8777cf2897e402dcabdbc6119d112e6e173a 100644 (file)
@@ -6971,6 +6971,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
                        || type_dependent_expression_p (fn)
                        || any_type_dependent_arguments_p (args)))
                  {
+                   maybe_generic_this_capture (instance, fn);
                    postfix_expression
                      = build_nt_call_vec (postfix_expression, args);
                    release_tree_vector (args);
index dec7d39f0d092fd31e5e47364748d3a7c5660623..022ffda9ed28337676b4a70042e5a09c912180d7 100644 (file)
@@ -17142,19 +17142,34 @@ tsubst_copy_and_build (tree t,
 
                if (unq != function)
                  {
-                   tree fn = unq;
-                   if (INDIRECT_REF_P (fn))
-                     fn = TREE_OPERAND (fn, 0);
-                   if (TREE_CODE (fn) == COMPONENT_REF)
-                     fn = TREE_OPERAND (fn, 1);
-                   if (is_overloaded_fn (fn))
-                     fn = get_first_fn (fn);
-                   if (permerror (EXPR_LOC_OR_LOC (t, input_location),
-                                  "%qD was not declared in this scope, "
-                                  "and no declarations were found by "
-                                  "argument-dependent lookup at the point "
-                                  "of instantiation", function))
+                   /* In a lambda fn, we have to be careful to not
+                      introduce new this captures.  Legacy code can't
+                      be using lambdas anyway, so it's ok to be
+                      stricter.  */
+                   bool in_lambda = (current_class_type
+                                     && LAMBDA_TYPE_P (current_class_type));
+                   char const *msg = "%qD was not declared in this scope, "
+                     "and no declarations were found by "
+                     "argument-dependent lookup at the point "
+                     "of instantiation";
+
+                   bool diag = true;
+                   if (in_lambda)
+                     error_at (EXPR_LOC_OR_LOC (t, input_location),
+                               msg, function);
+                   else
+                     diag = permerror (EXPR_LOC_OR_LOC (t, input_location),
+                                       msg, function);
+                   if (diag)
                      {
+                       tree fn = unq;
+                       if (INDIRECT_REF_P (fn))
+                         fn = TREE_OPERAND (fn, 0);
+                       if (TREE_CODE (fn) == COMPONENT_REF)
+                         fn = TREE_OPERAND (fn, 1);
+                       if (is_overloaded_fn (fn))
+                         fn = get_first_fn (fn);
+
                        if (!DECL_P (fn))
                          /* Can't say anything more.  */;
                        else if (DECL_CLASS_SCOPE_P (fn))
@@ -17177,7 +17192,13 @@ tsubst_copy_and_build (tree t,
                          inform (DECL_SOURCE_LOCATION (fn),
                                  "%qD declared here, later in the "
                                  "translation unit", fn);
+                       if (in_lambda)
+                         {
+                           release_tree_vector (call_args);
+                           RETURN (error_mark_node);
+                         }
                      }
+
                    function = unq;
                  }
              }
index 1f6425aa500106fe5366d7908099029260a184df..8697c8a1f3808770aa421ac42242dbe6b88f8925 100644 (file)
@@ -1,3 +1,10 @@
+2017-01-17  Nathan Sidwell  <nathan@acm.org>
+
+       PR c++/61636
+       * g++.dg/cpp1y/pr61636-1.C: New.
+       * g++.dg/cpp1y/pr61636-2.C: New.
+       * g++.dg/cpp1y/pr61636-3.C: New.
+
 2017-01-17  Martin Sebor  <msebor@redhat.com>
 
        PR testsuite/79115
 
        PR target/79004
        * gcc.target/powerpc/pr79004.c: New test.
+
 2017-01-12  Martin Sebor  <msebor@redhat.com>
 
        * gcc.dg/pr78138.c: Adjust.
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr61636-1.C b/gcc/testsuite/g++.dg/cpp1y/pr61636-1.C
new file mode 100644 (file)
index 0000000..9426d5f
--- /dev/null
@@ -0,0 +1,31 @@
+// PR c++/61636
+// { dg-do compile { target c++14 } }
+
+// ICE because we figure this capture too late.
+
+struct Base
+{
+  void Bar (int);
+};
+
+struct A : Base {
+  void b ();
+  void Foo (int);
+  using Base::Bar;
+  template <typename T> void Baz (T);
+};
+
+void A::b() {
+
+  auto lam = [&](auto asdf) { Foo (asdf); };
+
+  lam (0);
+
+  auto lam1 = [&](auto asdf) { Bar (asdf); };
+
+  lam1 (0);
+
+  auto lam2 = [&](auto asdf) { Baz (asdf); };
+
+  lam2 (0);
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr61636-2.C b/gcc/testsuite/g++.dg/cpp1y/pr61636-2.C
new file mode 100644 (file)
index 0000000..a1bd597
--- /dev/null
@@ -0,0 +1,72 @@
+// PR c++/61636
+// { dg-do run { target c++14 } }
+
+// Check we don't capture this (too) unnecessarily
+
+struct A {
+  int b ();
+  void f (int) {}
+  static void f (double) {}
+
+  static void g (int) {}
+  static void g (double) {}
+};
+
+struct O {
+  void x (int) {}
+  static void x (double) {}
+};
+
+namespace N {
+  void y (double) {}
+}
+
+int Check (bool expect, unsigned size)
+{
+  return (expect ? sizeof (void *) : 1) != size;
+}
+
+int A::b() {
+  int r = 0;
+
+  // one of the functions is non-static
+  auto l0 = [&](auto z) { f (z); };
+  r += Check (true, sizeof l0);
+  l0(0.0); // doesn't need this capture for A::f(double), but too late
+  l0 (0); // Needs this capture for A::f(int)
+
+  // no fn is non-static.
+  auto l00 = [&](auto z) { g (z); };
+  r += Check (false, sizeof l00);
+  l00(0.0); 
+  l00 (0);
+
+  // sizeof isn't an evaluation context, so no this capture
+  auto l1 = [&](auto z) { sizeof (f (z), 1); };
+  r += Check (false, sizeof l1);
+  l1(0.0); l1 (0); 
+
+  auto l2 = [&](auto) { f (2.4); };
+  auto l3 = [&](auto) { f (0); };
+  l2(0); l3(0); l2(0.0); l3 (0.0);
+  r += Check (false, sizeof l2);
+  r += Check (true, sizeof l3);
+
+  auto l4 = [&](auto) { O::x (2.4); };
+  auto l5 = [&](auto) { N::y (2.4); };
+  auto l6 = [&](auto) { };
+  l4(0); l5(0); l6(0);
+  l4(0.0); l5(0.0); l6(0.0);
+  r += Check (false, sizeof l4);
+  r += Check (false, sizeof l5);
+  r += Check (false, sizeof l6);
+
+  return r;
+}
+
+int main ()
+{
+  A a;
+
+  return a.b () ? 1 : 0;
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr61636-3.C b/gcc/testsuite/g++.dg/cpp1y/pr61636-3.C
new file mode 100644 (file)
index 0000000..18f83fe
--- /dev/null
@@ -0,0 +1,25 @@
+// PR c++/61636
+// { dg-do compile { target c++14 } }
+// permissiveness doesn't make this permitted
+// { dg-additional-options "-fpermissive" }
+
+// ICE because we attempt to use dependent Foo during error recovery
+// and die with an unexpected this capture need.
+
+template <typename T> struct Base
+{
+  void Foo (int);
+};
+
+template <typename T> struct A : Base<T> {
+  void b ();
+};
+
+template <typename T> void A<T>::b() {
+
+  auto lam = [&](auto asdf) { Foo (asdf); }; // { dg-error "not declared" }
+
+  lam (T(0));
+}
+
+template void A<int>::b ();