This patch adds proper lambda capturing of pointer and reference variables
as specified in OpenMP 5.0. We map the entire closure object as a to-map,
attach pointers to zero-length array sections, and perform mapping of
references.

The main way of implementation is by tree-walk when finishing processing
of target directives. Due to this nature, it seemed only complete to
combine the processing with all of the this[:1] map creation handling.
This makes this patch also a partial rewrite of PR92120, though things
seem to look better in the new form.
(and yes, the submitted PR92120 patch for mainline is in need of a "v3" re-work)

Now this tree walk is applied in the non-template case and after/during
template instantiation, so a prior patch to relax finish_omp_clauses()
cases to force the this[:1] changes to work are no longer needed, thus
reverted in this patch.

Tested without regressions on x86_64-linux with nvptx offloading,
and pushed to devel/omp/gcc-10.

2021-03-18  Chung-Lin Tang  <clt...@codesourcery.com>

gcc/cp/ChangeLog:

        * cp-tree.h (set_omp_target_this_expr): Delete.
        (finish_omp_target_clauses): New prototype.
        * lambda.c (lambda_expr_this_capture): Remove call to
        set_omp_target_this_expr.
        * parser.c (cp_parser_omp_target): Likewise.
        * pt.c (tsubst_expr): Add call to finish_omp_target_clauses for target
        directives.
        * semantics.c (omp_target_this_expr): Delete.
        (omp_target_ptr_members_accessed): Delete.
        (finish_non_static_data_member): Remove call to
        set_omp_target_this_expr. Remove use of omp_target_ptr_members_accessed.
        (finish_this_expr): Remove call to set_omp_target_this_expr.
        (struct omp_target_walk_data): New struct for walking over
        target-directive tree body.
        (finish_omp_target_clauses_r): New function for tree walk.
        (finish_omp_target_clauses): New function, with code factored out from
        finish_omp_target. Add lambda object handling case.
        (finish_omp_target): Factor code out and adjust to use
        finish_omp_target_clauses.
        (finish_omp_clauses): Revert prior "Adjustments to allow '*ptr' and
        'ptr->member' cases in map clausess.", since not needed with new
        organization of target-directive clause processing.

gcc/testsuite/ChangeLog:

        * g++.dg/gomp/target-lambda-1.C: New test.

libgomp/testsuite/ChangeLog:

        * libgomp.c++/target-lambda-1.C: New test.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index b77bdc380a0..247a3bb1ec3 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7316,7 +7316,7 @@ extern void finish_lambda_scope                   (void);
 extern tree start_lambda_function              (tree fn, tree lambda_expr);
 extern void finish_lambda_function             (tree body);
 extern tree finish_omp_target                  (location_t, tree, tree, bool);
-extern void set_omp_target_this_expr           (tree);
+extern void finish_omp_target_clauses          (location_t, tree, tree *);
 
 /* in tree.c */
 extern int cp_tree_operand_length              (const_tree);
diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index 9ecf0dbed0c..b55c2f85d27 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -842,9 +842,6 @@ lambda_expr_this_capture (tree lambda, int add_capture_p)
         type cast (_expr.cast_ 5.4) to the type of 'this'. [ The cast
         ensures that the transformed expression is an rvalue. ] */
       result = rvalue (result);
