Fix lookup of initialized captures in unevaluated context.
authorJason Merrill <jason@redhat.com>
Tue, 6 Oct 2009 22:14:01 +0000 (18:14 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Tue, 6 Oct 2009 22:14:01 +0000 (18:14 -0400)
* cp-tree.h (DECL_NORMAL_CAPTURE_P): New.
* name-lookup.c (qualify_lookup): Check it.
* parser.c (cp_parser_lambda_introducer): Pass explicit_init_p
to add_capture.
* semantics.c (add_capture): Set DECL_NORMAL_CAPTURE_P
on captures without explicit init.
(add_default_capture): Pass explicit_init_p.

Fix capture by copy of types with explicit copy constructor.
* cp-tree.h (TARGET_EXPR_DIRECT_INIT_P): New.
(DIRECT_INIT_EXPR_P): New.
* typeck.c (convert_for_initialization): Just return if
DIRECT_INIT_EXPR_P.
* parser.c (cp_parser_lambda_introducer): Use
TARGET_EXPR_DIRECT_INIT_P for normal captures.

From-SVN: r152500

gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/name-lookup.c
gcc/cp/parser.c
gcc/cp/semantics.c
gcc/cp/typeck.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-direct-init.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-init.C [new file with mode: 0644]

index 03f26c9..bb800f1 100644 (file)
@@ -1,3 +1,22 @@
+2009-10-06  Jason Merrill  <jason@redhat.com>
+
+       Fix lookup of initialized captures in unevaluated context.
+       * cp-tree.h (DECL_NORMAL_CAPTURE_P): New.
+       * name-lookup.c (qualify_lookup): Check it.
+       * parser.c (cp_parser_lambda_introducer): Pass explicit_init_p
+       to add_capture.
+       * semantics.c (add_capture): Set DECL_NORMAL_CAPTURE_P
+       on captures without explicit init.
+       (add_default_capture): Pass explicit_init_p.
+
+       Fix capture by copy of types with explicit copy constructor.
+       * cp-tree.h (TARGET_EXPR_DIRECT_INIT_P): New.
+       (DIRECT_INIT_EXPR_P): New.
+       * typeck.c (convert_for_initialization): Just return if
+       DIRECT_INIT_EXPR_P.
+       * semantics.c (build_lambda_object): Use
+       TARGET_EXPR_DIRECT_INIT_P for normal captures.
+
 2009-10-05  Jason Merrill  <jason@redhat.com>
 
        * parser.c: Mark lambda_scope and lambda_count for PCH.
index 8a18575..4b273c1 100644 (file)
@@ -97,6 +97,7 @@ framework extensions, you must include this file before toplev.h, not after.
       STATEMENT_LIST_TRY_BLOCK (in STATEMENT_LIST)
       TYPENAME_IS_RESOLVING_P (in TYPE_NAME_TYPE)
       LAMBDA_EXPR_DEDUCE_RETURN_TYPE_P (in LAMBDA_EXPR)
+      TARGET_EXPR_DIRECT_INIT_P (in TARGET_EXPR)
    3: (TREE_REFERENCE_EXPR) (in NON_LVALUE_EXPR) (commented-out).
       ICS_BAD_FLAG (in _CONV)
       FN_TRY_BLOCK_P (in TRY_BLOCK)
@@ -147,6 +148,7 @@ framework extensions, you must include this file before toplev.h, not after.
       DECL_FIELD_IS_BASE (in FIELD_DECL)
    7: DECL_DEAD_FOR_LOCAL (in VAR_DECL).
       DECL_THUNK_P (in a member FUNCTION_DECL)
+      DECL_NORMAL_CAPTURE_P (in FIELD_DECL)
 
    Usage of language-independent fields in a language-dependent manner:
 
@@ -3199,6 +3201,12 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define DECL_FIELD_IS_BASE(NODE) \
   DECL_LANG_FLAG_6 (FIELD_DECL_CHECK (NODE))
 
+/* Nonzero for FIELD_DECL node means that this field is a simple (no
+   explicit initializer) lambda capture field, making it invisible to
+   name lookup in unevaluated contexts.  */
+#define DECL_NORMAL_CAPTURE_P(NODE) \
+  DECL_LANG_FLAG_7 (FIELD_DECL_CHECK (NODE))
+
 /* Nonzero if TYPE is an anonymous union or struct type.  We have to use a
    flag for this because "A union for which objects or pointers are
    declared is not an anonymous union" [class.union].  */
@@ -3633,6 +3641,16 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define TARGET_EXPR_LIST_INIT_P(NODE) \
   TREE_LANG_FLAG_1 (TARGET_EXPR_CHECK (NODE))
 
+/* True if this TARGET_EXPR expresses direct-initialization of an object
+   to be named later.  */
+#define TARGET_EXPR_DIRECT_INIT_P(NODE) \
+  TREE_LANG_FLAG_2 (TARGET_EXPR_CHECK (NODE))
+
+/* True if EXPR expresses direct-initialization of a TYPE.  */
+#define DIRECT_INIT_EXPR_P(TYPE,EXPR)                                  \
+  (TREE_CODE (EXPR) == TARGET_EXPR && TREE_LANG_FLAG_2 (EXPR)          \
+   && same_type_ignoring_top_level_qualifiers_p (TYPE, TREE_TYPE (EXPR)))
+
 /* An enumeration of the kind of tags that C++ accepts.  */
 enum tag_types {
   none_type = 0, /* Not a tag type.  */
@@ -5041,7 +5059,7 @@ extern tree lambda_capture_field_type             (tree);
 extern tree lambda_return_type                 (tree);
 extern tree lambda_function                    (tree);
 extern void apply_lambda_return_type            (tree, tree);
-extern tree add_capture                         (tree, tree, tree, bool);
+extern tree add_capture                         (tree, tree, tree, bool, bool);
 extern tree add_default_capture                 (tree, tree, tree);
 extern tree lambda_expr_this_capture            (tree);
 
index 459e739..6e31f8a 100644 (file)
@@ -3757,10 +3757,9 @@ qualify_lookup (tree val, int flags)
     return true;
   if (flags & (LOOKUP_PREFER_NAMESPACES | LOOKUP_PREFER_TYPES))
     return false;
-  /* In unevaluated context, look past capture fields.  */
-  /* FIXME this will cause trouble with the initializer extension.  */
+  /* In unevaluated context, look past normal capture fields.  */
   if (cp_unevaluated_operand && TREE_CODE (val) == FIELD_DECL
-      && LAMBDA_TYPE_P (DECL_CONTEXT (val)))
+      && DECL_NORMAL_CAPTURE_P (val))
     return false;
   return true;
 }
