[gcc/devel/omp/gcc-14] OpenMP: Reprocess expanded clauses after 'declare mapper' instantiation

Paul-Antoine Arras parras@gcc.gnu.org
Fri Jun 28 09:54:27 GMT 2024


https://gcc.gnu.org/g:628859fb41e9f21b9ee048efa5723b1ab4a39a63

commit 628859fb41e9f21b9ee048efa5723b1ab4a39a63
Author: Julian Brown <julian@codesourcery.com>
Date:   Sat Jul 15 09:16:44 2023 +0000

    OpenMP: Reprocess expanded clauses after 'declare mapper' instantiation
    
    This patch reprocesses expanded clauses after 'declare mapper'
    instantiation -- checking things such as duplicated clauses, illegal
    use of strided accesses, and so forth.  Two functions are broken out
    of the 'resolve_omp_clauses' function and reused in a new function
    'resolve_omp_mapper_clauses', called after mapper instantiation.
    
    This improves diagnostic output.
    
    2023-08-10  Julian Brown  <julian@codesourcery.com>
    
    gcc/fortran/
            * gfortran.h (gfc_omp_clauses): Add NS field.
            * openmp.cc (verify_omp_clauses_symbol_dups,
            omp_verify_map_motion_clauses): New functions, broken out of...
            (resolve_omp_clauses): Here.  Record namespace containing clauses.
            Call above functions.
            (resolve_omp_mapper_clauses): New function, using helper functions
            broken out above.
            (gfc_resolve_omp_directive): Add NS parameter to resolve_omp_clauses
            calls.
            (gfc_omp_instantiate_mappers): Call resolve_omp_mapper_clauses if we
            instantiate any mappers.
    
    gcc/testsuite/
            * gfortran.dg/gomp/declare-mapper-26.f90: New test.
            * gfortran.dg/gomp/declare-mapper-29.f90: New test.

Diff:
---
 gcc/fortran/ChangeLog.omp                          |   14 +
 gcc/fortran/gfortran.h                             |    1 +
 gcc/fortran/openmp.cc                              | 1123 +++++++++++---------
 gcc/testsuite/ChangeLog.omp                        |    5 +
 .../gfortran.dg/gomp/declare-mapper-26.f90         |   28 +
 .../gfortran.dg/gomp/declare-mapper-29.f90         |   22 +
 6 files changed, 672 insertions(+), 521 deletions(-)

diff --git a/gcc/fortran/ChangeLog.omp b/gcc/fortran/ChangeLog.omp
index 95b3bb90e8f..515a30cd557 100644
--- a/gcc/fortran/ChangeLog.omp
+++ b/gcc/fortran/ChangeLog.omp
@@ -1,3 +1,17 @@
+2023-08-10  Julian Brown  <julian@codesourcery.com>
+
+	* gfortran.h (gfc_omp_clauses): Add NS field.
+	* openmp.cc (verify_omp_clauses_symbol_dups,
+	omp_verify_map_motion_clauses): New functions, broken out of...
+	(resolve_omp_clauses): Here.  Record namespace containing clauses.
+	Call above functions.
+	(resolve_omp_mapper_clauses): New function, using helper functions
+	broken out above.
+	(gfc_resolve_omp_directive): Add NS parameter to resolve_omp_clauses
+	calls.
+	(gfc_omp_instantiate_mappers): Call resolve_omp_mapper_clauses if we
+	instantiate any mappers.
+
 2023-08-10  Julian Brown  <julian@codesourcery.com>
 
 	* gfortran.h (toc_directive): Move here.
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 3d4abfc6cfd..491a1498279 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -1590,6 +1590,7 @@ typedef struct gfc_omp_clauses
   struct gfc_omp_assumptions *assume;
   struct gfc_expr_list *sizes_list;
   const char *critical_name;
+  gfc_namespace *ns;
   enum gfc_omp_default_sharing default_sharing;
   enum gfc_omp_atomic_op atomic_op;
   enum gfc_omp_defaultmap defaultmap[OMP_DEFAULTMAP_CAT_NUM];
diff --git a/gcc/fortran/openmp.cc b/gcc/fortran/openmp.cc
index e7bb4dc80b7..574c1b2ba0c 100644
--- a/gcc/fortran/openmp.cc
+++ b/gcc/fortran/openmp.cc
@@ -7822,246 +7822,18 @@ gfc_resolve_omp_assumptions (gfc_omp_assumptions *assume)
 		 &el->expr->where);
 }
 
-
-/* OpenMP directive resolving routines.  */
+/* Check OMP_CLAUSES for duplicate symbols and various other constraints.
+   Helper function for resolve_omp_clauses and resolve_omp_mapper_clauses.  */
 
 static void
-resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses,
-		     gfc_namespace *ns, bool openacc = false)
+verify_omp_clauses_symbol_dups (gfc_code *code, gfc_omp_clauses *omp_clauses,
+				gfc_namespace *ns, bool openacc)
 {
-  gfc_omp_namelist *n, *last;
-  gfc_expr_list *el;
+  gfc_omp_namelist *n;
   int list;
-  int ifc;
-  bool if_without_mod = false;
-  gfc_omp_linear_op linear_op = OMP_LINEAR_DEFAULT;
-  static const char *clause_names[]
-    = { "PRIVATE", "FIRSTPRIVATE", "LASTPRIVATE", "COPYPRIVATE", "SHARED",
-	"COPYIN", "UNIFORM", "AFFINITY", "ALIGNED", "LINEAR", "DEPEND", "MAP",
-	"TO", "FROM", "INCLUSIVE", "EXCLUSIVE",
-	"REDUCTION", "REDUCTION" /*inscan*/, "REDUCTION" /*task*/,
-	"IN_REDUCTION", "TASK_REDUCTION",
-	"DEVICE_RESIDENT", "LINK", "USE_DEVICE",
-	"CACHE", "IS_DEVICE_PTR", "USE_DEVICE_PTR", "USE_DEVICE_ADDR",
-	"NONTEMPORAL", "ALLOCATE", "HAS_DEVICE_ADDR", "ENTER",
-	"USES_ALLOCATORS" };
-  STATIC_ASSERT (ARRAY_SIZE (clause_names) == OMP_LIST_NUM);
-
-  if (omp_clauses == NULL)
-    return;
-
-  if (ns == NULL)
-    ns = gfc_current_ns;
-
-  if (omp_clauses->orderedc && omp_clauses->orderedc < omp_clauses->collapse)
-    gfc_error ("ORDERED clause parameter is less than COLLAPSE at %L",
-	       &code->loc);
-  if (omp_clauses->order_concurrent && omp_clauses->ordered)
-    gfc_error ("ORDER clause must not be used together ORDERED at %L",
-	       &code->loc);
-  if (omp_clauses->if_expr)
-    {
-      gfc_expr *expr = omp_clauses->if_expr;
-      if (!gfc_resolve_expr (expr)
-	  || expr->ts.type != BT_LOGICAL || expr->rank != 0)
-	gfc_error ("IF clause at %L requires a scalar LOGICAL expression",
-		   &expr->where);
-      if_without_mod = true;
-    }
-  for (ifc = 0; ifc < OMP_IF_LAST; ifc++)
-    if (omp_clauses->if_exprs[ifc])
-      {
-	gfc_expr *expr = omp_clauses->if_exprs[ifc];
-	bool ok = true;
-	if (!gfc_resolve_expr (expr)
-	    || expr->ts.type != BT_LOGICAL || expr->rank != 0)
-	  gfc_error ("IF clause at %L requires a scalar LOGICAL expression",
-		     &expr->where);
-	else if (if_without_mod)
-	  {
-	    gfc_error ("IF clause without modifier at %L used together with "
-		       "IF clauses with modifiers",
-		       &omp_clauses->if_expr->where);
-	    if_without_mod = false;
-	  }
-	else
-	  switch (code->op)
-	    {
-	    case EXEC_OMP_CANCEL:
-	      ok = ifc == OMP_IF_CANCEL;
-	      break;
-
-	    case EXEC_OMP_PARALLEL:
-	    case EXEC_OMP_PARALLEL_DO:
-	    case EXEC_OMP_PARALLEL_LOOP:
-	    case EXEC_OMP_PARALLEL_MASKED:
-	    case EXEC_OMP_PARALLEL_MASTER:
-	    case EXEC_OMP_PARALLEL_SECTIONS:
-	    case EXEC_OMP_PARALLEL_WORKSHARE:
-	    case EXEC_OMP_DISTRIBUTE_PARALLEL_DO:
-	    case EXEC_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO:
-	      ok = ifc == OMP_IF_PARALLEL;
-	      break;
-
-	    case EXEC_OMP_PARALLEL_DO_SIMD:
-	    case EXEC_OMP_DISTRIBUTE_PARALLEL_DO_SIMD:
-	    case EXEC_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD:
-	      ok = ifc == OMP_IF_PARALLEL || ifc == OMP_IF_SIMD;
-	      break;
-
-	    case EXEC_OMP_PARALLEL_MASKED_TASKLOOP:
-	    case EXEC_OMP_PARALLEL_MASTER_TASKLOOP:
-	      ok = ifc == OMP_IF_PARALLEL || ifc == OMP_IF_TASKLOOP;
-	      break;
-
-	    case EXEC_OMP_PARALLEL_MASKED_TASKLOOP_SIMD:
-	    case EXEC_OMP_PARALLEL_MASTER_TASKLOOP_SIMD:
-	      ok = (ifc == OMP_IF_PARALLEL
-		    || ifc == OMP_IF_TASKLOOP
-		    || ifc == OMP_IF_SIMD);
-	      break;
-
-	    case EXEC_OMP_SIMD:
-	    case EXEC_OMP_DO_SIMD:
-	    case EXEC_OMP_DISTRIBUTE_SIMD:
-	    case EXEC_OMP_TEAMS_DISTRIBUTE_SIMD:
-	      ok = ifc == OMP_IF_SIMD;
-	      break;
-
-	    case EXEC_OMP_TASK:
-	      ok = ifc == OMP_IF_TASK;
-	      break;
-
-	    case EXEC_OMP_TASKLOOP:
-	    case EXEC_OMP_MASKED_TASKLOOP:
-	    case EXEC_OMP_MASTER_TASKLOOP:
-	      ok = ifc == OMP_IF_TASKLOOP;
-	      break;
-
-	    case EXEC_OMP_TASKLOOP_SIMD:
-	    case EXEC_OMP_MASKED_TASKLOOP_SIMD:
-	    case EXEC_OMP_MASTER_TASKLOOP_SIMD:
-	      ok = ifc == OMP_IF_TASKLOOP || ifc == OMP_IF_SIMD;
-	      break;
-
-	    case EXEC_OMP_TARGET:
-	    case EXEC_OMP_TARGET_TEAMS:
-	    case EXEC_OMP_TARGET_TEAMS_DISTRIBUTE:
-	    case EXEC_OMP_TARGET_TEAMS_LOOP:
-	      ok = ifc == OMP_IF_TARGET;
-	      break;
-
-	    case EXEC_OMP_TARGET_TEAMS_DISTRIBUTE_SIMD:
-	    case EXEC_OMP_TARGET_SIMD:
-	      ok = ifc == OMP_IF_TARGET || ifc == OMP_IF_SIMD;
-	      break;
-
-	    case EXEC_OMP_TARGET_DATA:
-	      ok = ifc == OMP_IF_TARGET_DATA;
-	      break;
-
-	    case EXEC_OMP_TARGET_UPDATE:
-	      ok = ifc == OMP_IF_TARGET_UPDATE;
-	      break;
-
-	    case EXEC_OMP_TARGET_ENTER_DATA:
-	      ok = ifc == OMP_IF_TARGET_ENTER_DATA;
-	      break;
-
-	    case EXEC_OMP_TARGET_EXIT_DATA:
-	      ok = ifc == OMP_IF_TARGET_EXIT_DATA;
-	      break;
-
-	    case EXEC_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO:
-	    case EXEC_OMP_TARGET_PARALLEL:
-	    case EXEC_OMP_TARGET_PARALLEL_DO:
-	    case EXEC_OMP_TARGET_PARALLEL_LOOP:
-	      ok = ifc == OMP_IF_TARGET || ifc == OMP_IF_PARALLEL;
-	      break;
-
-	    case EXEC_OMP_TARGET_PARALLEL_DO_SIMD:
-	    case EXEC_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD:
-	      ok = (ifc == OMP_IF_TARGET
-		    || ifc == OMP_IF_PARALLEL
-		    || ifc == OMP_IF_SIMD);
-	      break;
-
-	    default:
-	      ok = false;
-	      break;
-	  }
-	if (!ok)
-	  {
-	    static const char *ifs[] = {
-	      "CANCEL",
-	      "PARALLEL",
-	      "SIMD",
-	      "TASK",
-	      "TASKLOOP",
-	      "TARGET",
-	      "TARGET DATA",
-	      "TARGET UPDATE",
-	      "TARGET ENTER DATA",
-	      "TARGET EXIT DATA"
-	    };
-	    gfc_error ("IF clause modifier %s at %L not appropriate for "
-		       "the current OpenMP construct", ifs[ifc], &expr->where);
-	  }
-      }
-
-  if (omp_clauses->self_expr)
-    {
-      gfc_expr *expr = omp_clauses->self_expr;
-      if (!gfc_resolve_expr (expr)
-	  || expr->ts.type != BT_LOGICAL || expr->rank != 0)
-	gfc_error ("SELF clause at %L requires a scalar LOGICAL expression",
-		   &expr->where);
-    }
-
-  if (omp_clauses->final_expr)
-    {
-      gfc_expr *expr = omp_clauses->final_expr;
-      if (!gfc_resolve_expr (expr)
-	  || expr->ts.type != BT_LOGICAL || expr->rank != 0)
-	gfc_error ("FINAL clause at %L requires a scalar LOGICAL expression",
-		   &expr->where);
-    }
-  if (omp_clauses->num_threads)
-    resolve_positive_int_expr (omp_clauses->num_threads, "NUM_THREADS");
-  if (omp_clauses->chunk_size)
-    {
-      gfc_expr *expr = omp_clauses->chunk_size;
-      if (!gfc_resolve_expr (expr)
-	  || expr->ts.type != BT_INTEGER || expr->rank != 0)
-	gfc_error ("SCHEDULE clause's chunk_size at %L requires "
-		   "a scalar INTEGER expression", &expr->where);
-      else if (expr->expr_type == EXPR_CONSTANT
-	       && expr->ts.type == BT_INTEGER
-	       && mpz_sgn (expr->value.integer) <= 0)
-	gfc_warning (OPT_Wopenmp, "INTEGER expression of SCHEDULE clause's "
-		     "chunk_size at %L must be positive", &expr->where);
-    }
-  if (omp_clauses->sched_kind != OMP_SCHED_NONE
-      && omp_clauses->sched_nonmonotonic)
-    {
-      if (omp_clauses->sched_monotonic)
-	gfc_error ("Both MONOTONIC and NONMONOTONIC schedule modifiers "
-		   "specified at %L", &code->loc);
-      else if (omp_clauses->ordered)
-	gfc_error ("NONMONOTONIC schedule modifier specified with ORDERED "
-		   "clause at %L", &code->loc);
-    }
 
-  if (omp_clauses->depobj
-      && (!gfc_resolve_expr (omp_clauses->depobj)
-	  || omp_clauses->depobj->ts.type != BT_INTEGER
-	  || omp_clauses->depobj->ts.kind != 2 * gfc_index_integer_kind
-	  || omp_clauses->depobj->rank != 0))
-    gfc_error ("DEPOBJ in DEPOBJ construct at %L shall be a scalar integer "
-	       "of OMP_DEPEND_KIND kind", &omp_clauses->depobj->where);
-
-  /* Check that no symbol appears on multiple clauses, except that
-     a symbol can appear on both firstprivate and lastprivate.  */
+  /* Check that no symbol appears on multiple clauses, except that a symbol
+     can appear on both firstprivate and lastprivate.  */
   for (list = 0; list < OMP_LIST_NUM; list++)
     for (n = omp_clauses->lists[list]; n; n = n->next)
       {
@@ -8090,22 +7862,23 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses,
 	    && n->sym->result == n->sym
 	    && n->sym->attr.function)
 	  {
-	    if (ns->proc_name == n->sym
-		|| (ns->parent && ns->parent->proc_name == n->sym))
+	    if (gfc_current_ns->proc_name == n->sym
+		|| (gfc_current_ns->parent
+		    && gfc_current_ns->parent->proc_name == n->sym))
 	      continue;
-	    if (ns->proc_name->attr.entry_master)
+	    if (gfc_current_ns->proc_name->attr.entry_master)
 	      {
-		gfc_entry_list *el = ns->entries;
+		gfc_entry_list *el = gfc_current_ns->entries;
 		for (; el; el = el->next)
 		  if (el->sym == n->sym)
 		    break;
 		if (el)
 		  continue;
 	      }
-	    if (ns->parent
-		&& ns->parent->proc_name->attr.entry_master)
+	    if (gfc_current_ns->parent
+		&& gfc_current_ns->parent->proc_name->attr.entry_master)
 	      {
-		gfc_entry_list *el = ns->parent->entries;
+		gfc_entry_list *el = gfc_current_ns->parent->entries;
 		for (; el; el = el->next)
 		  if (el->sym == n->sym)
 		    break;
@@ -8138,8 +7911,7 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses,
 	  && code->op != EXEC_OMP_PARALLEL_DO
 	  && code->op != EXEC_OMP_PARALLEL_DO_SIMD)
 	gfc_error ("%<inscan%> REDUCTION clause on construct other than DO, "
-		   "SIMD, DO SIMD, PARALLEL DO, PARALLEL DO SIMD at %L",
-		   loc);
+		   "SIMD, DO SIMD, PARALLEL DO, PARALLEL DO SIMD at %L", loc);
       if (omp_clauses->ordered)
 	gfc_error ("ORDERED clause specified together with %<inscan%> "
 		   "REDUCTION clause at %L", loc);
@@ -8233,7 +8005,7 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses,
 
   /* Detect specifically the case where we have "map(x) private(x)" and raise
      an error.  If we have "...simd" combined directives though, the "private"
-     applies to the simd part, so this is permitted though.  */
+     applies to the simd part, so this is permitted.  */
   for (n = omp_clauses->lists[OMP_LIST_PRIVATE]; n; n = n->next)
     if (n->sym->mark
 	&& n->sym->gen_mark
@@ -8243,31 +8015,48 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses,
 	&& code->op != EXEC_OMP_TARGET_PARALLEL_DO_SIMD
 	&& code->op != EXEC_OMP_TARGET_TEAMS_DISTRIBUTE_SIMD
 	&& code->op != EXEC_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD)
-      gfc_error ("Symbol %qs present on multiple clauses at %L",
-		 n->sym->name, &n->where);
+      gfc_error ("Symbol %qs present on multiple clauses at %L", n->sym->name,
+		 &n->where);
 
   gcc_assert (OMP_LIST_LASTPRIVATE == OMP_LIST_FIRSTPRIVATE + 1);
   for (list = OMP_LIST_FIRSTPRIVATE; list <= OMP_LIST_LASTPRIVATE; list++)
-    for (n = omp_clauses->lists[list]; n; n = n->next)
-      if (n->sym->data_mark || n->sym->gen_mark || n->sym->dev_mark)
+    {
+      gfc_omp_namelist **pn = &omp_clauses->lists[list];
+      while ((n = *pn) != NULL)
 	{
-	  gfc_error ("Symbol %qs present on multiple clauses at %L",
-		     n->sym->name, &n->where);
-	  n->sym->data_mark = n->sym->gen_mark = n->sym->dev_mark = 0;
-	}
-      else if (n->sym->mark
-	       && code->op != EXEC_OMP_TARGET_TEAMS
-	       && code->op != EXEC_OMP_TARGET_TEAMS_DISTRIBUTE
-	       && code->op != EXEC_OMP_TARGET_TEAMS_LOOP
-	       && code->op != EXEC_OMP_TARGET_TEAMS_DISTRIBUTE_SIMD
-	       && code->op != EXEC_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO
-	       && code->op != EXEC_OMP_TARGET_PARALLEL
-	       && code->op != EXEC_OMP_TARGET_PARALLEL_DO
-	       && code->op != EXEC_OMP_TARGET_PARALLEL_LOOP
-	       && code->op != EXEC_OMP_TARGET_PARALLEL_DO_SIMD
-	       && code->op != EXEC_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD)
-	gfc_error ("Symbol %qs present on both data and map clauses "
-		   "at %L", n->sym->name, &n->where);
+	  bool remove = false;
+
+	  if (n->sym->data_mark || n->sym->gen_mark || n->sym->dev_mark)
+	    {
+	      gfc_error ("Symbol %qs present on multiple clauses at %L",
+			 n->sym->name, &n->where);
+	      n->sym->data_mark = n->sym->gen_mark = n->sym->dev_mark = 0;
+	    }
+	  else if (n->sym->mark
+		   && code->op != EXEC_OMP_TARGET_TEAMS
+		   && code->op != EXEC_OMP_TARGET_TEAMS_DISTRIBUTE
+		   && code->op != EXEC_OMP_TARGET_TEAMS_LOOP
+		   && code->op != EXEC_OMP_TARGET_TEAMS_DISTRIBUTE_SIMD
+		   && code->op != EXEC_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO
+		   && code->op != EXEC_OMP_TARGET_PARALLEL
+		   && code->op != EXEC_OMP_TARGET_PARALLEL_DO
+		   && code->op != EXEC_OMP_TARGET_PARALLEL_LOOP
+		   && code->op != EXEC_OMP_TARGET_PARALLEL_DO_SIMD
+		   && (code->op
+		       != EXEC_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD))
+	    {
+	      gfc_error ("Symbol %qs present on both data and map clauses "
+			 "at %L", n->sym->name, &n->where);
+	      /* We've already shown an error.  Avoid confusing gimplify.  */
+	      remove = true;
+	    }
+
+	  if (remove)
+	    *pn = n->next;
+	  else
+	    pn = &n->next;
+	}
+    }
 
   for (n = omp_clauses->lists[OMP_LIST_FIRSTPRIVATE]; n; n = n->next)
     {
@@ -8395,8 +8184,8 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses,
 	  for (n = omp_clauses->lists[OMP_LIST_ALLOCATE]; n; n = n->next)
 	    if (n->sym->mark == 1)
 	      gfc_error ("%qs specified in %<allocate%> clause at %L but not "
-			 "in an explicit privatization clause",
-			 n->sym->name, &n->where);
+			 "in an explicit privatization clause", n->sym->name,
+			 &n->where);
 	}
       if (code
 	  && (code->op == EXEC_OMP_ALLOCATORS || code->op == EXEC_OMP_ALLOCATE)
@@ -8518,9 +8307,497 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses,
 			   "must specify an ALLOCATOR clause", &code->loc);
 	    }
 