-
-      /* Acknowledge to OpenMP target that 'this' was referenced.  */
-      set_omp_target_this_expr (result);
     }
 
   return result;
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 1af233690a2..9fc2a9b05eb 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -40786,7 +40786,6 @@ cp_parser_omp_target (cp_parser *parser, cp_token 
*pragma_tok,
          keep_next_level (true);
          tree sb = begin_omp_structured_block (), ret;
          unsigned save = cp_parser_begin_omp_structured_block (parser);
-         set_omp_target_this_expr (NULL_TREE);
          switch (ccode)
            {
            case OMP_TEAMS:
@@ -40881,7 +40880,6 @@ cp_parser_omp_target (cp_parser *parser, cp_token 
*pragma_tok,
                                            "#pragma omp target", pragma_tok);
   c_omp_adjust_map_clauses (clauses, true);
   keep_next_level (true);
-  set_omp_target_this_expr (NULL_TREE);
   tree body = cp_parser_omp_structured_block (parser, if_p);
 
   finish_omp_target (pragma_tok->location, clauses, body, false);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 90cee31bb5a..139d1075986 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -18631,6 +18631,11 @@ tsubst_expr (tree t, tree args, tsubst_flags_t 
complain, tree in_decl,
       t = copy_node (t);
       OMP_BODY (t) = stmt;
       OMP_CLAUSES (t) = tmp;
+
+      if (TREE_CODE (t) == OMP_TARGET)
+       finish_omp_target_clauses (EXPR_LOCATION (t), OMP_BODY (t),
+                                  &OMP_CLAUSES (t));
+
       if (TREE_CODE (t) == OMP_TARGET && OMP_TARGET_COMBINED (t))
        {
          tree teams = cp_walk_tree (&stmt, tsubst_find_omp_teams, NULL, NULL);
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 5b62fa35eb8..3e290767d5c 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -61,11 +61,6 @@ static hash_map<tree, tree> *omp_private_member_map;
 static vec<tree> omp_private_member_vec;
 static bool omp_private_member_ignore_next;
 
-/* Used for OpenMP target region 'this' references.  */
-static tree omp_target_this_expr = NULL_TREE;
-
-static hash_map<tree, tree> omp_target_ptr_members_accessed;
-
 /* Deferred Access Checking Overview
    ---------------------------------
 
@@ -1896,7 +1891,6 @@ tree
 finish_non_static_data_member (tree decl, tree object, tree qualifying_scope)
 {
   gcc_assert (TREE_CODE (decl) == FIELD_DECL);
-  tree orig_object = object;
   bool try_omp_private = !object && omp_private_member_map;
   tree ret;
 
@@ -1935,14 +1929,6 @@ finish_non_static_data_member (tree decl, tree object, 
tree qualifying_scope)
       return error_mark_node;
     }
 
-  if (orig_object == NULL_TREE)
-    {
-      tree this_expr = TREE_OPERAND (object, 0);
-
-      /* Acknowledge to OpenMP target that 'this' was referenced.  */
-      set_omp_target_this_expr (this_expr);
-    }
-
   if (current_class_ptr)
     TREE_USED (current_class_ptr) = 1;
   if (processing_template_decl)
@@ -2003,13 +1989,6 @@ finish_non_static_data_member (tree decl, tree object, 
tree qualifying_scope)
       if (v)
        ret = convert_from_reference (*v);
     }
-  else if (omp_target_this_expr
-          && TREE_TYPE (ret)
-          && POINTER_TYPE_P (TREE_TYPE (ret)))
-    {
-      if (omp_target_ptr_members_accessed.get (decl) == NULL)
-       omp_target_ptr_members_accessed.put (decl, ret);
-    }
 
   return ret;
 }
@@ -2773,9 +2752,6 @@ finish_this_expr (void)
       /* The keyword 'this' is a prvalue expression.  */
       result = rvalue (result);
 
-      /* Acknowledge to OpenMP target that 'this' was referenced.  */
-      set_omp_target_this_expr (result);
-
       return result;
     }
 
@@ -6407,7 +6383,6 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type 
ort)
   bool order_seen = false;
   bool schedule_seen = false;
   bool oacc_async = false;
-  bool indirect_ref_p = false;
   bool indir_component_ref_p = false;
   tree last_iterators = NULL_TREE;
   bool last_iterators_remove = false;
@@ -7517,14 +7492,6 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type 
ort)
              indir_component_ref_p = true;
              STRIP_NOPS (t);
            }
-         indirect_ref_p = false;
-         if ((ort == C_ORT_ACC || ort == C_ORT_OMP)
-             && INDIRECT_REF_P (t))
-           {
-             t = TREE_OPERAND (t, 0);
-             indirect_ref_p = true;
-             STRIP_NOPS (t);
-           }
          if (TREE_CODE (t) == COMPONENT_REF
              && ((ort & C_ORT_OMP_DECLARE_SIMD) == C_ORT_OMP
                  || ort == C_ORT_ACC)
@@ -7560,12 +7527,6 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type 
ort)
                      break;
                    }
                  t = TREE_OPERAND (t, 0);
-                 if (INDIRECT_REF_P (t))
-                   {
-                     t = TREE_OPERAND (t, 0);
-                     indir_component_ref_p = true;
-                     STRIP_NOPS (t);
-                   }
                }
              if (remove)
                break;
@@ -7629,7 +7590,6 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type 
ort)
                       || (OMP_CLAUSE_MAP_KIND (c)
                           != GOMP_MAP_FIRSTPRIVATE_POINTER))
                   && !indir_component_ref_p
-                  && !indirect_ref_p
                   && !cxx_mark_addressable (t))
            remove = true;
          else if (!(OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
@@ -7714,8 +7674,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type 
ort)
            }
          else
            {
-             if (!indirect_ref_p && !indir_component_ref_p)
-               bitmap_set_bit (&map_head, DECL_UID (t));
+             bitmap_set_bit (&map_head, DECL_UID (t));
              if (t != OMP_CLAUSE_DECL (c)
                  && TREE_CODE (OMP_CLAUSE_DECL (c)) == COMPONENT_REF)
                bitmap_set_bit (&map_field_head, DECL_UID (t));
@@ -8683,26 +8642,126 @@ finish_omp_construct (enum tree_code code, tree body, 
tree clauses)
   return add_stmt (stmt);
 }
 
