diff mbox series

[v2,06/11] OpenMP: lvalue parsing for map clauses (C++)

Message ID 62e4e371468638d2f155c528a5c1e597558a56ac.1647619144.git.julian@codesourcery.com
State New
Headers show
Series OpenMP 5.0: C & C++ "declare mapper" support (plus struct rework, etc.) | expand

Commit Message

Julian Brown March 18, 2022, 4:26 p.m. UTC
This patch changes parsing for OpenMP map clauses in C++ to use the
generic expression parser, hence adds support for parsing general
lvalues (as required by OpenMP 5.0+).  So far only a few new types of
expression are actually supported throughout compilation (including
everything in the testsuite of course, and newly-added tests), and we
attempt to reject unsupported expressions in order to avoid surprises
for the user.

This version of the patch adds code to recalculate the bias for ATTACH
operations on struct bases which are not DECL_P (edit: oops, and leaves
in a commented-out line).

2022-03-17  Julian Brown  <julian@codesourcery.com>

gcc/c-family/
        * c-omp.cc (c_omp_address_inspector::map_supported_p): Support
	OMP_ARRAY_SECTION.

gcc/cp/
        * error.cc (dump_expr): Handle OMP_ARRAY_SECTION.
        * parser.cc (cp_parser_new): Initialize parser->omp_array_section_p.
        (cp_parser_postfix_open_square_expression): Support OMP_ARRAY_SECTION
        parsing.
        (cp_parser_omp_var_list_no_open): Remove ALLOW_DEREF parameter, add
        MAP_LVALUE in its place.  Supported generalised lvalue parsing for map
        clauses.
        (cp_parser_omp_var_list): Remove ALLOW_DEREF parameter, add MAP_LVALUE.
        Pass to cp_parser_omp_var_list_no_open.
        (cp_parser_oacc_data_clause, cp_parser_omp_all_clauses): Update calls
        to cp_parser_omp_var_list.
        * parser.h (cp_parser): Add omp_array_section_p field.
        * semantics.cc (handle_omp_array_sections_1): Handle more types of map
        expression.
        (handle_omp_array_section): Disallow pointer-to-member mappings.
        (finish_omp_clauses): Check for supported types of expression.

gcc/
        * gimplify.cc (omp_containing_struct): Handle POINTER_PLUS_EXPR.
	(accumulate_sibling_list): Fix support for non-DECL_P struct bases.
	Don't create firstprivate pointers for struct bases that are explicitly
	mapped TO elsewhere in the clause list.  Add support for adjusting
	struct ATTACH bias.
        (omp_ptrmem_p): New function.
        (omp_build_struct_sibling_lists): Support length-two group for
        synthesized inner struct mapping.  Explicitly disallow pointers to
        members.  Recalculate bias for struct ATTACH nodes.
        * tree-pretty-print.c (dump_generic_node): Support OMP_ARRAY_SECTION.
        * tree.def (OMP_ARRAY_SECTION): New tree code.

gcc/testsuite/
        * c-c++-common/gomp/map-6.c: Update expected output.
        * g++.dg/gomp/pr67522.C: Likewise.
        * g++.dg/gomp/ind-base-3.C: New test.
        * g++.dg/gomp/map-assignment-1.C: New test.
        * g++.dg/gomp/map-inc-1.C: New test.
        * g++.dg/gomp/map-lvalue-ref-1.C: New test.
        * g++.dg/gomp/map-ptrmem-1.C: New test.
        * g++.dg/gomp/map-ptrmem-2.C: New test.
        * g++.dg/gomp/map-static-cast-lvalue-1.C: New test.
        * g++.dg/gomp/map-ternary-1.C: New test.
        * g++.dg/gomp/member-array-2.C: New test.

libgomp/
        * testsuite/libgomp.c++/ind-base-1.C: New test.
        * testsuite/libgomp.c++/ind-base-2.C: New test.
        * testsuite/libgomp.c++/map-comma-1.C: New test.
        * testsuite/libgomp.c++/map-rvalue-ref-1.C: New test.
        * testsuite/libgomp.c++/struct-ref-1.C: New test.
	* testsuite/libgomp.c-c++-common/array-field-1.c: New test.
	* testsuite/libgomp.c-c++-common/array-of-struct-1.c: New test.
	* testsuite/libgomp.c-c++-common/array-of-struct-2.c: New test.
---
 gcc/c-family/c-omp.cc                         |   1 +
 gcc/cp/error.cc                               |   9 +
 gcc/cp/parser.cc                              | 141 +++++++++++++--
 gcc/cp/parser.h                               |   3 +
 gcc/cp/semantics.cc                           |  59 ++++++-
 gcc/gimplify.cc                               | 119 +++++++++++--
 gcc/testsuite/c-c++-common/gomp/map-6.c       |   4 +-
 gcc/testsuite/g++.dg/gomp/ind-base-3.C        |  38 ++++
 gcc/testsuite/g++.dg/gomp/map-assignment-1.C  |  12 ++
 gcc/testsuite/g++.dg/gomp/map-inc-1.C         |  10 ++
 gcc/testsuite/g++.dg/gomp/map-lvalue-ref-1.C  |  19 ++
 gcc/testsuite/g++.dg/gomp/map-ptrmem-1.C      |  37 ++++
 gcc/testsuite/g++.dg/gomp/map-ptrmem-2.C      |  40 +++++
 .../g++.dg/gomp/map-static-cast-lvalue-1.C    |  17 ++
 gcc/testsuite/g++.dg/gomp/map-ternary-1.C     |  20 +++
 gcc/testsuite/g++.dg/gomp/member-array-2.C    |  92 ++++++++++
 gcc/testsuite/g++.dg/gomp/pr67522.C           |   2 +-
 gcc/tree-pretty-print.cc                      |  14 ++
 gcc/tree.def                                  |   3 +
 libgomp/testsuite/libgomp.c++/ind-base-1.C    | 162 ++++++++++++++++++
 libgomp/testsuite/libgomp.c++/ind-base-2.C    |  49 ++++++
 libgomp/testsuite/libgomp.c++/map-comma-1.C   |  15 ++
 .../testsuite/libgomp.c++/map-rvalue-ref-1.C  |  22 +++
 libgomp/testsuite/libgomp.c++/struct-ref-1.C  |  97 +++++++++++
 .../libgomp.c-c++-common/array-field-1.c      |  35 ++++
 .../libgomp.c-c++-common/array-of-struct-1.c  |  65 +++++++
 .../libgomp.c-c++-common/array-of-struct-2.c  |  65 +++++++
 27 files changed, 1123 insertions(+), 27 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/gomp/ind-base-3.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/map-assignment-1.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/map-inc-1.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/map-lvalue-ref-1.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/map-ptrmem-1.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/map-ptrmem-2.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/map-static-cast-lvalue-1.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/map-ternary-1.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/member-array-2.C
 create mode 100644 libgomp/testsuite/libgomp.c++/ind-base-1.C
 create mode 100644 libgomp/testsuite/libgomp.c++/ind-base-2.C
 create mode 100644 libgomp/testsuite/libgomp.c++/map-comma-1.C
 create mode 100644 libgomp/testsuite/libgomp.c++/map-rvalue-ref-1.C
 create mode 100644 libgomp/testsuite/libgomp.c++/struct-ref-1.C
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/array-field-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/array-of-struct-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/array-of-struct-2.c

Comments

Jakub Jelinek May 24, 2022, 2:15 p.m. UTC | #1
On Fri, Mar 18, 2022 at 09:26:47AM -0700, Julian Brown wrote:
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -4266,6 +4266,9 @@ cp_parser_new (cp_lexer *lexer)
>    parser->omp_declare_simd = NULL;
>    parser->oacc_routine = NULL;
>  
> +  /* Allow array slice in expression.  */

Better /* Disallow OpenMP array sections in expressions.  */

> +  parser->omp_array_section_p = false;
> +
>    /* Not declaring an implicit function template.  */
>    parser->auto_is_implicit_function_template_parm_p = false;
>    parser->fully_implicit_function_template_p = false;

I think we should figure out when we should temporarily disable
  parser->omp_array_section_p = false;
and restore it afterwards to a saved value.  E.g.
cp_parser_lambda_expression seems like a good candidate, the fact that
OpenMP array sections are allowed say in map clause doesn't mean they are
allowed inside of lambdas and it would be especially hard when the lambda
is defining a separate function and the search for OMP_ARRAY_SECTION
probably wouldn't be able to discover those.
Other spots to consider might be statement expressions, perhaps type
definitions etc.

> @@ -8021,6 +8024,7 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
>    releasing_vec expression_list = NULL;
>    location_t loc = cp_lexer_peek_token (parser->lexer)->location;
>    bool saved_greater_than_is_operator_p;
> +  bool saved_colon_corrects_to_scope_p;
>  
>    /* Consume the `[' token.  */
>    cp_lexer_consume_token (parser->lexer);
> @@ -8028,6 +8032,9 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
>    saved_greater_than_is_operator_p = parser->greater_than_is_operator_p;
>    parser->greater_than_is_operator_p = true;
>  
> +  saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p;
> +  parser->colon_corrects_to_scope_p = false;

I think the last above line should be guarded on
  if (parser->omp_array_section_p)
There is no reason to get worse diagnostics in non-OpenMP code or even in
OpenMP code where array sections aren't allowed.

> +
> +      /* NOTE: We are reusing using the type of the whole array as the type of
> +	 the array section here, which isn't necessarily entirely correct.
> +	 Might need revisiting.  */

"reusing using" looks weird.
As for the type of OMP_ARRAY_SECTION trees, perhaps we could initially use
an incomplete array (so array element would be meaningful)
and when we figure out the details and the array section is contiguous
change its type to array type covering it.

> +      return build3_loc (input_location, OMP_ARRAY_SECTION,
> +			 TREE_TYPE (postfix_expression),
> +			 postfix_expression, index, length);
> +    }
> +
> +  parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
> +
>    /* Look for the closing `]'.  */
>    cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
>  
> @@ -36536,7 +36570,7 @@ struct omp_dim
>  static tree
>  cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
>  				tree list, bool *colon,
> -				bool allow_deref = false)
> +				bool map_lvalue = false)
>  {
>    auto_vec<omp_dim> dims;
>    bool array_section_p;
> @@ -36547,12 +36581,95 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
>        parser->colon_corrects_to_scope_p = false;
>        *colon = false;
>      }
> +  begin_scope (sk_omp, NULL);

Why?  Base-language-wise, clauses don't introduce a new scope
for name-lookup.
And if it is really needed, I'd strongly prefer to either do it solely
for the clauses that might need it, or do begin_scope before first
such clause and finish at the end if it has been introduced.