-	}
+	}
+    }
+  for (n = omp_clauses->lists[OMP_LIST_TO]; n; n = n->next)
+    n->sym->mark = 0;
+  for (n = omp_clauses->lists[OMP_LIST_FROM]; n; n = n->next)
+    if (n->expr == NULL)
+      n->sym->mark = 1;
+  for (n = omp_clauses->lists[OMP_LIST_TO]; n; n = n->next)
+    {
+      if (n->expr == NULL && n->sym->mark)
+	gfc_error ("Symbol %qs present on both FROM and TO clauses at %L",
+		   n->sym->name, &n->where);
+      else
+	n->sym->mark = 1;
+    }
+}
+
+/* Check that the parameter of a MAP, TO and FROM clause N meets certain
+   constraints.  Helper function for resolve_omp_clauses and
+   resolve_omp_mapper_clauses.  */
+
+static bool
+omp_verify_map_motion_clauses (gfc_code *code, int list, const char *name,
+			       gfc_omp_namelist *n, bool openacc)
+{
+  gfc_ref *lastref = NULL, *lastslice = NULL;
+  bool resolved = false;
+  if (n->expr)
+    {
+      lastref = n->expr->ref;
+      resolved = gfc_resolve_expr (n->expr);
+
+      /* Look through component refs to find last array
+	 reference.  */
+      if (resolved)
+	{
+	  for (gfc_ref *ref = n->expr->ref; ref; ref = ref->next)
+	    if (ref->type == REF_COMPONENT
+		|| ref->type == REF_SUBSTRING
+		|| ref->type == REF_INQUIRY)
+	      lastref = ref;
+	    else if (ref->type == REF_ARRAY)
+	      {
+		for (int i = 0; i < ref->u.ar.dimen; i++)
+		  if (ref->u.ar.dimen_type[i] == DIMEN_RANGE)
+		    lastslice = ref;
+
+		lastref = ref;
+	      }
+
+	  /* The "!$acc cache" directive allows rectangular subarrays to be
+	     specified, with some restrictions on the form of bounds (not
+	     implemented).
+	     Only raise an error here if we're really sure the array isn't
+	     contiguous.  An expression such as arr(-n:n,-n:n) could be
+	     contiguous even if it looks like it may not be.  Also OpenMP's
+	     'target update' permits strides for the to/from clause. */
+	  if (code
+	      && code->op != EXEC_OACC_UPDATE
+	      && code->op != EXEC_OMP_TARGET_UPDATE
+	      && list != OMP_LIST_CACHE
+	      && list != OMP_LIST_DEPEND
+	      && !gfc_is_simply_contiguous (n->expr, false, true)
+	      && gfc_is_not_contiguous (n->expr)
+	      && !(lastslice && (lastslice->next
+				 || lastslice->type != REF_ARRAY)))
+	    gfc_error ("Array is not contiguous at %L",
+		       &n->where);
+	}
+    }
+  if (openacc && list == OMP_LIST_MAP
+      && (n->u.map.op == OMP_MAP_ATTACH || n->u.map.op == OMP_MAP_DETACH))
+    {
+      symbol_attribute attr;
+      if (n->expr)
+	attr = gfc_expr_attr (n->expr);
+      else
+	attr = n->sym->attr;
+      if (!attr.pointer && !attr.allocatable)
+	gfc_error ("%qs clause argument must be ALLOCATABLE or a POINTER "
+		   "at %L",
+		   (n->u.map.op == OMP_MAP_ATTACH) ? "attach" : "detach",
+		   &n->where);
+    }
+  if (lastref
+      || (n->expr && (!resolved || n->expr->expr_type != EXPR_VARIABLE)))
+    {
+      if (!lastslice && lastref && lastref->type == REF_SUBSTRING)
+	gfc_error ("Unexpected substring reference in %s clause "
+		   "at %L", name, &n->where);
+      else if (!lastslice && lastref && lastref->type == REF_INQUIRY)
+	{
+	  gcc_assert (lastref->u.i == INQUIRY_RE
+		      || lastref->u.i == INQUIRY_IM);
+	  gfc_error ("Unexpected complex-parts designator "
+		     "reference in %s clause at %L",
+		     name, &n->where);
+	}
+      else if (!resolved
+	       || n->expr->expr_type != EXPR_VARIABLE
+	       || (lastslice
+		   && (lastslice->next || lastslice->type != REF_ARRAY)))
+	gfc_error ("%qs in %s clause at %L is not a proper "
+		   "array section", n->sym->name, name,
+		   &n->where);
+      else if (lastslice)
+	{
+	  int i;
+	  gfc_array_ref *ar = &lastslice->u.ar;
+	  for (i = 0; i < ar->dimen; i++)
+	    if (ar->stride[i]
+		&& code
+		&& code->op != EXEC_OACC_UPDATE
+		&& code->op != EXEC_OMP_TARGET_UPDATE)
+	      {
+		gfc_error ("Stride should not be specified for "
+			   "array section in %s clause at %L",
+			   name, &n->where);
+		return false;
+	      }
+	    else if (ar->dimen_type[i] != DIMEN_ELEMENT
+		     && ar->dimen_type[i] != DIMEN_RANGE)
+	      {
+		gfc_error ("%qs in %s clause at %L is not a "
+			   "proper array section",
+			   n->sym->name, name, &n->where);
+		return false;
+	      }
+	    else if ((list == OMP_LIST_DEPEND || list == OMP_LIST_AFFINITY)
+		     && ar->start[i]
+		     && ar->start[i]->expr_type == EXPR_CONSTANT
+		     && ar->end[i]
+		     && ar->end[i]->expr_type == EXPR_CONSTANT
+		     && mpz_cmp (ar->start[i]->value.integer,
+				 ar->end[i]->value.integer) > 0)
+	      {
+		gfc_error ("%qs in %s clause at %L is a zero size array "
+			   "section", n->sym->name, list == OMP_LIST_DEPEND
+			   ? "DEPEND" : "AFFINITY", &n->where);
+		return false;
+	      }
+	}
+    }
+  else if (openacc)
+    {
+      if (list == OMP_LIST_MAP && n->u.map.op == OMP_MAP_FORCE_DEVICEPTR)
+	resolve_oacc_deviceptr_clause (n->sym, n->where, name);
+      else
+	resolve_oacc_data_clauses (n->sym, n->where, name);
+    }
+  else if (list != OMP_LIST_DEPEND
+	   && n->sym->as
+	   && n->sym->as->type == AS_ASSUMED_SIZE)
+    gfc_error ("Assumed size array %qs in %s clause at %L",
+	       n->sym->name, name, &n->where);
+
+  if (!code || list != OMP_LIST_MAP || openacc)
+    return true;
+
+  switch (code->op)
+    {
+    case EXEC_OMP_TARGET:
+    case EXEC_OMP_TARGET_DATA:
+      switch (n->u.map.op)
+	{
+	case OMP_MAP_TO:
+	case OMP_MAP_ALWAYS_TO:
+	case OMP_MAP_PRESENT_TO:
+	case OMP_MAP_ALWAYS_PRESENT_TO:
+	case OMP_MAP_FROM:
+	case OMP_MAP_ALWAYS_FROM:
+	case OMP_MAP_PRESENT_FROM:
+	case OMP_MAP_ALWAYS_PRESENT_FROM:
+	case OMP_MAP_TOFROM:
+	case OMP_MAP_ALWAYS_TOFROM:
+	case OMP_MAP_PRESENT_TOFROM:
+	case OMP_MAP_ALWAYS_PRESENT_TOFROM:
+	case OMP_MAP_ALLOC:
+	case OMP_MAP_PRESENT_ALLOC:
+	  break;
+	default:
+	  gfc_error ("TARGET%s with map-type other than TO, FROM, TOFROM, or "
+		     "ALLOC on MAP clause at %L",
+		     code->op == EXEC_OMP_TARGET ? "" : " DATA", &n->where);
+	  break;
+	}
+      break;
+    case EXEC_OMP_TARGET_ENTER_DATA:
+      switch (n->u.map.op)
+	{
+	case OMP_MAP_TO:
+	case OMP_MAP_ALWAYS_TO:
+	case OMP_MAP_PRESENT_TO:
+	case OMP_MAP_ALWAYS_PRESENT_TO:
+	case OMP_MAP_ALLOC:
+	case OMP_MAP_PRESENT_ALLOC:
+	  break;
+	case OMP_MAP_TOFROM:
+	  n->u.map.op = OMP_MAP_TO;
+	  break;
+	case OMP_MAP_ALWAYS_TOFROM:
+	  n->u.map.op = OMP_MAP_ALWAYS_TO;
+	  break;
+	case OMP_MAP_PRESENT_TOFROM:
+	  n->u.map.op = OMP_MAP_PRESENT_TO;
+	  break;
+	case OMP_MAP_ALWAYS_PRESENT_TOFROM:
+	  n->u.map.op = OMP_MAP_ALWAYS_PRESENT_TO;
+	  break;
+	default:
+	  gfc_error ("TARGET ENTER DATA with map-type other than TO, TOFROM "
+		     "or ALLOC on MAP clause at %L", &n->where);
+	  break;
+	}
+      break;
+    case EXEC_OMP_TARGET_EXIT_DATA:
+      switch (n->u.map.op)
+	{
+	case OMP_MAP_FROM:
+	case OMP_MAP_ALWAYS_FROM:
+	case OMP_MAP_PRESENT_FROM:
+	case OMP_MAP_ALWAYS_PRESENT_FROM:
+	case OMP_MAP_RELEASE:
+	case OMP_MAP_DELETE:
+	  break;
+	case OMP_MAP_TOFROM:
+	  n->u.map.op = OMP_MAP_FROM;
+	  break;
+	case OMP_MAP_ALWAYS_TOFROM:
+	  n->u.map.op = OMP_MAP_ALWAYS_FROM;
+	  break;
+	case OMP_MAP_PRESENT_TOFROM:
+	  n->u.map.op = OMP_MAP_PRESENT_FROM;
+	  break;
+	case OMP_MAP_ALWAYS_PRESENT_TOFROM:
+	  n->u.map.op = OMP_MAP_ALWAYS_PRESENT_FROM;
+	  break;
+	default:
+	  gfc_error ("TARGET EXIT DATA with map-type other than FROM, TOFROM, "
+		     "RELEASE, or DELETE on MAP clause at %L", &n->where);
+	  break;
+	}
+      break;
+    default:
+      ;
+    }
+
+  return true;
+}
+
+/* OpenMP directive resolving routines.  */
+
+static void
+resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses,
+		     gfc_namespace *ns, bool openacc = false)
+{
+  gfc_omp_namelist *n, *last;
+  gfc_expr_list *el;
+  int list;
+  int ifc;
+  bool if_without_mod = false;
+  gfc_omp_linear_op linear_op = OMP_LINEAR_DEFAULT;
+  static const char *clause_names[]
+    = { "PRIVATE", "FIRSTPRIVATE", "LASTPRIVATE", "COPYPRIVATE", "SHARED",
+	"COPYIN", "UNIFORM", "AFFINITY", "ALIGNED", "LINEAR", "DEPEND", "MAP",
+	"TO", "FROM", "INCLUSIVE", "EXCLUSIVE",
+	"REDUCTION", "REDUCTION" /*inscan*/, "REDUCTION" /*task*/,
+	"IN_REDUCTION", "TASK_REDUCTION",
+	"DEVICE_RESIDENT", "LINK", "USE_DEVICE",
+	"CACHE", "IS_DEVICE_PTR", "USE_DEVICE_PTR", "USE_DEVICE_ADDR",
+	"NONTEMPORAL", "ALLOCATE", "HAS_DEVICE_ADDR", "ENTER",
+	"USES_ALLOCATORS" };
+  STATIC_ASSERT (ARRAY_SIZE (clause_names) == OMP_LIST_NUM);
+
+  if (omp_clauses == NULL)
+    return;
+
+  if (omp_clauses->orderedc && omp_clauses->orderedc < omp_clauses->collapse)
+    gfc_error ("ORDERED clause parameter is less than COLLAPSE at %L",
+	       &code->loc);
+  if (omp_clauses->order_concurrent && omp_clauses->ordered)
+    gfc_error ("ORDER clause must not be used together ORDERED at %L",
+	       &code->loc);
+  /* If we're invoking any declared mappers as a result of these clauses, we may
+     need to know the namespace their directive was originally defined within in
+     order to resolve clauses again after substitution.  Record it here.  */
+  if (ns)
+    omp_clauses->ns = ns;
+  if (omp_clauses->if_expr)
+    {
+      gfc_expr *expr = omp_clauses->if_expr;
+      if (!gfc_resolve_expr (expr)
+	  || expr->ts.type != BT_LOGICAL || expr->rank != 0)
+	gfc_error ("IF clause at %L requires a scalar LOGICAL expression",
+		   &expr->where);
+      if_without_mod = true;
+    }
+  for (ifc = 0; ifc < OMP_IF_LAST; ifc++)
+    if (omp_clauses->if_exprs[ifc])
+      {
+	gfc_expr *expr = omp_clauses->if_exprs[ifc];
+	bool ok = true;
+	if (!gfc_resolve_expr (expr)
+	    || expr->ts.type != BT_LOGICAL || expr->rank != 0)
+	  gfc_error ("IF clause at %L requires a scalar LOGICAL expression",
+		     &expr->where);
+	else if (if_without_mod)
+	  {
+	    gfc_error ("IF clause without modifier at %L used together with "
+		       "IF clauses with modifiers",
+		       &omp_clauses->if_expr->where);
+	    if_without_mod = false;
+	  }
+	else
+	  switch (code->op)
+	    {
+	    case EXEC_OMP_CANCEL:
+	      ok = ifc == OMP_IF_CANCEL;
+	      break;
+
+	    case EXEC_OMP_PARALLEL:
+	    case EXEC_OMP_PARALLEL_DO:
+	    case EXEC_OMP_PARALLEL_LOOP:
+	    case EXEC_OMP_PARALLEL_MASKED:
+	    case EXEC_OMP_PARALLEL_MASTER:
+	    case EXEC_OMP_PARALLEL_SECTIONS:
+	    case EXEC_OMP_PARALLEL_WORKSHARE:
+	    case EXEC_OMP_DISTRIBUTE_PARALLEL_DO:
+	    case EXEC_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO:
+	      ok = ifc == OMP_IF_PARALLEL;
+	      break;
+
+	    case EXEC_OMP_PARALLEL_DO_SIMD:
+	    case EXEC_OMP_DISTRIBUTE_PARALLEL_DO_SIMD:
+	    case EXEC_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD:
+	      ok = ifc == OMP_IF_PARALLEL || ifc == OMP_IF_SIMD;
+	      break;
+
+	    case EXEC_OMP_PARALLEL_MASKED_TASKLOOP:
+	    case EXEC_OMP_PARALLEL_MASTER_TASKLOOP:
+	      ok = ifc == OMP_IF_PARALLEL || ifc == OMP_IF_TASKLOOP;
+	      break;
+
+	    case EXEC_OMP_PARALLEL_MASKED_TASKLOOP_SIMD:
+	    case EXEC_OMP_PARALLEL_MASTER_TASKLOOP_SIMD:
+	      ok = (ifc == OMP_IF_PARALLEL
+		    || ifc == OMP_IF_TASKLOOP
+		    || ifc == OMP_IF_SIMD);
+	      break;
+
+	    case EXEC_OMP_SIMD:
+	    case EXEC_OMP_DO_SIMD:
+	    case EXEC_OMP_DISTRIBUTE_SIMD:
+	    case EXEC_OMP_TEAMS_DISTRIBUTE_SIMD:
+	      ok = ifc == OMP_IF_SIMD;
+	      break;
+
+	    case EXEC_OMP_TASK:
+	      ok = ifc == OMP_IF_TASK;
+	      break;
+
+	    case EXEC_OMP_TASKLOOP:
+	    case EXEC_OMP_MASKED_TASKLOOP:
+	    case EXEC_OMP_MASTER_TASKLOOP:
+	      ok = ifc == OMP_IF_TASKLOOP;
+	      break;
+
+	    case EXEC_OMP_TASKLOOP_SIMD:
+	    case EXEC_OMP_MASKED_TASKLOOP_SIMD:
+	    case EXEC_OMP_MASTER_TASKLOOP_SIMD:
+	      ok = ifc == OMP_IF_TASKLOOP || ifc == OMP_IF_SIMD;
+	      break;
+
+	    case EXEC_OMP_TARGET:
+	    case EXEC_OMP_TARGET_TEAMS:
+	    case EXEC_OMP_TARGET_TEAMS_DISTRIBUTE:
+	    case EXEC_OMP_TARGET_TEAMS_LOOP:
+	      ok = ifc == OMP_IF_TARGET;
+	      break;
+
+	    case EXEC_OMP_TARGET_TEAMS_DISTRIBUTE_SIMD:
+	    case EXEC_OMP_TARGET_SIMD:
+	      ok = ifc == OMP_IF_TARGET || ifc == OMP_IF_SIMD;
+	      break;
+
+	    case EXEC_OMP_TARGET_DATA:
+	      ok = ifc == OMP_IF_TARGET_DATA;
+	      break;
+
+	    case EXEC_OMP_TARGET_UPDATE:
+	      ok = ifc == OMP_IF_TARGET_UPDATE;
+	      break;
+
+	    case EXEC_OMP_TARGET_ENTER_DATA:
+	      ok = ifc == OMP_IF_TARGET_ENTER_DATA;
+	      break;
+
+	    case EXEC_OMP_TARGET_EXIT_DATA:
+	      ok = ifc == OMP_IF_TARGET_EXIT_DATA;
+	      break;
+
+	    case EXEC_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO:
+	    case EXEC_OMP_TARGET_PARALLEL:
+	    case EXEC_OMP_TARGET_PARALLEL_DO:
+	    case EXEC_OMP_TARGET_PARALLEL_LOOP:
+	      ok = ifc == OMP_IF_TARGET || ifc == OMP_IF_PARALLEL;
+	      break;
+
+	    case EXEC_OMP_TARGET_PARALLEL_DO_SIMD:
+	    case EXEC_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD:
+	      ok = (ifc == OMP_IF_TARGET
+		    || ifc == OMP_IF_PARALLEL
+		    || ifc == OMP_IF_SIMD);
+	      break;
+
+	    default:
+	      ok = false;
+	      break;
+	  }
+	if (!ok)
+	  {
+	    static const char *ifs[] = {
+	      "CANCEL",
+	      "PARALLEL",
+	      "SIMD",
+	      "TASK",
+	      "TASKLOOP",
+	      "TARGET",
+	      "TARGET DATA",
+	      "TARGET UPDATE",
+	      "TARGET ENTER DATA",
+	      "TARGET EXIT DATA"
+	    };
+	    gfc_error ("IF clause modifier %s at %L not appropriate for "
+		       "the current OpenMP construct", ifs[ifc], &expr->where);
+	  }
+      }
+
+  if (omp_clauses->self_expr)
+    {
+      gfc_expr *expr = omp_clauses->self_expr;
+      if (!gfc_resolve_expr (expr)
+	  || expr->ts.type != BT_LOGICAL || expr->rank != 0)
+	gfc_error ("SELF clause at %L requires a scalar LOGICAL expression",
+		   &expr->where);
+    }
+
+  if (omp_clauses->final_expr)
+    {
+      gfc_expr *expr = omp_clauses->final_expr;
+      if (!gfc_resolve_expr (expr)
+	  || expr->ts.type != BT_LOGICAL || expr->rank != 0)
+	gfc_error ("FINAL clause at %L requires a scalar LOGICAL expression",
+		   &expr->where);
+    }
+  if (omp_clauses->num_threads)
+    resolve_positive_int_expr (omp_clauses->num_threads, "NUM_THREADS");
+  if (omp_clauses->chunk_size)
+    {
+      gfc_expr *expr = omp_clauses->chunk_size;
+      if (!gfc_resolve_expr (expr)
+	  || expr->ts.type != BT_INTEGER || expr->rank != 0)
+	gfc_error ("SCHEDULE clause's chunk_size at %L requires "
+		   "a scalar INTEGER expression", &expr->where);
+      else if (expr->expr_type == EXPR_CONSTANT
+	       && expr->ts.type == BT_INTEGER
+	       && mpz_sgn (expr->value.integer) <= 0)
+	gfc_warning (0, "INTEGER expression of SCHEDULE clause's chunk_size "
+		     "at %L must be positive", &expr->where);
+    }
+  if (omp_clauses->sched_kind != OMP_SCHED_NONE
+      && omp_clauses->sched_nonmonotonic)
+    {
+      if (omp_clauses->sched_monotonic)
+	gfc_error ("Both MONOTONIC and NONMONOTONIC schedule modifiers "
+		   "specified at %L", &code->loc);
+      else if (omp_clauses->ordered)
+	gfc_error ("NONMONOTONIC schedule modifier specified with ORDERED "
+		   "clause at %L", &code->loc);
     }
 
+  if (omp_clauses->depobj
+      && (!gfc_resolve_expr (omp_clauses->depobj)
+	  || omp_clauses->depobj->ts.type != BT_INTEGER
+	  || omp_clauses->depobj->ts.kind != 2 * gfc_index_integer_kind
+	  || omp_clauses->depobj->rank != 0))
+    gfc_error ("DEPOBJ in DEPOBJ construct at %L shall be a scalar integer "
+	       "of OMP_DEPEND_KIND kind", &omp_clauses->depobj->where);
+
+  verify_omp_clauses_symbol_dups (code, omp_clauses, ns, openacc);
+
   /* OpenACC reductions.  */
   if (openacc)
     {
@@ -8542,20 +8819,6 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses,
 	}
     }
   