-void
-set_omp_target_this_expr (tree this_val)
+/* Used to walk OpenMP target directive body.  */
+
+struct omp_target_walk_data
 {
-  omp_target_this_expr = this_val;
+  tree current_object;
+  bool this_expr_accessed;
+
+  hash_map<tree, tree> ptr_members_accessed;
+  hash_set<tree> lambda_objects_accessed;
 
-  if (omp_target_this_expr == NULL_TREE)
-    omp_target_ptr_members_accessed.empty ();
+  tree current_closure;
+  hash_set<tree> closure_vars_accessed;
+};
+
+static tree
+finish_omp_target_clauses_r (tree *tp, int *walk_subtrees, void *ptr)
+{
+  tree t = *tp;
+  struct omp_target_walk_data *data = (struct omp_target_walk_data *) ptr;
+  tree current_object = data->current_object;
+  tree current_closure = data->current_closure;
+
+  if (current_object)
+    {
+      tree this_expr = TREE_OPERAND (current_object, 0);
+
+      if (operand_equal_p (t, this_expr))
+       {
+         data->this_expr_accessed = true;
+         *walk_subtrees = 0;
+         return NULL_TREE;
+       }
+
+      if (TREE_CODE (t) == COMPONENT_REF
+         && POINTER_TYPE_P (TREE_TYPE (t))
+         && operand_equal_p (TREE_OPERAND (t, 0), current_object)
+         && TREE_CODE (TREE_OPERAND (t, 1)) == FIELD_DECL)
+       {
+         data->this_expr_accessed = true;
+         tree fld = TREE_OPERAND (t, 1);
+         if (data->ptr_members_accessed.get (fld) == NULL)
+           {
+             if (TREE_CODE (TREE_TYPE (t)) == REFERENCE_TYPE)
+               t = convert_from_reference (t);
+             data->ptr_members_accessed.put (fld, t);
+           }
+         *walk_subtrees = 0;
+         return NULL_TREE;
+       }
+    }
+
+  /* When the current_function_decl is a lambda function, the closure object
+     argument's type seems to not yet have fields layed out, so a recording
+     of DECL_VALUE_EXPRs during the target body walk seems the only way to
+     find them.  */
+  if (current_closure
+      && (TREE_CODE (t) == VAR_DECL
+         || TREE_CODE (t) == PARM_DECL
+         || TREE_CODE (t) == RESULT_DECL)
+      && DECL_HAS_VALUE_EXPR_P (t)
+      && TREE_CODE (DECL_VALUE_EXPR (t)) == COMPONENT_REF
+      && operand_equal_p (current_closure,
+                         TREE_OPERAND (DECL_VALUE_EXPR (t), 0)))
+    {
+      if (!data->closure_vars_accessed.contains (t))
+       data->closure_vars_accessed.add (t);
+      *walk_subtrees = 0;
+      return NULL_TREE;
+    }
+
+  if (TREE_TYPE(t) && LAMBDA_TYPE_P (TREE_TYPE (t)))
+    {
+      tree lt = TREE_TYPE (t);
+      gcc_assert (CLASS_TYPE_P (lt));
+
+      if (!data->lambda_objects_accessed.contains (t))
+       data->lambda_objects_accessed.add (t);
+      *walk_subtrees = 0;
+      return NULL_TREE;
+    }
+
+  return NULL_TREE;
 }
 
-tree
-finish_omp_target (location_t loc, tree clauses, tree body, bool combined_p)
+void
+finish_omp_target_clauses (location_t loc, tree body, tree *clauses_ptr)
 {
-  tree last_inserted_clause = NULL_TREE;
+  omp_target_walk_data data;
+  data.this_expr_accessed = false;
 
-  if (omp_target_this_expr)
+  tree ct = current_nonlambda_class_type ();
+  if (ct)
     {
+      tree object = maybe_dummy_object (ct, NULL);
+      object = maybe_resolve_dummy (object, true);
+      data.current_object = object;
+    }
+  else
+    data.current_object = NULL_TREE;
+
+  if (DECL_LAMBDA_FUNCTION_P (current_function_decl))
+    {
+      tree closure = DECL_ARGUMENTS (current_function_decl);
+      data.current_closure = build_indirect_ref (loc, closure, RO_UNARY_STAR);
+    }
+  else
+    data.current_closure = NULL_TREE;
+
+  cp_walk_tree_without_duplicates (&body, finish_omp_target_clauses_r, &data);
+
+  auto_vec<tree, 16> new_clauses;
+
+  if (data.this_expr_accessed)
+    {
+      tree omp_target_this_expr = TREE_OPERAND (data.current_object, 0);
+
       /* See if explicit user-specified map(this[:]) clause already exists.
         If not, we create an implicit map(tofrom:this[:1]) clause.  */
       tree *explicit_this_deref_map = NULL;
-      for (tree *c = &clauses; *c; c = &OMP_CLAUSE_CHAIN (*c))
+      for (tree *c = clauses_ptr; *c; c = &OMP_CLAUSE_CHAIN (*c))
        if (OMP_CLAUSE_CODE (*c) == OMP_CLAUSE_MAP
            && TREE_CODE (OMP_CLAUSE_DECL (*c)) == INDIRECT_REF
            && operand_equal_p (TREE_OPERAND (OMP_CLAUSE_DECL (*c), 0),
@@ -8722,23 +8781,72 @@ finish_omp_target (location_t loc, tree clauses, tree 
body, bool combined_p)
          OMP_CLAUSE_DECL (c)
            = build_indirect_ref (loc, closure, RO_UNARY_STAR);
          OMP_CLAUSE_SIZE (c)
-           = (processing_template_decl
-              ? NULL_TREE
-              : TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (closure))));
+           = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (closure)));
+         new_clauses.safe_push (c);
+
+         tree closure_obj = OMP_CLAUSE_DECL (c);
+         tree closure_type = TREE_TYPE (closure_obj);
+
+         gcc_assert (LAMBDA_TYPE_P (closure_type)
+                     && CLASS_TYPE_P (closure_type));
 
          tree c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
          OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_POINTER);
          OMP_CLAUSE_DECL (c2) = closure;
          OMP_CLAUSE_SIZE (c2) = size_zero_node;
