diff mbox series

C++ PATCH for c++/81236, missed 'this' capture with template-id in generic lambda

Message ID CADzB+2ntf4kfg4A0eJ7KY-i+Szj0TMud1gtptOjyNN-+FJpCNw@mail.gmail.com
State New
Headers show
Series C++ PATCH for c++/81236, missed 'this' capture with template-id in generic lambda | expand

Commit Message

Jason Merrill Aug. 29, 2017, 9:37 p.m. UTC
The problem here was that normally in finish_id_expression we add
'this->' to a mention of a non-static member, but there is special
code for doing less if the lookup result is dependent, and so we
didn't get that processing.

A few months back Adam fixed 64382 by deciding to do all the normal
processing if we're in a generic lambda in a template, but in this
testcase the generic lambda isn't in a template.

We could approach this by extending that change to all generic
lambdas, and that might be appropriate for GCC 7, but it seems to me
that this approach will just mean any problems with doing all the
normal processing in a template will remain latent until someone
happens to use them in a generic lambda; instead, this patch removes
the template special case and fixes the normal code to work properly
in templates.

The cp_parser_postfix_dot_deref_expression hunk was a latent
diagnostic quality issue: there's no point in trying to be permissive
about an incomplete type like void, since it can't be completed by
instantiation time.

The cp_walk_subtrees hunk was necessary to make abi-tag21.C work with
this change; without walking into the scope of the BASELINK, we didn't
see the explicit scope in the return type of fv.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit 28df39b2192b5feeffa6dd2b8208cf0270ac5330
Author: Jason Merrill <jason@redhat.com>
Date:   Thu Aug 24 14:09:01 2017 -0400

            PR c++/81236 - ICE with template-id in generic lambda
    
            * semantics.c (finish_id_expression): Remove special dependent case.
            Avoid some later pieces when dependent.
            (finish_qualified_id_expr): Do normal BASELINK handling in a
            template.  Always build a SCOPE_REF for a destructor BIT_NOT_EXPR.
            (parsing_default_capturing_generic_lambda_in_template): Remove.
            * parser.c (cp_parser_postfix_dot_deref_expression): Always give an
            error for types that will never be complete.
            * mangle.c (write_expression): Add sanity check.
            * tree.c (build_qualified_name): Add sanity check.
            (cp_walk_subtrees): Walk into the class context of a BASELINK.
            * lambda.c (add_capture): Improve diagnostic for generic lambda
            capture failure.
            * call.c (build_new_method_call_1): Print the right constructor
            name.

Comments

Jason Merrill March 15, 2018, 3:07 a.m. UTC | #1
On Tue, Aug 29, 2017 at 5:37 PM, Jason Merrill <jason@redhat.com> wrote:
> We could approach this by extending that change to all generic
> lambdas, and that might be appropriate for GCC 7, but it seems to me
> that this approach will just mean any problems with doing all the
> normal processing in a template will remain latent until someone
> happens to use them in a generic lambda; instead, this patch removes
> the template special case and fixes the normal code to work properly
> in templates.