index 8ab930c..44dceb2 100644 (file)
@@ -7125,6 +7125,7 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
       tree capture_id;
       tree capture_init_expr;
       cp_id_kind idk = CP_ID_KIND_NONE;
+      bool explicit_init_p = false;
 
       enum capture_kind_type
       {
@@ -7151,7 +7152,8 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
          add_capture (lambda_expr,
                       /*id=*/get_identifier ("__this"),
                       /*initializer=*/finish_this_expr(),
-                      /*by_reference_p=*/false);
+                      /*by_reference_p=*/false,
+                      explicit_init_p);
          continue;
        }
 
@@ -7190,6 +7192,7 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
          capture_init_expr = cp_parser_assignment_expression (parser,
                                                               /*cast_p=*/true,
                                                               &idk);
+         explicit_init_p = true;
        }
       else
        {
@@ -7231,7 +7234,8 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
       add_capture (lambda_expr,
                   capture_id,
                   capture_init_expr,
-                  /*by_reference_p=*/capture_kind == BY_REFERENCE);
+                  /*by_reference_p=*/capture_kind == BY_REFERENCE,
+                  explicit_init_p);
     }
 
   cp_parser_require (parser, CPP_CLOSE_SQUARE, "%<]%>");
index 8199af0..391228b 100644 (file)
@@ -5328,6 +5328,20 @@ build_lambda_object (tree lambda_expr)
         do some magic to make it work here.  */
       if (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE)
        val = build_array_copy (val);