>    while (1)
>      {
>        tree name, decl;
>  
>        if (kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY)
>  	cp_parser_parse_tentatively (parser);
> +      else if (map_lvalue && kind == OMP_CLAUSE_MAP)
> +	{

This shouldn't be done just for OMP_CLAUSE_MAP, but for all the
other clauses that accept array sections, including
OMP_CLAUSE_DEPEND, OMP_CLAUSE_AFFINITY, OMP_CLAUSE_MAP, OMP_CLAUSE_TO,
OMP_CLAUSE_FROM, OMP_CLAUSE_INCLUSIVE, OMP_CLAUSE_EXCLUSIVE,
OMP_CLAUSE_USE_DEVICE_ADDR, OMP_CLAUSE_HAS_DEVICE_ADDR,
OMP_CLAUSE_*REDUCTION.
And preferrably, they should be kept in the IL until *finish_omp_clauses,
which should handle those instead of TREE_LIST that represented them before.
Additionally, something should diagnose incorrect uses of OMP_ARRAY_SECTION,
which is everywhere in the expressions but as the outermost node(s),
i.e. for clauses that do allow array sections scan OMP_CLAUSE_DECL after
handling handleable array sections and complain about embedded
OMP_ARRAY_SECTION, including OMP_ARRAY_SECTION say in the lower-bound,
length and/or stride expressions of the valid OMP_ARRAY_SECTION.

For C++ that also means handling OMP_ARRAY_SECTION code in pt.c.

	Jakub
Julian Brown Nov. 1, 2022, 9:50 p.m. UTC | #2
Hi,

On Tue, 24 May 2022 16:15:31 +0200
Jakub Jelinek via Fortran <fortran@gcc.gnu.org> wrote:

> On Fri, Mar 18, 2022 at 09:26:47AM -0700, Julian Brown wrote:
> > --- a/gcc/cp/parser.cc
> > +++ b/gcc/cp/parser.cc
> > @@ -4266,6 +4266,9 @@ cp_parser_new (cp_lexer *lexer)
> >    parser->omp_declare_simd = NULL;
> >    parser->oacc_routine = NULL;
> >  
> > +  /* Allow array slice in expression.  */  
> 
> Better /* Disallow OpenMP array sections in expressions.  */

Fixed.

> > +  parser->omp_array_section_p = false;
> > +
> >    /* Not declaring an implicit function template.  */
> >    parser->auto_is_implicit_function_template_parm_p = false;
> >    parser->fully_implicit_function_template_p = false;  
> 
> I think we should figure out when we should temporarily disable
>   parser->omp_array_section_p = false;
> and restore it afterwards to a saved value.  E.g.
> cp_parser_lambda_expression seems like a good candidate, the fact that
> OpenMP array sections are allowed say in map clause doesn't mean they
> are allowed inside of lambdas and it would be especially hard when
> the lambda is defining a separate function and the search for
> OMP_ARRAY_SECTION probably wouldn't be able to discover those.
> Other spots to consider might be statement expressions, perhaps type
> definitions etc.

I've had a go at doing this -- several expression types now forbid
array-section syntax (see new "bad-array-section-*" tests added). I'm
afraid my C++ isn't quite up to figuring out how it's possible to
define a type inside an expression (inside a map clause) if we forbid
lambdas and statement expressions though -- can you give an example?

> > @@ -8021,6 +8024,7 @@ cp_parser_postfix_open_square_expression
> > (cp_parser *parser, releasing_vec expression_list = NULL;
> >    location_t loc = cp_lexer_peek_token (parser->lexer)->location;
> >    bool saved_greater_than_is_operator_p;
> > +  bool saved_colon_corrects_to_scope_p;
> >  
> >    /* Consume the `[' token.  */
> >    cp_lexer_consume_token (parser->lexer);
> > @@ -8028,6 +8032,9 @@ cp_parser_postfix_open_square_expression
> > (cp_parser *parser, saved_greater_than_is_operator_p =
> > parser->greater_than_is_operator_p;
> > parser->greater_than_is_operator_p = true; 
> > +  saved_colon_corrects_to_scope_p =
> > parser->colon_corrects_to_scope_p;
> > +  parser->colon_corrects_to_scope_p = false;  
> 
> I think the last above line should be guarded on
>   if (parser->omp_array_section_p)
> There is no reason to get worse diagnostics in non-OpenMP code or
> even in OpenMP code where array sections aren't allowed.

Fixed.

> > +
> > +      /* NOTE: We are reusing using the type of the whole array as
> > the type of
> > +	 the array section here, which isn't necessarily entirely
> > correct.
> > +	 Might need revisiting.  */  
> 
> "reusing using" looks weird.
> As for the type of OMP_ARRAY_SECTION trees, perhaps we could
> initially use an incomplete array (so array element would be
> meaningful) and when we figure out the details and the array section
> is contiguous change its type to array type covering it.

This version of the patch makes a best-effort attempt to create an
exact-sized array type at parse time, else falls back to an incomplete
array type if there are e.g. variable bounds. The type is essentially
only used for diagnostics anyway, I think, so that should hopefully be
good enough.

> > +      return build3_loc (input_location, OMP_ARRAY_SECTION,
> > +			 TREE_TYPE (postfix_expression),
> > +			 postfix_expression, index, length);
> > +    }
> > +
> > +  parser->colon_corrects_to_scope_p =
> > saved_colon_corrects_to_scope_p; +
> >    /* Look for the closing `]'.  */
> >    cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
> >  
> > @@ -36536,7 +36570,7 @@ struct omp_dim
> >  static tree
> >  cp_parser_omp_var_list_no_open (cp_parser *parser, enum
> > omp_clause_code kind, tree list, bool *colon,
> > -				bool allow_deref = false)
> > +				bool map_lvalue = false)
> >  {
> >    auto_vec<omp_dim> dims;
> >    bool array_section_p;
> > @@ -36547,12 +36581,95 @@ cp_parser_omp_var_list_no_open (cp_parser
> > *parser, enum omp_clause_code kind,
> > parser->colon_corrects_to_scope_p = false; *colon = false;
> >      }
> > +  begin_scope (sk_omp, NULL);  
> 
> Why?  Base-language-wise, clauses don't introduce a new scope
> for name-lookup.

I think this was in aid of a particular test case
(c-c++-common/gomp/map-6.c) that tests various bad usages of "always"
and "close" modifiers, together with variables called literally
"always" and "close".  Parse failures during earlier tests could make
later tests fail without the scope.  I've moved the scope-creation to
the appropriate caller.  (Is there a better way?  Discarding
newly-created symbols on error, perhaps?)

> And if it is really needed, I'd strongly prefer to either do it solely
> for the clauses that might need it, or do begin_scope before first
> such clause and finish at the end if it has been introduced.
> 
> >    while (1)
> >      {
> >        tree name, decl;
> >  
> >        if (kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY)
> >  	cp_parser_parse_tentatively (parser);
> > +      else if (map_lvalue && kind == OMP_CLAUSE_MAP)
> > +	{  
> 
> This shouldn't be done just for OMP_CLAUSE_MAP, but for all the
> other clauses that accept array sections, including
> OMP_CLAUSE_DEPEND, OMP_CLAUSE_AFFINITY, OMP_CLAUSE_MAP, OMP_CLAUSE_TO,
> OMP_CLAUSE_FROM, OMP_CLAUSE_INCLUSIVE, OMP_CLAUSE_EXCLUSIVE,
> OMP_CLAUSE_USE_DEVICE_ADDR, OMP_CLAUSE_HAS_DEVICE_ADDR,
> OMP_CLAUSE_*REDUCTION.

I'm not too sure about all of those -- Tobias points out that
"INCLUSIVE", "EXCLUSIVE", *DEVICE* and *REDUCTION* take "variable list"
item types, not "locator list", though sometimes with an array section
being permitted (in OpenMP 5.2+).

This version of the patch supports MAP, TO and FROM -- it might be
possible to unify the parsing for DEPEND and AFFINITY too, maybe as a
later patch.

> And preferrably, they should be kept in the IL until
> *finish_omp_clauses, which should handle those instead of TREE_LIST
> that represented them before.

The next patch makes that change.

> Additionally, something should diagnose
> incorrect uses of OMP_ARRAY_SECTION, which is everywhere in the
> expressions but as the outermost node(s), i.e. for clauses that do
> allow array sections scan OMP_CLAUSE_DECL after handling handleable
> array sections and complain about embedded OMP_ARRAY_SECTION,
> including OMP_ARRAY_SECTION say in the lower-bound, length and/or
> stride expressions of the valid OMP_ARRAY_SECTION.

This version of the patch handles low bound/length incorrectly being
array sections, though no extra scan has been needed so far for that (I
guess "handle_omp_array_sections" or address parsing in gimplify.cc can
handle other cases that might arise).

> For C++ that also means handling OMP_ARRAY_SECTION code in pt.c.

The next patch handles that bit too.

Tested (alongside next patch) with offloading to NVPTX -- with my
previously-posted "address tokenization" patch also applied.

OK?

Thanks,

Julian
Jakub Jelinek Nov. 2, 2022, 11:58 a.m. UTC | #3
Hi!

Thanks for working on this!

On Tue, Nov 01, 2022 at 09:50:38PM +0000, Julian Brown wrote:
> > I think we should figure out when we should temporarily disable
> >   parser->omp_array_section_p = false;
> > and restore it afterwards to a saved value.  E.g.
> > cp_parser_lambda_expression seems like a good candidate, the fact that
> > OpenMP array sections are allowed say in map clause doesn't mean they
> > are allowed inside of lambdas and it would be especially hard when
> > the lambda is defining a separate function and the search for
> > OMP_ARRAY_SECTION probably wouldn't be able to discover those.
> > Other spots to consider might be statement expressions, perhaps type
> > definitions etc.
> 
> I've had a go at doing this -- several expression types now forbid
> array-section syntax (see new "bad-array-section-*" tests added). I'm
> afraid my C++ isn't quite up to figuring out how it's possible to
> define a type inside an expression (inside a map clause) if we forbid
> lambdas and statement expressions though -- can you give an example?

But we can't forbid lambdas inside of the map clause expressions,
they are certainly valid in OpenMP, and IMNSHO shouldn't disallow statement
expressions, people might not even know they use a statement expression,
they could just use some standard macro which uses a statement expression
under the hood.  Though your testcases look good.

> > This shouldn't be done just for OMP_CLAUSE_MAP, but for all the
> > other clauses that accept array sections, including
> > OMP_CLAUSE_DEPEND, OMP_CLAUSE_AFFINITY, OMP_CLAUSE_MAP, OMP_CLAUSE_TO,
> > OMP_CLAUSE_FROM, OMP_CLAUSE_INCLUSIVE, OMP_CLAUSE_EXCLUSIVE,
> > OMP_CLAUSE_USE_DEVICE_ADDR, OMP_CLAUSE_HAS_DEVICE_ADDR,
> > OMP_CLAUSE_*REDUCTION.
> 
> I'm not too sure about all of those -- Tobias points out that
> "INCLUSIVE", "EXCLUSIVE", *DEVICE* and *REDUCTION* take "variable list"
> item types, not "locator list", though sometimes with an array section
> being permitted (in OpenMP 5.2+).

That is true.  For the clauses that don't use locator lists but variable
lists but accept array sections there are strict restrictions on what one
can use, basically one can only have varname or varname[...] or
varname[...][...] etc. where ... is the normal array element or array
section syntax.  So, we probably should continue to parse them as now,
but we can use OMP_ARRAY_SECTION to hold what we've parsed or even share
code with parsing array sections and the [...] on those clauses.

> Tested (alongside next patch) with offloading to NVPTX -- with my
> previously-posted "address tokenization" patch also applied.

> 2022-11-01  Julian Brown  <julian@codesourcery.com>
> 
> gcc/c-family/
>         * c-omp.cc (c_omp_address_inspector::map_supported_p): Handle
> 	OMP_ARRAY_SECTION.
> 
> gcc/cp/
> 	* constexpr.cc (potential_consant_expression_1): Handle
> 	OMP_ARRAY_SECTION.
>         * error.cc (dump_expr): Handle OMP_ARRAY_SECTION.
>         * parser.cc (cp_parser_new): Initialize parser->omp_array_section_p.
> 	(cp_parser_statement_expr): Disallow array sections.
>         (cp_parser_postfix_open_square_expression): Support OMP_ARRAY_SECTION
>         parsing.
> 	(cp_parser_parenthesized_expression_list, cp_parser_lambda_expression,
> 	cp_parser_braced_list): Disallow array sections.
>         (cp_parser_omp_var_list_no_open): Remove ALLOW_DEREF parameter, add
>         MAP_LVALUE in its place.  Supported generalised lvalue parsing for
> 	OpenMP map, to and from clauses.
>         (cp_parser_omp_var_list): Remove ALLOW_DEREF parameter, add MAP_LVALUE.
>         Pass to cp_parser_omp_var_list_no_open.
>         (cp_parser_oacc_data_clause, cp_parser_omp_all_clauses): Update calls
>         to cp_parser_omp_var_list.
> 	(cp_parser_omp_clause_map): Add sk_omp scope around
> 	cp_parser_omp_var_list_no_open call.
>         * parser.h (cp_parser): Add omp_array_section_p field.
>         * semantics.cc (handle_omp_array_sections_1): Handle more types of map
>         expression.
>         (handle_omp_array_section): Handle non-DECL_P attachment points.
>         (finish_omp_clauses): Check for supported types of expression.
> 
> gcc/
>         * tree-pretty-print.c (dump_generic_node): Support OMP_ARRAY_SECTION.
>         * tree.def (OMP_ARRAY_SECTION): New tree code.
> 
> gcc/testsuite/
>         * c-c++-common/gomp/map-6.c: Update expected output.
> 	* g++.dg/gomp/bad-array-section-1.C: New test.
> 	* g++.dg/gomp/bad-array-section-2.C: New test.
> 	* g++.dg/gomp/bad-array-section-3.C: New test.
> 	* g++.dg/gomp/bad-array-section-4.C: New test.
> 	* g++.dg/gomp/bad-array-section-5.C: New test.
> 	* g++.dg/gomp/bad-array-section-6.C: New test.
> 	* g++.dg/gomp/bad-array-section-7.C: New test.
> 	* g++.dg/gomp/bad-array-section-8.C: New test.
> 	* g++.dg/gomp/bad-array-section-9.C: New test.
> 	* g++.dg/gomp/has_device_addr-non-lvalue-1.C: New test.
>         * g++.dg/gomp/pr67522.C: Update expected output.
>         * g++.dg/gomp/ind-base-3.C: New test.
>         * g++.dg/gomp/map-assignment-1.C: New test.
>         * g++.dg/gomp/map-inc-1.C: New test.
>         * g++.dg/gomp/map-lvalue-ref-1.C: New test.
>         * g++.dg/gomp/map-ptrmem-1.C: New test.
>         * g++.dg/gomp/map-ptrmem-2.C: New test.
>         * g++.dg/gomp/map-static-cast-lvalue-1.C: New test.
>         * g++.dg/gomp/map-ternary-1.C: New test.
>         * g++.dg/gomp/member-array-2.C: New test.
> 
> libgomp/
> 	* testsuite/libgomp.c++/baseptrs-4.C: Remove commented-out cases that
> 	now work.
>         * testsuite/libgomp.c++/ind-base-1.C: New test.
>         * testsuite/libgomp.c++/ind-base-2.C: New test.
> 	* testsuite/libgomp.c++/lvalue-tofrom-1.C: New test.
> 	* testsuite/libgomp.c++/lvalue-tofrom-2.C: New test.
>         * testsuite/libgomp.c++/map-comma-1.C: New test.
>         * testsuite/libgomp.c++/map-rvalue-ref-1.C: New test.
>         * testsuite/libgomp.c++/member-array-1.C: New test.
>         * testsuite/libgomp.c++/struct-ref-1.C: New test.
>         * testsuite/libgomp.c++/array-field-1.C: New test.
>         * testsuite/libgomp.c++/array-of-struct-1.C: New test.
>         * testsuite/libgomp.c++/array-of-struct-2.C: New test.

Some lines are (correctly) tab indented above, but others 8 spaces.
This will not get through pre-commit hook.

> @@ -5265,6 +5268,9 @@ static cp_expr
>  cp_parser_statement_expr (cp_parser *parser)
>  {
>    cp_token_position start = cp_parser_start_tentative_firewall (parser);
> +  bool saved_omp_array_section_p = parser->omp_array_section_p;
> +
> +  parser->omp_array_section_p = false;

The modern C++ FE way of doing this is
  auto oas = make_temp_override (parser->omp_array_section_p, false);
where you don't need to do anything at the end and it handles say return
in the middle or goto out of the block.  It isn't appropriate when
you need to restore it at some other location than the end of the containing
block.

> +      if ((index && error_operand_p (index))
> +	  || (length && error_operand_p (length)))
> +	return error_mark_node;

error_operand_p (NULL) works and returns false, so you can just do
      if (error_operand_p (index) || error_operand_p (length))
	return error_mark_node;
Though I wonder if the
> +
> +      cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);

shouldn't be done before it, if one of the expressions is erroneous,
but we have there ] after it, I think we want to continue parsing after it.
> +
> +      tree idxtype;
> +
> +      if (index)
> +	index = maybe_constant_value (index);
> +      if (length)
> +	length = maybe_constant_value (length);

This is incorrect in templates (when processing_template_decl).
maybe_constant_value should be only called when !processing_template_decl,
above you should call fold_non_dependent_expr instead.
And those handle (pass through) NULL_TREE, so no need to conditionalize.
Or maybe even better maybe_fold_non_dependent_expr which will only
return different tree from the original if it actually managed to
fold it into a constant.

Another thing is that in the C++ FE we usually do just parsing in
parser.cc and the actual building of the expressions in some routine
in semantics.cc or decl2.cc or so.
The point is that it sometimes needs to be called twice, once
during parsing (where for !processing_template_decl we handle everything
right away, otherwise for processing_template_decl if the expressions
(in your case postfix_expression, index and/or length) are
type_dependent_expression_p, one doesn't do much processing and
just build_min_nt_loc or so some (dependent) tree just to hold
the expressions (OMP_ARRAY_SECTION in your case).
Or, if processing_template_decl but nothing is type dependent, it does some
diagnostics etc. with build_non_dependent_expr around the expressions,
only to throw it away at the end unless it resulted in an error and
still build a tree with the original expressions (but perhaps a
non-dependent type of the whole expression).
And then there needs to be a pt.cc case which handles the raw
OMP_ARRAY_SECTION tree, will recurse on the subexpressions and finally
call the function that is also called during the parsing to build the tree,
but this time usually with !processing_template_decl, so it builds the final
thing.

For the normal array element parsing, this is done in grok_array_decl.

In C++ test coverage, it is usually needed to have normal non-template
tests (those can be often shared with C FE too in c-c++-common/gomp/),
and then some tests in templates, and there both the non-dependent stuff,
say if you write
template <int N>
void foo ()
{
// normal C/C++ subset stuff here
}
void bar () { foo<0> (); }
and then dependent stuff, where you have say
template <typename T, typename U, typename V>
void baz (T x, U y, V z)
{
... map(x[y:z])
}
and the like or say value dependent N from the earlier template
etc. to test that it works properly if some or all expressions are
type or value dependent during parsing and only later on are instantiated.

Yet another thing is that C++23 allows and we handle multidimensional
subscript operator, see
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2128r6.pdf
Right now the multi-dimensional subscript only applies to cases where
there is a user operator[] and I believe OpenMP array sections on the
other side are strictly for cases where overloaded operators do not apply,
but one can certainly write:
  #pragma omp target map(tofrom:a[1,2,3:4:5])
and while in C++20 and older that is parsed as
a[(1,2,3):4:5]
perhaps with a -Wcomma-subscript warning, in C++23 parsing that yields
a vector of expressions.  So, probably we need to diagnose the case
where there is vector of expressions (i.e. 2 or more) as an error.
C++23 also allows a[] but in that case one can't use the OpenMP
array section syntax, it is handled only if [ is immediately followed by ]
so there is no :.

> +
> +      /* If we know the integer bounds, create an index type with exact
> +	 low/high (or zero/length) bounds.  Otherwise, create an incomplete
> +	 array type.  (This mostly only affects diagnostics.)  */
> +      if (index != NULL_TREE
> +	  && length != NULL_TREE
> +	  && TREE_CODE (index) == INTEGER_CST
> +	  && TREE_CODE (length) == INTEGER_CST)
> +	{
> +	  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
> +	       && TREE_CODE (length) == INTEGER_CST)
> +	idxtype = build_index_type (length);
> +      else
> +	idxtype = NULL_TREE;
> +
> +      tree eltype = ((postfix_expression != error_mark_node
> +		      && TREE_TYPE (postfix_expression))
> +		     ? TREE_TYPE (TREE_TYPE (postfix_expression))
> +		     : NULL_TREE);
> +
> +      tree sectype;
> +
> +      /* 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)
> +	sectype = TREE_TYPE (postfix_expression);
> +      else
> +	sectype = build_array_type (eltype, idxtype);
> +
> +      return build3_loc (input_location, OMP_ARRAY_SECTION,
> +			 sectype, postfix_expression, index, length);
> +    }
> +
> +  parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
> +
>    /* Look for the closing `]'.  */
>    cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
>  
> @@ -8484,6 +8563,7 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
>  {
>    vec<tree, va_gc> *expression_list;
>    bool saved_greater_than_is_operator_p;
> +  bool saved_omp_array_section_p;
>  
>    /* Assume all the expressions will be constant.  */
>    if (non_constant_p)
> @@ -8501,6 +8581,9 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
>      = parser->greater_than_is_operator_p;
>    parser->greater_than_is_operator_p = true;
>  
> +  saved_omp_array_section_p = parser->omp_array_section_p;
> +  parser->omp_array_section_p = false;

Here the surrounding code uses the old style save/restore, so perhaps
it is ok as is.
> +
>    cp_expr expr (NULL_TREE);
>  
>    /* Consume expressions until there are no more.  */
> @@ -8571,6 +8654,7 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
>  
>    parser->greater_than_is_operator_p
>      = saved_greater_than_is_operator_p;
> +  parser->omp_array_section_p = saved_omp_array_section_p;
>  
>    return expression_list;
>  }
> @@ -11051,6 +11135,7 @@ cp_parser_lambda_expression (cp_parser* parser)
>      cp_binding_level* implicit_template_scope = parser->implicit_template_scope;
>      bool auto_is_implicit_function_template_parm_p
>          = parser->auto_is_implicit_function_template_parm_p;
> +    bool saved_omp_array_section_p = parser->omp_array_section_p;
>  
>      parser->num_template_parameter_lists = 0;
>      parser->in_statement = 0;
> @@ -11059,6 +11144,7 @@ cp_parser_lambda_expression (cp_parser* parser)
>      parser->implicit_template_parms = 0;
>      parser->implicit_template_scope = 0;
>      parser->auto_is_implicit_function_template_parm_p = false;
> +    parser->omp_array_section_p = false;
>  
>      /* The body of a lambda in a discarded statement is not discarded.  */
>      bool discarded = in_discarded_stmt;
> @@ -11111,6 +11197,7 @@ cp_parser_lambda_expression (cp_parser* parser)
>      parser->implicit_template_scope = implicit_template_scope;
>      parser->auto_is_implicit_function_template_parm_p
>  	= auto_is_implicit_function_template_parm_p;
> +    parser->omp_array_section_p = saved_omp_array_section_p;
>    }

Here too.
>  
>    /* This field is only used during parsing of the lambda.  */
> @@ -25359,6 +25446,9 @@ cp_parser_braced_list (cp_parser* parser, bool* non_constant_p)
>  {
>    tree initializer;
>    location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
> +  bool saved_omp_array_section_p = parser->omp_array_section_p;
> +
> +  parser->omp_array_section_p = false;

But this could use make_temp_override.

> +      /* This condition doesn't include OMP_CLAUSE_DEPEND or
> +	 OMP_CLAUSE_AFFINITY since lvalue ("locator list") parsing for those is
> +	 handled further down the function.  */
> +      else if (map_lvalue
> +	       && (kind == OMP_CLAUSE_MAP
> +		   || kind == OMP_CLAUSE_TO
> +		   || kind == OMP_CLAUSE_FROM))
> +	{
> +	  auto s = make_temp_override (parser->omp_array_section_p, true);

You use make_temp_override already here ;)

	Jakub
Julian Brown Nov. 2, 2022, 12:20 p.m. UTC | #4
On Wed, 2 Nov 2022 12:58:37 +0100
Jakub Jelinek via Fortran <fortran@gcc.gnu.org> wrote:

> On Tue, Nov 01, 2022 at 09:50:38PM +0000, Julian Brown wrote:
> > > I think we should figure out when we should temporarily disable
> > >   parser->omp_array_section_p = false;
> > > and restore it afterwards to a saved value.  E.g.
> > > cp_parser_lambda_expression seems like a good candidate, the fact
> > > that OpenMP array sections are allowed say in map clause doesn't
> > > mean they are allowed inside of lambdas and it would be
> > > especially hard when the lambda is defining a separate function
> > > and the search for OMP_ARRAY_SECTION probably wouldn't be able to
> > > discover those. Other spots to consider might be statement
> > > expressions, perhaps type definitions etc.  
> > 
> > I've had a go at doing this -- several expression types now forbid
> > array-section syntax (see new "bad-array-section-*" tests added).
> > I'm afraid my C++ isn't quite up to figuring out how it's possible
> > to define a type inside an expression (inside a map clause) if we
> > forbid lambdas and statement expressions though -- can you give an
> > example?  
> 
> But we can't forbid lambdas inside of the map clause expressions,
> they are certainly valid in OpenMP, and IMNSHO shouldn't disallow
> statement expressions, people might not even know they use a
> statement expression, they could just use some standard macro which
> uses a statement expression under the hood.  Though your testcases
> look good.

I meant "forbid array sections within lambdas and statement
expressions" -- FAOD, does that seem reasonable? Technically it might
not be that hard to support e.g. a statement expression with an array
section on the final expression, but that doesn't work at the moment.
Maybe a follow-on patch could support that if we want it?

I'll take a look at addressing your other review comments, thanks!

Cheers,

Julian
Jakub Jelinek Nov. 2, 2022, 12:35 p.m. UTC | #5
On Wed, Nov 02, 2022 at 12:20:11PM +0000, Julian Brown wrote:
> > But we can't forbid lambdas inside of the map clause expressions,
> > they are certainly valid in OpenMP, and IMNSHO shouldn't disallow
> > statement expressions, people might not even know they use a
> > statement expression, they could just use some standard macro which
> > uses a statement expression under the hood.  Though your testcases
> > look good.
> 
> I meant "forbid array sections within lambdas and statement
> expressions" -- FAOD, does that seem reasonable? Technically it might

Yeah, my response was to the wording you wrote above the patch, not what
is inside of the patch which looked ok.

> not be that hard to support e.g. a statement expression with an array
> section on the final expression, but that doesn't work at the moment.

And I think we want to keep it that way.

	Jakub
Julian Brown Nov. 8, 2022, 2:36 p.m. UTC | #6
Hi Jakub,

Thanks for the review! Here's a new version.

On Wed, 2 Nov 2022 12:58:37 +0100
Jakub Jelinek via Fortran <fortran@gcc.gnu.org> wrote:

> > > This shouldn't be done just for OMP_CLAUSE_MAP, but for all the
> > > other clauses that accept array sections, including
> > > OMP_CLAUSE_DEPEND, OMP_CLAUSE_AFFINITY, OMP_CLAUSE_MAP,
> > > OMP_CLAUSE_TO, OMP_CLAUSE_FROM, OMP_CLAUSE_INCLUSIVE,
> > > OMP_CLAUSE_EXCLUSIVE, OMP_CLAUSE_USE_DEVICE_ADDR,
> > > OMP_CLAUSE_HAS_DEVICE_ADDR, OMP_CLAUSE_*REDUCTION.  
> > 
> > I'm not too sure about all of those -- Tobias points out that
> > "INCLUSIVE", "EXCLUSIVE", *DEVICE* and *REDUCTION* take "variable
> > list" item types, not "locator list", though sometimes with an
> > array section being permitted (in OpenMP 5.2+).  
> 
> That is true.  For the clauses that don't use locator lists but
> variable lists but accept array sections there are strict
> restrictions on what one can use, basically one can only have varname
> or varname[...] or varname[...][...] etc. where ... is the normal
> array element or array section syntax.  So, we probably should
> continue to parse them as now, but we can use OMP_ARRAY_SECTION to
> hold what we've parsed or even share code with parsing array sections
> and the [...] on those clauses.

This patch uses OMP_ARRAY_SECTION for the previous parsing method also.
(I've incorporated the followup "TREE_LIST->OMP_ARRAY_SECTION" for C++
patch into this one, since there probably isn't much point in keeping
them separate.)

> > @@ -5265,6 +5268,9 @@ static cp_expr
> >  cp_parser_statement_expr (cp_parser *parser)
> >  {
> >    cp_token_position start = cp_parser_start_tentative_firewall
> > (parser);
> > +  bool saved_omp_array_section_p = parser->omp_array_section_p;
> > +
> > +  parser->omp_array_section_p = false;  
> 
> The modern C++ FE way of doing this is
>   auto oas = make_temp_override (parser->omp_array_section_p, false);
> where you don't need to do anything at the end and it handles say
> return in the middle or goto out of the block.  It isn't appropriate
> when you need to restore it at some other location than the end of
> the containing block.

I've changed these where surrounding code isn't using the old method,
as suggested.

> > +      if ((index && error_operand_p (index))
> > +	  || (length && error_operand_p (length)))
> > +	return error_mark_node;  
> 
> error_operand_p (NULL) works and returns false, so you can just do
>       if (error_operand_p (index) || error_operand_p (length))
> 	return error_mark_node;
> Though I wonder if the
> > +
> > +      cp_parser_require (parser, CPP_CLOSE_SQUARE,
> > RT_CLOSE_SQUARE);  
> 
> shouldn't be done before it, if one of the expressions is erroneous,
> but we have there ] after it, I think we want to continue parsing
> after it.

I've changed that.

> > +
> > +      tree idxtype;
> > +
> > +      if (index)
> > +	index = maybe_constant_value (index);
> > +      if (length)
> > +	length = maybe_constant_value (length);  
> 
> This is incorrect in templates (when processing_template_decl).
> maybe_constant_value should be only called when
> !processing_template_decl, above you should call
> fold_non_dependent_expr instead. And those handle (pass through)
> NULL_TREE, so no need to conditionalize. Or maybe even better
> maybe_fold_non_dependent_expr which will only return different tree
> from the original if it actually managed to fold it into a constant.
> 
> Another thing is that in the C++ FE we usually do just parsing in
> parser.cc and the actual building of the expressions in some routine
> in semantics.cc or decl2.cc or so.
> The point is that it sometimes needs to be called twice, once
> during parsing (where for !processing_template_decl we handle
> everything right away, otherwise for processing_template_decl if the
> expressions (in your case postfix_expression, index and/or length) are
> type_dependent_expression_p, one doesn't do much processing and
> just build_min_nt_loc or so some (dependent) tree just to hold
> the expressions (OMP_ARRAY_SECTION in your case).
> Or, if processing_template_decl but nothing is type dependent, it
> does some diagnostics etc. with build_non_dependent_expr around the
> expressions, only to throw it away at the end unless it resulted in
> an error and still build a tree with the original expressions (but
> perhaps a non-dependent type of the whole expression).
> And then there needs to be a pt.cc case which handles the raw
> OMP_ARRAY_SECTION tree, will recurse on the subexpressions and finally
> call the function that is also called during the parsing to build the
> tree, but this time usually with !processing_template_decl, so it
> builds the final thing.
> 
> For the normal array element parsing, this is done in grok_array_decl.

This version of the patch splits OMP_ARRAY_SECTION node creation into
two new functions in decl2.cc and semantics.cc, approximately mimicking
ARRAY_REF handling -- which works, though I'm not sure if the
separation of responsibilities is perfect in our case.

> In C++ test coverage, it is usually needed to have normal non-template
> tests (those can be often shared with C FE too in c-c++-common/gomp/),
> and then some tests in templates, and there both the non-dependent
> stuff, say if you write
> template <int N>
> void foo ()
> {
> // normal C/C++ subset stuff here
> }
> void bar () { foo<0> (); }
> and then dependent stuff, where you have say
> template <typename T, typename U, typename V>
> void baz (T x, U y, V z)
> {
> ... map(x[y:z])
> }
> and the like or say value dependent N from the earlier template
> etc. to test that it works properly if some or all expressions are
> type or value dependent during parsing and only later on are
> instantiated.

I've augmented some of the tests with template bits and added a couple
of new ones also.

> Yet another thing is that C++23 allows and we handle multidimensional
> subscript operator, see
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2128r6.pdf
> Right now the multi-dimensional subscript only applies to cases where
> there is a user operator[] and I believe OpenMP array sections on the
> other side are strictly for cases where overloaded operators do not
> apply, but one can certainly write:
>   #pragma omp target map(tofrom:a[1,2,3:4:5])
> and while in C++20 and older that is parsed as
> a[(1,2,3):4:5]
> perhaps with a -Wcomma-subscript warning, in C++23 parsing that yields
> a vector of expressions.  So, probably we need to diagnose the case
> where there is vector of expressions (i.e. 2 or more) as an error.
> C++23 also allows a[] but in that case one can't use the OpenMP
> array section syntax, it is handled only if [ is immediately followed
> by ] so there is no :.

...and I've added an error for attempts to use multidimensional arrays
in C++23, and a deprecation warning for "," in OMP array sections for
C++20 (see new tests). I've also added tests for some
potentially-awkward parsing ambiguities, namely the ternary operator
and the scope-resolution operator. E.g. for the latter we can do:

map(myarray[::x : ::y])

which is unfortunately ambiguous if we omit the spaces because the
triple colon can be interpreted in two ways (a little like the famous
">>" double template close vs. right-shift ambiguity).

How does this version look?

Re-tested with offloading to NVPTX and bootstrapped (with
"OpenMP/OpenACC: Rework clause expansion and nested struct handling"
also applied).

Thanks,

Julian
Jakub Jelinek Nov. 25, 2022, 1:22 p.m. UTC | #7
On Tue, Nov 08, 2022 at 02:36:17PM +0000, Julian Brown wrote:
> @@ -3258,6 +3273,7 @@ c_omp_address_inspector::get_origin (tree t)
>  	       || TREE_CODE (t) == SAVE_EXPR)
>  	t = TREE_OPERAND (t, 0);
>        else if (TREE_CODE (t) == INDIRECT_REF
> +	       && TREE_TYPE (TREE_OPERAND (t, 0))
>  	       && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == REFERENCE_TYPE)
>  	t = TREE_OPERAND (t, 0);
>        else
> @@ -3274,7 +3290,9 @@ c_omp_address_inspector::get_origin (tree t)
>  tree
>  c_omp_address_inspector::maybe_unconvert_ref (tree t)
>  {
> +  /* The TREE_TYPE can be null if we had an earlier error.  */
>    if (TREE_CODE (t) == INDIRECT_REF
> +      && TREE_TYPE (TREE_OPERAND (t, 0))
>        && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == REFERENCE_TYPE)
>      return TREE_OPERAND (t, 0);
>  

I'd prefer avoiding changes like the above.
If we had an earlier error, it should be error_mark_node or have
error_mark_node type, not NULL.
NULL type means something different in the C++ FE, in particular that
something is type dependent and the type will be only figured out after
instantiation.

Other than that LGTM.

	Jakub
diff mbox series

Patch

diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc
index 3907afe0418..421ccdddd76 100644
--- a/gcc/c-family/c-omp.cc
+++ b/gcc/c-family/c-omp.cc
@@ -3232,6 +3232,7 @@  c_omp_address_inspector::map_supported_p ()
 	 || TREE_CODE (t) == SAVE_EXPR
 	 || TREE_CODE (t) == POINTER_PLUS_EXPR
 	 || TREE_CODE (t) == NON_LVALUE_EXPR
+	 || TREE_CODE (t) == OMP_ARRAY_SECTION
 	 || TREE_CODE (t) == NOP_EXPR)
     if (TREE_CODE (t) == COMPOUND_EXPR)
       t = TREE_OPERAND (t, 1);
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index e76842e1a2a..a2693aadcd0 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -2436,6 +2436,15 @@  dump_expr (cxx_pretty_printer *pp, tree t, int flags)
       pp_cxx_right_bracket (pp);
       break;
 
+    case OMP_ARRAY_SECTION:
+      dump_expr (pp, TREE_OPERAND (t, 0), flags);
+      pp_cxx_left_bracket (pp);
+      dump_expr (pp, TREE_OPERAND (t, 1), flags);
+      pp_colon (pp);
+      dump_expr (pp, TREE_OPERAND (t, 2), flags);
+      pp_cxx_right_bracket (pp);
+      break;
+
     case UNARY_PLUS_EXPR:
       dump_unary_op (pp, "+", t, flags);
       break;
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 94a5c64be4c..efb65543e11 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -4266,6 +4266,9 @@  cp_parser_new (cp_lexer *lexer)
   parser->omp_declare_simd = NULL;
   parser->oacc_routine = NULL;
 
+  /* Allow array slice in expression.  */
+  parser->omp_array_section_p = false;
+
   /* Not declaring an implicit function template.  */
   parser->auto_is_implicit_function_template_parm_p = false;
   parser->fully_implicit_function_template_p = false;
@@ -8021,6 +8024,7 @@  cp_parser_postfix_open_square_expression (cp_parser *parser,
   releasing_vec expression_list = NULL;
   location_t loc = cp_lexer_peek_token (parser->lexer)->location;
   bool saved_greater_than_is_operator_p;
+  bool saved_colon_corrects_to_scope_p;
 
   /* Consume the `[' token.  */
   cp_lexer_consume_token (parser->lexer);
@@ -8028,6 +8032,9 @@  cp_parser_postfix_open_square_expression (cp_parser *parser,
   saved_greater_than_is_operator_p = parser->greater_than_is_operator_p;
   parser->greater_than_is_operator_p = true;
 
+  saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p;
+  parser->colon_corrects_to_scope_p = false;
+
   /* Parse the index expression.  */
   /* ??? For offsetof, there is a question of what to allow here.  If
      offsetof is not being used in an integral constant expression context,
@@ -8038,7 +8045,8 @@  cp_parser_postfix_open_square_expression (cp_parser *parser,
      constant expressions here.  */
   if (for_offsetof)
     index = cp_parser_constant_expression (parser);
-  else
+  else if (!parser->omp_array_section_p
+	   || cp_lexer_next_token_is_not (parser->lexer, CPP_COLON))
     {
       if (cxx_dialect >= cxx23
 	  && cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE))
@@ -8097,6 +8105,32 @@  cp_parser_postfix_open_square_expression (cp_parser *parser,
 
   parser->greater_than_is_operator_p = saved_greater_than_is_operator_p;
 
+  if (parser->omp_array_section_p
+      && cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+    {
+      cp_lexer_consume_token (parser->lexer);
+      tree length = NULL_TREE;
+      if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SQUARE))
+	length = cp_parser_expression (parser);
+
+      parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
+
+      if ((index && error_operand_p (index))
+	  || (length && error_operand_p (length)))
+	return error_mark_node;
+
+      cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
+
+      /* NOTE: We are reusing using the type of the whole array as the type of
+	 the array section here, which isn't necessarily entirely correct.
+	 Might need revisiting.  */
+      return build3_loc (input_location, OMP_ARRAY_SECTION,
+			 TREE_TYPE (postfix_expression),
+			 postfix_expression, index, length);
+    }
+
+  parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
+
   /* Look for the closing `]'.  */
   cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
 
@@ -36536,7 +36570,7 @@  struct omp_dim
 static tree
 cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 				tree list, bool *colon,
-				bool allow_deref = false)
+				bool map_lvalue = false)
 {
   auto_vec<omp_dim> dims;
   bool array_section_p;
@@ -36547,12 +36581,95 @@  cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
       parser->colon_corrects_to_scope_p = false;
       *colon = false;
     }
+  begin_scope (sk_omp, NULL);
   while (1)
     {
       tree name, decl;
 
       if (kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY)
 	cp_parser_parse_tentatively (parser);
+      else if (map_lvalue && kind == OMP_CLAUSE_MAP)
+	{
+	  auto s = make_temp_override (parser->omp_array_section_p, true);
+	  token = cp_lexer_peek_token (parser->lexer);
+	  location_t loc = token->location;
+	  decl = cp_parser_assignment_expression (parser);
+
+	  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);
+		}
+
+	      while (TREE_CODE (decl) == ARRAY_REF
+		     || TREE_CODE (decl) == INDIRECT_REF
+		     || TREE_CODE (decl) == COMPOUND_EXPR)
+		{
+		  if (REFERENCE_REF_P (decl))
+		    break;
+
+		  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);
+		    }
+		}
+
+	      /* Bare references have their own special handling, so remove
+		 the explicit dereference added by convert_from_reference.  */
+	      if (REFERENCE_REF_P (decl))
+		decl = TREE_OPERAND (decl, 0);
+
+	      for (int i = dims.length () - 1; i >= 0; i--)
+		decl = tree_cons (dims[i].low_bound, dims[i].length, decl);
+	    }
+	  else if (TREE_CODE (decl) == INDIRECT_REF)
+	    {
+	      bool ref_p = REFERENCE_REF_P (decl);
+
+	      /* Turn *foo into the representation previously used for
+		 foo[0].  */
+	      decl = TREE_OPERAND (decl, 0);
+	      STRIP_NOPS (decl);
+
+	      /* ...but don't add the [0:1] representation for references
+		 (because they have special handling elsewhere).  */
+	      if (!ref_p)
+		decl = tree_cons (integer_zero_node, integer_one_node, decl);
+	    }
+	  else if (TREE_CODE (decl) == ARRAY_REF)
+	    {
+	      tree idx = TREE_OPERAND (decl, 1);
+
+	      decl = TREE_OPERAND (decl, 0);
+	      STRIP_NOPS (decl);
+
+	      decl = tree_cons (idx, integer_one_node, decl);
+	    }
+	  else if (TREE_CODE (decl) == NON_LVALUE_EXPR
+		   || CONVERT_EXPR_P (decl))
+	    decl = TREE_OPERAND (decl, 0);
+
+	  goto build_clause;
+	}
       token = cp_lexer_peek_token (parser->lexer);
       if (kind != 0
 	  && cp_parser_is_keyword (token, RID_THIS))
@@ -36622,8 +36739,7 @@  cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 	    case OMP_CLAUSE_TO:
 	    start_component_ref:
 	      while (cp_lexer_next_token_is (parser->lexer, CPP_DOT)
-		     || (allow_deref
-			 && cp_lexer_next_token_is (parser->lexer, CPP_DEREF)))
+		     || cp_lexer_next_token_is (parser->lexer, CPP_DEREF))
 		{
 		  cpp_ttype ttype
 		    = cp_lexer_next_token_is (parser->lexer, CPP_DOT)
@@ -36709,9 +36825,7 @@  cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 		   || kind == OMP_CLAUSE_TO)
 		  && !array_section_p
 		  && (cp_lexer_next_token_is (parser->lexer, CPP_DOT)
-		      || (allow_deref
-			  && cp_lexer_next_token_is (parser->lexer,
-						     CPP_DEREF))))
+		      || cp_lexer_next_token_is (parser->lexer, CPP_DEREF)))
 		{
 		  for (unsigned i = 0; i < dims.length (); i++)
 		    {
@@ -36745,6 +36859,7 @@  cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 		cp_parser_parse_definitely (parser);
 	    }
 
+	build_clause:
 	  tree u = build_omp_clause (token->location, kind);
 	  OMP_CLAUSE_DECL (u) = decl;
 	  OMP_CLAUSE_CHAIN (u) = list;
@@ -36766,6 +36881,7 @@  cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
     {
       *colon = true;
       cp_parser_require (parser, CPP_COLON, RT_COLON);
+      finish_scope ();
       return list;
     }
 
@@ -36786,6 +36902,7 @@  cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 	goto get_comma;
     }
 