-  for (n = omp_clauses->lists[OMP_LIST_TO]; n; n = n->next)
-    n->sym->mark = 0;
-  for (n = omp_clauses->lists[OMP_LIST_FROM]; n; n = n->next)
-    if (n->expr == NULL)
-      n->sym->mark = 1;
-  for (n = omp_clauses->lists[OMP_LIST_TO]; n; n = n->next)
-    {
-      if (n->expr == NULL && n->sym->mark)
-	gfc_error ("Symbol %qs present on both FROM and TO clauses at %L",
-		   n->sym->name, &n->where);
-      else
-	n->sym->mark = 1;
-    }
-
   bool has_inscan = false, has_notinscan = false;
   for (list = 0; list < OMP_LIST_NUM; list++)
     if ((n = omp_clauses->lists[list]) != NULL)
@@ -8724,242 +8987,9 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses,
 				 "type shall be a scalar integer of "
 				 "OMP_DEPEND_KIND kind", &n->expr->where);
 		  }
-		gfc_ref *lastref = NULL, *lastslice = NULL;
-		bool resolved = false;
-		if (n->expr)
-		  {
-		    lastref = n->expr->ref;
-		    resolved = gfc_resolve_expr (n->expr);
-
-		    /* Look through component refs to find last array
-		       reference.  */
-		    if (resolved)
-		      {
-			for (gfc_ref *ref = n->expr->ref; ref; ref = ref->next)
-			  if (ref->type == REF_COMPONENT
-			      || ref->type == REF_SUBSTRING
-			      || ref->type == REF_INQUIRY)
-			    lastref = ref;
-			  else if (ref->type == REF_ARRAY)
-			    {
-			      for (int i = 0; i < ref->u.ar.dimen; i++)
-				if (ref->u.ar.dimen_type[i] == DIMEN_RANGE)
-				  lastslice = ref;
-
-			      lastref = ref;
-			    }
-
-			/* The "!$acc cache" directive allows rectangular
-			   subarrays to be specified, with some restrictions
-			   on the form of bounds (not implemented).
-			   Only raise an error here if we're really sure the
-			   array isn't contiguous.  An expression such as
-			   arr(-n:n,-n:n) could be contiguous even if it looks
-			   like it may not be.
-			   And OpenMP's 'target update' permits strides for
-			   the to/from clause. */
-			if (code
-			    && code->op != EXEC_OACC_UPDATE
-			    && code->op != EXEC_OMP_TARGET_UPDATE
-			    && list != OMP_LIST_CACHE
-			    && list != OMP_LIST_DEPEND
-			    && !gfc_is_simply_contiguous (n->expr, false, true)
-			    && gfc_is_not_contiguous (n->expr)
-			    && !(lastslice
-				 && (lastslice->next
-				     || lastslice->type != REF_ARRAY)))
-			  gfc_error ("Array is not contiguous at %L",
-				     &n->where);
-		      }
-		  }
-		if (openacc
-		    && list == OMP_LIST_MAP
-		    && (n->u.map.op == OMP_MAP_ATTACH
-			|| n->u.map.op == OMP_MAP_DETACH))
-		  {
-		    symbol_attribute attr;
-		    if (n->expr)
-		      attr = gfc_expr_attr (n->expr);
-		    else
-		      attr = n->sym->attr;
-		    if (!attr.pointer && !attr.allocatable)
-		      gfc_error ("%qs clause argument must be ALLOCATABLE or "
-				 "a POINTER at %L",
-				 (n->u.map.op == OMP_MAP_ATTACH) ? "attach"
-				 : "detach", &n->where);
-		  }
-		if (lastref
-		    || (n->expr
-			&& (!resolved || n->expr->expr_type != EXPR_VARIABLE)))
-		  {
-		    if (!lastslice
-			&& lastref
-			&& lastref->type == REF_SUBSTRING)
-		      gfc_error ("Unexpected substring reference in %s clause "
-				 "at %L", name, &n->where);
-		    else if (!lastslice
-			     && lastref
-			     && lastref->type == REF_INQUIRY)
-		      {
-			gcc_assert (lastref->u.i == INQUIRY_RE
-				    || lastref->u.i == INQUIRY_IM);
-			gfc_error ("Unexpected complex-parts designator "
-				   "reference in %s clause at %L",
-				   name, &n->where);
-		      }
-		    else if (!resolved
-			     || n->expr->expr_type != EXPR_VARIABLE
-			     || (lastslice
-				 && (lastslice->next
-				     || lastslice->type != REF_ARRAY)))
-		      gfc_error ("%qs in %s clause at %L is not a proper "
-				 "array section", n->sym->name, name,
-				 &n->where);
-		    else if (lastslice)
-		      {
-			int i;
-			gfc_array_ref *ar = &lastslice->u.ar;
-			for (i = 0; i < ar->dimen; i++)
-			  if (ar->stride[i]
-			      && code->op != EXEC_OACC_UPDATE
-			      && code->op != EXEC_OMP_TARGET_UPDATE)
-			    {
-			      gfc_error ("Stride should not be specified for "
-					 "array section in %s clause at %L",
-					 name, &n->where);
-			      break;
-			    }
-			  else if (ar->dimen_type[i] != DIMEN_ELEMENT
-				   && ar->dimen_type[i] != DIMEN_RANGE)
-			    {
-			      gfc_error ("%qs in %s clause at %L is not a "
-					 "proper array section",
-					 n->sym->name, name, &n->where);
-			      break;
-			    }
-			  else if ((list == OMP_LIST_DEPEND
-				    || list == OMP_LIST_AFFINITY)
-				   && ar->start[i]
-				   && ar->start[i]->expr_type == EXPR_CONSTANT
-				   && ar->end[i]
-				   && ar->end[i]->expr_type == EXPR_CONSTANT
-				   && mpz_cmp (ar->start[i]->value.integer,
-					       ar->end[i]->value.integer) > 0)
-			    {
-			      gfc_error ("%qs in %s clause at %L is a "
-					 "zero size array section",
-					 n->sym->name,
-					 list == OMP_LIST_DEPEND
-					 ? "DEPEND" : "AFFINITY", &n->where);
-			      break;
-			    }
-		      }
-		  }
-		else if (openacc)
-		  {
-		    if (list == OMP_LIST_MAP
-			&& n->u.map.op == OMP_MAP_FORCE_DEVICEPTR)
-		      resolve_oacc_deviceptr_clause (n->sym, n->where, name);
-		    else
-		      resolve_oacc_data_clauses (n->sym, n->where, name);
-		  }
-		else if (list != OMP_LIST_DEPEND
-			 && n->sym->as
-			 && n->sym->as->type == AS_ASSUMED_SIZE)
-		  gfc_error ("Assumed size array %qs in %s clause at %L",
-			     n->sym->name, name, &n->where);
-		if (code && list == OMP_LIST_MAP && !openacc)
-		  switch (code->op)
-		    {
-		    case EXEC_OMP_TARGET:
-		    case EXEC_OMP_TARGET_DATA:
-		      switch (n->u.map.op)
-			{
-			case OMP_MAP_TO:
-			case OMP_MAP_ALWAYS_TO:
-			case OMP_MAP_PRESENT_TO:
-			case OMP_MAP_ALWAYS_PRESENT_TO:
-			case OMP_MAP_FROM:
-			case OMP_MAP_ALWAYS_FROM:
-			case OMP_MAP_PRESENT_FROM:
-			case OMP_MAP_ALWAYS_PRESENT_FROM:
-			case OMP_MAP_TOFROM:
-			case OMP_MAP_ALWAYS_TOFROM:
-			case OMP_MAP_PRESENT_TOFROM:
-			case OMP_MAP_ALWAYS_PRESENT_TOFROM:
-			case OMP_MAP_ALLOC:
-			case OMP_MAP_PRESENT_ALLOC:
-			  break;
-			default:
-			  gfc_error ("TARGET%s with map-type other than TO, "
-				     "FROM, TOFROM, or ALLOC on MAP clause "
-				     "at %L",
-				     code->op == EXEC_OMP_TARGET
-				     ? "" : " DATA", &n->where);
-			  break;
-			}
-		      break;
-		    case EXEC_OMP_TARGET_ENTER_DATA:
-		      switch (n->u.map.op)
-			{
-			case OMP_MAP_TO:
-			case OMP_MAP_ALWAYS_TO:
-			case OMP_MAP_PRESENT_TO:
-			case OMP_MAP_ALWAYS_PRESENT_TO:
-			case OMP_MAP_ALLOC:
-			case OMP_MAP_PRESENT_ALLOC:
-			  break;
-			case OMP_MAP_TOFROM:
-			  n->u.map.op = OMP_MAP_TO;
-			  break;
-			case OMP_MAP_ALWAYS_TOFROM:
-			  n->u.map.op = OMP_MAP_ALWAYS_TO;
-			  break;
-			case OMP_MAP_PRESENT_TOFROM:
-			  n->u.map.op = OMP_MAP_PRESENT_TO;
-			  break;
-			case OMP_MAP_ALWAYS_PRESENT_TOFROM:
-			  n->u.map.op = OMP_MAP_ALWAYS_PRESENT_TO;
-			  break;
-			default:
-			  gfc_error ("TARGET ENTER DATA with map-type other "
-				     "than TO, TOFROM or ALLOC on MAP clause "
-				     "at %L", &n->where);
-			  break;
-			}
-		      break;
-		    case EXEC_OMP_TARGET_EXIT_DATA:
-		      switch (n->u.map.op)
-			{
-			case OMP_MAP_FROM:
-			case OMP_MAP_ALWAYS_FROM:
-			case OMP_MAP_PRESENT_FROM:
-			case OMP_MAP_ALWAYS_PRESENT_FROM:
-			case OMP_MAP_RELEASE:
-			case OMP_MAP_DELETE:
-			  break;
-			case OMP_MAP_TOFROM:
-			  n->u.map.op = OMP_MAP_FROM;
-			  break;
-			case OMP_MAP_ALWAYS_TOFROM:
-			  n->u.map.op = OMP_MAP_ALWAYS_FROM;
-			  break;
-			case OMP_MAP_PRESENT_TOFROM:
-			  n->u.map.op = OMP_MAP_PRESENT_FROM;
-			  break;
-			case OMP_MAP_ALWAYS_PRESENT_TOFROM:
-			  n->u.map.op = OMP_MAP_ALWAYS_PRESENT_FROM;
-			  break;
-			default:
-			  gfc_error ("TARGET EXIT DATA with map-type other "
-				     "than FROM, TOFROM, RELEASE, or DELETE on "
-				     "MAP clause at %L", &n->where);
-			  break;
-			}
-		      break;
-		    default:
-		      break;
-		    }
+		if (!omp_verify_map_motion_clauses (code, list, name, n,
+						    openacc))
+		  break;
 	      }
 
 	    if (list != OMP_LIST_DEPEND)