+      else if (DECL_NORMAL_CAPTURE_P (field)
+              && TREE_CODE (TREE_TYPE (field)) != REFERENCE_TYPE)
+       {
+         /* "the entities that are captured by copy are used to
+            direct-initialize each corresponding non-static data
+            member of the resulting closure object."
+
+            There's normally no way to express direct-initialization
+            from an element of a CONSTRUCTOR, so we build up a special
+            TARGET_EXPR to bypass the usual copy-initialization.  */
+         val = force_rvalue (val);
+         if (TREE_CODE (val) == TARGET_EXPR)
+           TARGET_EXPR_DIRECT_INIT_P (val) = true;
+       }
 
       CONSTRUCTOR_APPEND_ELT (elts, DECL_NAME (field), val);
     }
@@ -5545,7 +5559,8 @@ capture_decltype (tree decl)
    and return it.  */
 
 tree
-add_capture (tree lambda, tree id, tree initializer, bool by_reference_p)
+add_capture (tree lambda, tree id, tree initializer, bool by_reference_p,
+            bool explicit_init_p)
 {
   tree type;
   tree member;
@@ -5560,6 +5575,13 @@ add_capture (tree lambda, tree id, tree initializer, bool by_reference_p)
 
   /* Make member variable.  */
   member = build_lang_decl (FIELD_DECL, id, type);
+  if (!explicit_init_p)
+    /* Normal captures are invisible to name lookup but uses are replaced
+       with references to the capture field; we implement this by only
+       really making them invisible in unevaluated context; see
+       qualify_lookup.  For now, let's make explicitly initialized captures
+       always visible.  */
+    DECL_NORMAL_CAPTURE_P (member) = true;
 
   /* Add it to the appropriate closure class.  */
   finish_member_declaration (member);
@@ -5605,7 +5627,8 @@ add_default_capture (tree lambda_stack, tree id, tree initializer)
                             /*by_reference_p=*/
                            (!this_capture_p
                             && (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda)
-                                == CPLD_REFERENCE)));
+                                == CPLD_REFERENCE)),
+                           /*explicit_init_p=*/false);
 
       {
         /* Have to get the old value of current_class_ref.  */
index b4d54fc..79b0201 100644 (file)
@@ -6893,6 +6893,11 @@ convert_for_initialization (tree exp, tree type, tree rhs, int flags,
 
   type = complete_type (type);
 
+  if (DIRECT_INIT_EXPR_P (type, rhs))
+    /* Don't try to do copy-initialization if we already have
+       direct-initialization.  */
+    return rhs;
+
   if (MAYBE_CLASS_TYPE_P (type))
     return ocp_convert (type, rhs, CONV_IMPLICIT|CONV_FORCE_TEMP, flags);
 
index 7d5ed50..dd18805 100644 (file)
@@ -1,3 +1,8 @@
+2009-10-06  Jason Merrill  <jason@redhat.com>
+
+       * g++.dg/cpp0x/lambda/lambda-init.C: New.
+       * g++.dg/cpp0x/lambda/lambda-direct-init.C: New.
+
 2009-10-06  Richard Guenther  <rguenther@suse.de>
 
        PR lto/41502
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-direct-init.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-direct-init.C
new file mode 100644 (file)
index 0000000..bbc2a1c
--- /dev/null
@@ -0,0 +1,14 @@
+// Test that capture by copy uses direct-initialization.
+// { dg-options "-std=c++0x" }
+
+struct A
+{
+  A();
+  explicit A(const A&);
+};
+
+int main()
+{
+  A a;
+  [a]{};
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-init.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-init.C
new file mode 100644 (file)
index 0000000..03c94e9
--- /dev/null
@@ -0,0 +1,8 @@
+// Test for the explicit initializer extension
+// { dg-options "-std=c++0x" }
+
+int main()
+{
+  int j = [i = 2]{sizeof(i); return i;}();
+  return (j != 2);
+}