+  finish_scope ();
   return list;
 }
 
@@ -36794,11 +36911,11 @@  cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 
 static tree
 cp_parser_omp_var_list (cp_parser *parser, enum omp_clause_code kind, tree list,
-			bool allow_deref = false)
+			bool map_lvalue = false)
 {
   if (cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
     return cp_parser_omp_var_list_no_open (parser, kind, list, NULL,
-					   allow_deref);
+					   map_lvalue);
   return list;
 }
 
@@ -36865,7 +36982,7 @@  cp_parser_oacc_data_clause (cp_parser *parser, pragma_omp_clause c_kind,
       gcc_unreachable ();
     }
   tree nl, c;
-  nl = cp_parser_omp_var_list (parser, OMP_CLAUSE_MAP, list, true);
+  nl = cp_parser_omp_var_list (parser, OMP_CLAUSE_MAP, list, false);
 
   for (c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
     OMP_CLAUSE_SET_MAP_KIND (c, kind);
@@ -40227,12 +40344,12 @@  cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
 					      clauses);
 	  else
 	    clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_TO, clauses,
-					      true);
+					      false);
 	  c_name = "to";
 	  break;
 	case PRAGMA_OMP_CLAUSE_FROM:
 	  clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_FROM, clauses,
-					    true);
+					    false);
 	  c_name = "from";
 	  break;
 	case PRAGMA_OMP_CLAUSE_UNIFORM:
diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h
index d688fd18fd5..7ae507bb135 100644
--- a/gcc/cp/parser.h
+++ b/gcc/cp/parser.h
@@ -404,6 +404,9 @@  struct GTY(()) cp_parser {
   /* TRUE if omp::directive or omp::sequence attributes may not appear.  */
   bool omp_attrs_forbidden_p;
 
+  /* TRUE if an OpenMP array section is allowed.  */
+  bool omp_array_section_p;
+
   /* Tracks the function's template parameter list when declaring a function
      using generic type parameters.  This is either a new chain in the case of a
      fully implicit function template or an extension of the function's existing
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 81e2788f43a..931af062c87 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -5123,7 +5123,9 @@  handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
       ret = t_refto;
       if (TREE_CODE (t) == FIELD_DECL)
 	ret = finish_non_static_data_member (t, NULL_TREE, NULL_TREE);
-      else if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL)
+      else if (!VAR_P (t)
+	       && (ort == C_ORT_ACC || !EXPR_P (t))
+	       && TREE_CODE (t) != PARM_DECL)
 	{
 	  if (processing_template_decl && TREE_CODE (t) != OVERLOAD)
 	    return NULL_TREE;
@@ -5667,6 +5669,32 @@  handle_omp_array_sections (tree c, enum c_omp_region_type ort)
 	  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
 	    return false;
 
+	  if (TREE_CODE (first) == INDIRECT_REF)
+	    {
+	      /* Detect and skip adding extra nodes for pointer-to-member
+		 mappings.  These are unsupported for now.  */
+	      tree tmp = TREE_OPERAND (first, 0);
+
+	      if (TREE_CODE (tmp) == NON_LVALUE_EXPR)
+		tmp = TREE_OPERAND (tmp, 0);
+
+	      if (TREE_CODE (tmp) == INDIRECT_REF)
+		tmp = TREE_OPERAND (tmp, 0);
+
+	      if (TREE_CODE (tmp) == POINTER_PLUS_EXPR)
+		{
+		  tree offset = TREE_OPERAND (tmp, 1);
+		  STRIP_NOPS (offset);
+		  if (TYPE_PTRMEM_P (TREE_TYPE (offset)))
+		    {
+		      sorry_at (OMP_CLAUSE_LOCATION (c),
+				"pointer-to-member mapping %qE not supported",
+				OMP_CLAUSE_DECL (c));
+		      return true;
+		    }
+		}
+	    }
+
 	  cp_omp_address_inspector t_insp (OMP_CLAUSE_LOCATION (c), t);
 
 	  if (t_insp.maybe_zero_length_array_section (c))