-         OMP_CLAUSE_CHAIN (c2) = clauses;
-         OMP_CLAUSE_CHAIN (c) = c2;
-         last_inserted_clause = c2;
-         clauses = c;
+         new_clauses.safe_push (c2);
 
          STRIP_NOPS (omp_target_this_expr);
          gcc_assert (DECL_HAS_VALUE_EXPR_P (omp_target_this_expr));
          omp_target_this_expr = DECL_VALUE_EXPR (omp_target_this_expr);
 
+         for (hash_set<tree>::iterator i = data.closure_vars_accessed.begin ();
+              i != data.closure_vars_accessed.end (); ++i)
+           {
+             tree orig_decl = *i;
+             tree closure_expr = DECL_VALUE_EXPR (orig_decl);
+
+             if (TREE_CODE (TREE_TYPE (orig_decl)) == POINTER_TYPE)
+               {
+                 /* this-pointer is processed outside this loop.  */
+                 if (operand_equal_p (closure_expr, omp_target_this_expr))
+                   continue;
+
+                 tree c = build_omp_clause (loc, OMP_CLAUSE_MAP);
+                 OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ALLOC);
+                 OMP_CLAUSE_DECL (c)
+                   = build_indirect_ref (loc, closure_expr, RO_UNARY_STAR);
+                 OMP_CLAUSE_SIZE (c) = size_zero_node;
+                 OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1;
+                 new_clauses.safe_push (c);
+
+                 c = build_omp_clause (loc, OMP_CLAUSE_MAP);
+                 OMP_CLAUSE_SET_MAP_KIND
+                   (c, GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION);
+                 OMP_CLAUSE_DECL (c) = closure_expr;
+                 OMP_CLAUSE_SIZE (c) = size_zero_node;
+                 new_clauses.safe_push (c);
+               }
+             else if (TREE_CODE (TREE_TYPE (orig_decl)) == REFERENCE_TYPE)
+               {
+                 tree c = build_omp_clause (loc, OMP_CLAUSE_MAP);
+                 OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_TO);
+                 OMP_CLAUSE_DECL (c)
+                   = build1 (INDIRECT_REF,
+                             TREE_TYPE (TREE_TYPE (closure_expr)),
+                             closure_expr);
+                 OMP_CLAUSE_SIZE (c)
+                   = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (closure_expr)));
+                 new_clauses.safe_push (c);
+
+                 c = build_omp_clause (loc, OMP_CLAUSE_MAP);
+                 OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ALWAYS_POINTER);
+                 OMP_CLAUSE_DECL (c) = closure_expr;
+                 OMP_CLAUSE_SIZE (c) = size_zero_node;
+                 new_clauses.safe_push (c);
+               }
+           }
+
          if (explicit_this_deref_map)
            {
              /* Transform *this into *__closure->this in maps.  */
@@ -8753,12 +8861,13 @@ finish_omp_target (location_t loc, tree clauses, tree 
body, bool combined_p)
              OMP_CLAUSE_DECL (nc) = omp_target_this_expr;
              OMP_CLAUSE_SET_MAP_KIND (nc, GOMP_MAP_ALWAYS_POINTER);
 
+             /* Unlink this two-map sequence away from the chain.  */
+             *explicit_this_deref_map = OMP_CLAUSE_CHAIN (nc);
+
              /* Move map(*__closure->this) map(always_pointer:__closure->this)
                 sequence to right after __closure map.  */
-             *explicit_this_deref_map = OMP_CLAUSE_CHAIN (nc);
-             OMP_CLAUSE_CHAIN (nc) = OMP_CLAUSE_CHAIN (c2);
-             OMP_CLAUSE_CHAIN (c2) = this_map;
-             last_inserted_clause = nc;
+             new_clauses.safe_push (this_map);
+             new_clauses.safe_push (nc);
            }
          else
            {
@@ -8767,9 +8876,7 @@ finish_omp_target (location_t loc, tree clauses, tree 
body, bool combined_p)
              OMP_CLAUSE_DECL (c3)
                = build_indirect_ref (loc, omp_target_this_expr, RO_UNARY_STAR);
              OMP_CLAUSE_SIZE (c3)
-               = (processing_template_decl
-                  ? NULL_TREE
-                  : TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE 
(omp_target_this_expr))));
+               = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (omp_target_this_expr)));
 
              tree c4 = build_omp_clause (loc, OMP_CLAUSE_MAP);
              OMP_CLAUSE_SET_MAP_KIND (c4, GOMP_MAP_ALWAYS_POINTER);
