PR c++/88123 - lambda and using-directive.
authorJason Merrill <jason@redhat.com>
Fri, 8 Mar 2019 02:54:41 +0000 (21:54 -0500)
committerJason Merrill <jason@gcc.gnu.org>
Fri, 8 Mar 2019 02:54:41 +0000 (21:54 -0500)
For named function calls in a template, the result of unqualified lookup is
safed in CALL_EXPR_FN.  But for operator expressions, no unqualified lookup
is performed until we know whether the operands have class type.  So when we
see in a lambda a use of an operator that might be overloaded, we need to do
that lookup then and save it away somewhere.  One possibility would be in
the expression, but we can't really add extra conditional operands to
standard tree codes.  I mostly implemented another approach using a new
WITH_LOOKUP_EXPR code, but teaching everywhere how to handle a new tree code
is always complicated.  Then it occurred to me that we could associate the
lookups with the function, which is both simpler and smaller.  So this patch
stores any operator bindings needed by a lambda function in an internal
attribute on the lambda call operator.

* name-lookup.c (op_unqualified_lookup)
(maybe_save_operator_binding, discard_operator_bindings)
(push_operator_bindings): New.
* typeck.c (build_x_binary_op, build_x_unary_op): Call
maybe_save_operator_binding.
* decl.c (start_preparsed_function): Call push_operator_bindings.
* tree.c (cp_free_lang_data): Call discard_operator_bindings.

From-SVN: r269477

gcc/cp/ChangeLog
gcc/cp/decl.c
gcc/cp/name-lookup.c
gcc/cp/name-lookup.h
gcc/cp/tree.c
gcc/cp/typeck.c
gcc/testsuite/g++.dg/cpp1y/lambda-generic-using1.C [new file with mode: 0644]

index 94e278dc9445dc463056fe3945374822338d28ac..ae5735d30d2f7f036ada1be968fea3da0c4b7835 100644 (file)
@@ -1,5 +1,14 @@
 2019-03-07  Jason Merrill  <jason@redhat.com>
 
+       PR c++/88123 - lambda and using-directive.
+       * name-lookup.c (op_unqualified_lookup)
+       (maybe_save_operator_binding, discard_operator_bindings)
+       (push_operator_bindings): New.
+       * typeck.c (build_x_binary_op, build_x_unary_op): Call
+       maybe_save_operator_binding.
+       * decl.c (start_preparsed_function): Call push_operator_bindings.
+       * tree.c (cp_free_lang_data): Call discard_operator_bindings.
+
        PR c++/88820 - ICE with CTAD and member template used in DMI.
        * pt.c (do_class_deduction): Handle parm used as its own arg.
 
index 173758feddf1a6275303162c458558d305b96afe..0187db5ff1c1d4b170f9b3dd584debf404dd9f29 100644 (file)
@@ -15553,6 +15553,8 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
 
   store_parm_decls (current_function_parms);
 
+  push_operator_bindings ();
+
   if (!processing_template_decl
       && (flag_lifetime_dse > 1)
       && DECL_CONSTRUCTOR_P (decl1)
index 1ddcde26ef487d5c9852d1579e2c0b6dfcb3f698..2ba888fd1c2e47fba79b700a55b698cc275697a5 100644 (file)
@@ -7556,4 +7556,103 @@ cp_emit_debug_info_for_using (tree t, tree context)
     }
 }
 