@@ -7929,6 +7957,15 @@  finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 
 		  cp_omp_address_inspector t_insp (OMP_CLAUSE_LOCATION (c), t);
 
+		  if (!t_insp.map_supported_p ())
+		    {
+		      sorry_at (OMP_CLAUSE_LOCATION (c),
+				"unsupported map expression %qE",
+				OMP_CLAUSE_DECL (c));
+		      remove = true;
+		      break;
+		    }
+
 		  if (t_insp.component_access_p ()
 		      && !t_insp.get_base_pointer ())
 		    {
@@ -7939,6 +7976,15 @@  finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		      if (!DECL_P (rt))
 			break;
 
+		      if (!t_insp.map_supported_p ())
+			{
+			  sorry_at (OMP_CLAUSE_LOCATION (c),
+				    "unsupported map expression %qE",
+				    OMP_CLAUSE_DECL (c));
+			  remove = true;
+			  break;
+			}
+
 		      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 			  && OMP_CLAUSE_MAP_IMPLICIT (c)
 			  && (bitmap_bit_p (&map_head, DECL_UID (rt))
@@ -8023,6 +8069,14 @@  finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	      OMP_CLAUSE_DECL (c) = t_refto;
 	    if (type_dependent_expression_p (t_refto))
 	      break;
+	    if (!t_insp.map_supported_p ())
+	      {
+		sorry_at (OMP_CLAUSE_LOCATION (c),
+			  "unsupported map expression %qE",
+			  OMP_CLAUSE_DECL (c));
+		remove = true;
+		break;
+	      }
 	    if (t == error_mark_node)
 	      {
 		remove = true;
@@ -8052,7 +8106,8 @@  finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 		  && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER
 		      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_POINTER
-		      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH))
+		      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH
+		      || (ort != C_ORT_ACC && EXPR_P (t))))
 		break;
 	      if (DECL_P (t))
 		error_at (OMP_CLAUSE_LOCATION (c),
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index 0b8c221e4c8..4ad3c4fad58 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -9241,7 +9241,8 @@  omp_containing_struct (tree expr)
 	  || TREE_CODE (TREE_OPERAND (expr, 0)) == INDIRECT_REF
 	  || (TREE_CODE (TREE_OPERAND (expr, 0)) == MEM_REF
 	      && integer_zerop (TREE_OPERAND (TREE_OPERAND (expr, 0), 1)))
-	  || TREE_CODE (TREE_OPERAND (expr, 0)) == ARRAY_REF)
+	  || TREE_CODE (TREE_OPERAND (expr, 0)) == ARRAY_REF
+	  || TREE_CODE (TREE_OPERAND (expr, 0)) == POINTER_PLUS_EXPR)
 	expr = TREE_OPERAND (expr, 0);
       else
 	internal_error ("unhandled component");