@@ -8777,10 +8884,8 @@ finish_omp_target (location_t loc, tree clauses, tree 
body, bool combined_p)
              OMP_CLAUSE_DECL (c4) = omp_target_this_expr;
              OMP_CLAUSE_SIZE (c4) = size_zero_node;
 
-             OMP_CLAUSE_CHAIN (c3) = c4;
-             OMP_CLAUSE_CHAIN (c4) = OMP_CLAUSE_CHAIN (c2);
-             OMP_CLAUSE_CHAIN (c2) = c3;
-             last_inserted_clause = c4;
+             new_clauses.safe_push (c3);
+             new_clauses.safe_push (c4);
            }
        }
       else
@@ -8794,112 +8899,177 @@ finish_omp_target (location_t loc, tree clauses, tree 
body, bool combined_p)
              OMP_CLAUSE_DECL (c)
                = build_indirect_ref (loc, omp_target_this_expr, RO_UNARY_STAR);
              OMP_CLAUSE_SIZE (c)
-               = (processing_template_decl
-                  ? NULL_TREE
-                  : TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE 
(omp_target_this_expr))));
+               = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (omp_target_this_expr)));
 
              tree c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
              OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_POINTER);
              STRIP_NOPS (omp_target_this_expr);
              OMP_CLAUSE_DECL (c2) = omp_target_this_expr;
              OMP_CLAUSE_SIZE (c2) = size_zero_node;
-             OMP_CLAUSE_CHAIN (c2) = clauses;
-             OMP_CLAUSE_CHAIN (c) = c2;
-             clauses = c;
-             last_inserted_clause = c2;
+
+             new_clauses.safe_push (c);
+             new_clauses.safe_push (c2);
            }
        }
