https://gcc.gnu.org/g:8f4ee36bd5248cd244f65282167e3a13a3c98bc2

commit r16-75-g8f4ee36bd5248cd244f65282167e3a13a3c98bc2
Author: Andre Vehreschild <ve...@gcc.gnu.org>
Date:   Mon Apr 7 09:36:24 2025 +0200

    Fortran: Improve F2018 TEAM handling [PR87326, PR87556, PR88254, PR103896]
    
    Improve the implementation of F2018 TEAM handling routines. Add
    runtime-functions to caf_single to allow testing.
    
            PR fortran/87326
            PR fortran/87556
            PR fortran/88254
            PR fortran/103796
    
    gcc/fortran/ChangeLog:
    
            * coarray.cc (split_expr_at_caf_ref): Treat polymorphic types
            correctly.  Ensure resolve of expression after coindex.
            (create_allocated_callback): Fix parameter of allocated function
            for coarrays.
            (coindexed_expr_callback): Improve detection of coarrays in
            allocated function.
            * decl.cc (gfc_match_end): Add team block matching.
            * dump-parse-tree.cc (show_code_node): Dump change team block as
            such.
            * frontend-passes.cc (gfc_code_walker): Recognice team block.
            * gfortran.texi: Add documentation for team api functions.
            * intrinsic.texi: Add documentation about team_type in
            iso_fortran_env module.
            * iso-fortran-env.def (team_type): Use helper to get pointer
            kind.
            * match.cc (gfc_match_associate): Factor out matching of
            association list, because it is used in change team as well.
            (check_coarray_assoc): Ensure, that the association is to a
            coarray.
            (match_association_list): Match a list of association either in
            associate or in change team.
            (gfc_match_form_team): Match form team correctly include
            new_index.
            (gfc_match_change_team): Match change team with association
            list.
            (gfc_match_end_team): Match end team including stat and errmsg.
            (gfc_match_return): Prevent return from team block.
            * parse.cc (decode_statement): Sort team block.
            (next_statement): Same.
            (check_statement_label): Same.
            (accept_statement): Same.
            (verify_st_order): Same.
            (parse_associate): Renamed to move_associates_to_block...
            (move_associates_to_block): ... to enable reuse for change team.
            (parse_change_team): Parse it as block.
            (parse_executable): Same.
            * parse.h (enum gfc_compile_state): Add team block as compiler
            state.
            * resolve.cc (resolve_scalar_argument): New function to resolve
            an argument to a statement as a scalar.
            (resolve_form_team): Resolve its members.
            (resolve_change_team): Same.
            (resolve_branch): Prevent branch from jumping out of team block.
            (check_team): Removed.
            * trans-decl.cc (gfc_build_builtin_function_decls): Add stat and
            errmsg to team API functions and update their arguments.
            * trans-expr.cc (gfc_trans_subcomponent_assign): Also null the
            token when moving memory or an allocated() will not detect a
            free.
            * trans-intrinsic.cc (gfc_conv_intrinsic_caf_is_present_remote):
            Adapt to signature change no longer a pointer-pointer.
            * trans-stmt.cc (gfc_trans_form_team): Translate a form team
            including new_index.
            (gfc_trans_change_team): Translate a change team as a block.
    
    libgfortran/ChangeLog:
    
            * caf/libcaf.h: Remove commented block.
            (_gfortran_caf_form_team): Allow for all relevant arguments.
            (_gfortran_caf_change_team): Same.
            (_gfortran_caf_end_team): Same.
            (_gfortran_caf_sync_team): Same.
            * caf/single.c (struct caf_single_team): Team handling
            structures.
            (_gfortran_caf_init): Initialize initial team.
            (free_team_list): Free all teams and the memory they hold.
            (_gfortran_caf_finalize): Free initial and sibling teams.
            (_gfortran_caf_register): Add memory registered to current team.
            (_gfortran_caf_deregister): Unregister memory from current team.
            (_gfortran_caf_is_present_on_remote): Check token's memptr for
            llocation.  May have been deallocated by an end team.
            (_gfortran_caf_form_team): Push a new team stub to the list.
            (_gfortran_caf_change_team): Push a formed team on top of the
            ctive teams stack.
            (_gfortran_caf_end_team): End the active team, free all memory
            allocated during its livespan.
            (_gfortran_caf_sync_team): Take stat and errmsg into account.
    
    gcc/testsuite/ChangeLog:
    
            * gfortran.dg/team_change_2.f90: New test.
            * gfortran.dg/team_change_3.f90: New test.
            * gfortran.dg/team_end_2.f90: New test.
            * gfortran.dg/team_end_3.f90: New test.
            * gfortran.dg/team_form_2.f90: New test.
            * gfortran.dg/team_form_3.f90: New test.
            * gfortran.dg/team_sync_2.f90: New test.

Diff:
---
 gcc/fortran/coarray.cc                      |  12 +-
 gcc/fortran/decl.cc                         |  20 +-
 gcc/fortran/dump-parse-tree.cc              |  30 ++-
 gcc/fortran/frontend-passes.cc              |   1 +
 gcc/fortran/gfortran.texi                   | 146 ++++++++++++++-
 gcc/fortran/intrinsic.texi                  |   4 +
 gcc/fortran/iso-fortran-env.def             |   4 +-
 gcc/fortran/match.cc                        | 280 ++++++++++++++++++++++++----
 gcc/fortran/parse.cc                        | 143 +++++++++-----
 gcc/fortran/parse.h                         |   2 +-
 gcc/fortran/resolve.cc                      |  89 ++++++---
 gcc/fortran/trans-decl.cc                   |  24 ++-
 gcc/fortran/trans-expr.cc                   |   7 +-
 gcc/fortran/trans-intrinsic.cc              |   4 +-
 gcc/fortran/trans-stmt.cc                   |  91 ++++-----
 gcc/testsuite/gfortran.dg/team_change_2.f90 |  86 +++++++++
 gcc/testsuite/gfortran.dg/team_change_3.f90 |  29 +++
 gcc/testsuite/gfortran.dg/team_end_2.f90    |  33 ++++
 gcc/testsuite/gfortran.dg/team_end_3.f90    |  39 ++++
 gcc/testsuite/gfortran.dg/team_form_2.f90   |  27 +++
 gcc/testsuite/gfortran.dg/team_form_3.f90   |  34 ++++
 gcc/testsuite/gfortran.dg/team_sync_2.f90   |  27 +++
 libgfortran/caf/libcaf.h                    |  20 +-
 libgfortran/caf/single.c                    | 173 ++++++++++++++++-
 24 files changed, 1116 insertions(+), 209 deletions(-)