@@ -9582,6 +9612,46 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses,
     gfc_resolve_omp_assumptions (omp_clauses->assume);
 }
 
+/* This very simplified version of the above function is for use after mapper
+   instantiation.  It avoids dealing with anything other than basic
+   verification for map/to/from clauses.  */
+
+static void
+resolve_omp_mapper_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses,
+			    gfc_namespace *ns)
+{
+  gfc_omp_namelist *n;
+  int list;
+
+  verify_omp_clauses_symbol_dups (code, omp_clauses, ns, false);
+
+  for (list = OMP_LIST_MAP; list <= OMP_LIST_FROM; list++)
+    if ((n = omp_clauses->lists[list]) != NULL)
+      {
+	const char *name = NULL;
+	switch (list)
+	  {
+	  case OMP_LIST_MAP:
+	    if (name == NULL)
+	      name = "MAP";
+	    /* Fallthrough.  */
+	  case OMP_LIST_TO:
+	    if (name == NULL)
+	      name = "TO";
+	    /* Fallthrough.  */
+	  case OMP_LIST_FROM:
+	    if (name == NULL)
+	      name = "FROM";
+	    for (; n != NULL; n = n->next)
+	      if (!omp_verify_map_motion_clauses (code, list, name, n, false))
+		break;
+	    break;
+	  default:
+	    ;
+	  }
+      }
+}
+
 
 /* Return true if SYM is ever referenced in EXPR except in the SE node.  */
 