-      omp_target_this_expr = NULL_TREE;
-    }
-
-  if (last_inserted_clause && !omp_target_ptr_members_accessed.is_empty ())
-    for (hash_map<tree, tree>::iterator i
-          = omp_target_ptr_members_accessed.begin ();
-        i != omp_target_ptr_members_accessed.end (); ++i)
-      {
-       /* For each referenced member that is of pointer or reference-to-pointer
-          type, create the equivalent of map(alloc:this->ptr[:0]).  */
-       tree field_decl = (*i).first;
-       tree ptr_member = (*i).second;
 
-       for (tree nc = OMP_CLAUSE_CHAIN (last_inserted_clause);
-            nc != NULL_TREE; nc = OMP_CLAUSE_CHAIN (nc))
+      if (!data.ptr_members_accessed.is_empty ())
+       for (hash_map<tree, tree>::iterator i
+              = data.ptr_members_accessed.begin ();
+            i != data.ptr_members_accessed.end (); ++i)
          {
-           /* If map(this->ptr[:N] already exists, avoid creating another
-              such map.  */
-           tree decl = OMP_CLAUSE_DECL (nc);
-           if ((TREE_CODE (decl) == INDIRECT_REF
-                || TREE_CODE (decl) == MEM_REF)
-               && operand_equal_p (TREE_OPERAND (decl, 0),
-                                   ptr_member))
-             goto next_ptr_member;
-         }
+           /* For each referenced member that is of pointer or
+              reference-to-pointer type, create the equivalent of
+              map(alloc:this->ptr[:0]).  */
+           tree field_decl = (*i).first;
+           tree ptr_member = (*i).second;
 
-       if (!cxx_mark_addressable (ptr_member))
-         gcc_unreachable ();
+           for (tree c = *clauses_ptr; c; c = OMP_CLAUSE_CHAIN (c))
+             {
+               /* If map(this->ptr[:N] already exists, avoid creating another
+                  such map.  */
+               tree decl = OMP_CLAUSE_DECL (c);
+               if ((TREE_CODE (decl) == INDIRECT_REF
+                    || TREE_CODE (decl) == MEM_REF)
+                   && operand_equal_p (TREE_OPERAND (decl, 0),
+                                       ptr_member))
+                 goto next_ptr_member;
+             }
 
-       if (TREE_CODE (TREE_TYPE (field_decl)) == REFERENCE_TYPE)
-         {
-           /* For reference to pointers, we need to map the referenced pointer
-              first for things to be correct.  */
-           tree ptr_member_type = TREE_TYPE (ptr_member);
-
-           /* Map pointer target as zero-length array section.  */
-           tree c = build_omp_clause (loc, OMP_CLAUSE_MAP);
-           OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ALLOC);
-           OMP_CLAUSE_DECL (c)
-             = build1 (INDIRECT_REF, TREE_TYPE (ptr_member_type), ptr_member);
-           OMP_CLAUSE_SIZE (c) = size_zero_node;
-           OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1;
-
-           /* Map pointer to zero-length array section.  */
-           tree c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
-           OMP_CLAUSE_SET_MAP_KIND
-             (c2, GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION);
-           OMP_CLAUSE_DECL (c2) = ptr_member;
-           OMP_CLAUSE_SIZE (c2) = size_zero_node;
-
-           /* Attach reference-to-pointer field to pointer.  */
-           tree c3 = build_omp_clause (loc, OMP_CLAUSE_MAP);
-           OMP_CLAUSE_SET_MAP_KIND (c3, GOMP_MAP_ATTACH);
-           OMP_CLAUSE_DECL (c3) = TREE_OPERAND (ptr_member, 0);
-           OMP_CLAUSE_SIZE (c3) = size_zero_node;
-
-           OMP_CLAUSE_CHAIN (c) = c2;
-           OMP_CLAUSE_CHAIN (c2) = c3;
-           OMP_CLAUSE_CHAIN (c3) = OMP_CLAUSE_CHAIN (last_inserted_clause);
-
-           OMP_CLAUSE_CHAIN (last_inserted_clause) = c;
-           last_inserted_clause = c3;
-         }
-       else if (TREE_CODE (TREE_TYPE (field_decl)) == POINTER_TYPE)
-         {
-           /* Map pointer target as zero-length array section.  */
-           tree c = build_omp_clause (loc, OMP_CLAUSE_MAP);
-           OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ALLOC);
-           OMP_CLAUSE_DECL (c)
-             = build_indirect_ref (loc, ptr_member, RO_UNARY_STAR);
-           OMP_CLAUSE_SIZE (c) = size_zero_node;
-           OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1;
-
-           /* Attach zero-length array section to pointer.  */
-           tree c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
-           OMP_CLAUSE_SET_MAP_KIND
-             (c2, GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION);
-           OMP_CLAUSE_DECL (c2) = ptr_member;
-           OMP_CLAUSE_SIZE (c2) = size_zero_node;
-
-           OMP_CLAUSE_CHAIN (c) = c2;
-           OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (last_inserted_clause);
-           OMP_CLAUSE_CHAIN (last_inserted_clause) = c;
-           last_inserted_clause = c2;
+           if (!cxx_mark_addressable (ptr_member))
+             gcc_unreachable ();
+
+           if (TREE_CODE (TREE_TYPE (field_decl)) == REFERENCE_TYPE)
+             {
+               /* For reference to pointers, we need to map the referenced
+                  pointer first for things to be correct.  */
+               tree ptr_member_type = TREE_TYPE (ptr_member);
+
+               /* Map pointer target as zero-length array section.  */
+               tree c = build_omp_clause (loc, OMP_CLAUSE_MAP);
+               OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ALLOC);
+               OMP_CLAUSE_DECL (c)
+                 = build1 (INDIRECT_REF, TREE_TYPE (ptr_member_type), 
ptr_member);
+               OMP_CLAUSE_SIZE (c) = size_zero_node;
+               OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1;
+
+               /* Map pointer to zero-length array section.  */
+               tree c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+               OMP_CLAUSE_SET_MAP_KIND
+                 (c2, GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION);
+               OMP_CLAUSE_DECL (c2) = ptr_member;
+               OMP_CLAUSE_SIZE (c2) = size_zero_node;
+
+               /* Attach reference-to-pointer field to pointer.  */
+               tree c3 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+               OMP_CLAUSE_SET_MAP_KIND (c3, GOMP_MAP_ATTACH);
+               OMP_CLAUSE_DECL (c3) = TREE_OPERAND (ptr_member, 0);
+               OMP_CLAUSE_SIZE (c3) = size_zero_node;
+
+               new_clauses.safe_push (c);
+               new_clauses.safe_push (c2);
+               new_clauses.safe_push (c3);
+             }
+           else if (TREE_CODE (TREE_TYPE (field_decl)) == POINTER_TYPE)
+             {
+               /* Map pointer target as zero-length array section.  */
+               tree c = build_omp_clause (loc, OMP_CLAUSE_MAP);
+               OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ALLOC);
+               OMP_CLAUSE_DECL (c)
+                 = build_indirect_ref (loc, ptr_member, RO_UNARY_STAR);
+               OMP_CLAUSE_SIZE (c) = size_zero_node;
+               OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1;
+
+               /* Attach zero-length array section to pointer.  */
+               tree c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+               OMP_CLAUSE_SET_MAP_KIND
+                 (c2, GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION);
+               OMP_CLAUSE_DECL (c2) = ptr_member;
+               OMP_CLAUSE_SIZE (c2) = size_zero_node;
+
+               new_clauses.safe_push (c);
+               new_clauses.safe_push (c2);
+             }
+           else
+             gcc_unreachable ();
+
+         next_ptr_member:
+           ;
          }
-       else
-         gcc_unreachable ();
+    }
 