+/* Return the result of unqualified lookup for the overloaded operator
+   designated by CODE, if we are in a template and the binding we find is
+   not.  */
+
+static tree
+op_unqualified_lookup (tree fnname)
+{
+  if (cxx_binding *binding = IDENTIFIER_BINDING (fnname))
+    {
+      cp_binding_level *l = binding->scope;
+      while (l && !l->this_entity)
+       l = l->level_chain;
+      if (l && uses_template_parms (l->this_entity))
+       /* Don't preserve decls from an uninstantiated template,
+          wait until that template is instantiated.  */
+       return NULL_TREE;
+    }
+  tree fns = lookup_name (fnname);
+  if (fns && fns == get_global_binding (fnname))
+    /* The instantiation can find these.  */
+    return NULL_TREE;
+  return fns;
+}
+
+/* E is an expression representing an operation with dependent type, so we
+   don't know yet whether it will use the built-in meaning of the operator or a
+   function.  Remember declarations of that operator in scope.  */
+
+const char *const op_bind_attrname = "operator bindings";
+
+void
+maybe_save_operator_binding (tree e)
+{
+  /* This is only useful in a generic lambda.  */
+  if (!processing_template_decl)
+    return;
+  tree cfn = current_function_decl;
+  if (!cfn)
+    return;
+
+  /* Let's only do this for generic lambdas for now, we could do it for all
+     function templates if we wanted to.  */
+  if (!current_lambda_expr())
+    return;
+
+  tree fnname = ovl_op_identifier (false, TREE_CODE (e));
+  if (!fnname)
+    return;
+
+  tree attributes = DECL_ATTRIBUTES (cfn);
+  tree attr = lookup_attribute (op_bind_attrname, attributes);
+  tree bindings = NULL_TREE;
+  tree fns = NULL_TREE;
+  if (attr)
+    {
+      bindings = TREE_VALUE (attr);
+      if (tree elt = purpose_member (fnname, bindings))
+       fns = TREE_VALUE (elt);
+    }
+
+  if (!fns && (fns = op_unqualified_lookup (fnname)))
+    {
+      bindings = tree_cons (fnname, fns, bindings);
+      if (attr)
+       TREE_VALUE (attr) = bindings;
+      else
+       DECL_ATTRIBUTES (cfn)
+         = tree_cons (get_identifier (op_bind_attrname),
+                      bindings,
+                      attributes);
+    }
+}
+
+/* Called from cp_free_lang_data so we don't put this into LTO.  */
+
+void
+discard_operator_bindings (tree decl)
+{
+  DECL_ATTRIBUTES (decl) = remove_attribute (op_bind_attrname,
+                                            DECL_ATTRIBUTES (decl));
+}
+
+/* Subroutine of start_preparsed_function: push the bindings we saved away in
+   maybe_save_op_lookup into the function parameter binding level.  */
+
+void
+push_operator_bindings ()
+{
+  tree decl1 = current_function_decl;
+  if (tree attr = lookup_attribute (op_bind_attrname,
+                                   DECL_ATTRIBUTES (decl1)))
+    for (tree binds = TREE_VALUE (attr); binds; binds = TREE_CHAIN (binds))
+      {
+       tree name = TREE_PURPOSE (binds);
+       tree val = TREE_VALUE (binds);
+       push_local_binding (name, val, /*using*/true);
+      }
+}
+
 #include "gt-cp-name-lookup.h"
index 36816df5ada0c96b65fabdf1dec0daf9a322f329..a47486d1b8a9c209c73166773147c3408f936b9e 100644 (file)
@@ -330,5 +330,8 @@ extern void push_nested_namespace (tree);
 extern void pop_nested_namespace (tree);
 extern void push_to_top_level (void);
 extern void pop_from_top_level (void);
+extern void maybe_save_operator_binding (tree);
+extern void push_operator_bindings (void);
+extern void discard_operator_bindings (tree);
 
 #endif /* GCC_CP_NAME_LOOKUP_H */
index be33d4186f9e78781f4867fe125d5a9cb6b4f092..eca6b523c5f32aa660c2016aca909a374530886f 100644 (file)
@@ -5398,6 +5398,8 @@ cp_free_lang_data (tree t)
       DECL_EXTERNAL (t) = 1;
       TREE_STATIC (t) = 0;
     }
+  if (TREE_CODE (t) == FUNCTION_DECL)
+    discard_operator_bindings (t);
   if (TREE_CODE (t) == NAMESPACE_DECL)
     /* We do not need the leftover chaining of namespaces from the
        binding level.  */
index 9ceb7af00220c058bbd225914df3985e9e83d3b2..8d9224b668e2ad74949d08ab7fcf29dd31dca7fd 100644 (file)
@@ -4141,7 +4141,11 @@ build_x_binary_op (const op_location_t &loc, enum tree_code code, tree arg1,
     {
       if (type_dependent_expression_p (arg1)
          || type_dependent_expression_p (arg2))
-       return build_min_nt_loc (loc, code, arg1, arg2);
+       {
+         expr = build_min_nt_loc (loc, code, arg1, arg2);
+         maybe_save_operator_binding (expr);
+         return expr;
+       }
       arg1 = build_non_dependent_expr (arg1);
       arg2 = build_non_dependent_expr (arg2);
     }
@@ -5725,7 +5729,11 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
   if (processing_template_decl)
     {
       if (type_dependent_expression_p (xarg))
-       return build_min_nt_loc (loc, code, xarg.get_value (), NULL_TREE);
+       {
+         tree e = build_min_nt_loc (loc, code, xarg.get_value (), NULL_TREE);
+         maybe_save_operator_binding (e);
+         return e;
+       }
 
       xarg = build_non_dependent_expr (xarg);
     }
diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-using1.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-using1.C
new file mode 100644 (file)
index 0000000..2912a75
--- /dev/null
@@ -0,0 +1,29 @@
+// PR c++/88123
+// { dg-do compile { target c++14 } }
+
+struct bar {};
+struct baz {};
+struct baq {};
+
+namespace foo
+{
+  void operator+(bar);
+} // namespace foo
+
+namespace foo2
+{
+  void operator-(baz);
+}  
+
+auto fn() {
+  using foo::operator+;
+  using namespace foo2;
+  extern void operator!(baq);
+  return [](auto x, auto y, auto z) { +x; -y; !z; };
+}
+
+int main()
+{
+  auto l = fn();
+  l(bar(),baz(),baq());
+}