diff --git a/gcc/fortran/coarray.cc b/gcc/fortran/coarray.cc
index 70583254d0d8..2f067f855e54 100644
--- a/gcc/fortran/coarray.cc
+++ b/gcc/fortran/coarray.cc
@@ -357,7 +357,9 @@ split_expr_at_caf_ref (gfc_expr *expr, gfc_namespace *ns,
 
   gcc_assert (expr->expr_type == EXPR_VARIABLE);
   caf_ts = &expr->symtree->n.sym->ts;
-  if (!expr->symtree->n.sym->attr.codimension)
+  if (!(expr->symtree->n.sym->ts.type == BT_CLASS
+         ? CLASS_DATA (expr->symtree->n.sym)->attr.codimension
+         : expr->symtree->n.sym->attr.codimension))
     {
       /* The coarray is in some component.  Find it.  */
       caf_ref = expr->ref;
@@ -432,6 +434,9 @@ split_expr_at_caf_ref (gfc_expr *expr, gfc_namespace *ns,
   else if (base->ts.type == BT_CLASS)
     convert_coarray_class_to_derived_type (base, ns);
 
+  memset (&(*post_caf_ref_expr)->ts, 0, sizeof (gfc_typespec));
+  gfc_resolve_expr (*post_caf_ref_expr);
+  (*post_caf_ref_expr)->corank = 0;
   gfc_expression_rank (*post_caf_ref_expr);
   if (for_send)
     gfc_expression_rank (expr);
@@ -1130,8 +1135,8 @@ create_allocated_callback (gfc_expr *expr)
 
   // ADD_ARG (expr->symtree->name, base, BT_VOID, INTENT_IN);
   base = post_caf_ref_expr->symtree->n.sym;
+  base->attr.pointer = !base->attr.dimension;
   gfc_set_sym_referenced (base);
-  gfc_commit_symbol (base);
   *argptr = gfc_get_formal_arglist ();
   (*argptr)->sym = base;
   argptr = &(*argptr)->next;
@@ -1420,7 +1425,8 @@ coindexed_expr_callback (gfc_expr **e, int *walk_subtrees,
          {
          case GFC_ISYM_ALLOCATED:
            if ((*e)->value.function.actual->expr
-               && gfc_is_coindexed ((*e)->value.function.actual->expr))
+               && (gfc_is_coarray ((*e)->value.function.actual->expr)
+                   || gfc_is_coindexed ((*e)->value.function.actual->expr)))
              {
                rewrite_caf_allocated (e);
                *walk_subtrees = 0;
diff --git a/gcc/fortran/decl.cc b/gcc/fortran/decl.cc
index feb454ea5b36..69acd2da9815 100644
--- a/gcc/fortran/decl.cc
+++ b/gcc/fortran/decl.cc
@@ -8459,6 +8459,7 @@ gfc_match_end (gfc_statement *st)
     {
     case COMP_ASSOCIATE:
     case COMP_BLOCK:
+    case COMP_CHANGE_TEAM:
       if (startswith (block_name, "block@"))
        block_name = NULL;
       break;
@@ -8515,7 +8516,7 @@ gfc_match_end (gfc_statement *st)
     case COMP_SUBROUTINE:
       *st = ST_END_SUBROUTINE;
       if (!abbreviated_modproc_decl)
-      target = " subroutine";
+       target = " subroutine";
       else
        target = " procedure";
       eos_ok = !contained_procedure ();
@@ -8524,7 +8525,7 @@ gfc_match_end (gfc_statement *st)
     case COMP_FUNCTION:
       *st = ST_END_FUNCTION;
       if (!abbreviated_modproc_decl)
-      target = " function";
+       target = " function";
       else
        target = " procedure";
       eos_ok = !contained_procedure ();
@@ -8646,6 +8647,12 @@ gfc_match_end (gfc_statement *st)
       eos_ok = 0;
       break;
 
+    case COMP_CHANGE_TEAM:
+      *st = ST_END_TEAM;
+      target = " team";
+      eos_ok = 0;
+      break;
+
     default:
       gfc_error ("Unexpected END statement at %C");
       goto cleanup;
@@ -8683,14 +8690,19 @@ gfc_match_end (gfc_statement *st)
   else
     got_matching_end = true;
 
+  if (*st == ST_END_TEAM && gfc_match_end_team () == MATCH_ERROR)
+    /* Emit errors of stat and errmsg parsing now to finish the block and
+       continue analysis of compilation unit.  */
+    gfc_error_check ();
+
   old_loc = gfc_current_locus;
   /* If we're at the end, make sure a block name wasn't required.  */
   if (gfc_match_eos () == MATCH_YES)
     {
-
       if (*st != ST_ENDDO && *st != ST_ENDIF && *st != ST_END_SELECT
          && *st != ST_END_FORALL && *st != ST_END_WHERE && *st != ST_END_BLOCK
-         && *st != ST_END_ASSOCIATE && *st != ST_END_CRITICAL)
+         && *st != ST_END_ASSOCIATE && *st != ST_END_CRITICAL
+         && *st != ST_END_TEAM)
        return MATCH_YES;
 
       if (!block_name)
diff --git a/gcc/fortran/dump-parse-tree.cc b/gcc/fortran/dump-parse-tree.cc
index 4ace093738ca..dd920f3ab08a 100644
--- a/gcc/fortran/dump-parse-tree.cc
+++ b/gcc/fortran/dump-parse-tree.cc
@@ -2769,17 +2769,21 @@ show_code_node (int level, gfc_code *c)
       fputs ("FAIL IMAGE ", dumpfile);
       break;
 
-    case EXEC_CHANGE_TEAM:
-      fputs ("CHANGE TEAM", dumpfile);
-      break;
-
     case EXEC_END_TEAM:
       fputs ("END TEAM", dumpfile);
       show_sync_stat (&c->ext.sync_stat);
       break;
 
     case EXEC_FORM_TEAM:
-      fputs ("FORM TEAM", dumpfile);
+      fputs ("FORM TEAM ", dumpfile);
+      show_expr (c->expr1);
+      show_expr (c->expr2);
+      if (c->expr3)
+       {
+         fputs (" NEW_INDEX", dumpfile);
+         show_expr (c->expr3);
+       }
+      show_sync_stat (&c->ext.sync_stat);
       break;
 
     case EXEC_SYNC_TEAM:
@@ -2930,6 +2934,7 @@ show_code_node (int level, gfc_code *c)
       fputs ("ENDIF", dumpfile);
       break;
 
+    case EXEC_CHANGE_TEAM:
     case EXEC_BLOCK:
       {
        const char *blocktype, *sname = NULL;
@@ -2945,17 +2950,23 @@ show_code_node (int level, gfc_code *c)
            if (fcn && fcn->expr_type == EXPR_FUNCTION)
              sname = fcn->value.function.actual->expr->symtree->n.sym->name;
          }
+       else if (c->op == EXEC_CHANGE_TEAM)
+         blocktype = "CHANGE TEAM";
        else if (c->ext.block.assoc)
          blocktype = "ASSOCIATE";
        else
          blocktype = "BLOCK";
        show_indent ();
        fprintf (dumpfile, "%s ", blocktype);
+       if (c->op == EXEC_CHANGE_TEAM)
+         show_expr (c->expr1);
        for (alist = c->ext.block.assoc; alist; alist = alist->next)
          {
            fprintf (dumpfile, " %s = ", sname ? sname : alist->name);
            show_expr (alist->target);
          }
+       if (c->op == EXEC_CHANGE_TEAM)
+         show_sync_stat (&c->ext.block.sync_stat);
 
        ++show_level;
        ns = c->ext.block.ns;
@@ -2965,8 +2976,13 @@ show_code_node (int level, gfc_code *c)
        gfc_current_ns = saved_ns;
        show_code (show_level, ns->code);
        --show_level;
-       show_indent ();
-       fprintf (dumpfile, "END %s ", blocktype);
+       if (c->op != EXEC_CHANGE_TEAM)
+         {
+           /* A CHANGE_TEAM is terminated by a END_TEAM, which have its own
+              stat and errmsg.  Therefore, let it print itself.  */
+           show_indent ();
+           fprintf (dumpfile, "END %s ", blocktype);
+         }
        break;
       }
 
diff --git a/gcc/fortran/frontend-passes.cc b/gcc/fortran/frontend-passes.cc
index ef9c80147cc4..02a0a2326a66 100644
--- a/gcc/fortran/frontend-passes.cc
+++ b/gcc/fortran/frontend-passes.cc
@@ -5340,6 +5340,7 @@ gfc_code_walker (gfc_code **c, walk_code_fn_t codefn, 
walk_expr_fn_t exprfn,
            {
 
            case EXEC_BLOCK:
+           case EXEC_CHANGE_TEAM:
              WALK_SUBCODE (co->ext.block.ns->code);
              if (co->ext.block.assoc)
                {
diff --git a/gcc/fortran/gfortran.texi b/gcc/fortran/gfortran.texi
index 963216173085..ff385671d214 100644
--- a/gcc/fortran/gfortran.texi
+++ b/gcc/fortran/gfortran.texi
@@ -4230,6 +4230,10 @@ future implementation of teams.  It is about to change 
without further notice.
 * _gfortran_caf_co_min:: Collective minimum reduction
 * _gfortran_caf_co_sum:: Collective summing reduction
 * _gfortran_caf_co_reduce:: Generic collective reduction
+* _gfortran_caf_form_team:: Team creation function
+* _gfortran_caf_change_team:: Team activation function
+* _gfortran_caf_end_team:: Team termination function
+* _gfortran_caf_sync_team:: Synchronize all images of a given team
 @end menu
 
 
@@ -4705,9 +4709,9 @@ structure.
 operation, i.e., zero on success and non-zero on error.  When @code{NULL} and 
an
 error occurs, then an error message is printed and the program is terminated.
 @item @var{team} @tab intent(in)  The opaque team handle as returned by
-@code{FORM TEAM}.  Unused at the moment.
+@code{FORM TEAM}.
 @item @var{team_number} @tab intent(in)  The number of the team this access is
-to be part of.  Unused at the moment.
+to be part of.
 @end multitable
 
 @item @emph{Notes}:
@@ -4806,9 +4810,9 @@ structure.
 operation, i.e., zero on success and non-zero on error.  When @code{NULL} and 
an
 error occurs, then an error message is printed and the program is terminated.
 @item @var{team} @tab intent(in)  The opaque team handle as returned by
-@code{FORM TEAM}.  Unused at the moment.
+@code{FORM TEAM}.
 @item @var{team_number} @tab intent(in)  The number of the team this access is
-to be part of.  Unused at the moment.
+to be part of.
 @end multitable
 
 @item @emph{Notes}:
@@ -4906,13 +4910,13 @@ the operation on the sending side, i.e., zero on 
success and non-zero on error.
 When @code{NULL} and an error occurs, then an error message is printed and the
 program is terminated.
 @item @var{dst_team} @tab intent(in)  The opaque team handle as returned by
-@code{FORM TEAM}.  Unused at the moment.
+@code{FORM TEAM}.
 @item @var{dst_team_number} @tab intent(in)  The number of the team this access
-is to be part of.  Unused at the moment.
+is to be part of.
 @item @var{src_team} @tab intent(in)  The opaque team handle as returned by
-@code{FORM TEAM}.  Unused at the moment.
+@code{FORM TEAM}.
 @item @var{src_team_number} @tab intent(in)  The number of the team this access
-is to be part of.  Unused at the moment.
+is to be part of.
 @end multitable
 
 @item @emph{Notes}:
@@ -5656,6 +5660,132 @@ or an array descriptor.
 @end table
 
 
+
+@node _gfortran_caf_form_team
+@subsection @code{_gfortran_caf_form_team} --- Team creation function
+@cindex Coarray, _gfortran_caf_form_team
+
+@table @asis
+@item @emph{Synopsis}:
+@code{void _gfortran_caf_form_team (int team_id, caf_team_t *team,
+int *new_index, int *stat, char *errmsg, size_t errmsg_len)}
+
+@item @emph{Description}:
+Create a team.  All images giving the same @var{team_id} in a call to
+@code{FORM TEAM} will form a new team addressable by the opaque handle
+@var{team} which is of type @code{team_type} from the intrinsic module
+@ref{ISO_FORTRAN_ENV}.  In the team the image gets the image index given by
+@var{new_index} if present.  If @var{new_index} is absent, then an
+implementation specific index is assigned.
+
+@item @emph{Arguments}:
+@multitable @columnfractions .15 .70
+@item @var{team_id} @tab intent(in)  A unique id for each team to form.  Images
+giving the same @var{team_id} in a call to @code{FORM TEAM} belong to the same
+team.
+@item @var{team} @tab intent(out)  The opaque pointer to the newly formed team
+@item @var{new_index} @tab intent(in)  If non-null gives the unique index of
+this image in the newly formed team.  When no @var{new_index} is given, the
+caf-library is free to choose a unique index.
+@item @var{stat} @tab intent(out)  Stores the status STAT= and may be NULL.
+@item @var{errmsg} @tab intent(out)  When an error occurs, this is set to
+an error message; may be NULL.
+@item @var{errmsg_len} @tab intent(in)  the buffer size of errmsg
+@end multitable
+
+@item @emph{Notes}:
+The id given in @var{team_id} has to be unique in all subsequent calls to
+@code{FORM TEAM} on the same image.  That id is the same used in
+@code{TEAM_NUMBER=} of coarray indexes, which motivates the uniqueness.
+
+The index given in @var{new_index} needs to be unique among all members of
+team to create.  Failing uniqueness may lead to misbehaviour, which depends
+on the caf-library's implementation.  The library is free to implement
+checks for this, which imposes overhead and therefore may be avoided.
+@end table
+
+
+
+@node _gfortran_caf_change_team
+@subsection @code{_gfortran_caf_change_team} --- Team activation function
+@cindex Coarray, _gfortran_caf_change_team
+
+@table @asis
+@item @emph{Synopsis}:
+@code{void _gfortran_caf_change_team (caf_team_t team, int *stat, char *errmsg,
+size_t errmsg_len)}
+
+@item @emph{Description}:
+Actives the team given by @var{team}, which must be formed but not active
+yet.  This routine starts a new epoch on the coarray memory pool.  All
+coarrays registered from now on, will be freeed once the team is terminated.
+
+@item @emph{Arguments}:
+@multitable @columnfractions .15 .70
+@item @var{team} @tab intent(inout)  The opaque pointer to an already formed
+team
+@item @var{stat} @tab intent(out)  Stores the status STAT= and may be NULL.
+@item @var{errmsg} @tab intent(out)  When an error occurs, this is set to
+an error message; may be NULL.
+@item @var{errmsg_len} @tab intent(in)  the buffer size of errmsg
+@end multitable
+
+@item @emph{Notes}:
+When an error occurs and @var{stat} is non-null, it will be set.  Nevertheless
+will the Fortran program continue with the first statement in the change team
+block.
+@end table
+
+
+
+@node _gfortran_caf_end_team
+@subsection @code{_gfortran_caf_end_team} --- Team termination function
+@cindex Coarray, _gfortran_caf_end_team
+
+@table @asis
+@item @emph{Synopsis}:
+@code{void _gfortran_caf_end_team (int *stat, char *errmsg, size_t errmsg_len)}
+
+@item @emph{Description}:
+Terminates the last team changed to.  The coarray memory epoch is
+terminated and all coarrays allocated since the execution of @code{CHANGE TEAM}
+are freeed.
+
+@item @emph{Arguments}:
+@multitable @columnfractions .15 .70
+@item @var{stat} @tab intent(out)  Stores the status STAT= and may be NULL.
+@item @var{errmsg} @tab intent(out)  When an error occurs, this is set to
+an error message; may be NULL.
+@item @var{errmsg_len} @tab intent(in)  the buffer size of errmsg
+@end multitable
+@end table
+
+
+
+@node _gfortran_caf_sync_team
+@subsection @code{_gfortran_caf_sync_team} --- Synchronize all images of a 
given team
+@cindex Coarray, _gfortran_caf_sync_team
+
+@table @asis
+@item @emph{Synopsis}:
+@code{void _gfortran_caf_sync_team (caf_team_t team, int *stat, char *errmsg,
+size_t errmsg_len)}
+
+@item @emph{Description}:
+Blocks execution of the image calling @code{SYNC TEAM} until all images of the
+team given by @var{team} have joined the synchronisation call.
+
+@item @emph{Arguments}:
+@multitable @columnfractions .15 .70
+@item @var{team} @tab intent(in)  The opaque pointer to an active team
+@item @var{stat} @tab intent(out)  Stores the status STAT= and may be NULL.
+@item @var{errmsg} @tab intent(out)  When an error occurs, this is set to
+an error message; may be NULL.
+@item @var{errmsg_len} @tab intent(in)  the buffer size of errmsg
+@end multitable
+@end table
+
+
 @c Intrinsic Procedures
 @c ---------------------------------------------------------------------
 
diff --git a/gcc/fortran/intrinsic.texi b/gcc/fortran/intrinsic.texi
index 8c160e58b00a..ad89064cb595 100644
--- a/gcc/fortran/intrinsic.texi
+++ b/gcc/fortran/intrinsic.texi
@@ -15445,6 +15445,10 @@ Derived type with private components to be use with 
the @code{LOCK} and
 @code{UNLOCK} statement. A variable of its type has to be always declared
 as coarray and may not appear in a variable-definition context.
 (Fortran 2008 or later.)
+@item @code{TEAM_TYPE}:
+An opaque type for handling teams.  Note that a variable of type
+@code{TEAM_TYPE} is not comparable with other variables of the same or other
+types nor with null.
 @end table
 
 The module also provides the following intrinsic procedures:
diff --git a/gcc/fortran/iso-fortran-env.def b/gcc/fortran/iso-fortran-env.def
index b8926f4df93b..970f09fddd3a 100644
--- a/gcc/fortran/iso-fortran-env.def
+++ b/gcc/fortran/iso-fortran-env.def
@@ -134,9 +134,7 @@ NAMED_DERIVED_TYPE (ISOFORTRAN_EVENT_TYPE, "event_type", \
                    : gfc_default_integer_kind, GFC_STD_F2018)
 
 NAMED_DERIVED_TYPE (ISOFORTRAN_TEAM_TYPE, "team_type", \
-                   flag_coarray == GFC_FCOARRAY_LIB
-                   ? get_int_kind_from_node (ptr_type_node)
-                   : gfc_default_integer_kind, GFC_STD_F2018)
+                   get_int_kind_from_node (ptr_type_node), GFC_STD_F2018)
 
 NAMED_INTCST (ISOFORTRANENV_LOGICAL8, "logical8", \
               gfc_get_int_kind_from_width_isofortranenv (8), GFC_STD_F2023)
diff --git a/gcc/fortran/match.cc b/gcc/fortran/match.cc
index 4d77e094ab9f..0d81b69025e0 100644
--- a/gcc/fortran/match.cc
+++ b/gcc/fortran/match.cc
@@ -1966,29 +1966,29 @@ gfc_match_block (void)
   return MATCH_YES;
 }
 
-
-/* Match an ASSOCIATE statement.  */
-
-match
-gfc_match_associate (void)
+bool
+check_coarray_assoc (const char *name, gfc_association_list *assoc)
 {
-  if (gfc_match_label () == MATCH_ERROR)
-    return MATCH_ERROR;
-
-  if (gfc_match (" associate") != MATCH_YES)
-    return MATCH_NO;
-
-  /* Match the association list.  */
-  if (gfc_match_char ('(') != MATCH_YES)
+  if (assoc->target->expr_type == EXPR_VARIABLE
+      && !strcmp (assoc->target->symtree->name, name))
     {
-      gfc_error ("Expected association list at %C");
-      return MATCH_ERROR;
+      gfc_error ("Codimension decl name %qs in association at %L "
+                "must not be the same as a selector",
+                name, &assoc->where);
+      return false;
     }
+  return true;
+}
+
+match
+match_association_list (bool for_change_team = false)
+{
   new_st.ext.block.assoc = NULL;
   while (true)
     {
-      gfc_association_list* newAssoc = gfc_get_association_list ();
-      gfc_association_list* a;
+      gfc_association_list *newAssoc = gfc_get_association_list ();
+      gfc_association_list *a;
+      locus pre_name = gfc_current_locus;
 
       /* Match the next association.  */
       if (gfc_match (" %n ", newAssoc->name) != MATCH_YES)
@@ -1998,7 +1998,7 @@ gfc_match_associate (void)
        }
 
       /* Required for an assumed rank target.  */
-      if (gfc_peek_char () == '(')
+      if (!for_change_team && gfc_peek_char () == '(')
        {
          newAssoc->ar = gfc_get_array_ref ();
          if (gfc_match_array_ref (newAssoc->ar, NULL, 0, 0) != MATCH_YES)
@@ -2012,26 +2012,53 @@ gfc_match_associate (void)
        gfc_error_now ("The bounds remapping list at %C is an experimental "
                       "F202y feature. Use std=f202y to enable");
 
+      if (for_change_team && gfc_peek_char () == '[')
+       {
+         if (!newAssoc->ar)
+           newAssoc->ar = gfc_get_array_ref ();
+         if (gfc_match_array_spec (&newAssoc->ar->as, false, true)
+             == MATCH_ERROR)
+           goto assocListError;
+       }
+
       /* Match the next association.  */
       if (gfc_match (" =>", newAssoc->name) != MATCH_YES)
        {
-         gfc_error ("Expected association at %C");
-         goto assocListError;
+         if (for_change_team)
+           gfc_current_locus = pre_name;
+
+         free (newAssoc);
+         return MATCH_NO;
        }
 
-      if (gfc_match (" %e", &newAssoc->target) != MATCH_YES)
+      if (!for_change_team)
        {
-         /* Have another go, allowing for procedure pointer selectors.  */
-         gfc_matching_procptr_assignment = 1;
          if (gfc_match (" %e", &newAssoc->target) != MATCH_YES)
            {
+             /* Have another go, allowing for procedure pointer selectors.  */
+             gfc_matching_procptr_assignment = 1;
+             if (gfc_match (" %e", &newAssoc->target) != MATCH_YES)
+               {
+                 gfc_matching_procptr_assignment = 0;
+                 gfc_error ("Invalid association target at %C");
+                 goto assocListError;
+               }
              gfc_matching_procptr_assignment = 0;
-             gfc_error ("Invalid association target at %C");
+           }
+         newAssoc->where = gfc_current_locus;
+       }
+      else
+       {
+         newAssoc->where = gfc_current_locus;
+         /* F2018, C1116: A selector in a coarray-association shall be a named
+            coarray.  */
+         if (gfc_match (" %v", &newAssoc->target) != MATCH_YES)
+           {
+             gfc_error ("Selector in coarray association as %C shall be a "
+                        "named coarray");
              goto assocListError;
            }
-         gfc_matching_procptr_assignment = 0;
        }
-      newAssoc->where = gfc_current_locus;
 
       /* Check that the current name is not yet in the list.  */
       for (a = new_st.ext.block.assoc; a; a = a->next)
@@ -2042,6 +2069,35 @@ gfc_match_associate (void)
            goto assocListError;
          }
 
+      if (for_change_team)
+       {
+         /* F2018, C1113: In a change-team-stmt, a coarray-name in a
+            codimension-decl shall not be the same as a selector, or another
+            coarray-name, in that statement.
+            The latter is already checked for above.  So check only the
+            former.
+          */
+         if (!check_coarray_assoc (newAssoc->name, newAssoc))
+           goto assocListError;
+
+         for (a = new_st.ext.block.assoc; a; a = a->next)
+           {
+             if (!check_coarray_assoc (newAssoc->name, a)
+                 || !check_coarray_assoc (a->name, newAssoc))
+               goto assocListError;
+
+             /* F2018, C1115: No selector shall appear more than once in a
+              * given change-team-stmt.  */
+             if (!strcmp (newAssoc->target->symtree->name,
+                          a->target->symtree->name))
+               {
+                 gfc_error ("Selector at %L duplicates selector at %L",
+                            &newAssoc->target->where, &a->target->where);
+                 goto assocListError;
+               }
+           }
+       }
+
       /* The target expression must not be coindexed.  */
       if (gfc_is_coindexed (newAssoc->target))
        {
@@ -2108,8 +2164,40 @@ gfc_match_associate (void)
 
 assocListError:
       free (newAssoc);
+      return MATCH_ERROR;
+    }
+
+  return MATCH_YES;
+}
+
+/* Match an ASSOCIATE statement.  */
+
+match
+gfc_match_associate (void)
+{
+  match m;
+  if (gfc_match_label () == MATCH_ERROR)
+    return MATCH_ERROR;
+
+  if (gfc_match (" associate") != MATCH_YES)
+    return MATCH_NO;
+
+  /* Match the association list.  */
+  if (gfc_match_char ('(') != MATCH_YES)
+    {
+      gfc_error ("Expected association list at %C");
+      return MATCH_ERROR;
+    }
+
+  m = match_association_list ();
+  if (m == MATCH_ERROR)
+    goto error;
+  else if (m == MATCH_NO)
+    {
+      gfc_error ("Expected association at %C");
       goto error;
     }
+
   if (gfc_match_char (')') != MATCH_YES)
     {
       /* This should never happen as we peek above.  */
@@ -3914,7 +4002,9 @@ match
 gfc_match_form_team (void)
 {
   match m;
-  gfc_expr *teamid,*team;
+  gfc_expr *teamid, *team, *new_index;
+
+  teamid = team = new_index = NULL;
 
   if (!gfc_notify_std (GFC_STD_F2018, "FORM TEAM statement at %C"))
     return MATCH_ERROR;
@@ -3932,18 +4022,61 @@ gfc_match_form_team (void)
   if (gfc_match ("%e", &team) != MATCH_YES)
     goto syntax;
 
-  m = gfc_match_char (')');
+  m = gfc_match_char (',');
+  if (m == MATCH_ERROR)
+    goto syntax;
   if (m == MATCH_NO)
+    {
+      m = gfc_match_char (')');
+      if (m == MATCH_YES)
+       goto done;
+      goto syntax;
+    }
+
+  for (;;)
+    {
+      m = match_stat_errmsg (&new_st.ext.sync_stat, ST_FORM_TEAM);
+      if (m == MATCH_ERROR)
+       goto cleanup;
+
+      m = match_named_arg (" new_index = %e", "NEW_INDEX", &new_index,
+                          ST_FORM_TEAM);
+      if (m == MATCH_ERROR)
+       goto cleanup;
+
+      m = gfc_match_char (',');
+      if (m == MATCH_YES)
+       continue;
+
+      break;
+    }
+
+  if (m == MATCH_ERROR)
     goto syntax;
 
+  if (gfc_match (" )%t") != MATCH_YES)
+    goto syntax;
+
+done:
+
   new_st.expr1 = teamid;
   new_st.expr2 = team;
+  new_st.expr3 = new_index;
 
   return MATCH_YES;
 
 syntax:
   gfc_syntax_error (ST_FORM_TEAM);
 
+cleanup:
+  gfc_free_expr (new_index);
+  gfc_free_expr (new_st.ext.sync_stat.stat);
+  gfc_free_expr (new_st.ext.sync_stat.errmsg);
+  new_st.ext.sync_stat = {NULL, NULL};
+
+  gfc_free_expr (team);
+  gfc_free_expr (teamid);
+
   return MATCH_ERROR;
 }
 
@@ -3953,7 +4086,13 @@ match
 gfc_match_change_team (void)
 {
   match m;
-  gfc_expr *team;
+  gfc_expr *team = NULL;
+
+  if (gfc_match_label () == MATCH_ERROR)
+    return MATCH_ERROR;
+
+  if (gfc_match (" change% team") != MATCH_YES)
+    return MATCH_NO;
 
   if (!gfc_notify_std (GFC_STD_F2018, "CHANGE TEAM statement at %C"))
     return MATCH_ERROR;
@@ -3961,15 +4100,41 @@ gfc_match_change_team (void)
   if (gfc_match_char ('(') == MATCH_NO)
     goto syntax;
 
-  new_st.op = EXEC_CHANGE_TEAM;
-
   if (gfc_match ("%e", &team) != MATCH_YES)
     goto syntax;
 
-  m = gfc_match_char (')');
+  m = gfc_match_char (',');
+  if (m == MATCH_ERROR)
+    goto syntax;
   if (m == MATCH_NO)
+    {
+      m = gfc_match_char (')');
+      if (m == MATCH_YES)
+       goto done;
+      goto syntax;
+    }
+
+  m = match_association_list (true);
+  if (m == MATCH_ERROR)
+    goto cleanup;
+  else if (m == MATCH_NO)
+    for (;;)
+      {
+       m = match_stat_errmsg (&new_st.ext.block.sync_stat, ST_CHANGE_TEAM);
+       if (m == MATCH_ERROR)
+         goto cleanup;
+
+       if (gfc_match_char (',') == MATCH_YES)
+         continue;
+
+       break;
+      }
+
+  if (gfc_match (" )%t") != MATCH_YES)
     goto syntax;
 
+done:
+
   new_st.expr1 = team;
 
   return MATCH_YES;
@@ -3977,20 +4142,46 @@ gfc_match_change_team (void)
 syntax:
   gfc_syntax_error (ST_CHANGE_TEAM);
 
+cleanup:
+  gfc_free_expr (new_st.ext.block.sync_stat.stat);
+  gfc_free_expr (new_st.ext.block.sync_stat.errmsg);
+  new_st.ext.block.sync_stat = {NULL, NULL};
+  gfc_free_association_list (new_st.ext.block.assoc);
+  new_st.ext.block.assoc = NULL;
+  gfc_free_expr (team);
+
   return MATCH_ERROR;
 }
 
-/* Match a END TEAM statement.  */
+/* Match an END TEAM statement.  */
 
 match
 gfc_match_end_team (void)
 {
-  if (!gfc_notify_std (GFC_STD_F2018, "END TEAM statement at %C"))
-    return MATCH_ERROR;
+  if (gfc_match_eos () == MATCH_YES)
+    goto done;
 
-  if (gfc_match_char ('(') == MATCH_YES)
+  if (gfc_match_char ('(') != MATCH_YES)
+    /* There could be a team-construct-name following.  Let caller decide
+       about error.  */
+    return MATCH_NO;
+
+  for (;;)
+    {
+      if (match_stat_errmsg (&new_st.ext.sync_stat, ST_END_TEAM) == 
MATCH_ERROR)
+       goto cleanup;
+
+      if (gfc_match_char (',') == MATCH_YES)
+       continue;
+
+      break;
+    }
+
+  if (gfc_match_char (')') != MATCH_YES)
     goto syntax;
 
+done:
+
   new_st.op = EXEC_END_TEAM;
 
   return MATCH_YES;
@@ -3998,6 +4189,14 @@ gfc_match_end_team (void)
 syntax:
   gfc_syntax_error (ST_END_TEAM);
 
+cleanup:
+  gfc_free_expr (new_st.ext.sync_stat.stat);
+  gfc_free_expr (new_st.ext.sync_stat.errmsg);
+  new_st.ext.sync_stat = {NULL, NULL};
+
+  /* Try to match the closing bracket to allow error recovery.  */
+  gfc_match_char (')');
+
   return MATCH_ERROR;
 }
 
@@ -5358,6 +5557,15 @@ gfc_match_return (void)
       return MATCH_ERROR;
     }
 
+  if (gfc_find_state (COMP_CHANGE_TEAM))
+    {
+      /* F2018, C1111: A RETURN statement shall not appear within a CHANGE TEAM
+        construct.  */
+      gfc_error (
+       "Image control statement RETURN at %C in CHANGE TEAM-END TEAM block");
+      return MATCH_ERROR;
+    }
+
   if (gfc_match_eos () == MATCH_YES)
     goto done;
 
diff --git a/gcc/fortran/parse.cc b/gcc/fortran/parse.cc
index a95bb62afb8c..538eb65b4bfe 100644
--- a/gcc/fortran/parse.cc
+++ b/gcc/fortran/parse.cc
@@ -488,6 +488,7 @@ decode_statement (void)
   match (NULL, gfc_match_do, ST_DO);
   match (NULL, gfc_match_block, ST_BLOCK);
   match (NULL, gfc_match_associate, ST_ASSOCIATE);
+  match (NULL, gfc_match_change_team, ST_CHANGE_TEAM);
   match (NULL, gfc_match_critical, ST_CRITICAL);
   match (NULL, gfc_match_select, ST_SELECT_CASE);
   match (NULL, gfc_match_select_type, ST_SELECT_TYPE);
@@ -517,7 +518,6 @@ decode_statement (void)
 
     case 'c':
       match ("call", gfc_match_call, ST_CALL);
-      match ("change% team", gfc_match_change_team, ST_CHANGE_TEAM);
       match ("close", gfc_match_close, ST_CLOSE);
       match ("continue", gfc_match_continue, ST_CONTINUE);
       match ("contiguous", gfc_match_contiguous, ST_ATTR_DECL);
@@ -537,7 +537,6 @@ decode_statement (void)
 
     case 'e':
       match ("end file", gfc_match_endfile, ST_END_FILE);
-      match ("end team", gfc_match_end_team, ST_END_TEAM);
       match ("exit", gfc_match_exit, ST_EXIT);
       match ("else", gfc_match_else, ST_ELSE);
       match ("else where", gfc_match_elsewhere, ST_ELSEWHERE);
@@ -1927,8 +1926,7 @@ next_statement (void)
   case ST_OMP_INTEROP: \
   case ST_ERROR_STOP: case ST_OMP_SCAN: case ST_SYNC_ALL: \
   case ST_SYNC_IMAGES: case ST_SYNC_MEMORY: case ST_LOCK: case ST_UNLOCK: \
-  case ST_FORM_TEAM: case ST_CHANGE_TEAM: \
-  case ST_END_TEAM: case ST_SYNC_TEAM: \
+  case ST_FORM_TEAM: case ST_SYNC_TEAM: \
   case ST_EVENT_POST: case ST_EVENT_WAIT: case ST_FAIL_IMAGE: \
   case ST_OACC_UPDATE: case ST_OACC_WAIT: case ST_OACC_CACHE: \
   case ST_OACC_ENTER_DATA: case ST_OACC_EXIT_DATA
@@ -2032,7 +2030,8 @@ next_statement (void)
 
 #define case_end case ST_END_BLOCK_DATA: case ST_END_FUNCTION: \
                 case ST_END_PROGRAM: case ST_END_SUBROUTINE: \
-                case ST_END_BLOCK: case ST_END_ASSOCIATE
+                case ST_END_BLOCK: case ST_END_ASSOCIATE: \
+                case ST_END_TEAM
 
 
 /* Push a new state onto the stack.  */
@@ -2164,6 +2163,7 @@ check_statement_label (gfc_statement st)
     case ST_END_CRITICAL:
     case ST_END_BLOCK:
     case ST_END_ASSOCIATE:
+    case ST_END_TEAM:
     case_executable:
     case_exec_markers:
       if (st == ST_ENDDO || st == ST_CONTINUE)
@@ -3199,6 +3199,8 @@ accept_statement (gfc_statement st)
     case ST_ENTRY:
     case ST_OMP_METADIRECTIVE:
     case ST_OMP_BEGIN_METADIRECTIVE:
+    case ST_CHANGE_TEAM:
+    case ST_END_TEAM:
     case_executable:
     case_exec_markers:
       add_statement ();
@@ -3383,6 +3385,8 @@ verify_st_order (st_state *p, gfc_statement st, bool 
silent)
        goto order;
       break;
 
+    case ST_CHANGE_TEAM:
+    case ST_END_TEAM:
     case_executable:
     case_exec_markers:
       if (p->state < ORDER_EXEC)
@@ -5238,30 +5242,12 @@ parse_block_construct (void)
   pop_state ();
 }
 
-
-/* Parse an ASSOCIATE construct.  This is essentially a BLOCK construct
-   behind the scenes with compiler-generated variables.  */
-
 static void
-parse_associate (void)
+move_associates_to_block ()
 {
-  gfc_namespace* my_ns;
-  gfc_state_data s;
-  gfc_statement st;
-  gfc_association_list* a;
+  gfc_association_list *a;
   gfc_array_spec *as;
 
-  gfc_notify_std (GFC_STD_F2003, "ASSOCIATE construct at %C");
-
-  my_ns = gfc_build_block_ns (gfc_current_ns);
-
-  new_st.op = EXEC_BLOCK;
-  new_st.ext.block.ns = my_ns;
-  gcc_assert (new_st.ext.block.assoc);
-
-  /* Add all associate-names as BLOCK variables.  Creating them is enough
-     for now, they'll get their values during trans-* phase.  */
-  gfc_current_ns = my_ns;
   for (a = new_st.ext.block.assoc; a; a = a->next)
     {
       gfc_symbol *sym, *tsym;
@@ -5298,26 +5284,23 @@ parse_associate (void)
 
       /* Don’t share the character length information between associate
         variable and target if the length is not a compile-time constant,
-        as we don’t want to touch some other character length variable when
-        we try to initialize the associate variable’s character length
-        variable.
-        We do it here rather than later so that expressions referencing the
-        associate variable will automatically have the correctly setup length
-        information.  If we did it at resolution stage the expressions would
-        use the original length information, and the variable a new different
-        one, but only the latter one would be correctly initialized at
-        translation stage, and the former one would need some additional setup
-        there.  */
-      if (sym->ts.type == BT_CHARACTER
-         && sym->ts.u.cl
+        as we don’t want to touch some other character length variable
+        when we try to initialize the associate variable’s character
+        length variable.  We do it here rather than later so that expressions
+        referencing the associate variable will automatically have the
+        correctly setup length information.  If we did it at resolution stage
+        the expressions would use the original length information, and the
+        variable a new different one, but only the latter one would be
+        correctly initialized at translation stage, and the former one would
+        need some additional setup there.  */
+      if (sym->ts.type == BT_CHARACTER && sym->ts.u.cl
          && !(sym->ts.u.cl->length
               && sym->ts.u.cl->length->expr_type == EXPR_CONSTANT))
        sym->ts.u.cl = gfc_new_charlen (gfc_current_ns, NULL);
 
       /* If the function has been parsed, go straight to the result to
         obtain the expression rank.  */
-      if (target->expr_type == EXPR_FUNCTION
-         && target->symtree
+      if (target->expr_type == EXPR_FUNCTION && target->symtree
          && target->symtree->n.sym)
        {
          tsym = target->symtree->n.sym;
@@ -5344,8 +5327,7 @@ parse_associate (void)
         by calling gfc_resolve_expr because the context is unavailable.
         However, the references can be resolved and the rank of the target
         expression set.  */
-      if (!sym->assoc->inferred_type
-         && target->ref && gfc_resolve_ref (target)
+      if (!sym->assoc->inferred_type && target->ref && gfc_resolve_ref (target)
          && target->expr_type != EXPR_ARRAY
          && target->expr_type != EXPR_COMPCALL)
        gfc_expression_rank (target);
@@ -5353,13 +5335,12 @@ parse_associate (void)
       /* Determine whether or not function expressions with unknown type are
         structure constructors. If so, the function result can be converted
         to be a derived type.  */
-      if (target->expr_type == EXPR_FUNCTION
-         && target->ts.type == BT_UNKNOWN)
+      if (target->expr_type == EXPR_FUNCTION && target->ts.type == BT_UNKNOWN)
        {
          gfc_symbol *derived;
          /* The derived type has a leading uppercase character.  */
          gfc_find_symbol (gfc_dt_upper_string (target->symtree->name),
-                          my_ns->parent, 1, &derived);
+                          gfc_current_ns->parent, 1, &derived);
          if (derived && derived->attr.flavor == FL_DERIVED)
            {
              sym->ts.type = BT_DERIVED;
@@ -5394,7 +5375,7 @@ parse_associate (void)
                  attr.codimension = as->corank ? 1 : 0;
                  sym->assoc->variable = true;
                }
-              else if (rank || corank)
+             else if (rank || corank)
                {
                  as = gfc_get_array_spec ();
                  as->type = AS_DEFERRED;
@@ -5449,6 +5430,30 @@ parse_associate (void)
        }
       gfc_commit_symbols ();
     }
+}
+
+/* Parse an ASSOCIATE construct.  This is essentially a BLOCK construct
+   behind the scenes with compiler-generated variables.  */
+
+static void
+parse_associate (void)
+{
+  gfc_namespace* my_ns;
+  gfc_state_data s;
+  gfc_statement st;
+
+  gfc_notify_std (GFC_STD_F2003, "ASSOCIATE construct at %C");
+
+  my_ns = gfc_build_block_ns (gfc_current_ns);
+
+  new_st.op = EXEC_BLOCK;
+  new_st.ext.block.ns = my_ns;
+  gcc_assert (new_st.ext.block.assoc);
+
+  /* Add all associate-names as BLOCK variables.  Creating them is enough
+     for now, they'll get their values during trans-* phase.  */
+  gfc_current_ns = my_ns;
+  move_associates_to_block ();
 
   accept_statement (ST_ASSOCIATE);
   push_state (&s, COMP_ASSOCIATE, my_ns->proc_name);
@@ -5474,6 +5479,49 @@ loop:
   pop_state ();
 }
 
+static void
+parse_change_team (void)
+{
+  gfc_namespace *my_ns;
+  gfc_state_data s;
+  gfc_statement st;
+
+  gfc_notify_std (GFC_STD_F2018, "CHANGE TEAM construct at %C");
+
+  my_ns = gfc_build_block_ns (gfc_current_ns);
+
+  new_st.op = EXEC_CHANGE_TEAM;
+  new_st.ext.block.ns = my_ns;
+
+  /* Add all associate-names as BLOCK variables.  Creating them is enough
+     for now, they'll get their values during trans-* phase.  */
+  gfc_current_ns = my_ns;
+  if (new_st.ext.block.assoc)
+    move_associates_to_block ();
+
+  accept_statement (ST_CHANGE_TEAM);
+  push_state (&s, COMP_CHANGE_TEAM, my_ns->proc_name);
+
+loop:
+  st = parse_executable (ST_NONE);
+  switch (st)
+    {
+    case ST_NONE:
+      unexpected_eof ();
+
+    case_end:
+      accept_statement (st);
+      my_ns->code = gfc_state_stack->head;
+      break;
+
+    default:
+      unexpected_statement (st);
+      goto loop;
+    }
+
+  gfc_current_ns = gfc_current_ns->parent;
+  pop_state ();
+}
 
 /* Parse a DO loop.  Note that the ST_CYCLE and ST_EXIT statements are
    handled inside of parse_executable(), because they aren't really
@@ -6576,6 +6624,7 @@ parse_executable (gfc_statement st)
          case ST_STOP:
          case ST_ERROR_STOP:
          case ST_END_SUBROUTINE:
+         case ST_END_TEAM:
 
          case ST_DO:
          case ST_FORALL:
@@ -6615,6 +6664,10 @@ parse_executable (gfc_statement st)
          parse_associate ();
          break;
 
+       case ST_CHANGE_TEAM:
+         parse_change_team ();
+         break;
+
        case ST_IF_BLOCK:
          parse_if_block ();
          break;
diff --git a/gcc/fortran/parse.h b/gcc/fortran/parse.h
index 722e94cef541..7bf0fa497e92 100644
--- a/gcc/fortran/parse.h
+++ b/gcc/fortran/parse.h
@@ -32,7 +32,7 @@ enum gfc_compile_state
   COMP_DO, COMP_SELECT, COMP_FORALL, COMP_WHERE, COMP_CONTAINS, COMP_ENUM,
   COMP_SELECT_TYPE, COMP_SELECT_RANK, COMP_OMP_STRUCTURED_BLOCK, COMP_CRITICAL,
   COMP_DO_CONCURRENT, COMP_OMP_STRICTLY_STRUCTURED_BLOCK,
-  COMP_OMP_METADIRECTIVE, COMP_OMP_BEGIN_METADIRECTIVE
+  COMP_OMP_METADIRECTIVE, COMP_OMP_BEGIN_METADIRECTIVE, COMP_CHANGE_TEAM
 };
 
 /* Stack element for the current compilation state.  These structures
diff --git a/gcc/fortran/resolve.cc b/gcc/fortran/resolve.cc
index e9053b49392b..e51f83b6618b 100644
--- a/gcc/fortran/resolve.cc
+++ b/gcc/fortran/resolve.cc
@@ -11519,6 +11519,62 @@ gfc_resolve_sync_stat (struct sync_stat *sync_stat)
                                  gfc_default_character_kind,
                                  sync_stat->errmsg);
 }
+
+static void
+resolve_scalar_argument (const char *name, bt exp_type, int exp_kind,
+                        gfc_expr *e)
+{
+  gfc_resolve_expr (e);
+  if (e
+      && (e->ts.type != exp_type || e->ts.kind < exp_kind || e->rank != 0))
+    gfc_error ("%s argument at %L must be a scalar %s of at least kind %d",
+              name, &e->where, gfc_basic_typename (exp_type), exp_kind);
+}
+
+static void
+resolve_form_team (gfc_code *code)
+{
+  resolve_scalar_argument ("TEAM NUMBER", BT_INTEGER, gfc_default_integer_kind,
+                          code->expr1);
+  resolve_team_argument (code->expr2);
+  resolve_scalar_argument ("NEW_INDEX=", BT_INTEGER, gfc_default_integer_kind,
+                          code->expr3);
+  gfc_resolve_sync_stat (&code->ext.sync_stat);
+}
+
+static void resolve_block_construct (gfc_code *);
+
+static void
+resolve_change_team (gfc_code *code)
+{
+  resolve_team_argument (code->expr1);
+  gfc_resolve_sync_stat (&code->ext.block.sync_stat);
+  resolve_block_construct (code);
+  /* Map the coarray bounds as selected.  */
+  for (gfc_association_list *a = code->ext.block.assoc; a; a = a->next)
+    if (a->ar)
+      {
+       gfc_array_spec *src = a->ar->as, *dst;
+       if (a->st->n.sym->ts.type == BT_CLASS)
+         dst = CLASS_DATA (a->st->n.sym)->as;
+       else
+         dst = a->st->n.sym->as;
+       dst->corank = src->corank;
+       dst->cotype = src->cotype;
+       for (int i = 0; i < src->corank; ++i)
+         {
+           dst->lower[dst->rank + i] = src->lower[i];
+           dst->upper[dst->rank + i] = src->upper[i];
+           src->lower[i] = src->upper[i] = nullptr;
+         }
+       gfc_free_array_spec (src);
+       free (a->ar);
+       a->ar = nullptr;
+       dst->resolved = false;
+       gfc_resolve_array_spec (dst, 0);
+      }
+}
+
 static void
 resolve_sync_team (gfc_code *code)
 {
@@ -11665,8 +11721,8 @@ resolve_branch (gfc_st_label *label, gfc_code *code)
 
   if (code->here == label)
     {
-      gfc_warning (0,
-                  "Branch at %L may result in an infinite loop", &code->loc);
+      gfc_warning (0, "Branch at %L may result in an infinite loop",
+                  &code->loc);
       return;
     }
 
@@ -11689,6 +11745,10 @@ resolve_branch (gfc_st_label *label, gfc_code *code)
                   && bitmap_bit_p (stack->reachable_labels, label->value))
            gfc_error ("GOTO statement at %L leaves DO CONCURRENT construct "
                      "for label at %L", &code->loc, &label->where);
+         else if (stack->current->op == EXEC_CHANGE_TEAM
+                  && bitmap_bit_p (stack->reachable_labels, label->value))
+           gfc_error ("GOTO statement at %L leaves CHANGE TEAM construct "
+                     "for label at %L", &code->loc, &label->where);
        }
 
       return;
@@ -13325,23 +13385,6 @@ deferred_op_assign (gfc_code **code, gfc_namespace *ns)
 }
 
 
-static bool
-check_team (gfc_expr *team, const char *intrinsic)
-{
-  if (team->rank != 0
-      || team->ts.type != BT_DERIVED
-      || team->ts.u.derived->from_intmod != INTMOD_ISO_FORTRAN_ENV
-      || team->ts.u.derived->intmod_sym_id != ISOFORTRAN_TEAM_TYPE)
-    {
-      gfc_error ("TEAM argument to %qs at %L must be a scalar expression "
-                "of type TEAM_TYPE", intrinsic, &team->where);
-      return false;
-    }
-
-  return true;
-}
-
-
 /* Given a block of code, recursively resolve everything pointed to by this
    code block.  */
 
@@ -13530,15 +13573,11 @@ start:
          break;
 
        case EXEC_FORM_TEAM:
-         if (code->expr1 != NULL
-             && (code->expr1->ts.type != BT_INTEGER || code->expr1->rank))
-           gfc_error ("TEAM NUMBER argument to FORM TEAM at %L must be "
-                      "a scalar INTEGER", &code->expr1->where);
-         check_team (code->expr2, "FORM TEAM");
+         resolve_form_team (code);
          break;
 
        case EXEC_CHANGE_TEAM:
-         check_team (code->expr1, "CHANGE TEAM");
+         resolve_change_team (code);
          break;
 
        case EXEC_END_TEAM:
diff --git a/gcc/fortran/trans-decl.cc b/gcc/fortran/trans-decl.cc
index 5e5311e4f0c2..ae996a05f07e 100644
--- a/gcc/fortran/trans-decl.cc
+++ b/gcc/fortran/trans-decl.cc
@@ -4201,21 +4201,19 @@ gfc_build_builtin_function_decls (void)
            void_type_node, 3, pvoid_type_node, ppvoid_type_node,
            integer_type_node);
 
-      gfor_fndecl_caf_form_team
-       = gfc_build_library_function_decl_with_spec (
-           get_identifier (PREFIX("caf_form_team")), ". . W . ",
-           void_type_node, 3, integer_type_node, ppvoid_type_node,
-           integer_type_node);
+      gfor_fndecl_caf_form_team = gfc_build_library_function_decl_with_spec (
+       get_identifier (PREFIX ("caf_form_team")), ". r w r w w w ",
+       void_type_node, 6, integer_type_node, ppvoid_type_node, pint_type,
+       pint_type, pchar_type_node, size_type_node);
 
-      gfor_fndecl_caf_change_team
-       = gfc_build_library_function_decl_with_spec (
-           get_identifier (PREFIX("caf_change_team")), ". w . ",
-           void_type_node, 2, ppvoid_type_node,
-           integer_type_node);
+      gfor_fndecl_caf_change_team = gfc_build_library_function_decl_with_spec (
+       get_identifier (PREFIX ("caf_change_team")), ". r w w w ",
+       void_type_node, 4, pvoid_type_node, pint_type, pchar_type_node,
+       size_type_node);
 
-      gfor_fndecl_caf_end_team
-       = gfc_build_library_function_decl (
-           get_identifier (PREFIX("caf_end_team")), void_type_node, 0);
+      gfor_fndecl_caf_end_team = gfc_build_library_function_decl_with_spec (
+       get_identifier (PREFIX ("caf_end_team")), ". w w w ", void_type_node, 3,
+       pint_type, pchar_type_node, size_type_node);
 
       gfor_fndecl_caf_get_team
        = gfc_build_library_function_decl (
diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc
index 62dd38d6f9d0..276f325cc483 100644
--- a/gcc/fortran/trans-expr.cc
+++ b/gcc/fortran/trans-expr.cc
@@ -9836,7 +9836,12 @@ gfc_trans_subcomponent_assign (tree dest, gfc_component 
* cm,
           && !cm->attr.proc_pointer)
     {
       if (cm->attr.allocatable && expr->expr_type == EXPR_NULL)
-       gfc_conv_descriptor_data_set (&block, dest, null_pointer_node);
+       {
+         gfc_conv_descriptor_data_set (&block, dest, null_pointer_node);
+         if (cm->attr.codimension && flag_coarray == GFC_FCOARRAY_LIB)
+           gfc_add_modify (&block, gfc_conv_descriptor_token (dest),
+                           null_pointer_node);
+       }
       else if (cm->attr.allocatable || cm->attr.pdt_array)
        {
          tmp = gfc_trans_alloc_subarray_assign (dest, cm, expr);
diff --git a/gcc/fortran/trans-intrinsic.cc b/gcc/fortran/trans-intrinsic.cc
index 16ade8d4d552..cab3ebc00086 100644
--- a/gcc/fortran/trans-intrinsic.cc
+++ b/gcc/fortran/trans-intrinsic.cc
@@ -1379,9 +1379,9 @@ gfc_conv_intrinsic_caf_is_present_remote (gfc_se *se, 
gfc_expr *e)
   present_fn = e->value.function.actual->next->next->expr;
   add_data_sym = present_fn->symtree->n.sym->formal->sym;
 
-  fn_index = conv_caf_func_index (&se->pre, gfc_current_ns,
+  fn_index = conv_caf_func_index (&se->pre, e->symtree->n.sym->ns,
                                  "__caf_present_on_remote_fn_index_%d", hash);
-  add_data_tree = conv_caf_add_call_data (&se->pre, gfc_current_ns,
+  add_data_tree = conv_caf_add_call_data (&se->pre, e->symtree->n.sym->ns,
                                          "__caf_present_on_remote_add_data_%d",
                                          add_data_sym, &add_data_size);
   ++caf_call_cnt;
diff --git a/gcc/fortran/trans-stmt.cc b/gcc/fortran/trans-stmt.cc
index e79209e94aa0..f128b4c843ba 100644
--- a/gcc/fortran/trans-stmt.cc
+++ b/gcc/fortran/trans-stmt.cc
@@ -792,38 +792,42 @@ gfc_trans_form_team (gfc_code *code)
 {
   if (flag_coarray == GFC_FCOARRAY_LIB)
     {
-      gfc_se se;
-      gfc_se argse1, argse2;
-      tree team_id, team_type, tmp;
+      gfc_se se, argse;
+      tree team_id, team_type, new_index, stat, errmsg, errmsg_len, tmp;
 
       gfc_init_se (&se, NULL);
-      gfc_init_se (&argse1, NULL);
-      gfc_init_se (&argse2, NULL);
-      gfc_start_block (&se.pre);
+      gfc_init_se (&argse, NULL);
 
-      gfc_conv_expr_val (&argse1, code->expr1);
-      gfc_conv_expr_val (&argse2, code->expr2);
-      team_id = fold_convert (integer_type_node, argse1.expr);
-      team_type = gfc_build_addr_expr (ppvoid_type_node, argse2.expr);
+      gfc_conv_expr_val (&argse, code->expr1);
+      team_id = fold_convert (integer_type_node, argse.expr);
+      gfc_conv_expr_reference (&argse, code->expr2);
+      team_type = argse.expr;
 
-      gfc_add_block_to_block (&se.pre, &argse1.pre);
-      gfc_add_block_to_block (&se.pre, &argse2.pre);
-      tmp = build_call_expr_loc (input_location,
-                                gfor_fndecl_caf_form_team, 3,
-                                team_id, team_type,
-                                integer_zero_node);
+      /* NEW_INDEX=.  */
+      if (code->expr3)
+       {
+         gfc_conv_expr_reference (&argse, code->expr3);
+         new_index = argse.expr;
+       }
+      else
+       new_index = null_pointer_node;
+
+      gfc_add_block_to_block (&se.post, &argse.post);
+
+      gfc_trans_sync_stat (&code->ext.sync_stat, &se, &stat, &errmsg,
+                          &errmsg_len);
+
+      gfc_add_block_to_block (&se.pre, &argse.pre);
+
+      tmp = build_call_expr_loc (input_location, gfor_fndecl_caf_form_team, 6,
+                                team_id, team_type, new_index, stat, errmsg,
+                                errmsg_len);
       gfc_add_expr_to_block (&se.pre, tmp);
-      gfc_add_block_to_block (&se.pre, &argse1.post);
-      gfc_add_block_to_block (&se.pre, &argse2.post);
+      gfc_add_block_to_block (&se.pre, &se.post);
       return gfc_finish_block (&se.pre);
-    }
+     }
   else
-    {
-      const char *name = gfc_get_string (PREFIX ("exit_i%d"), 4);
-      gfc_symbol *exsym = gfc_get_intrinsic_sub_symbol (name);
-      tree tmp = gfc_get_symbol_decl (exsym);
-      return build_call_expr_loc (input_location, tmp, 1, integer_zero_node);
-    }
+    return trans_exit ();
 }
 
 /* Translate the CHANGE TEAM statement.  */
@@ -833,27 +837,30 @@ gfc_trans_change_team (gfc_code *code)
 {
   if (flag_coarray == GFC_FCOARRAY_LIB)
     {
-      gfc_se argse;
-      tree team_type, tmp;
+      stmtblock_t block;
+      gfc_se se;
+      tree team_type, stat, errmsg, errmsg_len, tmp;
 
-      gfc_init_se (&argse, NULL);
-      gfc_conv_expr_val (&argse, code->expr1);
-      team_type = gfc_build_addr_expr (ppvoid_type_node, argse.expr);
+      gfc_init_se (&se, NULL);
+      gfc_start_block (&block);
 
-      tmp = build_call_expr_loc (input_location,
-                                gfor_fndecl_caf_change_team, 2, team_type,
-                                integer_zero_node);
-      gfc_add_expr_to_block (&argse.pre, tmp);
-      gfc_add_block_to_block (&argse.pre, &argse.post);
-      return gfc_finish_block (&argse.pre);
+      gfc_conv_expr_val (&se, code->expr1);
+      team_type = se.expr;
+
+      gfc_trans_sync_stat (&code->ext.block.sync_stat, &se, &stat, &errmsg,
+                          &errmsg_len);
+
+      tmp = build_call_expr_loc (input_location, gfor_fndecl_caf_change_team, 
4,
+                                team_type, stat, errmsg, errmsg_len);
+
+      gfc_add_expr_to_block (&se.pre, tmp);
+      gfc_add_block_to_block (&se.pre, &se.post);
+      gfc_add_block_to_block (&block, &se.pre);
+      gfc_add_expr_to_block (&block, gfc_trans_block_construct (code));
+      return gfc_finish_block (&block);
     }
   else
-    {
-      const char *name = gfc_get_string (PREFIX ("exit_i%d"), 4);
-      gfc_symbol *exsym = gfc_get_intrinsic_sub_symbol (name);
-      tree tmp = gfc_get_symbol_decl (exsym);
-      return build_call_expr_loc (input_location, tmp, 1, integer_zero_node);
-    }
+    return trans_exit ();
 }
 
 /* Translate the END TEAM statement.  */
diff --git a/gcc/testsuite/gfortran.dg/team_change_2.f90 
b/gcc/testsuite/gfortran.dg/team_change_2.f90
new file mode 100644
index 000000000000..00cc489bf1fd
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/team_change_2.f90
@@ -0,0 +1,86 @@
+!{ dg-do compile }
+!{ dg-additional-options "-fcoarray=lib" }
+
+! PR 87939
+! Tests change team syntax
+
+  use iso_fortran_env, only : team_type
+  implicit none
+  type(team_type) :: team
+  integer :: new_team, istat
+  character(len=30) :: err
+  integer :: caf[*], caf2[*]
+
+  new_team = mod(this_image(),2)+1
+
+  form team (new_team,team)
+
+  change team !{ dg-error "Syntax error in CHANGE TEAM statement" }
+    continue
+  end team !{ dg-error "Expecting END PROGRAM statement" }
+
+  change team (err) !{ dg-error "must be a scalar expression of type 
TEAM_TYPE" }
+    continue
+  end team
+
+  change team (team, stat=err) !{ dg-error "must be a scalar INTEGER" }
+    continue
+  end team
+
+  change team (team, stat=istat, stat=istat) !{ dg-error "Duplicate STAT" }
+    continue
+  end team !{ dg-error "Expecting END PROGRAM statement" }
+
+  change team (team, stat=istat, errmsg=istat) !{ dg-error "must be a scalar 
CHARACTER variable" }
+    continue
+  end team
+
+  change team (team, stat=istat, errmsg=str, errmsg=str) !{ dg-error 
"Duplicate ERRMSG" }
+    continue
+  end team !{ dg-error "Expecting END PROGRAM statement" }
+
+1234 if (istat /= 0) stop 1 !{ dg-error "leaves CHANGE TEAM" }
+
+  change team (team)
+    go to 1234 !{ dg-error "leaves CHANGE TEAM" }
+  end team
+
+  call foo(team)
+
+  ! F2018, C1113
+  change team (team, caf[3,*] => caf) !{ dg-error "Codimension decl name" }
+    continue
+  end team !{ dg-error "Expecting END PROGRAM statement" }
+
+  change team (team, c[3,*] => caf, c => caf2) !{ dg-error "Duplicate name" }
+    continue
+  end team !{ dg-error "Expecting END PROGRAM statement" }
+
+  change team (team, c[3,*] => caf, caf => caf2) !{ dg-error "Codimension decl 
name" }
+    continue
+  end team !{ dg-error "Expecting END PROGRAM statement" }
+
+  change team (team, caf2[3,*] => caf, c => caf2) !{ dg-error "Codimension 
decl name" }
+    continue
+  end team !{ dg-error "Expecting END PROGRAM statement" }
+
+  ! F2018, C1114
+  change team (team, c => [caf, caf2]) !{ dg-error "a named coarray" }
+    continue
+  end team !{ dg-error "Expecting END PROGRAM statement" }
+
+  ! F2018, C1115
+  change team (team, c => caf, c2 => caf) !{ dg-error "duplicates selector at" 
}
+    continue
+  end team !{ dg-error "Expecting END PROGRAM statement" }
+
+contains
+  subroutine foo(team)
+    type(team_type) :: team
+
+    change team (team)
+      return !{ dg-error "Image control statement" }
+    end team
+  end subroutine
+end
+
diff --git a/gcc/testsuite/gfortran.dg/team_change_3.f90 
b/gcc/testsuite/gfortran.dg/team_change_3.f90
new file mode 100644
index 000000000000..bc30c40bb6d7
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/team_change_3.f90
@@ -0,0 +1,29 @@
+!{ dg-do run }
+!{ dg-additional-options "-fcoarray=lib -fdump-tree-original -lcaf_single" }
+!{ dg-additional-options "-latomic" { target libatomic_available } }
+
+! PR 87939
+! Tests change team stat= and errmsg= specifiers
+
+  use iso_fortran_env, only : team_type
+  implicit none
+  type(team_type) :: team
+  integer :: new_team, istat = 42
+  character(len=30) :: err = 'unchanged'
+
+  new_team = mod(this_image(),2)+1
+
+  form team (new_team,team)
+
+  change team (team, stat=istat)
+    if (istat /= 0) stop 1
+  end team
+
+  change team (team, stat=istat, errmsg=err)
+    if (trim(err) /= 'unchanged') stop 2
+  end team
+
+end
+
+! { dg-final { scan-tree-dump "_gfortran_caf_change_team \\(team, &istat, 0B, 
0\\)" "original" } }
+! { dg-final { scan-tree-dump "_gfortran_caf_change_team \\(team, &istat, 
&err, 30\\)" "original" } }
diff --git a/gcc/testsuite/gfortran.dg/team_end_2.f90 
b/gcc/testsuite/gfortran.dg/team_end_2.f90
new file mode 100644
index 000000000000..64f072aed3de
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/team_end_2.f90
@@ -0,0 +1,33 @@
+!{ dg-do compile }
+!{ dg-additional-options "-fcoarray=lib" }
+
+! PR 87939
+! Tests change team syntax
+
+  use iso_fortran_env, only : team_type
+  implicit none
+  type(team_type) :: team
+  integer :: new_team, istat
+  character(len=30) :: err
+
+  new_team = mod(this_image(),2)+1
+
+  form team (new_team,team)
+
+  change team (team)
+    continue
+  end team (stat=err) ! { dg-error "must be a scalar INTEGER" }
+
+  change team (team)
+    continue
+  end team (stat=istat, stat=istat) ! { dg-error "Duplicate STAT" }
+
+  change team (team)
+    continue
+  end team (stat=istat, errmsg=istat) ! { dg-error "must be a scalar CHARACTER 
variable" }
+
+  change team (team)
+    continue
+  end team (stat=istat, errmsg=err, errmsg=err) ! { dg-error "Duplicate 
ERRMSG" }
+end
+
diff --git a/gcc/testsuite/gfortran.dg/team_end_3.f90 
b/gcc/testsuite/gfortran.dg/team_end_3.f90
new file mode 100644
index 000000000000..5e004ada64f7
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/team_end_3.f90
@@ -0,0 +1,39 @@
+!{ dg-do run }
+!{ dg-additional-options "-fcoarray=lib -fdump-tree-original -lcaf_single" }
+!{ dg-additional-options "-latomic" { target libatomic_available } }
+
+! PR 87939
+! Tests end team stat= and errmsg= specifiers
+
+  use iso_fortran_env, only : team_type
+  implicit none
+  type(team_type) :: team
+  integer :: new_team, istat = 42
+  character(len=30) :: err = 'unchanged'
+  integer, allocatable :: sample(:)[:]
+  integer, allocatable :: scal_caf[:]
+
+  new_team = mod(this_image(),2)+1
+
+  form team (new_team,team)
+
+  change team (team)
+    allocate(sample(5)[*], scal_caf[*])
+    if (.NOT. allocated(sample)) stop 1
+    if (.NOT. allocated(scal_caf)) stop 2
+  end team (stat=istat)
+  if (istat /= 0) stop 3
+  if (allocated(sample)) stop 4
+  if (allocated(scal_caf)) stop 5
+
+  deallocate(sample, stat=istat)
+  if (istat == 0) stop 6
+
+  change team (team)
+    continue
+  end team (stat=istat, errmsg=err)
+  if (trim(err) /= 'unchanged') stop 7
+end
+
+! { dg-final { scan-tree-dump "_gfortran_caf_end_team \\(&istat, 0B, 0\\)" 
"original" } }
+! { dg-final { scan-tree-dump "_gfortran_caf_end_team \\(&istat, &err, 30\\)" 
"original" } }
diff --git a/gcc/testsuite/gfortran.dg/team_form_2.f90 
b/gcc/testsuite/gfortran.dg/team_form_2.f90
new file mode 100644
index 000000000000..5c6d81ffb873
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/team_form_2.f90
@@ -0,0 +1,27 @@
+!{ dg-do compile }
+!{ dg-additional-options "-fcoarray=lib" }
+
+! PR 87939
+! Tests form team syntax errors
+
+  use iso_fortran_env, only : team_type
+  implicit none
+  integer :: istat, new_team
+  character(len=30) :: err
+  type(team_type) :: team
+
+   new_team = mod(this_image(),2)+1
+
+  form team ! { dg-error "Syntax error in FORM TEAM statement" }
+  form team (new_team) ! { dg-error "Syntax error in FORM TEAM statement" }
+  form team (new_team,err) ! { dg-error "must be a scalar expression of type 
TEAM_TYPE" }
+  form team (new_team,team,istat) ! { dg-error "Syntax error in FORM TEAM 
statement" }
+  form team (new_team,team,stat=istat,stat=istat) ! { dg-error "Duplicate 
STAT" }
+  form team (new_team,team,stat=istat,errmsg=istat) ! { dg-error "must be a 
scalar CHARACTER variable" }
+  form team (new_team,team,stat=istat,errmsg=err,errmsg=err) ! { dg-error 
"Duplicate ERRMSG" }
+  form team (new_team,team,new_index=1,new_index=1) ! { dg-error "Duplicate 
NEW_INDEX" }
+  form team (new_team,team,new_index=err) ! { dg-error "must be a scalar 
INTEGER" }
+  form team (new_team,team,new_index=1,new_index=1,stat=istat,errmsg=err) ! { 
dg-error "Duplicate NEW_INDEX" }
+  form team (new_team,team,new_index=1,stat=istat,errmsg=err,new_index=9) ! { 
dg-error "Duplicate NEW_INDEX" }
+
+end
diff --git a/gcc/testsuite/gfortran.dg/team_form_3.f90 
b/gcc/testsuite/gfortran.dg/team_form_3.f90
new file mode 100644
index 000000000000..d9aae3376ead
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/team_form_3.f90
@@ -0,0 +1,34 @@
+!{ dg-do run }
+!{ dg-additional-options "-fcoarray=lib -fdump-tree-original -lcaf_single" }
+!{ dg-additional-options "-latomic" { target libatomic_available } }
+
+! PR 87939
+! Tests form team with stat= and errmsg=
+
+  use iso_fortran_env, only : team_type
+  implicit none
+  integer :: istat = 42, new_team
+  character(len=30) :: err = "unchanged"
+  type(team_type) :: team
+
+  new_team = mod(this_image(),2)+1
+
+  form team (new_team,team)
+  form team (new_team,team,stat=istat)
+  if (istat /= 0) stop 1
+  form team (new_team,team,stat=istat, errmsg=err)
+  if (trim(err) /= 'unchanged') stop 2
+  form team (new_team,team,new_index=1)
+  istat = 42
+  form team (new_team,team,new_index=1,stat=istat)
+  if (istat /= 0) stop 3
+  form team (new_team,team,new_index=1,stat=istat,errmsg=err)
+  if (trim(err) /= 'unchanged') stop 4
+end
+
+! { dg-final { scan-tree-dump "_gfortran_caf_form_team \\(new_team, &team, 0B, 
0B, 0B, 0\\)" "original" } }
+! { dg-final { scan-tree-dump "_gfortran_caf_form_team \\(new_team, &team, 0B, 
&istat, 0B, 0\\)" "original" } }
+! { dg-final { scan-tree-dump "_gfortran_caf_form_team \\(new_team, &team, 0B, 
&istat, &err, 30\\)" "original" } }
+! { dg-final { scan-tree-dump "_gfortran_caf_form_team \\(new_team, &team, 
&C\\.\[0-9\]+, 0B, 0B, 0\\)" "original" } }
+! { dg-final { scan-tree-dump "_gfortran_caf_form_team \\(new_team, &team, 
&C\\.\[0-9\]+, &istat, 0B, 0\\)" "original" } }
+! { dg-final { scan-tree-dump "_gfortran_caf_form_team \\(new_team, &team, 
&C\\.\[0-9\]+, &istat, &err, 30\\)" "original" } }
diff --git a/gcc/testsuite/gfortran.dg/team_sync_2.f90 
b/gcc/testsuite/gfortran.dg/team_sync_2.f90
new file mode 100644
index 000000000000..947f65db8957
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/team_sync_2.f90
@@ -0,0 +1,27 @@
+!{ dg-do run }
+!{ dg-additional-options "-fcoarray=lib -fdump-tree-original -lcaf_single" }
+!{ dg-additional-options "-latomic" { target libatomic_available } }
+
+! PR 87939
+! Test sync team statement
+!
+  use iso_fortran_env, only : team_type
+  implicit none
+  integer :: istat = 42
+  type(team_type) :: team
+  character(len=30) :: err = "unchanged"
+
+  form team (mod(this_image(),2)+1, team)
+
+  change team (team)
+    sync team (team)
+    sync team (team, stat=istat)
+    if (istat /= 0) stop 1
+    sync team (team, stat=istat, errmsg=err)
+    if (trim(err) /= 'unchanged') stop 2
+  end team
+end
+
+! { dg-final { scan-tree-dump "_gfortran_caf_sync_team \\(team, 0B, 0B, 0\\)" 
"original" } }
+! { dg-final { scan-tree-dump "_gfortran_caf_sync_team \\(team, &istat, 0B, 
0\\)" "original" } }
+! { dg-final { scan-tree-dump "_gfortran_caf_sync_team \\(team, &istat, &err, 
30\\)" "original" } }
diff --git a/libgfortran/caf/libcaf.h b/libgfortran/caf/libcaf.h
index 0b371d02a18b..a674a1929e53 100644
--- a/libgfortran/caf/libcaf.h
+++ b/libgfortran/caf/libcaf.h
@@ -31,17 +31,6 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If 
not, see
 
 #include "libgfortran.h"
 
-#if 0
-#ifndef __GNUC__
-#define __attribute__(x)
-#define likely(x)       (x)
-#define unlikely(x)     (x)
-#else
-#define likely(x)       __builtin_expect(!!(x), 1)
-#define unlikely(x)     __builtin_expect(!!(x), 0)
-#endif
-#endif
-
 /* Definitions of the Fortran 2008 standard; need to kept in sync with
    ISO_FORTRAN_ENV, cf. gcc/fortran/libgfortran.h.  */
 typedef enum
@@ -78,8 +67,8 @@ typedef enum caf_deregister_t {
 }
 caf_deregister_t;
 
-typedef void* caf_token_t;
-typedef void * caf_team_t;
+typedef void *caf_token_t;
+typedef void *caf_team_t;
 typedef gfc_array_void gfc_descriptor_t;
 
 /* Linked list of static coarrays registered.  */
@@ -185,4 +174,9 @@ void _gfortran_caf_stopped_images (gfc_descriptor_t *,
 
 void _gfortran_caf_random_init (bool, bool);
 
+void _gfortran_caf_form_team (int, caf_team_t *, int *, int *, char *, size_t);
+void _gfortran_caf_change_team (caf_team_t, int *, char *, size_t);
+void _gfortran_caf_end_team (int *, char *, size_t);
+void _gfortran_caf_sync_team (caf_team_t, int *, char *, size_t);
+
 #endif  /* LIBCAF_H  */
diff --git a/libgfortran/caf/single.c b/libgfortran/caf/single.c
index 1d7af6b89722..a705699bfa93 100644
--- a/libgfortran/caf/single.c
+++ b/libgfortran/caf/single.c
@@ -50,6 +50,21 @@ typedef struct caf_single_token *caf_single_token_t;
 #define TOKEN(X) ((caf_single_token_t) (X))
 #define MEMTOK(X) ((caf_single_token_t) (X))->memptr
 
+struct caf_single_team
+{
+  struct caf_single_team *parent;
+  int team_no;
+  struct coarray_allocated
+  {
+    struct coarray_allocated *next;
+    caf_single_token_t token;
+  } *allocated;
+};
+typedef struct caf_single_team *caf_single_team_t;
+/* This points to the most current team.  */
+static caf_single_team_t caf_team_stack = NULL, caf_initial_team;
+static caf_single_team_t caf_teams_formed = NULL;
+
 /* Single-image implementation of the CAF library.
    Note: For performance reasons -fcoarry=single should be used
    rather than this library.  */
@@ -125,13 +140,39 @@ caf_internal_error (const char *msg, int *stat, char 
*errmsg,
   va_end (args);
 }
 
+static void
+init_caf_team_stack (void)
+{
+  caf_initial_team = caf_team_stack
+    = (caf_single_team_t) calloc (1, sizeof (struct caf_single_team));
+  caf_initial_team->team_no = -1;
+}
 
 void
 _gfortran_caf_init (int *argc __attribute__ ((unused)),
                    char ***argv __attribute__ ((unused)))
 {
+  if (likely (!caf_team_stack))
+    init_caf_team_stack ();
 }
 
+static void
+free_team_list (caf_single_team_t l)
+{
+  while (l != NULL)
+    {
+      caf_single_team_t p = l->parent;
+      struct coarray_allocated *ca = l->allocated;
+      while (ca)
+       {
+         struct coarray_allocated *nca = ca->next;
+         free (ca);
+         ca = nca;
+       }
+      free (l);
+      l = p;
+    }
+}
 
 void
 _gfortran_caf_finalize (void)
@@ -146,6 +187,11 @@ _gfortran_caf_finalize (void)
       free (caf_static_list);
       caf_static_list = tmp;
     }
+
+  free_team_list (caf_team_stack);
+  caf_initial_team = caf_team_stack = NULL;
+  free_team_list (caf_teams_formed);
+  caf_teams_formed = NULL;
 }
 
 
@@ -206,6 +252,8 @@ _gfortran_caf_register (size_t size, caf_register_t type, 
caf_token_t *token,
   single_token->owning_memory = type != 
CAF_REGTYPE_COARRAY_ALLOC_REGISTER_ONLY;
   single_token->desc = GFC_DESCRIPTOR_RANK (data) > 0 ? data : NULL;
 
+  if (unlikely (!caf_team_stack))
+    init_caf_team_stack ();
 
   if (stat)
     *stat = 0;
@@ -219,6 +267,20 @@ _gfortran_caf_register (size_t size, caf_register_t type, 
caf_token_t *token,
       tmp->token = *token;
       caf_static_list = tmp;
     }
+  else
+    {
+      struct coarray_allocated *ca = caf_team_stack->allocated;
+      for (; ca && ca->token != single_token; ca = ca->next)
+       ;
+      if (!ca)
+       {
+         ca = (struct coarray_allocated *) malloc (
+           sizeof (struct coarray_allocated));
+         *ca = (struct coarray_allocated) {caf_team_stack->allocated,
+                                           single_token};
+         caf_team_stack->allocated = ca;
+       }
+    }
   GFC_DESCRIPTOR_DATA (data) = local;
 }
 
@@ -231,10 +293,30 @@ _gfortran_caf_deregister (caf_token_t *token, 
caf_deregister_t type, int *stat,
   caf_single_token_t single_token = TOKEN (*token);
 
   if (single_token->owning_memory && single_token->memptr)
-    free (single_token->memptr);
+    {
+      free (single_token->memptr);
+      if (single_token->desc)
+       GFC_DESCRIPTOR_DATA (single_token->desc) = NULL;
+    }
 
   if (type != CAF_DEREGTYPE_COARRAY_DEALLOCATE_ONLY)
     {
+      struct coarray_allocated *ca = caf_team_stack->allocated;
+      if (ca && caf_team_stack->allocated->token == single_token)
+       caf_team_stack->allocated = ca->next;
+      else
+       {
+         struct coarray_allocated *pca = NULL;
+         for (; ca && ca->token != single_token; pca = ca, ca = ca->next)
+           ;
+         if (!ca)
+           caf_runtime_error (
+             "Coarray token to be freeed is not in current team %d", type);
+         /* Unhook found coarray_allocated node from list...  */
+         pca->next = ca->next;
+       }
+      /* ... and free.  */
+      free (ca);
       free (TOKEN (*token));
       *token = NULL;
     }
@@ -599,11 +681,10 @@ _gfortran_caf_is_present_on_remote (caf_token_t token, 
const int image_index,
   int32_t result;
   struct caf_single_token cb_token = {add_data, NULL, false};
 
-
-  accessor_hash_table[present_index].u.is_present (add_data, &image_index,
-                                                  &result,
-                                                  single_token->memptr,
-                                                  &cb_token, 0);
+  accessor_hash_table[present_index].u.is_present (
+    add_data, &image_index, &result,
+    single_token->desc ? single_token->desc : (void *) &single_token->memptr,
+    &cb_token, 0);
 
   return result;
 }
@@ -923,3 +1004,83 @@ void _gfortran_caf_random_init (bool repeatable, bool 
image_distinct)
      routine.  */
   _gfortran_random_init (repeatable, image_distinct, 1);
 }
+
+void
+_gfortran_caf_form_team (int team_no, caf_team_t *team,
+                        int *new_index __attribute__ ((unused)), int *stat,
+                        char *errmsg __attribute__ ((unused)),
+                        size_t errmsg_len __attribute__ ((unused)))
+{
+  const char alloc_fail_msg[] = "Failed to allocate team";
+  caf_single_team_t t;
+  if (stat)
+    *stat = 0;
+
+  *team = malloc (sizeof (struct caf_single_team));
+  if (unlikely (*team == NULL))
+    {
+      caf_internal_error (alloc_fail_msg, stat, errmsg, errmsg_len);
+      return;
+    }
+  t = *((caf_single_team_t *) team);
+  t->parent = caf_teams_formed;
+  t->team_no = team_no;
+  t->allocated = NULL;
+  caf_teams_formed = t;
+}
+
+void
+_gfortran_caf_change_team (caf_team_t team, int *stat,
+                          char *errmsg __attribute__ ((unused)),
+                          size_t errmsg_len __attribute__ ((unused)))
+{
+  caf_single_team_t t = (caf_single_team_t) team;
+
+  if (stat)
+    *stat = 0;
+
+  if (t == caf_teams_formed)
+    caf_teams_formed = t->parent;
+  else
+    for (caf_single_team_t p = caf_teams_formed; p; p = p->parent)
+      if (p->parent == t)
+       {
+         p->parent = t->parent;
+         break;
+       }
+
+  t->parent = caf_team_stack;
+  caf_team_stack = t;
+}
+
+void
+_gfortran_caf_end_team (int *stat, char *errmsg, size_t errmsg_len)
+{
+  caf_single_team_t t = caf_team_stack;
+
+  if (stat)
+    *stat = 0;
+
+  caf_team_stack = caf_team_stack->parent;
+  for (struct coarray_allocated *ca = t->allocated; ca;)
+    {
+      struct coarray_allocated *nca = ca->next;
+      _gfortran_caf_deregister ((caf_token_t *) &ca->token,
+                               CAF_DEREGTYPE_COARRAY_DEALLOCATE_ONLY, stat,
+                               errmsg, errmsg_len);
+      free (ca);
+      ca = nca;
+    }
+  t->allocated = NULL;
+  t->parent = caf_teams_formed;
+  caf_teams_formed = t;
+}
+
+void
+_gfortran_caf_sync_team (caf_team_t team __attribute__ ((unused)), int *stat,
+                        char *errmsg __attribute__ ((unused)),
+                        size_t errmsg_len __attribute__ ((unused)))
+{
+  if (stat)
+    *stat = 0;
+}

Reply via email to