-      next_ptr_member:
-       ;
-      }
+  if (!data.lambda_objects_accessed.is_empty ())
+    {
+      for (hash_set<tree>::iterator i = data.lambda_objects_accessed.begin ();
+          i != data.lambda_objects_accessed.end (); ++i)
+       {
+         tree lobj = *i;
+         tree lt = TREE_TYPE (lobj);
+         gcc_assert (LAMBDA_TYPE_P (lt) && CLASS_TYPE_P (lt));
+
+         tree lc = build_omp_clause (loc, OMP_CLAUSE_MAP);
+         OMP_CLAUSE_SET_MAP_KIND (lc, GOMP_MAP_TO);
+         OMP_CLAUSE_DECL (lc) = lobj;
+         OMP_CLAUSE_SIZE (lc) = TYPE_SIZE_UNIT (lt);
+         new_clauses.truncate (0);
+         new_clauses.safe_push (lc);
+
+         for (tree fld = TYPE_FIELDS (lt); fld; fld = DECL_CHAIN (fld))
+           {
+             if (TREE_CODE (TREE_TYPE (fld)) == POINTER_TYPE)
+               {
+                 tree exp = build3 (COMPONENT_REF, TREE_TYPE (fld),
+                                    lobj, fld, NULL_TREE);
+                 tree c = build_omp_clause (loc, OMP_CLAUSE_MAP);
+                 OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ALLOC);
+                 OMP_CLAUSE_DECL (c)
+                   = build_indirect_ref (loc, exp, RO_UNARY_STAR);
+                 OMP_CLAUSE_SIZE (c) = size_zero_node;
+                 OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1;
+                 new_clauses.safe_push (c);
+
+                 c = build_omp_clause (loc, OMP_CLAUSE_MAP);
+                 OMP_CLAUSE_SET_MAP_KIND
+                   (c, GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION);
+                 OMP_CLAUSE_DECL (c) = exp;
+                 OMP_CLAUSE_SIZE (c) = size_zero_node;
+                 new_clauses.safe_push (c);
+               }
+             else if (TREE_CODE (TREE_TYPE (fld)) == REFERENCE_TYPE)
+               {
+                 tree exp = build3 (COMPONENT_REF, TREE_TYPE (fld),
+                                    lobj, fld, NULL_TREE);
+                 tree c = build_omp_clause (loc, OMP_CLAUSE_MAP);
+                 OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_TO);
+                 OMP_CLAUSE_DECL (c)
+                   = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (exp)), exp);
+                 OMP_CLAUSE_SIZE (c)
+                   = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (exp)));
+                 new_clauses.safe_push (c);
+
+                 c = build_omp_clause (loc, OMP_CLAUSE_MAP);
+                 OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ALWAYS_POINTER);
+                 OMP_CLAUSE_DECL (c) = exp;
+                 OMP_CLAUSE_SIZE (c) = size_zero_node;
+                 new_clauses.safe_push (c);
+               }
+           }
+       }
+    }
+
+  tree c = *clauses_ptr;
+  for (int i = new_clauses.length () - 1; i >= 0; i--)
+    {
+      OMP_CLAUSE_CHAIN (new_clauses[i]) = c;
+      c = new_clauses[i];
+    }
+  *clauses_ptr = c;
+}
+
+tree
+finish_omp_target (location_t loc, tree clauses, tree body, bool combined_p)
+{
+  if (!processing_template_decl)
+    finish_omp_target_clauses (loc, body, &clauses);
 
   tree stmt = make_node (OMP_TARGET);
   TREE_TYPE (stmt) = void_type_node;
diff --git a/gcc/testsuite/g++.dg/gomp/target-lambda-1.C 
b/gcc/testsuite/g++.dg/gomp/target-lambda-1.C
new file mode 100644
index 00000000000..7dceef80f47
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/target-lambda-1.C
@@ -0,0 +1,94 @@
+// We use 'auto' without a function return type, so specify dialect here
+// { dg-additional-options "-std=c++14 -fdump-tree-gimple" }
+#include <cstdlib>
+#include <cstring>
+
+template <typename L>
+void
+omp_target_loop (int begin, int end, L loop)
+{
+  #pragma omp target teams distribute parallel for
+  for (int i = begin; i < end; i++)
+    loop (i);
+}
+
+struct S
+{
+  int a, len;
+  int *ptr;
+
+  auto merge_data_func (int *iptr, int &b)
+  {
+    auto fn = [=](void) -> bool
+      {
+       bool mapped;
+       #pragma omp target map(from:mapped)
+       {
+         mapped = (ptr != NULL && iptr != NULL);
+         if (mapped)
+           {
+             for (int i = 0; i < len; i++)
+               ptr[i] += a + b + iptr[i];
+           }
+       }
+       return mapped;
+      };
+    return fn;
+  }
+};
+
+int x = 1;
+
+int main (void)
+{
+  const int N = 10;
+  int *data1 = new int[N];
+  int *data2 = new int[N];
+  memset (data1, 0xab, sizeof (int) * N);
+  memset (data1, 0xcd, sizeof (int) * N);
+
+  int val = 1;
+  int &valref = val;
+  #pragma omp target enter data map(alloc: data1[:N], data2[:N])
+
+  omp_target_loop (0, N, [=](int i) { data1[i] = val; });
+  omp_target_loop (0, N, [=](int i) { data2[i] = valref + 1; });
+
+  #pragma omp target update from(data1[:N], data2[:N])
+
+  for (int i = 0; i < N; i++)
+    {
+      if (data1[i] != 1) abort ();
+      if (data2[i] != 2) abort ();
+    }
+
+  #pragma omp target exit data map(delete: data1[:N], data2[:N])
+
+  int b = 8;
+  S s = { 4, N, data1 };
+  auto f = s.merge_data_func (data2, b);
+
+  if (f ()) abort ();
+
+  #pragma omp target enter data map(to: data1[:N])
+  if (f ()) abort ();
+
+  #pragma omp target enter data map(to: data2[:N])
+  if (!f ()) abort ();
+
+  #pragma omp target exit data map(from: data1[:N], data2[:N])
+
+  for (int i = 0; i < N; i++)
+    {
+      if (data1[i] != 0xf) abort ();
+      if (data2[i] != 2) abort ();
+    }
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump {#pragma omp target num_teams.* 
map\(to:\*__closure \[len: [0-9]+\]\) map\(firstprivate:__closure \[pointer 
assign, bias: 0\]\) map\(attach_zero_length_array_section:__closure->__iptr 
\[bias: 0\]\) map\(struct:\*__closure \[len: 1\]\) map\(alloc:__closure->__this 
\[len: [0-9]+\]\) map\(tofrom:\*_[0-9]+ \[len: [0-9]+\]\) 
map\(always_pointer:__closure->__this \[pointer assign, bias: 0\]\) 
map\(attach_zero_length_array_section:_[0-9]+->ptr \[bias: 0\]\) 
map\(from:mapped \[len: [0-9]+\]\) map\(alloc:\*_[0-9]+ \[len: 0\]\) 
map\(alloc:\*_[0-9]+ \[len: 0\]\) firstprivate\(b\) map\(alloc:MEM.* \[len: 
0\]\) map\(firstprivate:iptr \[pointer assign, bias: 0\]\) map\(alloc:MEM.* 
\[len: 0\]\) map\(firstprivate:this \[pointer assign, bias: 0\]\)} "gimple" } } 
*/
+
+/* { dg-final { scan-tree-dump {#pragma omp target num_teams.* map\(to:loop 
\[len: [0-9]+\]\) map\(attach_zero_length_array_section:loop\.__data1 \[bias: 
0\]\) map\(alloc:\*_[0-9]+ \[len: 0\]\) firstprivate\(end\) 
firstprivate\(begin\)} "gimple" } } */
+
+/* { dg-final { scan-tree-dump {#pragma omp target num_teams.* map\(to:loop 
\[len: [0-9]+\]\) map\(attach_zero_length_array_section:loop\.__data2 \[bias: 
0\]\) map\(alloc:\*_[0-9]+ \[len: 0\]\) firstprivate\(end\) 
firstprivate\(begin\)} "gimple" } } */
diff --git a/libgomp/testsuite/libgomp.c++/target-lambda-1.C 
b/libgomp/testsuite/libgomp.c++/target-lambda-1.C
new file mode 100644
index 00000000000..06c6470b4ff
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-lambda-1.C
@@ -0,0 +1,86 @@
+#include <cstdlib>
+#include <cstring>
+
+template <typename L>
+void
+omp_target_loop (int begin, int end, L loop)
+{
+  #pragma omp target teams distribute parallel for
+  for (int i = begin; i < end; i++)
+    loop (i);
+}
+
+struct S
+{
+  int a, len;
+  int *ptr;
+
+  auto merge_data_func (int *iptr, int &b)
+  {
+    auto fn = [=](void) -> bool
+      {
+       bool mapped;
+       #pragma omp target map(from:mapped)
+       {
+         mapped = (ptr != NULL && iptr != NULL);
+         if (mapped)
+           {
+             for (int i = 0; i < len; i++)
+               ptr[i] += a + b + iptr[i];
+           }
+       }
+       return mapped;
+      };
+    return fn;
+  }
+};
+
+int x = 1;
+
+int main (void)
+{
+  const int N = 10;
+  int *data1 = new int[N];
+  int *data2 = new int[N];
+  memset (data1, 0xab, sizeof (int) * N);
+  memset (data1, 0xcd, sizeof (int) * N);
+
+  int val = 1;
+  int &valref = val;
+  #pragma omp target enter data map(alloc: data1[:N], data2[:N])
+
+  omp_target_loop (0, N, [=](int i) { data1[i] = val; });
+  omp_target_loop (0, N, [=](int i) { data2[i] = valref + 1; });
+
+  #pragma omp target update from(data1[:N], data2[:N])
+
+  for (int i = 0; i < N; i++)
+    {
+      if (data1[i] != 1) abort ();
+      if (data2[i] != 2) abort ();
+    }
+
+  #pragma omp target exit data map(delete: data1[:N], data2[:N])
+
+  int b = 8;
+  S s = { 4, N, data1 };
+  auto f = s.merge_data_func (data2, b);
+
+  if (f ()) abort ();
+
+  #pragma omp target enter data map(to: data1[:N])
+  if (f ()) abort ();
+
+  #pragma omp target enter data map(to: data2[:N])
+  if (!f ()) abort ();
+
+  #pragma omp target exit data map(from: data1[:N], data2[:N])
+
+  for (int i = 0; i < N; i++)
+    {
+      if (data1[i] != 0xf) abort ();
+      if (data2[i] != 2) abort ();
+    }
+
+  return 0;
+}

Reply via email to