From b5476e4c881b0d2bfbbfb84ee38d791123acf8e1 Mon Sep 17 00:00:00 2001
From: Julian Brown <julian@codesourcery.com>
Date: Mon, 15 Nov 2021 02:23:49 -0800
Subject: [PATCH] OpenMP: lvalue parsing for map/to/from clauses (C)

This patch adds support for parsing general lvalues ("locator list item
types") for OpenMP "map", "to" and "from" clauses to the C front-end,
similar to the previously-posted patch for C++.  Such syntax is permitted
for OpenMP 5.0 and above.  It was previously posted for mainline here:

  https://gcc.gnu.org/pipermail/gcc-patches/2022-December/609038.html

and for the og13 branch here:

  https://gcc.gnu.org/pipermail/gcc-patches/2023-June/623355.html

2024-01-11  Julian Brown  <julian@codesourcery.com>

gcc/c-family/
	* c-pretty-print.cc (c_pretty_printer::postfix_expression,
	c_pretty_printer::expression): Add OMP_ARRAY_SECTION support.

gcc/c/
	* c-parser.cc (c_parser_braced_init, c_parser_conditional_expression):
	Don't allow OpenMP array section.
	(c_parser_postfix_expression): Don't allow array section in statement
	expression.
	(c_parser_postfix_expression_after_primary): Add support for OpenMP
	array section parsing.
	(c_parser_expr_list): Don't allow OpenMP array section here.
	(c_parser_omp_variable_list): Change ALLOW_DEREF parameter to
	MAP_LVALUE.  Support parsing of general lvalues in "map", "to" and
	"from" clauses.
	(c_parser_omp_var_list_parens): Change ALLOW_DEREF parameter to
	MAP_LVALUE.  Update call to c_parser_omp_variable_list.
	(c_parser_oacc_data_clause): Update calls to
	c_parser_omp_var_list_parens.
	(c_parser_omp_clause_reduction): Use OMP_ARRAY_SECTION tree node
	instead of TREE_LIST for array sections.
	(c_parser_omp_target): Allow GOMP_MAP_ATTACH.
	* c-tree.h (c_omp_array_section_p): Add extern declaration.
	(build_omp_array_section): Add prototype.
	* c-typeck.cc (c_omp_array_section_p): Add flag.
	(mark_exp_read): Support OMP_ARRAY_SECTION.
	(build_omp_array_section): Add function.
	(build_external_ref): Tweak error path for OpenMP array sections.
	(handle_omp_array_sections_1): Use OMP_ARRAY_SECTION tree code instead
	of TREE_LIST.  Handle more kinds of expressions.
	(c_oacc_check_attachments): Use OMP_ARRAY_SECTION instead of TREE_LIST
	for array sections.
	(c_finish_omp_clauses): Use OMP_ARRAY_SECTION instead of TREE_LIST.
	Check for supported expression types.

gcc/testsuite/
	* gcc.dg/gomp/bad-array-section-c-1.c: New test.
	* gcc.dg/gomp/bad-array-section-c-2.c: New test.
	* gcc.dg/gomp/bad-array-section-c-3.c: New test.
	* gcc.dg/gomp/bad-array-section-c-4.c: New test.
	* gcc.dg/gomp/bad-array-section-c-5.c: New test.
	* gcc.dg/gomp/bad-array-section-c-6.c: New test.
	* gcc.dg/gomp/bad-array-section-c-7.c: New test.
	* gcc.dg/gomp/bad-array-section-c-8.c: New test.

libgomp/
	* libgomp.texi: C/C++ lvalues are supported now for map/to/from.
	* testsuite/libgomp.c-c++-common/ind-base-4.c: New test.
	* testsuite/libgomp.c-c++-common/unary-ptr-1.c: New test.
---
 gcc/c-family/c-pretty-print.cc                |  12 ++
 gcc/c/c-parser.cc                             | 155 ++++++++++++++++--
 gcc/c/c-tree.h                                |   2 +
 gcc/c/c-typeck.cc                             | 109 +++++++++---
 .../gcc.dg/gomp/bad-array-section-c-1.c       |  16 ++
 .../gcc.dg/gomp/bad-array-section-c-2.c       |  13 ++
 .../gcc.dg/gomp/bad-array-section-c-3.c       |  24 +++
 .../gcc.dg/gomp/bad-array-section-c-4.c       |  26 +++
 .../gcc.dg/gomp/bad-array-section-c-5.c       |  15 ++
 .../gcc.dg/gomp/bad-array-section-c-6.c       |  16 ++
 .../gcc.dg/gomp/bad-array-section-c-7.c       |  26 +++
 .../gcc.dg/gomp/bad-array-section-c-8.c       |  21 +++
 libgomp/libgomp.texi                          |   2 +-
 .../libgomp.c-c++-common/ind-base-4.c         |  50 ++++++
 .../libgomp.c-c++-common/unary-ptr-1.c        |  16 ++
 15 files changed, 465 insertions(+), 38 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-section-c-1.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-section-c-2.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-section-c-3.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-section-c-4.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-section-c-5.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-section-c-6.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-section-c-7.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-section-c-8.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/ind-base-4.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/unary-ptr-1.c

diff --git a/gcc/c-family/c-pretty-print.cc b/gcc/c-family/c-pretty-print.cc
index 51c8856b8630..45045fb2242f 100644
--- a/gcc/c-family/c-pretty-print.cc
+++ b/gcc/c-family/c-pretty-print.cc
@@ -1650,6 +1650,17 @@ c_pretty_printer::postfix_expression (tree e)
       pp_c_right_bracket (this);
       break;
 
+    case OMP_ARRAY_SECTION:
+      postfix_expression (TREE_OPERAND (e, 0));
+      pp_c_left_bracket (this);
+      if (TREE_OPERAND (e, 1))
+	expression (TREE_OPERAND (e, 1));
+      pp_colon (this);
+      if (TREE_OPERAND (e, 2))
+	expression (TREE_OPERAND (e, 2));
+      pp_c_right_bracket (this);
+      break;
+
     case CALL_EXPR:
       {
 	call_expr_arg_iterator iter;
@@ -2699,6 +2710,7 @@ c_pretty_printer::expression (tree e)
     case POSTINCREMENT_EXPR:
     case POSTDECREMENT_EXPR:
     case ARRAY_REF:
+    case OMP_ARRAY_SECTION:
     case CALL_EXPR:
     case COMPONENT_REF:
     case BIT_FIELD_REF:
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 878f323dd2f7..c31349dae2ff 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -6146,6 +6146,8 @@ c_parser_braced_init (c_parser *parser, tree type, bool nested_p,
   location_t brace_loc = c_parser_peek_token (parser)->location;
   gcc_obstack_init (&braced_init_obstack);
   gcc_assert (c_parser_next_token_is (parser, CPP_OPEN_BRACE));
+  bool save_c_omp_array_section_p = c_omp_array_section_p;
+  c_omp_array_section_p = false;
   matching_braces braces;
   braces.consume_open (parser);
   if (nested_p)
@@ -6184,6 +6186,7 @@ c_parser_braced_init (c_parser *parser, tree type, bool nested_p,
 	    break;
 	}
     }
+  c_omp_array_section_p = save_c_omp_array_section_p;
   c_token *next_tok = c_parser_peek_token (parser);
   if (next_tok->type != CPP_CLOSE_BRACE)
     {
@@ -9149,6 +9152,7 @@ c_parser_conditional_expression (c_parser *parser, struct c_expr *after,
 {
   struct c_expr cond, exp1, exp2, ret;
   location_t start, cond_loc, colon_loc;
+  bool save_c_omp_array_section_p = c_omp_array_section_p;
 
   gcc_assert (!after || c_dialect_objc ());
 
@@ -9156,6 +9160,7 @@ c_parser_conditional_expression (c_parser *parser, struct c_expr *after,
 
   if (c_parser_next_token_is_not (parser, CPP_QUERY))
     return cond;
+  c_omp_array_section_p = false;
   if (cond.value != error_mark_node)
     start = cond.get_start ();
   else
@@ -9208,6 +9213,7 @@ c_parser_conditional_expression (c_parser *parser, struct c_expr *after,
       ret.set_error ();
       ret.original_code = ERROR_MARK;
       ret.original_type = NULL;
+      c_omp_array_section_p = save_c_omp_array_section_p;
       return ret;
     }
   {
@@ -9254,6 +9260,7 @@ c_parser_conditional_expression (c_parser *parser, struct c_expr *after,
     }
   set_c_expr_source_range (&ret, start, exp2.get_finish ());
   ret.m_decimal = 0;
+  c_omp_array_section_p = save_c_omp_array_section_p;
   return ret;
 }
 
@@ -10705,6 +10712,7 @@ c_parser_postfix_expression (c_parser *parser)
 	  /* A statement expression.  */
 	  tree stmt;
 	  location_t brace_loc;
+	  bool save_c_omp_array_section_p = c_omp_array_section_p;
 	  c_parser_consume_token (parser);
 	  brace_loc = c_parser_peek_token (parser)->location;
 	  c_parser_consume_token (parser);
@@ -10721,6 +10729,7 @@ c_parser_postfix_expression (c_parser *parser)
 	      expr.set_error ();
 	      break;
 	    }
+	  c_omp_array_section_p = false;
 	  stmt = c_begin_stmt_expr ();
 	  c_parser_compound_statement_nostart (parser);
 	  location_t close_loc = c_parser_peek_token (parser)->location;
@@ -10731,6 +10740,7 @@ c_parser_postfix_expression (c_parser *parser)
 	  expr.value = c_finish_stmt_expr (brace_loc, stmt);
 	  set_c_expr_source_range (&expr, loc, close_loc);
 	  mark_exp_read (expr.value);
+	  c_omp_array_section_p = save_c_omp_array_section_p;
 	}
       else
 	{
@@ -12480,7 +12490,7 @@ c_parser_postfix_expression_after_primary (c_parser *parser,
 					   struct c_expr expr)
 {
   struct c_expr orig_expr;
-  tree ident, idx;
+  tree ident, idx, len;
   location_t sizeof_arg_loc[6], comp_loc;
   tree sizeof_arg[6];
   unsigned int literal_zero_mask;
@@ -12499,12 +12509,29 @@ c_parser_postfix_expression_after_primary (c_parser *parser,
 	case CPP_OPEN_SQUARE:
 	  /* Array reference.  */
 	  c_parser_consume_token (parser);
-	  idx = c_parser_expression (parser).value;
+	  idx = len = NULL_TREE;
+	  if (!c_omp_array_section_p
+	      || c_parser_next_token_is_not (parser, CPP_COLON))
+	    idx = c_parser_expression (parser).value;
+
+	  if (c_omp_array_section_p
+	      && c_parser_next_token_is (parser, CPP_COLON))
+	    {
+	      c_parser_consume_token (parser);
+	      if (c_parser_next_token_is_not (parser, CPP_CLOSE_SQUARE))
+		len = c_parser_expression (parser).value;
+
+	      expr.value = build_omp_array_section (op_loc, expr.value, idx,
+						    len);
+	    }
+	  else
+	    expr.value = build_array_ref (op_loc, expr.value, idx);
+
 	  c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE,
 				     "expected %<]%>");
+
 	  start = expr.get_start ();
 	  finish = parser->tokens_buf[0].location;
-	  expr.value = build_array_ref (op_loc, expr.value, idx);
 	  set_c_expr_source_range (&expr, start, finish);
 	  expr.original_code = ERROR_MARK;
 	  expr.original_type = NULL;
@@ -12829,6 +12856,8 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p,
   vec<tree, va_gc> *orig_types;
   struct c_expr expr;
   unsigned int idx = 0;
+  bool save_c_omp_array_section_p = c_omp_array_section_p;
+  c_omp_array_section_p = false;
 
   ret = make_tree_vector ();
   if (p_orig_types == NULL)
@@ -12882,6 +12911,7 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p,
     }
   if (orig_types)
     *p_orig_types = orig_types;
+  c_omp_array_section_p = save_c_omp_array_section_p;
   return ret;
 }
 
@@ -15126,7 +15156,7 @@ static tree
 c_parser_omp_variable_list (c_parser *parser,
 			    location_t clause_loc,
 			    enum omp_clause_code kind, tree list,
-			    bool allow_deref = false)
+			    bool map_lvalue = false)
 {
   auto_vec<omp_dim> dims;
   bool array_section_p;
@@ -15137,6 +15167,8 @@ c_parser_omp_variable_list (c_parser *parser,
 
   while (1)
     {
+      tree t = NULL_TREE;
+
       if (kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY)
 	{
 	  if (c_parser_next_token_is_not (parser, CPP_NAME)
@@ -15217,8 +15249,96 @@ c_parser_omp_variable_list (c_parser *parser,
 	  parser->tokens = tokens.address ();
 	  parser->tokens_avail = tokens.length ();
 	}
+      else if (map_lvalue
+	       && (kind == OMP_CLAUSE_MAP
+		   || kind == OMP_CLAUSE_TO
+		   || kind == OMP_CLAUSE_FROM))
+	{
+	  location_t loc = c_parser_peek_token (parser)->location;
+	  bool save_c_omp_array_section_p = c_omp_array_section_p;
+	  c_omp_array_section_p = true;
+	  c_expr expr = c_parser_expr_no_commas (parser, NULL);
+	  if (expr.value != error_mark_node)
+	    mark_exp_read (expr.value);
+	  c_omp_array_section_p = save_c_omp_array_section_p;
+	  tree decl = expr.value;
+
+	 /* This code rewrites a parsed expression containing various tree
+	    codes used to represent array accesses into a more uniform nest of
+	    OMP_ARRAY_SECTION nodes before it is processed by
+	    c-typeck.cc:handle_omp_array_sections_1.  It might be more
+	    efficient to move this logic to that function instead, analysing
+	    the parsed expression directly rather than this preprocessed
+	    form.  (See also equivalent code in cp/parser.cc,
+	    cp/semantics.cc).  */
+	  dims.truncate (0);
+	  if (TREE_CODE (decl) == OMP_ARRAY_SECTION)
+	    {
+	      while (TREE_CODE (decl) == OMP_ARRAY_SECTION)
+		{
+		  tree low_bound = TREE_OPERAND (decl, 1);
+		  tree length = TREE_OPERAND (decl, 2);
+		  dims.safe_push (omp_dim (low_bound, length, loc, false));
+		  decl = TREE_OPERAND (decl, 0);
+		}
 
-      tree t = NULL_TREE;
+	      while (TREE_CODE (decl) == ARRAY_REF
+		     || TREE_CODE (decl) == INDIRECT_REF
+		     || TREE_CODE (decl) == COMPOUND_EXPR)
+		{
+		  if (TREE_CODE (decl) == COMPOUND_EXPR)
+		    {
+		      decl = TREE_OPERAND (decl, 1);
+		      STRIP_NOPS (decl);
+		    }
+		  else if (TREE_CODE (decl) == INDIRECT_REF)
+		    {
+		      dims.safe_push (omp_dim (integer_zero_node,
+					       integer_one_node, loc, true));
+		      decl = TREE_OPERAND (decl, 0);
+		    }
+		  else  /* ARRAY_REF. */
+		    {
+		      tree index = TREE_OPERAND (decl, 1);
+		      dims.safe_push (omp_dim (index, integer_one_node, loc,
+					       true));
+		      decl = TREE_OPERAND (decl, 0);
+		    }
+		}
+
+	      for (int i = dims.length () - 1; i >= 0; i--)
+		decl = build_omp_array_section (loc,  decl, dims[i].low_bound,
+						dims[i].length);
+	    }
+	  else if (TREE_CODE (decl) == INDIRECT_REF)
+	    {
+	      /* Turn indirection of a pointer "*foo" into "foo[0:1]".  */
+	      decl = TREE_OPERAND (decl, 0);
+	      STRIP_NOPS (decl);
+
+	      decl = build_omp_array_section (loc, decl, integer_zero_node,
+					      integer_one_node);
+	    }
+	  else if (TREE_CODE (decl) == ARRAY_REF)
+	    {
+	      tree idx = TREE_OPERAND (decl, 1);
+
+	      decl = TREE_OPERAND (decl, 0);
+	      STRIP_NOPS (decl);
+
+	      decl = build_omp_array_section (loc, decl, idx, integer_one_node);
+	    }
+	  else if (TREE_CODE (decl) == NON_LVALUE_EXPR
+		   || CONVERT_EXPR_P (decl))
+	    decl = TREE_OPERAND (decl, 0);
+
+	  tree u = build_omp_clause (clause_loc, kind);
+	  OMP_CLAUSE_DECL (u) = decl;
+	  OMP_CLAUSE_CHAIN (u) = list;
+	  list = u;
+
+	  goto next_item;
+	}
 
       if (c_parser_next_token_is (parser, CPP_NAME)
 	  && c_parser_peek_token (parser)->id_kind == C_ID_ID)
@@ -15269,8 +15389,7 @@ c_parser_omp_variable_list (c_parser *parser,
 	    case OMP_CLAUSE_TO:
 	    start_component_ref:
 	      while (c_parser_next_token_is (parser, CPP_DOT)
-		     || (allow_deref
-			 && c_parser_next_token_is (parser, CPP_DEREF)))
+		     || c_parser_next_token_is (parser, CPP_DEREF))
 		{
 		  location_t op_loc = c_parser_peek_token (parser)->location;
 		  location_t arrow_loc = UNKNOWN_LOCATION;
@@ -15371,9 +15490,7 @@ c_parser_omp_variable_list (c_parser *parser,
 		       || kind == OMP_CLAUSE_TO)
 		      && !array_section_p
 		      && (c_parser_next_token_is (parser, CPP_DOT)
-			  || (allow_deref
-			      && c_parser_next_token_is (parser,
-							 CPP_DEREF))))
+			  || c_parser_next_token_is (parser, CPP_DEREF)))
 		    {
 		      for (unsigned i = 0; i < dims.length (); i++)
 			{
@@ -15385,7 +15502,9 @@ c_parser_omp_variable_list (c_parser *parser,
 		    }
 		  else
 		    for (unsigned i = 0; i < dims.length (); i++)
-		      t = tree_cons (dims[i].low_bound, dims[i].length, t);
+		      t = build_omp_array_section (clause_loc, t,
+						   dims[i].low_bound,
+						   dims[i].length);
 		}
 
 	      if ((kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY)
@@ -15433,6 +15552,8 @@ c_parser_omp_variable_list (c_parser *parser,
 	  parser->tokens = saved_tokens;
 	  parser->tokens_avail = tokens_avail;
 	}
+
+    next_item:
       if (c_parser_next_token_is_not (parser, CPP_COMMA))
 	break;
 
@@ -15449,7 +15570,7 @@ c_parser_omp_variable_list (c_parser *parser,
 
 static tree
 c_parser_omp_var_list_parens (c_parser *parser, enum omp_clause_code kind,
-			      tree list, bool allow_deref = false)
+			      tree list, bool map_lvalue = false)
 {
   /* The clauses location.  */
   location_t loc = c_parser_peek_token (parser)->location;
@@ -15470,7 +15591,7 @@ c_parser_omp_var_list_parens (c_parser *parser, enum omp_clause_code kind,
   matching_parens parens;
   if (parens.require_open (parser))
     {
-      list = c_parser_omp_variable_list (parser, loc, kind, list, allow_deref);
+      list = c_parser_omp_variable_list (parser, loc, kind, list, map_lvalue);
       parens.skip_until_found_close (parser);
     }
   return list;
@@ -15541,7 +15662,7 @@ c_parser_oacc_data_clause (c_parser *parser, pragma_omp_clause c_kind,
       gcc_unreachable ();
     }
   tree nl, c;
-  nl = c_parser_omp_var_list_parens (parser, OMP_CLAUSE_MAP, list, true);
+  nl = c_parser_omp_var_list_parens (parser, OMP_CLAUSE_MAP, list, false);
 
   for (c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
     OMP_CLAUSE_SET_MAP_KIND (c, kind);
@@ -17221,13 +17342,15 @@ c_parser_omp_clause_reduction (c_parser *parser, enum omp_clause_code kind,
 	  for (c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
 	    {
 	      tree d = OMP_CLAUSE_DECL (c), type;
-	      if (TREE_CODE (d) != TREE_LIST)
+	      if (TREE_CODE (d) != OMP_ARRAY_SECTION)
 		type = TREE_TYPE (d);
 	      else
 		{
 		  int cnt = 0;
 		  tree t;
-		  for (t = d; TREE_CODE (t) == TREE_LIST; t = TREE_CHAIN (t))
+		  for (t = d;
+		      TREE_CODE (t) == OMP_ARRAY_SECTION;
+		      t = TREE_OPERAND (t, 0))
 		    cnt++;
 		  type = TREE_TYPE (t);
 		  while (cnt > 0)
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 182315108a57..cf29534c0915 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -737,6 +737,7 @@ extern int in_alignof;
 extern int in_sizeof;
 extern int in_typeof;
 extern bool c_in_omp_for;
+extern bool c_omp_array_section_p;
 
 extern tree c_last_sizeof_arg;
 extern location_t c_last_sizeof_loc;
@@ -777,6 +778,7 @@ extern tree composite_type (tree, tree);
 extern tree build_component_ref (location_t, tree, tree, location_t,
 				 location_t);
 extern tree build_array_ref (location_t, tree, tree);
+extern tree build_omp_array_section (location_t, tree, tree, tree);
 extern tree build_external_ref (location_t, tree, bool, tree *);
 extern void pop_maybe_used (bool);
 extern struct c_expr c_expr_sizeof_expr (location_t, struct c_expr);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 2b90fa7f82b9..774dc1b3e6b4 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -76,6 +76,9 @@ int in_typeof;
 /* True when parsing OpenMP loop expressions.  */
 bool c_in_omp_for;
 
+/* True when parsing OpenMP map clause.  */
+bool c_omp_array_section_p;
+
 /* The argument of last parsed sizeof expression, only to be tested
    if expr.original_code == SIZEOF_EXPR.  */
 tree c_last_sizeof_arg;
@@ -1987,6 +1990,13 @@ mark_exp_read (tree exp)
     case C_MAYBE_CONST_EXPR:
       mark_exp_read (TREE_OPERAND (exp, 1));
       break;
+    case OMP_ARRAY_SECTION:
+      mark_exp_read (TREE_OPERAND (exp, 0));
+      if (TREE_OPERAND (exp, 1))
+	mark_exp_read (TREE_OPERAND (exp, 1));
+      if (TREE_OPERAND (exp, 2))
+	mark_exp_read (TREE_OPERAND (exp, 2));
+      break;
     default:
       break;
     }
@@ -2899,6 +2909,53 @@ build_array_ref (location_t loc, tree array, tree index)
       return ret;
     }
 }
+
+/* Build an OpenMP array section reference, creating an exact type for the
+   resulting expression based on the element type and bounds if possible.  If
+   we have variable bounds, create an incomplete array type for the result
+   instead.  */
+
+tree
+build_omp_array_section (location_t loc, tree array, tree index, tree length)
+{
+  tree type = TREE_TYPE (array);
+  gcc_assert (type);
+
+  tree sectype, eltype = TREE_TYPE (type);
+
+  /* It's not an array or pointer type.  Just reuse the type of the original
+     expression as the type of the array section (an error will be raised
+     anyway, later).  */
+  if (eltype == NULL_TREE || error_operand_p (eltype))
+    sectype = TREE_TYPE (array);
+  else
+    {
+      tree idxtype = NULL_TREE;
+
+      if (index != NULL_TREE
+	  && length != NULL_TREE
+	  && INTEGRAL_TYPE_P (TREE_TYPE (index))
+	  && INTEGRAL_TYPE_P (TREE_TYPE (length)))
+	{
+	  tree low = fold_convert (sizetype, index);
+	  tree high = fold_convert (sizetype, length);
+	  high = size_binop (PLUS_EXPR, low, high);
+	  high = size_binop (MINUS_EXPR, high, size_one_node);
+	  idxtype = build_range_type (sizetype, low, high);
+	}
+      else if ((index == NULL_TREE || integer_zerop (index))
+	       && length != NULL_TREE
+	       && INTEGRAL_TYPE_P (TREE_TYPE (length)))
+	idxtype = build_index_type (length);
+
+      gcc_assert (!error_operand_p (idxtype));
+
+      sectype = build_array_type (eltype, idxtype);
+    }
+
+  return build3_loc (loc, OMP_ARRAY_SECTION, sectype, array, index, length);
+}
+
 
 /* Build an external reference to identifier ID.  FUN indicates
    whether this will be used for a function call.  LOC is the source
@@ -2938,7 +2995,11 @@ build_external_ref (location_t loc, tree id, bool fun, tree *type)
       return error_mark_node;
     }
 
-  if (TREE_TYPE (ref) == error_mark_node)
+  /* For an OpenMP map clause, we can get better diagnostics for decls with
+     unmappable types if we return the decl with an error_mark_node type,
+     rather than returning error_mark_node for the decl itself.  */
+  if (TREE_TYPE (ref) == error_mark_node
+      && !c_omp_array_section_p)
     return error_mark_node;
 
   if (TREE_UNAVAILABLE (ref))
@@ -13762,7 +13823,7 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 {
   tree ret, low_bound, length, type;
   bool openacc = (ort & C_ORT_ACC) != 0;
-  if (TREE_CODE (t) != TREE_LIST)
+  if (TREE_CODE (t) != OMP_ARRAY_SECTION)
     {
       if (error_operand_p (t))
 	return error_mark_node;
@@ -13787,7 +13848,9 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 	t = ai.unconverted_ref_origin ();
       if (t == error_mark_node)
 	return error_mark_node;
-      if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL)
+      if (!VAR_P (t)
+	  && (ort == C_ORT_ACC || !EXPR_P (t))
+	  && TREE_CODE (t) != PARM_DECL)
 	{
 	  if (DECL_P (t))
 	    error_at (OMP_CLAUSE_LOCATION (c),
@@ -13835,14 +13898,14 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
       return ret;
     }
 
-  ret = handle_omp_array_sections_1 (c, TREE_CHAIN (t), types,
+  ret = handle_omp_array_sections_1 (c, TREE_OPERAND (t, 0), types,
 				     maybe_zero_len, first_non_one, ort);
   if (ret == error_mark_node || ret == NULL_TREE)
     return ret;
 
   type = TREE_TYPE (ret);
-  low_bound = TREE_PURPOSE (t);
-  length = TREE_VALUE (t);
+  low_bound = TREE_OPERAND (t, 1);
+  length = TREE_OPERAND (t, 2);
 
   if (low_bound == error_mark_node || length == error_mark_node)
     return error_mark_node;
@@ -14035,7 +14098,7 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 	  tree lb = save_expr (low_bound);
 	  if (lb != low_bound)
 	    {
-	      TREE_PURPOSE (t) = lb;
+	      TREE_OPERAND (t, 1) = lb;
 	      low_bound = lb;
 	    }
 	}
@@ -14066,14 +14129,15 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 	 array-section-subscript, the array section could be non-contiguous.  */
       if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND
 	  && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_AFFINITY
-	  && TREE_CODE (TREE_CHAIN (t)) == TREE_LIST)
+	  && TREE_CODE (TREE_OPERAND (t, 0)) == OMP_ARRAY_SECTION)
 	{
 	  /* If any prior dimension has a non-one length, then deem this
 	     array section as non-contiguous.  */
-	  for (tree d = TREE_CHAIN (t); TREE_CODE (d) == TREE_LIST;
-	       d = TREE_CHAIN (d))
+	  for (tree d = TREE_OPERAND (t, 0);
+	       TREE_CODE (d) == OMP_ARRAY_SECTION;
+	       d = TREE_OPERAND (d, 0))
 	    {
-	      tree d_length = TREE_VALUE (d);
+	      tree d_length = TREE_OPERAND (d, 2);
 	      if (d_length == NULL_TREE || !integer_onep (d_length))
 		{
 		  error_at (OMP_CLAUSE_LOCATION (c),
@@ -14096,7 +14160,7 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
   tree lb = save_expr (low_bound);
   if (lb != low_bound)
     {
-      TREE_PURPOSE (t) = lb;
+      TREE_OPERAND (t, 1) = lb;
       low_bound = lb;
     }
   ret = build_array_ref (OMP_CLAUSE_LOCATION (c), ret, low_bound);
@@ -14159,10 +14223,10 @@ handle_omp_array_sections (tree &c, enum c_omp_region_type ort)
 	maybe_zero_len = true;
 
       for (i = num, t = OMP_CLAUSE_DECL (c); i > 0;
-	   t = TREE_CHAIN (t))
+	   t = TREE_OPERAND (t, 0))
 	{
-	  tree low_bound = TREE_PURPOSE (t);
-	  tree length = TREE_VALUE (t);
+	  tree low_bound = TREE_OPERAND (t, 1);
+	  tree length = TREE_OPERAND (t, 2);
 
 	  i--;
 	  if (low_bound
@@ -14587,8 +14651,8 @@ c_oacc_check_attachments (tree c)
     {
       tree t = OMP_CLAUSE_DECL (c);
 
-      while (TREE_CODE (t) == TREE_LIST)
-	t = TREE_CHAIN (t);
+      while (TREE_CODE (t) == OMP_ARRAY_SECTION)
+	t = TREE_OPERAND (t, 0);
 
       if (TREE_CODE (TREE_TYPE (t)) != POINTER_TYPE)
 	{
@@ -14696,7 +14760,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	case OMP_CLAUSE_TASK_REDUCTION:
 	  need_implicitly_determined = true;
 	  t = OMP_CLAUSE_DECL (c);
-	  if (TREE_CODE (t) == TREE_LIST)
+	  if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	    {
 	      if (handle_omp_array_sections (c, ort))
 		{
@@ -15317,7 +15381,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    }
 	  else
 	    last_iterators = NULL_TREE;
-	  if (TREE_CODE (t) == TREE_LIST)
+	  if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	    {
 	      if (handle_omp_array_sections (c, ort))
 		remove = true;
@@ -15427,7 +15491,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    auto_vec<omp_addr_token *, 10> addr_tokens;
 
 	    t = OMP_CLAUSE_DECL (c);
-	    if (TREE_CODE (t) == TREE_LIST)
+	    if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	      {
 		grp_start_p = pc;
 		grp_sentinel = OMP_CLAUSE_CHAIN (c);
@@ -15597,6 +15661,9 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 
 	    if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL)
 	      {
+		if (ort != C_ORT_ACC && EXPR_P (t))
+		  break;
+
 		error_at (OMP_CLAUSE_LOCATION (c),
 			  "%qE is not a variable in %qs clause", t,
 			  omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
@@ -15827,7 +15894,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 
 	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	  t = OMP_CLAUSE_DECL (c);
-	  if (TREE_CODE (t) == TREE_LIST)
+	  if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	    {
 	      if (handle_omp_array_sections (c, ort))
 		remove = true;
diff --git a/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-1.c b/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-1.c
new file mode 100644
index 000000000000..36b2426de97c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+
+int foo (int *ptr);
+
+int main()
+{
+  int arr[20];
+  /* Reject array section as function argument.  */
+#pragma omp target map(foo(arr[3:5]))
+/* { dg-error {expected '\]' before ':' token} "" { target *-*-* } .-1 } */
+/* { dg-error {passing argument 1 of 'foo' makes pointer from integer without a cast} "" { target *-*-* } .-2 } */
+/* { dg-message {sorry, unimplemented: unsupported map expression} "" { target *-*-* } .-3 } */
+  { }
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-2.c b/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-2.c
new file mode 100644
index 000000000000..449487ad55d6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+
+int main()
+{
+  int arr[20];
+  /* Reject array section in statement expression.  */
+#pragma omp target map( ({ int x = 5; arr[0:x]; }) )
+/* { dg-error {expected '\]' before ':' token} "" { target *-*-* } .-1 } */
+/* { dg-message {sorry, unimplemented: unsupported map expression} "" { target *-*-* } .-2 } */
+  { }
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-3.c b/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-3.c
new file mode 100644
index 000000000000..8be15ced8c06
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-3.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+
+struct S {
+  int *ptr;
+};
+
+int main()
+{
+  int arr[20];
+
+  /* Reject array section in compound initialiser.  */
+#pragma omp target map( (struct S) { .ptr = (int *) arr[5:5] } )
+/* { dg-error {expected '\]' before ':' token} "" { target *-*-* } .-1 } */
+/* { dg-warning {cast to pointer from integer of different size} "" { target *-*-* } .-2 } */
+/* { dg-message {sorry, unimplemented: unsupported map expression} "" { target *-*-* } .-3 } */
+  { }
+
+  /* ...and this is unsupported too (probably not useful anyway).  */
+#pragma omp target map( (struct S) { .ptr = &arr[5] } )
+/* { dg-message {sorry, unimplemented: unsupported map expression} "" { target *-*-* } .-1 } */
+  { }
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-4.c b/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-4.c
new file mode 100644
index 000000000000..b78cdfc8a13e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-4.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+
+int x;
+
+int main()
+{
+  int arr[20];
+  int *ptr;
+  /* "arr[1:10]" looks like it might be an expression of array type, hence
+     able to be indexed (again).  This isn't allowed, though.  */
+#pragma omp target map(arr[1:10][2])
+/* { dg-error {'arr\[1\]' does not have pointer or array type} "" { target *-*-* } .-1 } */
+  { }
+#pragma omp target map(arr[1:x][2])
+/* { dg-error {'arr\[1\]' does not have pointer or array type} "" { target *-*-* } .-1 } */
+  { }
+  /* ...and nor is this.  */
+#pragma omp target map(ptr[1:10][2])
+/* { dg-error {'\*\(ptr \+ [0-9]+\)' does not have pointer or array type} "" { target *-*-* } .-1 } */
+  { }
+#pragma omp target map(ptr[1:x][2])
+/* { dg-error {'\*\(ptr \+ [0-9]+\)' does not have pointer or array type} "" { target *-*-* } .-1 } */
+  { }
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-5.c b/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-5.c
new file mode 100644
index 000000000000..2880d171abba
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-5.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+
+int partly = 0;
+
+int main()
+{
+  int arr[20];
+#pragma omp target map(partly ? arr[5:5] : arr)
+/* { dg-error {expected '\]' before ':' token} "" { target *-*-* } .-1 } */
+/* { dg-error {pointer/integer type mismatch in conditional expression} "" { target *-*-* } .-2 } */
+/* { dg-message {sorry, unimplemented: unsupported map expression} "" { target *-*-* } .-3 } */
+  { }
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-6.c b/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-6.c
new file mode 100644
index 000000000000..bfca4f0fca3f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-6.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+
+int x;
+
+int main()
+{
+  int arr[20];
+#pragma omp target map(arr[5:5] * 2)
+/* { dg-error {invalid operands to binary \*} "" { target *-*-* } .-1 } */
+  { }
+#pragma omp target map(arr[x:5] * 2)
+/* { dg-error {invalid operands to binary \*} "" { target *-*-* } .-1 } */
+  { }
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-7.c b/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-7.c
new file mode 100644
index 000000000000..1fd9e2b383a4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-7.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+
+int x;
+
+struct T {
+  int arr[20];
+};
+
+struct S {
+  struct T *tvec;
+};
+
+int main()
+{
+  struct S *s;
+  /* You can't use an array section like this.  Make sure sensible errors are
+     reported.  */
+#pragma omp target map(s->tvec[3:5].arr[0:20])
+/* { dg-error {'\(struct T \*\)&s->tvec\[3:5\]' is a pointer; did you mean to use '->'\?} "" { target *-*-* } .-1 } */
+  { }
+#pragma omp target map(s->tvec[5:x].arr[0:20])
+/* { dg-error {'\(struct T \*\)&s->tvec\[5:x\]' is a pointer; did you mean to use '->'\?} "" { target *-*-* } .-1 } */
+  { }
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-8.c b/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-8.c
new file mode 100644
index 000000000000..f90eca1fa9fa
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gomp/bad-array-section-c-8.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+
+int x;
+
+int main()
+{
+  int arr1[40];
+  int arr2[40];
+#pragma omp target map(arr1[arr2[4:5]:arr2[6:7]])
+/* { dg-error {low bound 'arr2\[4:5\]' of array section does not have integral type} "" { target *-*-* } .-1 } */
+  { }
+#pragma omp target map(arr1[arr2[:1]:arr2[6:1]])
+/* { dg-error {low bound 'arr2\[:1\]' of array section does not have integral type} "" { target *-*-* } .-1 } */
+  { }
+#pragma omp target map(arr1[x:arr2[6:1]])
+/* { dg-error {length 'arr2\[6:1\]' of array section does not have integral type} "" { target *-*-* } .-1 } */
+  { }
+
+  return 0;
+}
+
diff --git a/libgomp/libgomp.texi b/libgomp/libgomp.texi
index 30f69ee412b6..74d4ef34c437 100644
--- a/libgomp/libgomp.texi
+++ b/libgomp/libgomp.texi
@@ -242,7 +242,7 @@ The OpenMP 4.5 specification is fully supported.
 @item Discontiguous array section with @code{target update} construct
       @tab N @tab
 @item C/C++'s lvalue expressions in @code{to}, @code{from}
-      and @code{map} clauses @tab N @tab
+      and @code{map} clauses @tab Y @tab
 @item C/C++'s lvalue expressions in @code{depend} clauses @tab Y @tab
 @item Nested @code{declare target} directive @tab Y @tab
 @item Combined @code{master} constructs @tab Y @tab
diff --git a/libgomp/testsuite/libgomp.c-c++-common/ind-base-4.c b/libgomp/testsuite/libgomp.c-c++-common/ind-base-4.c
new file mode 100644
index 000000000000..91549ac4d245
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/ind-base-4.c
@@ -0,0 +1,50 @@
+// { dg-do run }
+// { dg-options "-fopenmp" }
+
+#include <assert.h>
+#include <stdlib.h>
+
+typedef struct
+{
+  int x[10];
+} S;
+
+typedef struct
+{
+  S ***s;
+} T;
+
+typedef struct
+{
+  T **t;
+} U;
+
+void
+foo (void)
+{
+  U *u = (U *) malloc (sizeof (U));
+  T *real_t = (T *) malloc (sizeof (T));
+  S *real_s = (S *) malloc (sizeof (S));
+  T **t_pp = &real_t;
+  S **s_pp = &real_s;
+  S ***s_ppp = &s_pp;
+  u->t = t_pp;
+  (*u->t)->s = s_ppp;
+  for (int i = 0; i < 10; i++)
+    (**((*u->t)->s))->x[i] = 0;
+#pragma omp target map(u->t, *u->t, (*u->t)->s, *(*u->t)->s, **(*u->t)->s, \
+		       (**(*u->t)->s)->x[0:10])
+  for (int i = 0; i < 10; i++)
+    (**((*u->t)->s))->x[i] = i * 3;
+  for (int i = 0; i < 10; i++)
+    assert ((**((*u->t)->s))->x[i] == i * 3);
+  free (real_s);
+  free (real_t);
+  free (u);
+}
+
+int main (int argc, char *argv[])
+{
+  foo ();
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/unary-ptr-1.c b/libgomp/testsuite/libgomp.c-c++-common/unary-ptr-1.c
new file mode 100644
index 000000000000..3623b2695763
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/unary-ptr-1.c
@@ -0,0 +1,16 @@
+#include <assert.h>
+
+int main (int argc, char *argv[])
+{
+  int y = 0;
+  int *x = &y;
+
+#pragma omp target map(*x)
+  {
+    (*x)++;
+  }
+
+  assert (y == 1);
+
+  return 0;
+}
-- 
GitLab