@@ -12084,11 +12154,11 @@ gfc_resolve_omp_directive (gfc_code *code, gfc_namespace *ns)
     case EXEC_OMP_WORKSHARE:
     case EXEC_OMP_DEPOBJ:
       if (code->ext.omp_clauses)
-	resolve_omp_clauses (code, code->ext.omp_clauses, NULL);
+	resolve_omp_clauses (code, code->ext.omp_clauses, ns);
       break;
     case EXEC_OMP_TARGET_UPDATE:
       if (code->ext.omp_clauses)
-	resolve_omp_clauses (code, code->ext.omp_clauses, NULL);
+	resolve_omp_clauses (code, code->ext.omp_clauses, ns);
       if (code->ext.omp_clauses == NULL
 	  || (code->ext.omp_clauses->lists[OMP_LIST_TO] == NULL
 	      && code->ext.omp_clauses->lists[OMP_LIST_FROM] == NULL))
@@ -12689,6 +12759,7 @@ gfc_omp_instantiate_mappers (gfc_code *code, gfc_omp_clauses *clauses,
 {
   gfc_omp_namelist *clause = clauses->lists[list];
   gfc_omp_namelist **clausep = &clauses->lists[list];
+  bool invoked_mappers = false;
 
   for (; clause; clause = *clausep)
     {
@@ -12715,10 +12786,20 @@ gfc_omp_instantiate_mappers (gfc_code *code, gfc_omp_clauses *clauses,
 	  clausep = gfc_omp_instantiate_mapper (clausep, clause, outer_map_op,
 						clause->u2.udm->udm, cd, list);
 	  *clausep = clause->next;
+	  invoked_mappers = true;
 	}
       else
 	clausep = &clause->next;
     }
+
+  if (invoked_mappers)
+    {
+      gfc_namespace *old_ns = gfc_current_ns;
+      if (clauses->ns)
+	gfc_current_ns = clauses->ns;
+      resolve_omp_mapper_clauses (code, clauses, gfc_current_ns);
+      gfc_current_ns = old_ns;
+    }
 }
 
 /* Resolve !$omp declare mapper constructs.  */
diff --git a/gcc/testsuite/ChangeLog.omp b/gcc/testsuite/ChangeLog.omp
index e4c8afd0293..49ea82ac86b 100644
--- a/gcc/testsuite/ChangeLog.omp
+++ b/gcc/testsuite/ChangeLog.omp
@@ -1,3 +1,8 @@
+2023-08-10  Julian Brown  <julian@codesourcery.com>
+
+	* gfortran.dg/gomp/declare-mapper-26.f90: New test.
+	* gfortran.dg/gomp/declare-mapper-29.f90: New test.
+
 2023-07-14  Julian Brown  <julian@codesourcery.com>
 
 	* c-c++-common/gomp/declare-mapper-3.c: Enable for C.
diff --git a/gcc/testsuite/gfortran.dg/gomp/declare-mapper-26.f90 b/gcc/testsuite/gfortran.dg/gomp/declare-mapper-26.f90
new file mode 100644
index 00000000000..c408b37f5a9
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/declare-mapper-26.f90
@@ -0,0 +1,28 @@
+! { dg-do compile }
+
+type t
+integer, allocatable :: arr(:)
+end type t
+
+!$omp declare mapper(even: T :: tv) map(tv%arr(2::2))
+
+type(t) :: var
+
+allocate(var%arr(100))
+
+var%arr = 0
+
+! You can't do this, the mapper specifies a noncontiguous access.
+!$omp target enter data map(mapper(even), to: var)
+! { dg-error {Stride should not be specified for array section in MAP clause} "" { target *-*-* } .-1 }
+
+var%arr = 1
+
+! But this is fine.  (Re-enabled by later patch.)
+!!$omp target update to(mapper(even): var)
+
+! As 'enter data'.
+!$omp target exit data map(mapper(even), delete: var)
+! { dg-error {Stride should not be specified for array section in MAP clause} "" { target *-*-* } .-1 }
+
+end
diff --git a/gcc/testsuite/gfortran.dg/gomp/declare-mapper-29.f90 b/gcc/testsuite/gfortran.dg/gomp/declare-mapper-29.f90
new file mode 100644
index 00000000000..e2039e80e57
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/declare-mapper-29.f90
@@ -0,0 +1,22 @@
+! { dg-do compile }
+
+! Check duplicate clause detection after mapper expansion.
+
+type t
+integer :: x
+end type t
+
+real(4) :: unrelated
+type(t) :: tvar
+
+!$omp declare mapper (t :: var) map(unrelated) map(var%x)
+
+tvar%x = 0
+unrelated = 5
+
+!$omp target firstprivate(unrelated) map(tofrom: tvar)
+! { dg-error "Symbol .unrelated. present on both data and map clauses" "" { target *-*-* } .-1 }
+tvar%x = unrelated
+!$omp end target
+
+end


More information about the Gcc-cvs mailing list