I noticed today that this caused a regression on the attached
testcases, because we weren't updating the type of the BASELINK after
instantiating the auto function.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit d1d91f146d88b9d7442cce1e03edca55693de139
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Mar 14 16:27:08 2018 -0400

            PR c++/81236 - auto variable and auto function
    
            * pt.c (tsubst_baselink): Update the type of the BASELINK after
            mark_used.

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 14321816cde..2ea5fc79a2c 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -14700,9 +14700,16 @@ tsubst_baselink (tree baselink, tree object_type,
   /* If lookup found a single function, mark it as used at this point.
      (If lookup found multiple functions the one selected later by
      overload resolution will be marked as used at that point.)  */
-  if (!template_id_p && !really_overloaded_fn (fns)
-      && !mark_used (OVL_FIRST (fns), complain) && !(complain & tf_error))
-    return error_mark_node;
+  if (!template_id_p && !really_overloaded_fn (fns))
+    {
+      tree fn = OVL_FIRST (fns);
+      bool ok = mark_used (fn, complain);
+      if (!ok && !(complain & tf_error))
+	return error_mark_node;
+      if (ok && BASELINK_P (baselink))
+	/* We might have instantiated an auto function.  */
+	TREE_TYPE (baselink) = TREE_TYPE (fn);
+    }
 
   if (BASELINK_P (baselink))
     {
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn48.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn48.C
new file mode 100644
index 00000000000..bf9448e793e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn48.C
@@ -0,0 +1,15 @@
+// { dg-do compile { target c++14 } }
+
+template <class T> struct A
+{
+  static auto fn() { }
+  static void f()
+  {
+    auto x = fn;
+  }
+};
+
+int main()
+{
+  A<int>::f();
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn49.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn49.C
new file mode 100644
index 00000000000..d2e490604a7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn49.C
@@ -0,0 +1,12 @@
+// CWG issue 2335
+// { dg-do compile { target c++14 } }
+
+template <class... Ts> struct partition_indices {
+  static auto compute_right () {}
+  static constexpr auto right = compute_right;
+};
+auto foo () -> partition_indices<>;
+void f() {
+  auto x = foo();
+  auto y = x.right;
+}
diff mbox series

Patch

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index f7f9297..c446057 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -9007,6 +9007,7 @@  build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,
       if (! (complain & tf_error))
 	return error_mark_node;
 
+      basetype = DECL_CONTEXT (fn);
       name = constructor_name (basetype);
       if (permerror (input_location,
 		     "cannot call constructor %<%T::%D%> directly",
diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index 4747a72..9ba3df1 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -590,7 +590,11 @@  add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p,
   /* Add it to the appropriate closure class if we've started it.  */
   if (current_class_type
       && current_class_type == LAMBDA_EXPR_CLOSURE (lambda))
-    finish_member_declaration (member);
+    {
+      if (COMPLETE_TYPE_P (current_class_type))
+	internal_error ("trying to capture %qD after closure is complete", id);
+      finish_member_declaration (member);
+    }
 
   tree listmem = member;
   if (variadic)
diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c
index a87f97f..ce7c0c5 100644
--- a/gcc/cp/mangle.c
+++ b/gcc/cp/mangle.c
@@ -3015,6 +3015,7 @@  write_expression (tree expr)
 	{
 	  scope = TREE_OPERAND (expr, 0);
 	  member = TREE_OPERAND (expr, 1);
+	  gcc_assert (!BASELINK_P (member));
 	}
       else
 	{
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index d0d71fa..47d91bf 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -7446,11 +7446,14 @@  cp_parser_postfix_dot_deref_expression (cp_parser *parser,
 	      /* In a template, be permissive by treating an object expression
 		 of incomplete type as dependent (after a pedwarn).  */
 	      diagnostic_t kind = (processing_template_decl
+				   && MAYBE_CLASS_TYPE_P (scope)
 				   ? DK_PEDWARN
 				   : DK_ERROR);
 	      cxx_incomplete_type_diagnostic
 		(location_of (postfix_expression),
 		 postfix_expression, scope, kind);
+	      if (!MAYBE_CLASS_TYPE_P (scope))
+		return error_mark_node;
 	      if (processing_template_decl)
 		{
 		  dependent_p = true;
@@ -20671,33 +20674,6 @@  parsing_nsdmi (void)
   return false;
 }
 
-/* Return true iff our current scope is a default capturing generic lambda
-   defined within a template.  FIXME: This is part of a workaround (see
-   semantics.c) to handle building lambda closure types correctly in templates
-   which we ultimately want to defer to instantiation time. */
-
-bool
-parsing_default_capturing_generic_lambda_in_template (void)
-{
-  if (!processing_template_decl || !current_class_type)
-    return false;
-
-  tree lam = CLASSTYPE_LAMBDA_EXPR (current_class_type);
-  if (!lam || LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lam) == CPLD_NONE)
-    return false;
-
-  tree callop = lambda_function (lam);
-  if (!callop)
-    return false;
-
-  return (DECL_TEMPLATE_INFO (callop)
-	  && (DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (callop)) == callop)
-	  && ((current_nonlambda_class_type ()
-	       && CLASSTYPE_TEMPLATE_INFO (current_nonlambda_class_type ()))
-	      || ((current_nonlambda_function ()
-		   && DECL_TEMPLATE_INFO (current_nonlambda_function ())))));
-}
-
 /* Parse a late-specified return type, if any.  This is not a separate
    non-terminal, but part of a function declarator, which looks like
 
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 8f28221..b2e58d2 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2035,7 +2035,7 @@  finish_qualified_id_expr (tree qualifying_class,
 					    qualifying_class);
       pop_deferring_access_checks ();
     }
-  else if (BASELINK_P (expr) && !processing_template_decl)
+  else if (BASELINK_P (expr))
     {
       /* See if any of the functions are non-static members.  */
       /* If so, the expression may be relative to 'this'.  */
@@ -2055,8 +2055,6 @@  finish_qualified_id_expr (tree qualifying_class,
 	expr = build_offset_ref (qualifying_class, expr, /*address_p=*/false,
 				 complain);
     }
-  else if (BASELINK_P (expr))
-    ;
   else
     {
       /* In a template, return a SCOPE_REF for most qualified-ids
@@ -2065,7 +2063,8 @@  finish_qualified_id_expr (tree qualifying_class,
 	 know we have access and building up the SCOPE_REF confuses
 	 non-type template argument handling.  */
       if (processing_template_decl
-	  && !currently_open_class (qualifying_class))
+	  && (!currently_open_class (qualifying_class)
+	      || TREE_CODE (expr) == BIT_NOT_EXPR))
 	expr = build_qualified_name (TREE_TYPE (expr),
 				     qualifying_class, expr,
 				     template_p);
@@ -3595,78 +3594,12 @@  finish_id_expression (tree id_expression,
 		    ? CP_ID_KIND_UNQUALIFIED_DEPENDENT
 		    : CP_ID_KIND_UNQUALIFIED)));
 
-      /* If the name was dependent on a template parameter and we're not in a
-	 default capturing generic lambda within a template, we will resolve the
-	 name at instantiation time.  FIXME: For lambdas, we should defer
-	 building the closure type until instantiation time then we won't need
-	 the extra test here.  */
       if (dependent_p
-	  && !parsing_default_capturing_generic_lambda_in_template ())
-	{
-	  if (DECL_P (decl)
-	      && any_dependent_type_attributes_p (DECL_ATTRIBUTES (decl)))
-	    /* Dependent type attributes on the decl mean that the TREE_TYPE is
-	       wrong, so just return the identifier.  */
-	    return id_expression;
-
-	  /* If we found a variable, then name lookup during the
-	     instantiation will always resolve to the same VAR_DECL
-	     (or an instantiation thereof).  */
-	  if (VAR_P (decl)
-	      || TREE_CODE (decl) == CONST_DECL
-	      || TREE_CODE (decl) == PARM_DECL)
-	    {
-	      mark_used (decl);
-	      return convert_from_reference (decl);
-	    }
-
-	  /* Create a SCOPE_REF for qualified names, if the scope is
-	     dependent.  */
-	  if (scope)
-	    {
-	      if (TYPE_P (scope))
-		{
-		  if (address_p && done)
-		    decl = finish_qualified_id_expr (scope, decl,
-						     done, address_p,
-						     template_p,
-						     template_arg_p,
-						     tf_warning_or_error);
-		  else
-		    {
-		      tree type = NULL_TREE;
-		      if (DECL_P (decl) && !dependent_scope_p (scope))
-			type = TREE_TYPE (decl);
-		      decl = build_qualified_name (type,
-						   scope,
-						   id_expression,
-						   template_p);
-		    }
-		}
-	      if (TREE_TYPE (decl))
-		decl = convert_from_reference (decl);
-	      return decl;
-	    }
-	  /* A TEMPLATE_ID already contains all the information we
-	     need.  */
-	  if (TREE_CODE (id_expression) == TEMPLATE_ID_EXPR)
-	    return id_expression;
-	  /* The same is true for FIELD_DECL, but we also need to
-	     make sure that the syntax is correct.  */
-	  else if (TREE_CODE (decl) == FIELD_DECL)
-	    {
-	      /* Since SCOPE is NULL here, this is an unqualified name.
-		 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, NULL_TREE,
-			/*qualifying_scope=*/NULL_TREE);
-	      pop_deferring_access_checks ();
-	      return decl;
-	    }
-	  return id_expression;
-	}
+	  && DECL_P (decl)
+	  && any_dependent_type_attributes_p (DECL_ATTRIBUTES (decl)))
+	/* Dependent type attributes on the decl mean that the TREE_TYPE is
+	   wrong, so just return the identifier.  */
+	return id_expression;
 
       if (TREE_CODE (decl) == NAMESPACE_DECL)
 	{
@@ -3700,6 +3633,7 @@  finish_id_expression (tree id_expression,
 	 expression.  Template parameters have already
 	 been handled above.  */
       if (! error_operand_p (decl)
+	  && !dependent_p
 	  && integral_constant_expression_p
 	  && ! decl_constant_var_p (decl)
 	  && TREE_CODE (decl) != CONST_DECL
@@ -3726,6 +3660,7 @@  finish_id_expression (tree id_expression,
 	  decl = build_cxx_call (wrap, 0, NULL, tf_warning_or_error);
 	}
       else if (TREE_CODE (decl) == TEMPLATE_ID_EXPR
+	       && !dependent_p
 	       && variable_template_p (TREE_OPERAND (decl, 0)))
 	{
 	  decl = finish_template_variable (decl);
@@ -3734,6 +3669,12 @@  finish_id_expression (tree id_expression,
 	}
       else if (scope)
 	{
+	  if (TREE_CODE (decl) == SCOPE_REF)
+	    {
+	      gcc_assert (same_type_p (scope, TREE_OPERAND (decl, 0)));
+	      decl = TREE_OPERAND (decl, 1);
+	    }
+
 	  decl = (adjust_result_of_qualified_name_lookup
 		  (decl, scope, current_nonlambda_class_type()));
 
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index dbac7f3..aab92d5 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -2028,6 +2028,7 @@  build_qualified_name (tree type, tree scope, tree name, bool template_p)
       || scope == error_mark_node
       || name == error_mark_node)
     return error_mark_node;
+  gcc_assert (TREE_CODE (name) != SCOPE_REF);
   t = build2 (SCOPE_REF, type, scope, name);
   QUALIFIED_NAME_IS_TEMPLATE (t) = template_p;
   PTRMEM_OK_P (t) = true;
@@ -4663,6 +4664,8 @@  cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func,
       break;
 
     case BASELINK:
+      if (BASELINK_QUALIFIED_P (*tp))
+	WALK_SUBTREE (BINFO_TYPE (BASELINK_ACCESS_BINFO (*tp)));
       WALK_SUBTREE (BASELINK_FUNCTIONS (*tp));
       *walk_subtrees_p = 0;
       break;
diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-this1.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-this1.C
new file mode 100644
index 0000000..52d25af
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-this1.C
@@ -0,0 +1,17 @@ 
+// PR c++/81236
+// { dg-do compile { target c++14 } }
+
+struct A { constexpr operator int() { return 24; } };
+
+struct MyType {
+  void crash() {
+    auto l = [&](auto i){
+      make_crash<i>(); // Line (1)
+    };
+
+    l(A{});
+  }
+    
+  template<int i>
+  void make_crash() {}
+};
diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-this1a.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-this1a.C
new file mode 100644
index 0000000..d321a07
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-this1a.C
@@ -0,0 +1,17 @@ 
+// PR c++/81236
+// { dg-do compile { target c++14 } }
+
+struct A { constexpr operator int() { return 24; } };
+
+struct MyType {
+  void crash() {
+    auto l = [&](auto i){
+      MyType::make_crash<i>(); // Line (1)
+    };
+
+    l(A{});
+  }
+    
+  template<int i>
+  void make_crash() {}
+};
diff --git a/gcc/testsuite/g++.dg/template/pseudodtor3.C b/gcc/testsuite/g++.dg/template/pseudodtor3.C
index 8f1f6a7..21a68aa 100644
--- a/gcc/testsuite/g++.dg/template/pseudodtor3.C
+++ b/gcc/testsuite/g++.dg/template/pseudodtor3.C
@@ -11,7 +11,7 @@  struct A
 template <typename T> struct B
 {
   T &foo ();
-  B () { foo.~T (); }	// { dg-error "15:invalid use of member" }
+  B () { foo.~T (); }	// { dg-error "10:invalid use of member" }
 };
 
 B<int> b;
@@ -37,7 +37,7 @@  template <typename T> struct E
 {
   T &foo ();
   typedef long int U;
-  E () { foo.~U (); }	// { dg-error "10:is not of type" }
+  E () { foo.~U (); }	// { dg-error "10:invalid use of member" }
 };
 
 E<int> e;