@@ -9785,7 +9786,10 @@  move_concat_nodes_after (tree first_new, tree *last_new_tail, tree *first_ptr,
 static tree *
 accumulate_sibling_list (enum omp_region_type region_type, enum tree_code code,
 			 hash_map<tree_operand_hash, tree>
-			   *&struct_map_to_clause, tree *grp_start_p,
+			   *&struct_map_to_clause,
+			 hash_map<tree_operand_hash, omp_mapping_group *>
+			   *group_map,
+			 tree *grp_start_p,
 			 tree grp_end, tree *inner)
 {
   poly_offset_int coffset;
@@ -9928,8 +9932,7 @@  accumulate_sibling_list (enum omp_region_type region_type, enum tree_code code,
       while (TREE_CODE (sdecl) == POINTER_PLUS_EXPR)
 	sdecl = TREE_OPERAND (sdecl, 0);
 
-      if (DECL_P (sdecl)
-	  && POINTER_TYPE_P (TREE_TYPE (sdecl))
+      if (POINTER_TYPE_P (TREE_TYPE (sdecl))
 	  && (region_type & ORT_TARGET))
 	{
 	  tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (grp_end),
@@ -9943,8 +9946,21 @@  accumulate_sibling_list (enum omp_region_type region_type, enum tree_code code,
 		       && (TREE_CODE (TREE_TYPE (TREE_OPERAND
 						  (TREE_OPERAND (base, 0), 0)))
 			   == REFERENCE_TYPE))));
-	  enum gomp_map_kind mkind = base_ref ? GOMP_MAP_FIRSTPRIVATE_REFERENCE
-					      : GOMP_MAP_FIRSTPRIVATE_POINTER;
+	  enum gomp_map_kind mkind;
+	  bool mapped_to_p = false;
+	  omp_mapping_group **decl_group_p = group_map->get (sdecl);
+	  if (decl_group_p)
+	    {
+	      tree grp_first = *(*decl_group_p)->grp_start;
+	      enum gomp_map_kind first_kind = OMP_CLAUSE_MAP_KIND (grp_first);
+	      if (*decl_group_p && GOMP_MAP_COPY_TO_P (first_kind))
+		mapped_to_p = true;
+	    }
+	  if (DECL_P (sdecl) && !mapped_to_p)
+	    mkind = base_ref ? GOMP_MAP_FIRSTPRIVATE_REFERENCE
+			     : GOMP_MAP_FIRSTPRIVATE_POINTER;
+	  else
+	    mkind = GOMP_MAP_ATTACH;
 	  OMP_CLAUSE_SET_MAP_KIND (c2, mkind);
 	  OMP_CLAUSE_DECL (c2) = sdecl;
 	  tree baddr = build_fold_addr_expr (base);
@@ -9960,7 +9976,10 @@  accumulate_sibling_list (enum omp_region_type region_type, enum tree_code code,
 	  OMP_CLAUSE_SIZE (c2)
 	    = fold_build2_loc (OMP_CLAUSE_LOCATION (grp_end), MINUS_EXPR,
 			       ptrdiff_type_node, baddr, decladdr);
-	  /* Insert after struct node.  */
+	  /* Insert after struct node.  If the mapping kind is GOMP_MAP_ATTACH,
+	     we are only putting this here until the end of
+	     omp_build_struct_sibling_lists, at which point we maybe adjust
+	     the bias and move the node to the end of the clause list.  */
 	  OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (l);
 	  OMP_CLAUSE_CHAIN (l) = c2;
 	}
@@ -9978,7 +9997,8 @@  accumulate_sibling_list (enum omp_region_type region_type, enum tree_code code,
 	 in omp-low.c after it has been processed there.)  */
       if (*sc != grp_end
 	  && (OMP_CLAUSE_MAP_KIND (*sc) == GOMP_MAP_FIRSTPRIVATE_POINTER
-	      || OMP_CLAUSE_MAP_KIND (*sc) == GOMP_MAP_FIRSTPRIVATE_REFERENCE))
+	      || OMP_CLAUSE_MAP_KIND (*sc) == GOMP_MAP_FIRSTPRIVATE_REFERENCE
+	      || OMP_CLAUSE_MAP_KIND (*sc) == GOMP_MAP_ATTACH))
 	sc = &OMP_CLAUSE_CHAIN (*sc);
       for (; *sc != grp_end; sc = &OMP_CLAUSE_CHAIN (*sc))
 	if ((ptr || attach_detach) && sc == grp_start_p)
@@ -10137,6 +10157,31 @@  accumulate_sibling_list (enum omp_region_type region_type, enum tree_code code,
   return continue_at;
 }
 
+/* Return the base (pointer to struct or class) of a pointer-to-member access
+   expression, or NULL_TREE if EXPR is something else.  */
+
+static bool
+omp_ptrmem_p (tree expr)
+{
+  if (TREE_CODE (expr) != INDIRECT_REF)
+    return false;
+
+  expr = TREE_OPERAND (expr, 0);
+
+  if (TREE_CODE (expr) == NON_LVALUE_EXPR)
+    expr = TREE_OPERAND (expr, 0);
+
+  if (TREE_CODE (expr) == POINTER_PLUS_EXPR)
+    {
+      tree base = TREE_OPERAND (expr, 0);
+      STRIP_NOPS (base);
+      if (AGGREGATE_TYPE_P (TREE_TYPE (TREE_TYPE (base))))
+	return true;
+    }
+
+  return false;
+}
+
 /* Scan through GROUPS, and create sorted structure sibling lists without
    gimplifying.  */
 
@@ -10199,7 +10244,14 @@  omp_build_struct_sibling_lists (enum tree_code code,
 
       STRIP_NOPS (decl);
 
-      if (TREE_CODE (decl) != COMPONENT_REF)
+      if (omp_ptrmem_p (decl))
+	{
+	  /* Pointer-to-member mapping types are not yet supported.  */
+	  sorry_at (OMP_CLAUSE_LOCATION (c), "unsupported map expression %qE",
+		    decl);
+	  continue;
+	}
+      else if (TREE_CODE (decl) != COMPONENT_REF)
 	continue;
 
       omp_mapping_group **wholestruct = NULL;
@@ -10265,7 +10317,8 @@  omp_build_struct_sibling_lists (enum tree_code code,
 
 	  new_next = accumulate_sibling_list (region_type, code,
 					      struct_map_to_clause,
-					      grp_start_p, grp_end, &inner);
+					      *grpmap, grp_start_p, grp_end,
+					      &inner);
 
 	  if (inner)
 	    {
@@ -10295,6 +10348,52 @@  omp_build_struct_sibling_lists (enum tree_code code,
 	}
     }
 
+  /* The list has been rearranged; "tail" might not point to the chain of the
+     last node any more.  Make it do so.  (Also, we have only been processing
+     "map" nodes, and non-map nodes might be present at the end of the list
+     too.)  */
+  while (*tail)
+    tail = &OMP_CLAUSE_CHAIN (*tail);
+
+  /* Now we have finished building the struct sibling lists, reprocess
+     newly-added "attach" nodes: we need the address of the first
+     mapped element of each struct sibling list for the bias of the attach
+     operation -- not necessarily the base address of the whole struct.  */
+  if (struct_map_to_clause)
+    for (hash_map<tree_operand_hash, tree>::iterator iter
+	   = struct_map_to_clause->begin ();
+	 iter != struct_map_to_clause->end ();
+	 ++iter)
+      {
+	tree struct_node = (*iter).second;
+	gcc_assert (OMP_CLAUSE_CODE (struct_node) == OMP_CLAUSE_MAP);
+	tree attach = OMP_CLAUSE_CHAIN (struct_node);
+
+	if (OMP_CLAUSE_CODE (attach) != OMP_CLAUSE_MAP
+	    || OMP_CLAUSE_MAP_KIND (attach) != GOMP_MAP_ATTACH)
+	  continue;
+
+	/* This is the first sorted node in the struct sibling list.  Use it
+	   to recalculate the correct bias to use.
+	   (&first_node - attach_decl).  */
+	tree first_node = OMP_CLAUSE_DECL (OMP_CLAUSE_CHAIN (attach));
+	first_node = build_fold_addr_expr (first_node);
+	first_node = fold_convert (ptrdiff_type_node, first_node);
+	tree attach_decl = OMP_CLAUSE_DECL (attach);
+	//attach_decl = build_fold_addr_expr (attach_decl);
+	attach_decl = fold_convert (ptrdiff_type_node, attach_decl);
+	OMP_CLAUSE_SIZE (attach)
+          = fold_build2 (MINUS_EXPR, ptrdiff_type_node, first_node,
+			 attach_decl);
+
+	/* Remove GOMP_MAP_ATTACH node from after struct node.  */
+	OMP_CLAUSE_CHAIN (struct_node) = OMP_CLAUSE_CHAIN (attach);
+	/* ...and re-insert it at the end of our clause list.  */
+	*tail = attach;
+	OMP_CLAUSE_CHAIN (attach) = NULL_TREE;
+	tail = &OMP_CLAUSE_CHAIN (attach);
+      }
+
 error_out:
   if (struct_map_to_clause)
     delete struct_map_to_clause;
diff --git a/gcc/testsuite/c-c++-common/gomp/map-6.c b/gcc/testsuite/c-c++-common/gomp/map-6.c
index 6ee59714847..c749db845b0 100644
--- a/gcc/testsuite/c-c++-common/gomp/map-6.c
+++ b/gcc/testsuite/c-c++-common/gomp/map-6.c
@@ -20,12 +20,12 @@  foo (void)
   ;
 
   #pragma omp target map (close a) /* { dg-error "'close' undeclared" "" { target c } } */ 
-  /* { dg-error "'close' has not been declared" "" { target c++ } .-1 } */ 
+  /* { dg-error "'close' was not declared in this scope" "" { target c++ } .-1 } */ 
   /* { dg-error "expected '\\)' before 'a'" "" { target *-*-* } .-2 } */
   ;
 
   #pragma omp target map (always a) /* { dg-error "'always' undeclared" "" { target c } } */
-  /* { dg-error "'always' has not been declared" "" { target c++ } .-1 } */ 
+  /* { dg-error "'always' was not declared in this scope" "" { target c++ } .-1 } */ 
   /* { dg-error "expected '\\)' before 'a'" "" { target *-*-* } .-2 } */
   ;
 
diff --git a/gcc/testsuite/g++.dg/gomp/ind-base-3.C b/gcc/testsuite/g++.dg/gomp/ind-base-3.C
new file mode 100644
index 00000000000..dbabaf7680c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/ind-base-3.C
@@ -0,0 +1,38 @@ 
+#include <cassert>
+
+struct S {
+  int x[10];
+};
+
+S *
+choose (S *a, S *b, int c)
+{
+  if (c < 5)
+    return a;
+  else
+    return b; 
+}
+
+int main (int argc, char *argv[])
+{
+  S a, b;
+
+  for (int i = 0; i < 10; i++)
+    a.x[i] = b.x[i] = 0;
+
+  for (int i = 0; i < 10; i++)
+    {
+#pragma omp target map(choose(&a, &b, i)->x[:10])
+/* { dg-message {sorry, unimplemented: unsupported map expression 'choose\(\(& a\), \(& b\), i\)->S::x\[0\]'} "" { target *-*-* } .-1 } */
+/* { dg-message {sorry, unimplemented: unsupported map expression 'choose\(\(& a\), \(& b\), i\)'} "" { target *-*-* } .-2 } */
+      for (int j = 0; j < 10; j++)
+        choose (&a, &b, i)->x[j]++;
+    }
+
+  for (int i = 0; i < 10; i++)
+    assert (a.x[i] == 5 && b.x[i] == 5);
+
+  return 0;
+}
+
+
diff --git a/gcc/testsuite/g++.dg/gomp/map-assignment-1.C b/gcc/testsuite/g++.dg/gomp/map-assignment-1.C
new file mode 100644
index 00000000000..5979ec379f1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/map-assignment-1.C
@@ -0,0 +1,12 @@ 
+#include <cassert>
+
+int main (int argc, char *argv[])
+{
+  int a = 5, b = 2;
+#pragma omp target map(a += b)
+  /* { dg-message {sorry, unimplemented: unsupported map expression '\(a = \(a \+ b\)\)'} "" { target *-*-* } .-1 } */
+  {
+    a++;
+  }
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/map-inc-1.C b/gcc/testsuite/g++.dg/gomp/map-inc-1.C
new file mode 100644
index 00000000000..b469a4bd548
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/map-inc-1.C
@@ -0,0 +1,10 @@ 
+int main (int argc, char *argv[])
+{
+  int a = 5;
+#pragma omp target map(++a)
+  /* { dg-message {sorry, unimplemented: unsupported map expression '\+\+ a'} "" { target *-*-* } .-1 } */
+  {
+    a++;
+  }
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/map-lvalue-ref-1.C b/gcc/testsuite/g++.dg/gomp/map-lvalue-ref-1.C
new file mode 100644
index 00000000000..d720d4318ae
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/map-lvalue-ref-1.C
@@ -0,0 +1,19 @@ 
+#include <cassert>
+
+int glob = 10;
+
+int& foo ()
+{
+  return glob;
+}
+
+int main (int argc, char *argv[])
+{
+#pragma omp target map(foo())
+  /* { dg-message {sorry, unimplemented: unsupported map expression 'foo\(\)'} "" { target *-*-* } .-1 } */
+  {
+    foo()++;
+  }
+  assert (glob == 11);
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/map-ptrmem-1.C b/gcc/testsuite/g++.dg/gomp/map-ptrmem-1.C
new file mode 100644
index 00000000000..c4023f59fc6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/map-ptrmem-1.C
@@ -0,0 +1,37 @@ 
+#include <cassert>
+
+struct S {
+  int x;
+  int *ptr;
+};
+
+int
+main (int argc, char *argv[])
+{
+  S s;
+  int S::* xp = &S::x;
+  int* S::* ptrp = &S::ptr;
+
+  s.ptr = new int[64];
+
+  s.*xp = 6;
+  for (int i = 0; i < 64; i++)
+    (s.*ptrp)[i] = i;
+
+#pragma omp target map(s.*xp, s.*ptrp, (s.*ptrp)[:64])
+  /* { dg-message {sorry, unimplemented: pointer-to-member mapping '\*\(\*\(\(\(int\*\*\)\(& s\)\) \+ \(\(sizetype\)ptrp\)\)\)' not supported} "" { target *-*-* } .-1 } */
+  /* { dg-message {sorry, unimplemented: pointer-to-member mapping '\*\(\(\(int\*\*\)\(& s\)\) \+ \(\(sizetype\)ptrp\)\)' not supported} "" { target *-*-* } .-2 } */
+  /* { dg-message {sorry, unimplemented: pointer-to-member mapping '\*\(\(\(int\*\)\(& s\)\) \+ \(\(sizetype\)xp\)\)' not supported} "" { target *-*-* } .-3 } */
+#pragma omp teams distribute parallel for
+  for (int i = 0; i < 64; i++)
+    {
+      (s.*xp)++;
+      (s.*ptrp)[i]++;
+    }
+
+  assert (s.*xp == 70);
+  for (int i = 0; i < 64; i++)
+    assert ((s.*ptrp)[i] == i + 1);
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/map-ptrmem-2.C b/gcc/testsuite/g++.dg/gomp/map-ptrmem-2.C
new file mode 100644
index 00000000000..fbf379da0eb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/map-ptrmem-2.C
@@ -0,0 +1,40 @@ 
+#include <cassert>
+
+struct S {
+  int x;
+  int *ptr;
+};
+
+int
+main (int argc, char *argv[])
+{
+  S *s = new S;
+  int S::* xp = &S::x;
+  int* S::* ptrp = &S::ptr;
+
+  s->ptr = new int[64];
+
+  s->*xp = 4;
+  for (int i = 0; i < 64; i++)
+    (s->*ptrp)[i] = i;
+
+#pragma omp target map(s->*xp, s->*ptrp, (s->*ptrp)[:64])
+  /* { dg-message {sorry, unimplemented: pointer-to-member mapping '\*\(\(\(int\*\*\)s\) \+ \(\(sizetype\)ptrp\)\)' not supported} "" { target *-*-* } .-1 } */
+  /* { dg-message {sorry, unimplemented: pointer-to-member mapping '\*\(\(\(int\*\)s\) \+ \(\(sizetype\)xp\)\)' not supported} "" { target *-*-* } .-2 } */
+  /* { dg-message {sorry, unimplemented: pointer-to-member mapping '\*\(\*\(\(\(int\*\*\)s\) \+ \(\(sizetype\)ptrp\)\)\)' not supported} "" { target *-*-* } .-3 } */
+#pragma omp teams distribute parallel for
+  for (int i = 0; i < 64; i++)
+    {
+      (s->*xp)++;
+      (s->*ptrp)[i]++;
+    }
+
+  assert (s->*xp == 68);
+  for (int i = 0; i < 64; i++)
+    assert ((s->*ptrp)[i] == i + 1);
+
+  delete s->ptr;
+  delete s;
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/map-static-cast-lvalue-1.C b/gcc/testsuite/g++.dg/gomp/map-static-cast-lvalue-1.C
new file mode 100644
index 00000000000..3af9668202c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/map-static-cast-lvalue-1.C
@@ -0,0 +1,17 @@ 
+#include <cassert>
+
+int foo (int x)
+{
+#pragma omp target map(static_cast<int&>(x))
+  /* { dg-message {sorry, unimplemented: unsupported map expression '& x'} "" { target *-*-* } .-1 } */
+  {
+    x += 3;
+  }
+  return x;
+}
+
+int main (int argc, char *argv[])
+{
+  assert (foo (5) == 8);
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/map-ternary-1.C b/gcc/testsuite/g++.dg/gomp/map-ternary-1.C
new file mode 100644
index 00000000000..7b365a909bb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/map-ternary-1.C
@@ -0,0 +1,20 @@ 
+#include <cassert>
+
+int foo (bool yesno)
+{
+  int x = 5, y = 7;
+#pragma omp target map(yesno ? x : y)
+  /* { dg-message {sorry, unimplemented: unsupported map expression '\(yesno \?  x :  y\)'} "" { target *-*-* } .-1 } */
+  {
+    x += 3;
+    y += 5;
+  }
+  return yesno ? x : y;
+}
+
+int main (int argc, char *argv[])
+{
+  assert (foo (true) == 8);
+  assert (foo (false) == 12);
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/member-array-2.C b/gcc/testsuite/g++.dg/gomp/member-array-2.C
new file mode 100644
index 00000000000..e60bb5585a1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/member-array-2.C
@@ -0,0 +1,92 @@ 
+#include <cassert>
+
+typedef int intarr100[100];
+
+class C {
+  int arr[100];
+  int *ptr;
+
+public:
+  C();
+  ~C();
+  void zero ();
+  void do_operation ();
+  void check (int, int);
+  intarr100 &get_arr () { return arr; }
+  int *get_ptr() { return ptr; }
+};
+
+C::C()
+{
+  ptr = new int[100];
+  for (int i = 0; i < 100; i++)
+    arr[i] = 0;
+}
+
+C::~C()
+{
+  delete ptr;
+}
+
+void
+C::zero ()
+{
+  for (int i = 0; i < 100; i++)
+    arr[i] = ptr[i] = 0;
+}
+
+void
+C::do_operation ()
+{
+#pragma omp target map(arr, ptr, ptr[:100])
+#pragma omp teams distribute parallel for
+  for (int i = 0; i < 100; i++)
+    {
+      arr[i] = arr[i] + 3;
+      ptr[i] = ptr[i] + 5;
+    }
+}
+
+void
+C::check (int arrval, int ptrval)
+{
+  for (int i = 0; i < 100; i++)
+    {
+      assert (arr[i] == arrval);
+      assert (ptr[i] == ptrval);
+    }
+}
+
+int
+main (int argc, char *argv[])
+{
+  C c;
+
+  c.zero ();
+  c.do_operation ();
+  c.check (3, 5);
+
+  /* It might sort of make sense to be able to do this, but we don't support
+     it for now.  */
+  #pragma omp target map(c.get_arr()[:100])
+  /* { dg-message {sorry, unimplemented: unsupported map expression 'c\.C::get_arr\(\)\[0\]'} "" { target *-*-* } .-1 } */
+  /* { dg-message {sorry, unimplemented: unsupported map expression 'c\.C::get_arr\(\)'} "" { target *-*-* } .-2 } */
+  #pragma omp teams distribute parallel for
+    for (int i = 0; i < 100; i++)
+      c.get_arr()[i] += 2;
+
+  c.check (5, 5);
+
+  /* Same for this.  */
+  #pragma omp target map(c.get_ptr(), c.get_ptr()[:100])
+  /* { dg-message {sorry, unimplemented: unsupported map expression 'c\.C::get_ptr\(\)'} "" { target *-*-* } .-1 } */
+  /* { dg-message {sorry, unimplemented: unsupported map expression '\* c\.C::get_ptr\(\)'} "" { target *-*-* } .-2 } */
+  #pragma omp teams distribute parallel for
+    for (int i = 0; i < 100; i++)
+      c.get_ptr()[i] += 3;
+
+  c.check (5, 8);
+
+  return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/gomp/pr67522.C b/gcc/testsuite/g++.dg/gomp/pr67522.C
index da8cb74d1fa..4a901ba68c7 100644
--- a/gcc/testsuite/g++.dg/gomp/pr67522.C
+++ b/gcc/testsuite/g++.dg/gomp/pr67522.C
@@ -12,7 +12,7 @@  foo (void)
   for (int i = 0; i < 16; i++)
     ;
 
-  #pragma omp target map (S[0:10])		// { dg-error "is not a variable in" }
+  #pragma omp target map (S[0:10])		// { dg-error "expected primary-expression before '\\\[' token" }
   ;
 
   #pragma omp task depend (inout: S[0:10])	// { dg-error "is not a variable in" }
diff --git a/gcc/tree-pretty-print.cc b/gcc/tree-pretty-print.cc
index 666b7a70ea2..86fc5c090d6 100644
--- a/gcc/tree-pretty-print.cc
+++ b/gcc/tree-pretty-print.cc
@@ -2485,6 +2485,20 @@  dump_generic_node (pretty_printer *pp, tree node, int spc, dump_flags_t flags,
 	}
       break;
 
+    case OMP_ARRAY_SECTION:
+      op0 = TREE_OPERAND (node, 0);
+      if (op_prio (op0) < op_prio (node))
+	pp_left_paren (pp);
+      dump_generic_node (pp, op0, spc, flags, false);
+      if (op_prio (op0) < op_prio (node))
+	pp_right_paren (pp);
+      pp_left_bracket (pp);
+      dump_generic_node (pp, TREE_OPERAND (node, 1), spc, flags, false);
+      pp_colon (pp);
+      dump_generic_node (pp, TREE_OPERAND (node, 2), spc, flags, false);
+      pp_right_bracket (pp);
+      break;
+
     case CONSTRUCTOR:
       {
 	unsigned HOST_WIDE_INT ix;
diff --git a/gcc/tree.def b/gcc/tree.def
index 62650b6934b..f015021e9dc 100644
--- a/gcc/tree.def
+++ b/gcc/tree.def
@@ -1310,6 +1310,9 @@  DEFTREECODE (OMP_ATOMIC_CAPTURE_NEW, "omp_atomic_capture_new", tcc_statement, 2)
 /* OpenMP clauses.  */
 DEFTREECODE (OMP_CLAUSE, "omp_clause", tcc_exceptional, 0)
 
+/* An OpenMP array section.  */
+DEFTREECODE (OMP_ARRAY_SECTION, "omp_array_section", tcc_expression, 3)
+
 /* TRANSACTION_EXPR tree code.
    Operand 0: BODY: contains body of the transaction.  */
 DEFTREECODE (TRANSACTION_EXPR, "transaction_expr", tcc_expression, 1)
diff --git a/libgomp/testsuite/libgomp.c++/ind-base-1.C b/libgomp/testsuite/libgomp.c++/ind-base-1.C
new file mode 100644
index 00000000000..4566854e60a
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/ind-base-1.C
@@ -0,0 +1,162 @@ 
+// { dg-do run }
+// { dg-options "-fopenmp" }
+
+#include <cassert>
+
+struct S
+{
+  int x[10];
+};
+
+struct T
+{
+  struct S *s;
+};
+
+struct U
+{
+  struct T *t;
+};
+
+void
+foo_siblist (void)
+{
+  U *u = new U;
+  u->t = new T;
+  u->t->s = new S;
+  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)
+  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);
+  delete u->t->s;
+  delete u->t;
+  delete u;
+}
+
+void
+foo (void)
+{
+  U *u = new U;
+  u->t = new T;
+  u->t->s = new S;
+  for (int i = 0; i < 10; i++)
+    u->t->s->x[i] = 0;
+#pragma omp target map(*u, u->t, *(u->t), u->t->s, *u->t->s)
+  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);
+  delete u->t->s;
+  delete u->t;
+  delete u;
+}
+
+void
+foo_tofrom (void)
+{
+  U *u = new U;
+  u->t = new T;
+  u->t->s = new S;
+  for (int i = 0; i < 10; i++)
+    u->t->s->x[i] = 0;
+#pragma omp target map(u, *u, u->t, *(u->t), u->t->s, *u->t->s)
+  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);
+  delete u->t->s;
+  delete u->t;
+  delete u;
+}
+
+void
+bar (void)
+{
+  U *u = new U;
+  U **up = &u;
+  u->t = new T;
+  u->t->s = new S;
+  for (int i = 0; i < 10; i++)
+    (*up)->t->s->x[i] = 0;
+#pragma omp target map(*up, (*up)->t, *(*up)->t, (*up)->t->s, *(*up)->t->s)
+  for (int i = 0; i < 10; i++)
+    (*up)->t->s->x[i] = i * 3;
+  for (int i = 0; i < 10; i++)
+    assert ((*up)->t->s->x[i] == i * 3);
+  delete u->t->s;
+  delete u->t;
+  delete u;
+}
+
+void
+bar_pp (void)
+{
+  U *u = new U;
+  U **up = &u;
+  u->t = new T;
+  u->t->s = new S;
+  for (int i = 0; i < 10; i++)
+    (*up)->t->s->x[i] = 0;
+#pragma omp target map(*up, **up, (*up)->t, *(*up)->t, (*up)->t->s, *(*up)->t->s)
+  for (int i = 0; i < 10; i++)
+    (*up)->t->s->x[i] = i * 3;
+  for (int i = 0; i < 10; i++)
+    assert ((*up)->t->s->x[i] == i * 3);
+  delete u->t->s;
+  delete u->t;
+  delete u;
+}
+
+void
+bar_tofrom (void)
+{
+  U *u = new U;
+  U **up = &u;
+  u->t = new T;
+  u->t->s = new S;
+  for (int i = 0; i < 10; i++)
+    (*up)->t->s->x[i] = 0;
+#pragma omp target map(*up, up, (*up)->t, *(*up)->t, (*up)->t->s, *(*up)->t->s)
+  for (int i = 0; i < 10; i++)
+    (*up)->t->s->x[i] = i * 3;
+  for (int i = 0; i < 10; i++)
+    assert ((*up)->t->s->x[i] == i * 3);
+  delete u->t->s;
+  delete u->t;
+  delete u;
+}
+
+void
+bar_tofrom_pp (void)
+{
+  U *u = new U;
+  U **up = &u;
+  u->t = new T;
+  u->t->s = new S;
+  for (int i = 0; i < 10; i++)
+    (*up)->t->s->x[i] = 0;
+#pragma omp target map(**up, *up, up, (*up)->t, *(*up)->t, (*up)->t->s, \
+		       *(*up)->t->s)
+  for (int i = 0; i < 10; i++)
+    (*up)->t->s->x[i] = i * 3;
+  for (int i = 0; i < 10; i++)
+    assert ((*up)->t->s->x[i] == i * 3);
+  delete u->t->s;
+  delete u->t;
+  delete u;
+}
+
+int main (int argc, char *argv[])
+{
+  foo_siblist ();
+  foo ();
+  foo_tofrom ();
+  bar ();
+  bar_pp ();
+  bar_tofrom ();
+  bar_tofrom_pp ();
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/ind-base-2.C b/libgomp/testsuite/libgomp.c++/ind-base-2.C
new file mode 100644
index 00000000000..706a1205c00
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/ind-base-2.C
@@ -0,0 +1,49 @@ 
+// { dg-do run }
+// { dg-options "-fopenmp" }
+
+#include <cassert>
+
+struct S
+{
+  int x[10];
+};
+
+struct T
+{
+  struct S ***s;
+};
+
+struct U
+{
+  struct T **t;
+};
+
+void
+foo (void)
+{
+  U *u = new U;
+  T *real_t = new T;
+  S *real_s = new 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);
+  delete real_s;
+  delete real_t;
+  delete u;
+}
+
+int main (int argc, char *argv[])
+{
+  foo ();
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/map-comma-1.C b/libgomp/testsuite/libgomp.c++/map-comma-1.C
new file mode 100644
index 00000000000..ee03c5ac1aa
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/map-comma-1.C
@@ -0,0 +1,15 @@ 
+/* { dg-do run } */
+
+#include <cassert>
+
+int main (int argc, char *argv[])
+{
+  int a = 5, b = 7;
+#pragma omp target map((a, b))
+  {
+    a++;
+    b++;
+  }
+  assert (a == 5 && b == 8);
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/map-rvalue-ref-1.C b/libgomp/testsuite/libgomp.c++/map-rvalue-ref-1.C
new file mode 100644
index 00000000000..93811da4000
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/map-rvalue-ref-1.C
@@ -0,0 +1,22 @@ 
+/* { dg-do run } */
+
+#include <cassert>
+
+int foo (int &&x)
+{
+  int y;
+#pragma omp target map(x, y)
+  {
+    x++;
+    y = x;
+  }
+  return y;
+}
+
+int main (int argc, char *argv[])
+{
+  int y = 5;
+  y = foo (y + 3);
+  assert (y == 9);
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/struct-ref-1.C b/libgomp/testsuite/libgomp.c++/struct-ref-1.C
new file mode 100644
index 00000000000..d3874650017
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/struct-ref-1.C
@@ -0,0 +1,97 @@ 
+// { dg-do run }
+// { dg-options "-fopenmp" }
+
+#include <cassert>
+
+struct S
+{
+  int x[10];
+};
+
+void
+foo (S *s, int x)
+{
+  S *&r = s;
+  for (int i = 0; i < x; i++)
+    s[0].x[i] = s[1].x[i] = 0;
+  #pragma omp target map (s, x)
+    ;
+  #pragma omp target map (s[0], x)
+  for (int i = 0; i < x; i++)
+    s[0].x[i] = i;
+  #pragma omp target map (s[1], x)
+  for (int i = 0; i < x; i++)
+    s[1].x[i] = i * 2;
+  for (int i = 0; i < x; i++)
+    {
+      assert (s[0].x[i] == i);
+      assert (s[1].x[i] == i * 2);
+      s[0].x[i] = 0;
+      s[1].x[i] = 0;
+    }
+  #pragma omp target map (r, x)
+    ;
+  #pragma omp target map (r[0], x)
+  for (int i = 0; i < x; i++)
+    r[0].x[i] = i;
+  #pragma omp target map (r[1], x)
+  for (int i = 0; i < x; i++)
+    r[1].x[i] = i * 2;
+  for (int i = 0; i < x; i++)
+    {
+      assert (r[0].x[i] == i);
+      assert (r[1].x[i] == i * 2);
+    }
+}
+
+template <int N>
+struct T
+{
+  int x[N];
+};
+
+template <int N>
+void
+bar (T<N> *t, int x)
+{
+  T<N> *&r = t;
+  for (int i = 0; i < x; i++)
+    t[0].x[i] = t[1].x[i] = 0;
+  #pragma omp target map (t, x)
+    ;
+  #pragma omp target map (t[0], x)
+  for (int i = 0; i < x; i++)
+    t[0].x[i] = i;
+  #pragma omp target map (t[1], x)
+  for (int i = 0; i < x; i++)
+    t[1].x[i] = i * 2;
+  for (int i = 0; i < x; i++)
+    {
+      assert (t[0].x[i] == i);
+      assert (t[1].x[i] == i * 2);
+      t[0].x[i] = 0;
+      t[1].x[i] = 0;
+    }
+  #pragma omp target map (r, x)
+    ;
+  #pragma omp target map (r[0], x)
+  for (int i = 0; i < x; i++)
+    r[0].x[i] = i;
+  #pragma omp target map (r[1], x)
+  for (int i = 0; i < x; i++)
+    r[1].x[i] = i * 2;
+  for (int i = 0; i < x; i++)
+    {
+      assert (r[0].x[i] == i);
+      assert (r[1].x[i] == i * 2);
+    }
+}
+
+int main (int argc, char *argv[])
+{
+  S s[2];
+  foo (s, 10);
+  T<10> t[2];
+  bar (t, 10);
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/array-field-1.c b/libgomp/testsuite/libgomp.c-c++-common/array-field-1.c
new file mode 100644
index 00000000000..6dd8b5c48e1
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/array-field-1.c
@@ -0,0 +1,35 @@ 
+/* { dg-do run } */
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#define N 16
+
+struct Z {
+  int *ptr;
+  int arr[N];
+  int c;
+};
+
+int main (int argc, char *argv[])
+{
+  struct Z *myz;
+  myz = (struct Z *) calloc (1, sizeof *myz);
+
+#pragma omp target map(tofrom:myz->arr[0:N], myz->c)
+  {
+    for (int i = 0; i < N; i++)
+      myz->arr[i]++;
+    myz->c++;
+  }
+
+  for (int i = 0; i < N; i++)
+    assert (myz->arr[i] == 1);
+  assert (myz->c == 1);
+
+  free (myz);
+
+  return 0;
+}
+
diff --git a/libgomp/testsuite/libgomp.c-c++-common/array-of-struct-1.c b/libgomp/testsuite/libgomp.c-c++-common/array-of-struct-1.c
new file mode 100644
index 00000000000..726eede6c31
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/array-of-struct-1.c
@@ -0,0 +1,65 @@ 
+/* { dg-do run } */
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#define N 16
+
+/* NOTE: This test is the same as array-of-struct-2.c, except the fields of
+   this struct are in a different order.  */
+
+struct Z {
+  int arr[N];
+  int *ptr;
+  int c;
+};
+
+void
+foo (struct Z *zarr, int len)
+{
+#pragma omp target map(to:zarr, zarr[5].ptr) map(tofrom:zarr[5].ptr[0:len])
+  {
+    for (int i = 0; i < len; i++)
+      zarr[5].ptr[i]++;
+  }
+
+#pragma omp target map(to:zarr) map(tofrom:zarr[4].arr[0:len])
+  {
+    for (int i = 0; i < len; i++)
+      zarr[4].arr[i]++;
+  }
+
+#pragma omp target map (to:zarr[3].ptr) map(tofrom:zarr[3].ptr[0:len])
+  {
+    for (int i = 0; i < len; i++)
+      zarr[3].ptr[i]++;
+  }
+
+#pragma omp target map(tofrom:zarr[2].arr[0:len])
+  {
+    for (int i = 0; i < len; i++)
+      zarr[2].arr[i]++;
+  }
+}
+
+int main (int argc, char *argv[])
+{
+  struct Z zs[10];
+  memset (zs, 0, sizeof zs);
+
+  for (int i = 0; i < 10; i++)
+    zs[i].ptr = (int *) calloc (N, sizeof (int));
+
+  foo (zs, N);
+
+  for (int i = 0; i < N; i++)
+    {
+      assert (zs[2].arr[i] == 1);
+      assert (zs[4].arr[i] == 1);
+      assert (zs[3].ptr[i] == 1);
+      assert (zs[5].ptr[i] == 1);
+    }
+  
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/array-of-struct-2.c b/libgomp/testsuite/libgomp.c-c++-common/array-of-struct-2.c
new file mode 100644
index 00000000000..c4b77cd13f1
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/array-of-struct-2.c
@@ -0,0 +1,65 @@ 
+/* { dg-do run } */
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#define N 16
+
+/* NOTE: This test is the same as array-of-struct-1.c, except the fields of
+   this struct are in a different order.  */
+
+struct Z {
+  int *ptr;
+  int arr[N];
+  int c;
+};
+
+void
+foo (struct Z *zarr, int len)
+{
+#pragma omp target map(to:zarr, zarr[5].ptr) map(tofrom:zarr[5].ptr[0:len])
+  {
+    for (int i = 0; i < len; i++)
+      zarr[5].ptr[i]++;
+  }
+
+#pragma omp target map(to:zarr) map(tofrom:zarr[4].arr[0:len])
+  {
+    for (int i = 0; i < len; i++)
+      zarr[4].arr[i]++;
+  }
+
+#pragma omp target map (to:zarr[3].ptr) map(tofrom:zarr[3].ptr[0:len])
+  {
+    for (int i = 0; i < len; i++)
+      zarr[3].ptr[i]++;
+  }
+
+#pragma omp target map(tofrom:zarr[2].arr[0:len])
+  {
+    for (int i = 0; i < len; i++)
+      zarr[2].arr[i]++;
+  }
+}
+
+int main (int argc, char *argv[])
+{
+  struct Z zs[10];
+  memset (zs, 0, sizeof zs);
+
+  for (int i = 0; i < 10; i++)
+    zs[i].ptr = (int *) calloc (N, sizeof (int));
+
+  foo (zs, N);
+
+  for (int i = 0; i < N; i++)
+    {
+      assert (zs[2].arr[i] == 1);
+      assert (zs[4].arr[i] == 1);
+      assert (zs[3].ptr[i] == 1);
+      assert (zs[5].ptr[i] == 1);
+    }
+  
+  return 0;
+}