aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp/parser.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/cp/parser.cc')
-rw-r--r--gcc/cp/parser.cc2630
1 files changed, 2393 insertions, 237 deletions
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 3628cfe..be86252 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -2705,7 +2705,7 @@ static cp_ref_qualifier cp_parser_ref_qualifier_opt
static tree cp_parser_tx_qualifier_opt
(cp_parser *);
static tree cp_parser_late_return_type_opt
- (cp_parser *, cp_declarator *, tree &, tree);
+ (cp_parser *, cp_declarator *, tree &);
static tree cp_parser_declarator_id
(cp_parser *, bool);
static tree cp_parser_type_id
@@ -2740,7 +2740,7 @@ static void cp_parser_ctor_initializer_opt_and_function_body
(cp_parser *, bool);
static tree cp_parser_late_parsing_omp_declare_simd
- (cp_parser *, tree, tree);
+ (cp_parser *, tree);
static tree cp_parser_late_parsing_oacc_routine
(cp_parser *, tree);
@@ -4524,6 +4524,54 @@ cp_parser_require_pragma_eol (cp_parser *parser, cp_token *pragma_tok)
}
}
+/* Skip tokens up to and including "#pragma omp end declare variant".
+ Properly handle nested "#pragma omp begin declare variant" pragmas. */
+static void
+cp_parser_skip_to_pragma_omp_end_declare_variant (cp_parser *parser)
+{
+ for (int depth = 0; depth >= 0; )
+ {
+ cp_token *token = cp_lexer_peek_token (parser->lexer);
+
+ switch (token->type)
+ {
+ case CPP_PRAGMA_EOL:
+ if (!parser->lexer->in_pragma)
+ break;
+ /* FALLTHRU */
+ case CPP_EOF:
+ /* If we've run out of tokens, stop. */
+ return;
+
+ case CPP_PRAGMA:
+ if ((cp_parser_pragma_kind (token) == PRAGMA_OMP_BEGIN
+ || cp_parser_pragma_kind (token) == PRAGMA_OMP_END)
+ && cp_lexer_nth_token_is (parser->lexer, 2, CPP_NAME)
+ && cp_lexer_nth_token_is (parser->lexer, 3, CPP_NAME))
+ {
+ tree id1 = cp_lexer_peek_nth_token (parser->lexer, 2)->u.value;
+ tree id2 = cp_lexer_peek_nth_token (parser->lexer, 3)->u.value;
+ if (strcmp (IDENTIFIER_POINTER (id1), "declare") == 0
+ && strcmp (IDENTIFIER_POINTER (id2), "variant") == 0)
+ {
+ if (cp_parser_pragma_kind (token) == PRAGMA_OMP_BEGIN)
+ depth++;
+ else
+ depth--;
+ }
+ }
+ cp_parser_skip_to_pragma_eol (parser, token);
+ continue;
+
+ default:
+ break;
+ }
+
+ /* Consume the token. */
+ cp_lexer_consume_token (parser->lexer);
+ }
+}
+
/* This is a simple wrapper around make_typename_type. When the id is
an unresolved identifier node, we can provide a superior diagnostic
using cp_parser_diagnose_invalid_type_name. */
@@ -4652,6 +4700,12 @@ cp_parser_new (cp_lexer *lexer)
/* Disallow OpenMP array sections in expressions. */
parser->omp_array_section_p = false;
+ /* Disallow OpenMP array-shaping operator in expressions. */
+ parser->omp_array_shaping_op_p = false;
+
+ /* We don't have an OpenMP array shape here. */
+ parser->omp_has_array_shape_p = false;
+
/* Not declaring an implicit function template. */
parser->auto_is_implicit_function_template_parm_p = false;
parser->fully_implicit_function_template_p = false;
@@ -5659,6 +5713,7 @@ cp_parser_statement_expr (cp_parser *parser)
{
cp_token_position start = cp_parser_start_tentative_firewall (parser);
auto oas = make_temp_override (parser->omp_array_section_p, false);
+ auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
/* Consume the '('. */
location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
@@ -8722,7 +8777,7 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
&& cp_lexer_next_token_is (parser->lexer, CPP_COLON))
{
cp_lexer_consume_token (parser->lexer);
- tree length = NULL_TREE;
+ tree length = NULL_TREE, stride = NULL_TREE;
if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SQUARE))
{
if (cxx_dialect >= cxx23)
@@ -8755,9 +8810,23 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
/*warn_comma_p=*/warn_comma_subscript);
}
+ if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+ {
+ cp_lexer_consume_token (parser->lexer);
+ /* We could check for C++-23 multidimensional/comma-separated
+ subscripts here, or not bother. */
+ if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SQUARE))
+ stride
+ = cp_parser_expression (parser, NULL, /*cast_p=*/false,
+ /*decltype_p=*/false,
+ /*warn_comma_p=*/warn_comma_subscript);
+ }
+
parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
- if (index == error_mark_node || length == error_mark_node)
+ if (index == error_mark_node
+ || length == error_mark_node
+ || stride == error_mark_node)
{
cp_parser_skip_to_closing_square_bracket (parser);
return error_mark_node;
@@ -8766,7 +8835,7 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
return grok_omp_array_section (input_location, postfix_expression, index,
- length);
+ length, stride);
}
parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
@@ -8774,11 +8843,23 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
/* Look for the closing `]'. */
cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
- /* Build the ARRAY_REF. */
- postfix_expression = grok_array_decl (loc, postfix_expression,
- index, &expression_list,
- tf_warning_or_error
- | (decltype_p ? tf_decltype : 0));
+ if (parser->omp_has_array_shape_p
+ && (expression_list.get () == NULL
+ || vec_safe_length (expression_list) == 1))
+ /* If we have an array-shaping operator, we may not be able to represent
+ a well-formed ARRAY_REF here, because we are coercing the type of the
+ innermost array base and the original type may not be compatible. Use
+ the OMP_ARRAY_SECTION code instead. We also want to explicitly avoid
+ creating INDIRECT_REFs for pointer bases, because that can lead to
+ parsing ambiguities (see cp_parser_omp_var_list_no_open). */
+ return grok_omp_array_section (loc, postfix_expression, index,
+ size_one_node, NULL_TREE);
+ else
+ /* Build the ARRAY_REF. */
+ postfix_expression = grok_array_decl (loc, postfix_expression,
+ index, &expression_list,
+ tf_warning_or_error
+ | (decltype_p ? tf_decltype : 0));
/* When not doing offsetof, array references are not permitted in
constant-expressions. */
@@ -9101,6 +9182,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;
+ bool saved_omp_array_shaping_op_p;
/* Assume all the expressions will be constant. */
if (non_constant_p)
@@ -9119,7 +9201,9 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
parser->greater_than_is_operator_p = true;
saved_omp_array_section_p = parser->omp_array_section_p;
+ saved_omp_array_shaping_op_p = parser->omp_array_shaping_op_p;
parser->omp_array_section_p = false;
+ parser->omp_array_shaping_op_p = false;
cp_expr expr (NULL_TREE);
@@ -9203,6 +9287,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;
+ parser->omp_array_shaping_op_p = saved_omp_array_shaping_op_p;
return NULL;
}
}
@@ -9210,6 +9295,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;
+ parser->omp_array_shaping_op_p = saved_omp_array_shaping_op_p;
return expression_list;
}
@@ -10505,6 +10591,8 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
cp_expr expr (NULL_TREE);
int cast_expression = 0;
const char *saved_message;
+ auto_vec<cp_expr, 4> omp_shape_dims;
+ bool omp_array_shape_p = false;
/* There's no way to know yet whether or not this is a cast.
For example, `(int (3))' is a unary-expression, while `(int)
@@ -10574,6 +10662,28 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
that the call to cp_parser_error_occurred below returns true. */
if (!cast_expression)
cp_parser_simulate_error (parser);
+ else if (parser->omp_array_shaping_op_p
+ && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
+ {
+ auto oas = make_temp_override (parser->omp_array_section_p, false);
+ auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
+
+ while (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
+ {
+ cp_lexer_consume_token (parser->lexer);
+ cp_expr e = cp_parser_expression (parser);
+ if (e.get_value () == error_mark_node)
+ break;
+ omp_shape_dims.safe_push (e);
+ if (!cp_parser_require (parser, CPP_CLOSE_SQUARE,
+ RT_CLOSE_SQUARE))
+ break;
+ }
+ cp_token *close_paren = parens.require_close (parser);
+ if (close_paren)
+ close_paren_loc = close_paren->location;
+ omp_array_shape_p = true;
+ }
else
{
type_id_in_expr_sentinel s (parser);
@@ -10593,6 +10703,10 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
function returning T. */
if (!cp_parser_error_occurred (parser))
{
+ auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
+ auto as = make_temp_override (parser->omp_has_array_shape_p,
+ omp_array_shape_p);
+
/* Only commit if the cast-expression doesn't start with
'++', '--', or '[' in C++11. */
if (cast_expression > 0)
@@ -10606,6 +10720,24 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
if (cp_parser_parse_definitely (parser))
{
+ if (omp_array_shape_p)
+ {
+ location_t cast_loc = make_location (open_paren_loc,
+ open_paren_loc,
+ expr.get_finish ());
+
+ type = cp_omp_create_arrayshape_type (cast_loc, expr,
+ &omp_shape_dims);
+
+ /* Things rapidly get worse below if we carry on from here
+ with an erroneous type... */
+ if (error_operand_p (type))
+ return error_mark_node;
+
+ return cp_build_omp_arrayshape_cast (cast_loc, type, expr,
+ tf_warning_or_error);
+ }
+
/* Warn about old-style casts, if so requested. */
if (warn_old_style_cast
&& !in_system_header_at (input_location)
@@ -11776,6 +11908,7 @@ cp_parser_lambda_expression (cp_parser* parser)
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;
+ bool saved_omp_array_shaping_op_p = parser->omp_array_shaping_op_p;
parser->num_template_parameter_lists = 0;
parser->in_statement = 0;
@@ -11785,6 +11918,7 @@ cp_parser_lambda_expression (cp_parser* parser)
parser->implicit_template_scope = 0;
parser->auto_is_implicit_function_template_parm_p = false;
parser->omp_array_section_p = false;
+ parser->omp_array_shaping_op_p = false;
/* Inside the lambda, outside unevaluated context do not apply. */
cp_evaluated ev;
@@ -11839,6 +11973,7 @@ cp_parser_lambda_expression (cp_parser* parser)
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;
+ parser->omp_array_shaping_op_p = saved_omp_array_shaping_op_p;
}
/* This lambda shouldn't have any proxies left at this point. */
@@ -23781,6 +23916,226 @@ cp_parser_maybe_adjust_declarator_for_dguide (cp_parser *parser,
}
}
+/* Helper function for OpenMP "begin declare variant" directives.
+ Function definitions inside the construct need to have their names
+ mangled according to the context selector CTX. The DECLARATOR is
+ modified in place to point to a new identifier; the original name of
+ the function is returned. */
+static tree
+omp_start_variant_function (cp_declarator *declarator, tree ctx)
+{
+ cp_declarator *id = get_id_declarator (declarator);
+ tree name = id->u.id.unqualified_name;
+ tree scope = id->u.id.qualifying_scope;
+ enum special_function_kind sfk = id->u.id.sfk;
+
+ /* There seems to be no reasonable interpretation of what the behavior
+ should be if the name is qualified. You cannot add the variant function
+ to a class or namespace from outside of that scope. */
+ if (scope)
+ {
+ sorry_at (id->id_loc,
+ "cannot handle qualified name for variant function");
+ return NULL_TREE;
+ }
+
+ /* Catch disallowed constructors and destructors now. We can't mangle
+ destructor names (which are not IDENTIFIER_NODEs) in any case. */
+ if (sfk == sfk_constructor)
+ {
+ error_at (id->id_loc,
+ "declare variant directives are not allowed on constructors");
+ return NULL_TREE;
+ }
+ if (sfk == sfk_destructor)
+ {
+ error_at (id->id_loc,
+ "declare variant directives are not allowed on destructors");
+ return NULL_TREE;
+ }
+ if (TREE_CODE (name) != IDENTIFIER_NODE)
+ {
+ sorry_at (id->id_loc,
+ "cannot handle %s identifier name",
+ get_tree_code_name (TREE_CODE (name)));
+ return NULL_TREE;
+ }
+
+ /* Mangle the name in the declarator. */
+ id->u.id.unqualified_name
+ = omp_mangle_variant_name (name, ctx, JOIN_STR);
+
+ return name;
+}
+
+/* Helper function for OpenMP "begin declare variant" directives. Now
+ that we have a DECL for the variant function, and BASE_NAME for the
+ base function, look up the decl for BASE_NAME in the same scope as
+ DECL, add an "omp declare variant base" attribute pointing at CTX
+ to the base decl, and an "omp declare variant variant" attribute to
+ the variant DECL. */
+static void
+omp_finish_variant_function (cp_parser *parser, tree decl, tree base_name,
+ tree ctx)
+{
+ tree match = NULL_TREE;
+ bool is_template = false;
+ tree decl_context = CP_DECL_CONTEXT (decl);
+
+ /* First find the base_decl. */
+ tree base_decl = cp_parser_lookup_name_simple (parser, base_name,
+ DECL_SOURCE_LOCATION (decl));
+
+ if (base_decl == error_mark_node)
+ base_decl = NULL_TREE;
+ if (!base_decl)
+ {
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "no previous declaration of base function in this scope");
+ return;
+ }
+
+ /* Find the right overloaded function. */
+ if (TREE_CODE (base_decl) == OVERLOAD)
+ {
+ for (ovl_iterator iter (base_decl); iter; ++iter)
+ {
+ tree bb = *iter;
+ if (decls_match (decl, bb))
+ {
+ match = bb;
+ break;
+ }
+ else if (TREE_CODE (bb) == TEMPLATE_DECL
+ && TREE_CODE (decl) == FUNCTION_DECL
+ && DECL_TEMPLATE_INFO (decl))
+ {
+ tree decl_template = DECL_TI_TEMPLATE (decl);
+ if (decl_template
+ && PRIMARY_TEMPLATE_P (decl_template)
+ && decls_match (bb, decl_template))
+ {
+ /* We want to put the attributes on the function rather
+ than on the TEMPLATE_DECL that points to it. */
+ match = DECL_TEMPLATE_RESULT (bb);
+ is_template = true;
+ break;
+ }
+ }
+ }
+ }
+ else if (decls_match (decl, base_decl))
+ match = base_decl;
+ else if (TREE_CODE (base_decl) == TEMPLATE_DECL)
+ /* Per comment in cp-tree.h, TEMPLATE_DECLs are always wrapped in an
+ OVERLOAD, so we should never see them here. */
+ gcc_unreachable ();
+ else if (TREE_CODE (base_decl) == TREE_LIST)
+ {
+ error_at (DECL_SOURCE_LOCATION (decl), "base function is ambiguous");
+ return;
+ }
+ else if (TREE_CODE (base_decl) == SCOPE_REF)
+ {
+ /* This shows up in some cases involving templates; it's apparently a
+ placeholder for names that can't be matched to a declaration
+ until template instantiation. */
+ sorry_at (DECL_SOURCE_LOCATION (decl),
+ "base function cannot be resolved");
+ return;
+ }
+
+ if (!match)
+ {
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "variant function definition does not match previous "
+ "declaration of %qE", base_decl);
+ return;
+ }
+ else if (CP_DECL_CONTEXT (match) != decl_context)
+ {
+ /* Reject inherited or using decls. */
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "variant function must be in the same scope as the "
+ "base function %qE", match);
+ return;
+ }
+ else if (DECL_VIRTUAL_P (decl) || DECL_VIRTUAL_P (match))
+ {
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "declare variant directives are not allowed on "
+ "virtual functions");
+ return;
+ }
+ else if (DECL_DEFAULTED_FN (decl) || DECL_DEFAULTED_FN (match))
+ {
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "declare variant directives are not allowed on "
+ "defaulted functions");
+ return;
+ }
+ else if (DECL_DELETED_FN (decl) || DECL_DELETED_FN (match))
+ {
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "declare variant directives are not allowed on "
+ "deleted functions");
+ return;
+ }
+ else if (DECL_IMMEDIATE_FUNCTION_P (decl)
+ || DECL_IMMEDIATE_FUNCTION_P (match))
+ {
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "declare variant directives are not allowed on "
+ "immediate functions");
+ return;
+ }
+
+ /* Inside a template, make the "omp declare variant base" attribute
+ point to the name of DECL rather than DECL itself. During template
+ instantiation, omp_declare_variant_finalize_one will handle this
+ using the same logic as for the non-delimited form of "declare variant",
+ causing template instantiation as needed. For the non-template case,
+ there is nothing that will trigger omp_declare_variant_finalize_one;
+ so we create the final form of the attribute here, which points
+ directly to DECL rather than its name. */
+ tree decl_or_name = decl;
+ cp_id_kind idk = CP_ID_KIND_NONE;
+ if (processing_template_decl && is_template)
+ {
+ decl_or_name = DECL_NAME (decl);
+ idk = CP_ID_KIND_TEMPLATE_ID;
+ }
+
+ omp_check_for_duplicate_variant (DECL_SOURCE_LOCATION (decl),
+ match, ctx);
+ tree construct
+ = omp_get_context_selector_list (ctx, OMP_TRAIT_SET_CONSTRUCT);
+ omp_mark_declare_variant (DECL_SOURCE_LOCATION (decl), decl, construct);
+
+ tree attrs = DECL_ATTRIBUTES (match);
+ tree match_loc_node
+ = maybe_wrap_with_location (integer_zero_node,
+ DECL_SOURCE_LOCATION (match));
+ tree loc_node = tree_cons (match_loc_node,
+ build_int_cst (integer_type_node, idk),
+ build_tree_list (match_loc_node,
+ integer_zero_node));
+ attrs = tree_cons (get_identifier ("omp declare variant base"),
+ tree_cons (decl_or_name, ctx, loc_node), attrs);
+ if (processing_template_decl)
+ ATTR_IS_DEPENDENT (attrs) = 1;
+ DECL_ATTRIBUTES (match) = attrs;
+
+ /* Variant functions are essentially anonymous and cannot be
+ referenced by name, so make them have internal linkage. Note
+ that class methods in C++ normally have external linkage with
+ weak/comdat semantics; this prevents that. */
+ TREE_PUBLIC (decl) = 0;
+ DECL_COMDAT (decl) = 0;
+ DECL_INTERFACE_KNOWN (decl) = 1;
+ DECL_NOT_REALLY_EXTERN (decl) = 1;
+}
+
/* Declarators [gram.dcl.decl] */
/* Parse an init-declarator.
@@ -23997,6 +24352,27 @@ cp_parser_init_declarator (cp_parser* parser,
/* This is a function-definition. */
*function_definition_p = true;
+ /* If we're in an OpenMP "begin declare variant" block, the
+ name in the declarator refers to the base function. We need
+ to save that and modify the declarator to have the mangled
+ name for the variant function instead. */
+ tree dv_base = NULL_TREE;
+ tree dv_ctx = NULL_TREE;
+ vec<cp_omp_declare_variant_attr, va_gc> *dv_state
+ = scope_chain->omp_declare_variant_attribute;
+
+ if (!vec_safe_is_empty (dv_state))
+ {
+ cp_omp_declare_variant_attr a = dv_state->last ();
+ dv_ctx = copy_list (a.selector);
+ dv_base = omp_start_variant_function (declarator, dv_ctx);
+ if (dv_base == NULL_TREE)
+ {
+ cp_parser_skip_to_end_of_statement (parser);
+ return error_mark_node;
+ }
+ }
+
/* Parse the function definition. */
if (member_p)
decl = cp_parser_save_member_function_body (parser,
@@ -24015,6 +24391,11 @@ cp_parser_init_declarator (cp_parser* parser,
= func_brace_location;
}
+ /* If this function was in a "begin declare variant" block,
+ store the pointer back to the base function and fix up
+ the attributes for the middle end. */
+ if (dv_base && decl != error_mark_node)
+ omp_finish_variant_function (parser, decl, dv_base, dv_ctx);
return decl;
}
}
@@ -24092,6 +24473,27 @@ cp_parser_init_declarator (cp_parser* parser,
is_initialized = SD_DEFAULTED;
else if (t2->keyword == RID_DELETE)
is_initialized = SD_DELETED;
+ if (!vec_safe_is_empty (scope_chain->omp_declare_variant_attribute))
+ {
+ /* We're in a "begin declare variant" construct. The parser
+ doesn't go through the normal function definition path for
+ these and hence doesn't invoke omp_finish_variant_function
+ where these errors would otherwise be caught. */
+ if (is_initialized == SD_DEFAULTED)
+ {
+ error_at (declarator->init_loc,
+ "declare variant directives are not allowed on "
+ "defaulted functions");
+ return error_mark_node;
+ }
+ else if (is_initialized == SD_DELETED)
+ {
+ error_at (declarator->init_loc,
+ "declare variant directives are not allowed on "
+ "deleted functions");
+ return error_mark_node;
+ }
+ }
}
}
else
@@ -24664,7 +25066,7 @@ cp_parser_direct_declarator (cp_parser* parser,
tree requires_clause = NULL_TREE;
late_return
= cp_parser_late_return_type_opt (parser, declarator,
- requires_clause, params);
+ requires_clause);
cp_finalize_omp_declare_simd (parser, &odsd);
@@ -25530,7 +25932,7 @@ parsing_function_declarator ()
static tree
cp_parser_late_return_type_opt (cp_parser *parser, cp_declarator *declarator,
- tree &requires_clause, tree parms)
+ tree &requires_clause)
{
cp_token *token;
tree type = NULL_TREE;
@@ -25586,8 +25988,8 @@ cp_parser_late_return_type_opt (cp_parser *parser, cp_declarator *declarator,
if (declare_simd_p)
declarator->attributes
- = cp_parser_late_parsing_omp_declare_simd (parser, declarator->attributes,
- parms);
+ = cp_parser_late_parsing_omp_declare_simd (parser,
+ declarator->attributes);
if (oacc_routine_p)
declarator->attributes
= cp_parser_late_parsing_oacc_routine (parser,
@@ -26892,6 +27294,7 @@ cp_parser_braced_list (cp_parser *parser, bool *non_constant_p /*=nullptr*/)
tree initializer;
location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
auto oas = make_temp_override (parser->omp_array_section_p, false);
+ auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
/* Consume the `{' token. */
matching_braces braces;
@@ -27565,6 +27968,10 @@ cp_parser_class_specifier (cp_parser* parser)
tree saved_ccr = current_class_ref;
current_class_ptr = NULL_TREE;
current_class_ref = NULL_TREE;
+ /* Set up for deferred lookup of "omp begin declare variant" base functions
+ in the class. */
+ tree save_unregistered_variants = parser->omp_unregistered_variants;
+ parser->omp_unregistered_variants = NULL_TREE;
/* Start the class. */
if (nested_name_specifier_p)
@@ -27586,6 +27993,19 @@ cp_parser_class_specifier (cp_parser* parser)
/* Parse the member-specification. */
cp_parser_member_specification_opt (parser);
+ /* Register any "begin declare variant" functions in this class, since
+ references to the base function can only be resolved after the
+ entire class is seen. */
+ for (tree bdv = parser->omp_unregistered_variants; bdv;
+ bdv = TREE_CHAIN (bdv))
+ {
+ tree dv_base = TREE_PURPOSE (TREE_PURPOSE (bdv));
+ tree dv_ctx = TREE_VALUE (TREE_PURPOSE (bdv));
+ tree dv_decl = TREE_VALUE (bdv);
+ omp_finish_variant_function (parser, dv_decl, dv_base, dv_ctx);
+ }
+ parser->omp_unregistered_variants = save_unregistered_variants;
+
/* Look for the trailing `}'. */
closing_brace = braces.require_close (parser);
/* Look for trailing attributes to apply to this class. */
@@ -29248,6 +29668,28 @@ cp_parser_member_declaration (cp_parser* parser)
if (initializer && initializer_token_start)
error_at (initializer_token_start->location,
"pure-specifier on function-definition");
+
+ /* If we're in an OpenMP "begin declare variant" block,
+ the name in the declarator refers to the base function.
+ We need to save that and modify the declarator to have
+ the mangled name for the variant function instead. */
+ tree dv_base = NULL_TREE;
+ tree dv_ctx = NULL_TREE;
+ vec<cp_omp_declare_variant_attr, va_gc> *dv_state
+ = scope_chain->omp_declare_variant_attribute;
+ if (!vec_safe_is_empty (dv_state))
+ {
+ cp_omp_declare_variant_attr a = dv_state->last ();
+ dv_ctx = copy_list (a.selector);
+ dv_base = omp_start_variant_function (declarator,
+ dv_ctx);
+ if (dv_base == NULL_TREE)
+ {
+ cp_parser_skip_to_end_of_statement (parser);
+ goto out;
+ }
+ }
+
decl = cp_parser_save_member_function_body (parser,
&decl_specifiers,
declarator,
@@ -29258,6 +29700,19 @@ cp_parser_member_declaration (cp_parser* parser)
/* If the member was not a friend, declare it here. */
if (!friend_p)
finish_member_declaration (decl);
+
+ /* If this function was in a "begin declare variant"
+ block, record the information we need to find the
+ base function and fix it up later. At this point in
+ parsing, we may not have seen the base function yet
+ so we defer looking it up and registering the variant
+ until the class is complete. */
+ if (dv_base && decl != error_mark_node)
+ parser->omp_unregistered_variants
+ = tree_cons (tree_cons (dv_base, dv_ctx, NULL_TREE),
+ decl,
+ parser->omp_unregistered_variants);
+
/* Peek at the next token. */
token = cp_lexer_peek_token (parser->lexer);
/* If the next token is a semicolon, consume it. */
@@ -38927,6 +39382,8 @@ cp_parser_omp_clause_name (cp_parser *parser)
result = PRAGMA_OMP_CLAUSE_USE_DEVICE_ADDR;
else if (!strcmp ("use_device_ptr", p))
result = PRAGMA_OMP_CLAUSE_USE_DEVICE_PTR;
+ else if (!strcmp ("uses_allocators", p))
+ result = PRAGMA_OMP_CLAUSE_USES_ALLOCATORS;
break;
case 'v':
if (!strcmp ("vector", p))
@@ -38983,16 +39440,17 @@ check_no_duplicate_clause (tree clauses, enum omp_clause_code code,
struct omp_dim
{
- tree low_bound, length;
+ tree low_bound, length, stride;
location_t loc;
bool no_colon;
- omp_dim (tree lb, tree len, location_t lo, bool nc)
- : low_bound (lb), length (len), loc (lo), no_colon (nc) {}
+ omp_dim (tree lb, tree len, tree str, location_t lo, bool nc)
+ : low_bound (lb), length (len), stride (str), loc (lo), no_colon (nc) {}
};
static tree
cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
tree list, bool *colon,
+ enum c_omp_region_type ort = C_ORT_OMP,
bool map_lvalue = false)
{
auto_vec<omp_dim> dims;
@@ -39019,10 +39477,23 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
|| kind == OMP_CLAUSE_FROM))
{
auto s = make_temp_override (parser->omp_array_section_p, true);
+ auto o = make_temp_override (parser->omp_array_shaping_op_p,
+ (kind == OMP_CLAUSE_TO
+ || kind == OMP_CLAUSE_FROM
+ || ort == C_ORT_OMP_DECLARE_MAPPER));
+ tree reshaped_to = NULL_TREE;
token = cp_lexer_peek_token (parser->lexer);
location_t loc = token->location;
decl = cp_parser_assignment_expression (parser);
+ if ((TREE_CODE (decl) == VIEW_CONVERT_EXPR
+ && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
+ || TREE_CODE (decl) == OMP_ARRAYSHAPE_CAST_EXPR)
+ {
+ reshaped_to = TREE_TYPE (decl);
+ decl = TREE_OPERAND (decl, 0);
+ }
+
/* 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
@@ -39033,49 +39504,159 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
dims.truncate (0);
if (TREE_CODE (decl) == OMP_ARRAY_SECTION)
{
+ size_t sections = 0;
+ tree orig_decl = decl;
+ bool update_p = (kind == OMP_CLAUSE_TO
+ || kind == OMP_CLAUSE_FROM);
+ bool maybe_ptr_based_noncontig_update = false;
+
+ while (update_p
+ && !reshaped_to
+ && (TREE_CODE (decl) == OMP_ARRAY_SECTION
+ || TREE_CODE (decl) == ARRAY_REF
+ || TREE_CODE (decl) == COMPOUND_EXPR))
+ {
+ if (TREE_CODE (decl) == COMPOUND_EXPR)
+ decl = TREE_OPERAND (decl, 1);
+ else
+ {
+ if (TREE_CODE (decl) == OMP_ARRAY_SECTION)
+ maybe_ptr_based_noncontig_update = true;
+ decl = TREE_OPERAND (decl, 0);
+ sections++;
+ }
+ }
+
+ decl = orig_decl;
+
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));
+ tree stride = TREE_OPERAND (decl, 3);
+ dims.safe_push (omp_dim (low_bound, length, stride, loc,
+ false));
decl = TREE_OPERAND (decl, 0);
+ if (sections > 0)
+ sections--;
}
+ /* The handling of INDIRECT_REF here in the presence of
+ array-shaping operations is a little tricky. We need to
+ avoid treating a pointer dereference as a unit-sized array
+ section when we have an array shaping operation, because we
+ don't want an indirection to consume one of the user's
+ requested array dimensions. E.g. if we have a
+ double-indirect pointer like:
+
+ int **foopp;
+ #pragma omp target update from(([N][N]) (*foopp)[0:X][0:Y])
+
+ We don't want to interpret this as:
+
+ foopp[0:1][0:X][0:Y]
+
+ else the array shape [N][N] won't match. Also we can't match
+ the array sections right-to-left instead, else this:
+
+ #pragma omp target update from(([N][N]) (*foopp)[0:X])
+
+ would not copy the dimensions:
+
+ (*foopp)[0:X][0:N]
+
+ as required. So, avoid descending through INDIRECT_REFs if
+ we have an array-shaping op.
+
+ If we *don't* have an array-shaping op, but we have a
+ multiply-indirected pointer and an array section like this:
+
+ int ***fooppp;
+ #pragma omp target update from((**fooppp)[0:X:S]
+
+ also avoid descending through more indirections than we have
+ array sections, since the noncontiguous update processing code
+ won't understand them (and doesn't need to traverse them
+ anyway). */
+
while (TREE_CODE (decl) == ARRAY_REF
- || TREE_CODE (decl) == INDIRECT_REF
+ || (TREE_CODE (decl) == INDIRECT_REF
+ && !reshaped_to)
|| TREE_CODE (decl) == COMPOUND_EXPR)
{
if (REFERENCE_REF_P (decl))
break;
+ if (maybe_ptr_based_noncontig_update && sections == 0)
+ break;
+
if (TREE_CODE (decl) == COMPOUND_EXPR)
{
decl = TREE_OPERAND (decl, 1);
STRIP_NOPS (decl);
+ continue;
}
- else if (TREE_CODE (decl) == INDIRECT_REF)
+ else if (TREE_CODE (decl) == INDIRECT_REF
+ && !reshaped_to)
{
dims.safe_push (omp_dim (integer_zero_node,
- integer_one_node, loc, true));
+ integer_one_node, NULL_TREE, 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));
+ dims.safe_push (omp_dim (index, integer_one_node,
+ NULL_TREE, loc, true));
decl = TREE_OPERAND (decl, 0);
+ if (sections > 0)
+ sections--;
}
}
+ if (reshaped_to)
+ {
+ unsigned reshaped_dims = 0;
+
+ for (tree t = reshaped_to;
+ TREE_CODE (t) == ARRAY_TYPE;
+ t = TREE_TYPE (t))
+ reshaped_dims++;
+
+ if (dims.length () > reshaped_dims)
+ {
+ error_at (loc, "too many array section specifiers "
+ "for %qT", reshaped_to);
+ decl = error_mark_node;
+ }
+ else
+ {
+ /* We have a pointer DECL whose target should be
+ interpreted as an array with particular dimensions,
+ not "the pointer itself". So, add an indirection
+ here. */
+ if (type_dependent_expression_p (decl))
+ decl = build_min_nt_loc (loc, INDIRECT_REF, decl);
+ else
+ {
+ /* We're interested in the reference target. */
+ decl = convert_from_reference (decl);
+ decl = cp_build_fold_indirect_ref (decl);
+ }
+ decl
+ = cp_build_omp_arrayshape_cast (loc, reshaped_to, decl,
+ tf_warning_or_error);
+ }
+ }
/* Bare references have their own special handling, so remove
the explicit dereference added by convert_from_reference. */
- if (REFERENCE_REF_P (decl))
+ else if (REFERENCE_REF_P (decl))
decl = TREE_OPERAND (decl, 0);
for (int i = dims.length () - 1; i >= 0; i--)
decl = grok_omp_array_section (loc, decl, dims[i].low_bound,
- dims[i].length);
+ dims[i].length, dims[i].stride);
}
else if (TREE_CODE (decl) == INDIRECT_REF)
{
@@ -39091,7 +39672,7 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
if (!ref_p)
decl = grok_omp_array_section (loc, decl, integer_zero_node,
- integer_one_node);
+ integer_one_node, NULL_TREE);
}
else if (TREE_CODE (decl) == ARRAY_REF)
{
@@ -39100,7 +39681,16 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
decl = TREE_OPERAND (decl, 0);
STRIP_NOPS (decl);
- decl = grok_omp_array_section (loc, decl, idx, integer_one_node);
+ decl = grok_omp_array_section (loc, decl, idx, integer_one_node,
+ NULL_TREE);
+ }
+ else if (reshaped_to)
+ {
+ /* We're copying the whole of a reshaped array, originally a
+ base pointer. Rewrite as an array section. */
+ tree elems = array_type_nelts_total (reshaped_to);
+ decl = grok_omp_array_section (loc, decl, size_zero_node, elems,
+ NULL_TREE);
}
else if (TREE_CODE (decl) == NON_LVALUE_EXPR
|| CONVERT_EXPR_P (decl))
@@ -39264,7 +39854,8 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
goto skip_comma;
}
- dims.safe_push (omp_dim (low_bound, length, loc, no_colon));
+ dims.safe_push (omp_dim (low_bound, length, NULL_TREE, loc,
+ no_colon));
}
if ((kind == OMP_CLAUSE_MAP
@@ -39286,7 +39877,8 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
for (unsigned i = 0; i < dims.length (); i++)
decl = build_omp_array_section (input_location, decl,
dims[i].low_bound,
- dims[i].length);
+ dims[i].length,
+ dims[i].stride);
break;
default:
break;
@@ -39299,6 +39891,8 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
&& cp_parser_simulate_error (parser))
{
depend_lvalue:
+ auto o = make_temp_override (parser->omp_array_shaping_op_p,
+ true);
cp_parser_abort_tentative_parse (parser);
decl = cp_parser_assignment_expression (parser, NULL,
false, false);
@@ -39357,6 +39951,7 @@ 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,
+ enum c_omp_region_type ort = C_ORT_OMP,
bool map_lvalue = false)
{
if (parser->lexer->in_omp_decl_attribute)
@@ -39375,11 +39970,422 @@ cp_parser_omp_var_list (cp_parser *parser, enum omp_clause_code kind, tree list,
}
if (cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
- return cp_parser_omp_var_list_no_open (parser, kind, list, NULL,
+ return cp_parser_omp_var_list_no_open (parser, kind, list, NULL, ort,
map_lvalue);
return list;
}
+/* Parse an OpenMP parameter-list.
+ parameter-list:
+ parameter-list-item[, parameter-list-item [, ...]]
+
+ parameter-list-item:
+ named parameter list item
+ parameter index (1 based)
+ numeric-range
+
+ numeric-range:
+ [bound]:[bound]
+
+ bound:
+ index-expr
+ omp_num_args[±logical_offset]
+
+ A named parameter list item is the name of a parameter. A parameter index
+ is a positive integer literal that is the 1 based index of a parameter.
+ A numeric-range is a pair of bounds of the form lb:ub, the values of each
+ bound form a closed interval of parameter indices. Bounds can be literal or
+ relative. An index-expr is a non-negative integer constant-expression that
+ is the value of a literal bound. The special identifier omp_num_args is
+ equal to the number of arguments passed to the function at the call site,
+ including the number of varargs. Optionally, a plus or minus with a
+ logical_offset may follow omp_num_args, logical_offset is a non-negative
+ integer constant-expression. A bound formed with omp_num_args is a relative
+ bound. If a bound is omitted, a default value is used. The default value
+ of lb is as if 1 were specified, the default value of ub is as if
+ omp_num_args were specified.
+
+ Each parameter-list-item is stored in a TREE_LIST. The PURPOSE is for
+ general use and left NULL_TREE here, and the item is stored in the VALUE.
+ An item is a TREE_LIST, the PURPOSE is an expression with the location of
+ the list item, and the VALUE is a representation of the item.
+ Each parameter-list-item is stored in a TREE_LIST node VALUE. The PURPOSE
+ is unused, and the VALUE is the item-repr.
+
+ Node - PUPOSE: NULL_TREE
+ - VALUE: item-with-location
+ item-with-location - PURPOSE: expr-with-location
+ - VALUE: item-repr
+
+ An item-repr is a PARM_DECL, a NOP_EXPR, or a TREE_LIST. A PARM_DECL is a
+ named parameter list item. A NOP_EXPR is the unadjusted 1-based parameter
+ index. A TREE_LIST is a numeric-range where its PURPOSE is a TREE_LIST
+ representing the lb, and its VALUE is a TREE_LIST representing the ub.
+
+ item-repr
+ PARM_DECL - parameter name
+ NOP_EXPR - parameter index (1 based)
+ TREE_LIST - PURPOSE: TREE_LIST (lb)
+ - VALUE: TREE_LIST (ub)
+
+ lb and ub are a TREE_LIST of the following form;
+ TREE_LIST - PURPOSE: relative bound marker (NULL_TREE if literal)
+ - VALUE: expr-value
+
+ This function strictly handles a parameter-list, it does not parse clause
+ modifiers, or parenthesis other than in the expr of a numeric range.
+
+ If a diagnostic is issued for a list item, it is not appened to the list and
+ parsing continues. Returns NULL_TREE if no valid list items are parsed. */
+
+static tree
+cp_parser_omp_parm_list (cp_parser *parser)
+{
+ tree list = NULL_TREE;
+ auto append_to_list = [chain = &list] (tree arg, location_t loc) mutable
+ {
+ gcc_assert (*chain == NULL_TREE);
+ *chain = build_tree_list (NULL_TREE,
+ build_tree_list (build_empty_stmt (loc), arg));
+ chain = &TREE_CHAIN (*chain);
+ };
+
+ auto tok_terminates_item_p = [] (const cp_token *tok)
+ {
+ return tok->type == CPP_COMMA
+ || tok->type == CPP_CLOSE_PAREN;
+ };
+ /* The first list item is (obviously) not preceded by a comma. */
+ goto first_element;
+ do
+ {
+ /* Consume the comma. */
+ cp_lexer_consume_token (parser->lexer);
+ first_element:
+
+ cp_token *const tok = cp_lexer_peek_token (parser->lexer);
+
+ /* OpenMP 6.0 (162:29-34)
+ A parameter list item can be one of the following:
+ • A named parameter list item;
+ • The position of a parameter in a parameter specification specified
+ by a positive integer, where 1 represents the first parameter; or
+ • A parameter range specified by lb : ub where both lb and ub must
+ be an expression of integer OpenMP type with the constant property
+ and the positive property.
+
+ The spec does not support arbitrary expression outside of a numeric
+ range. In theory they could be supported as a parameter index, but
+ for now we do not support that case. */
+
+ /* If we don't see a comma or close paren this can't be a named parameter
+ list item or a parameter index, it can only be a numeric range. */
+ if (!tok_terminates_item_p (cp_lexer_peek_nth_token (parser->lexer, 2))
+ /* Or this edge case, there is a default lower bound. */
+ || tok->type == CPP_COLON)
+ /* Early exit, numeric range case handled below. */;
+ else if (tok->type == CPP_NAME)
+ {
+ if (strcmp (IDENTIFIER_POINTER (tok->u.value), "omp_num_args") == 0)
+ {
+ error_at (tok->location, "%<omp_num_args%> may only be used at "
+ "the start of a numeric range bound");
+ cp_lexer_consume_token (parser->lexer);
+ continue;
+ }
+ /* This might not be the right way to do this, we might want to use
+ cp_parser_lookup_name_simple instead. */
+ tree parm = lookup_name (tok->u.value,
+ LOOK_where::BLOCK,
+ LOOK_want::NORMAL);
+ if (parm && TREE_CODE (parm) == PARM_DECL)
+ {
+ if (DECL_PACK_P (parm))
+ {
+ /* In theory we could just consider every element of the pack
+ as being specified, the spec does not say what to do
+ though. */
+ sorry_at (tok->location,
+ "parameter packs are not supported as an OpenMP "
+ "named parameter list item");
+ inform (DECL_SOURCE_LOCATION (parm),
+ "declared as a pack here");
+ }
+ else
+ append_to_list (parm, tok->location);
+ }
+ else
+ {
+ /* FIXME: Nice diagnostic, potentially using
+ cp_parser_name_lookup_error. */
+ error_at (tok->location,
+ "%qs is not a function parameter",
+ IDENTIFIER_POINTER (tok->u.value));
+ }
+ cp_lexer_consume_token (parser->lexer);
+ continue;
+ }
+ else if (tok->type == CPP_NUMBER)
+ {
+ if (wi::to_widest (tok->u.value) <= 0)
+ {
+ error_at (tok->location,
+ "parameter indices in an OpenMP "
+ "parameter list must be positive");
+ }
+ else if (wi::to_widest (tok->u.value) > INT_MAX)
+ error_at (tok->location, "parameter index is too big");
+ else
+ {
+ /* Don't adjust here, we can't finalize these until we know if we
+ are in a member function or not. We can probably hack this to
+ find out in here, but it belongs in finish_omp_parm_list, not
+ here.
+ FIXME: We have to come up with a better way of transporting
+ these and marking them as unfinalized. Wrapping in a NOP is
+ really quite bad. */
+ tree cst = build_int_cst (integer_type_node,
+ tree_to_shwi (tok->u.value));
+ append_to_list (build_nop (integer_type_node, cst),
+ tok->location);
+ }
+ cp_lexer_consume_token (parser->lexer);
+ continue;
+ }
+ else
+ {
+ gcc_checking_assert (tok_terminates_item_p
+ (cp_lexer_peek_nth_token (parser->lexer, 2)));
+ cp_parser_error (parser, "expected unqualified-id, "
+ "integer, or expression");
+ cp_lexer_consume_token (parser->lexer);
+ continue;
+ }
+ /* We have a numeric range or something ill formed now, this can be
+ an arbitrary expression. */
+
+ /* Empty bounds are delimited differently for lower and upper bounds,
+ handle them without calling parse_bound. */
+ auto parse_bound = [&] () -> tree
+ {
+ location_t bound_start
+ = cp_lexer_peek_token (parser->lexer)->location;
+ enum omp_num_args
+ {
+ num_args_none,
+ num_args_plus,
+ num_args_minus,
+ num_args_no_offset
+ };
+ /* (OpenMP 6.0, 162:35-37)
+ In both lb and ub, an expression using omp_num_args, that enables
+ identification of parameters relative to the last argument of the
+ call, can be used with the form:
+ omp_num_args [± logical_offset] */
+ const omp_num_args parsed_omp_num_args = [&] ()
+ {
+ cp_token *tok = cp_lexer_peek_token (parser->lexer);
+ if (tok->type == CPP_NAME
+ && strcmp (IDENTIFIER_POINTER (tok->u.value), "omp_num_args")
+ == 0)
+ {
+ /* Consume omp_num_args. */
+ cp_lexer_consume_token (parser->lexer);
+ cp_token *op_tok = cp_lexer_peek_token (parser->lexer);
+ if (op_tok->type == CPP_PLUS)
+ {
+ cp_lexer_consume_token (parser->lexer);
+ return num_args_plus;
+ }
+ else if (op_tok->type == CPP_MINUS)
+ {
+ cp_lexer_consume_token (parser->lexer);
+ return num_args_minus;
+ }
+ return num_args_no_offset;
+ }
+ else
+ return num_args_none;
+ } (); /* IILE. */
+ /* If there was omp_num_args but no operator an expr is not
+ permitted, we are finished with this bound. */
+ if (parsed_omp_num_args == num_args_no_offset)
+ {
+ tree cst = build_zero_cst (integer_type_node);
+ /* I hate this hack. We don't know if we are parsing a lb or ub,
+ so even though we know it's value we have to wait until later
+ to finalize it. */
+ return build_tree_list (get_identifier ("omp num args plus"),
+ build1_loc (bound_start,
+ NOP_EXPR,
+ integer_type_node,
+ cst));
+ }
+ const bool saved_flag = parser->colon_corrects_to_scope_p;
+ /* Disable this diagnostic to parse id:id cases such as
+ 'V:omp_num_args' where V is a constant expression variable. */
+ parser->colon_corrects_to_scope_p = false;
+ /* Function arguments are considered an assignment-expression by the
+ C++ standard, it seems to me that those semantics match what we
+ want from an expr in lb or ub. */
+ cp_expr expr = cp_parser_assignment_expression (parser);
+ parser->colon_corrects_to_scope_p = saved_flag;
+
+ if (!expr || expr == error_mark_node)
+ return error_mark_node;
+
+ auto finish_bound_expr = [&parsed_omp_num_args] (cp_expr expr_in)
+ {
+ const location_t loc = expr_in.get_location ();
+ tree expr = expr_in.get_value ();
+ /* Try to fold early if expr is not dependent. I'm pretty sure
+ this should be manifestly constant-evaluated. We require a
+ constant here, let fold_non_dependent_expr complain, but
+ handle everything else in finish_omp_parm_list. */
+ if (!value_dependent_expression_p (expr))
+ {
+ expr = fold_non_dependent_expr (expr,
+ tf_warning_or_error,
+ true);
+ if (!expr || error_operand_p (expr))
+ {
+ if (parsed_omp_num_args != num_args_none)
+ error_at (loc, "logical offset of a bound must "
+ "be a constant expression");
+ else
+ error_at (loc, "expression of a bound must be a "
+ "constant expression");
+ return error_mark_node;
+ }
+ }
+ /* We need a way to signal that an expr has not been adjusted,
+ the best way I came up with is checking if it is an
+ INTEGER_CST, but if we already have an INTEGER_CST at this
+ point, what now? Wrap it in a nop, that's what. */
+ if (TREE_CODE (expr) == INTEGER_CST)
+ return build1_loc (loc, NOP_EXPR, TREE_TYPE (expr), expr);
+ /* We still need this for things like template parameters. */
+ auto maybe_force_wrap_with_location = [&] ()
+ {
+ if (!expr
+ || error_operand_p (expr)
+ || CAN_HAVE_LOCATION_P (expr))
+ return expr;
+ /* Pulled from maybe_wrap_with_location. */
+ const tree_code code
+ = ((CONSTANT_CLASS_P (expr)
+ && TREE_CODE (expr) != STRING_CST)
+ || (TREE_CODE (expr) == CONST_DECL
+ && !TREE_STATIC (expr)))
+ ? NON_LVALUE_EXPR : VIEW_CONVERT_EXPR;
+ tree wrap = build1_loc (loc, code, TREE_TYPE (expr), expr);
+ EXPR_LOCATION_WRAPPER_P (wrap) = 1;
+ return wrap;
+ };
+ return maybe_force_wrap_with_location ();
+ };
+
+ gcc_assert (parsed_omp_num_args < num_args_no_offset);
+ switch (parsed_omp_num_args)
+ {
+ case num_args_none:
+ /* NULL_TREE represents literal. */
+ return build_tree_list (NULL_TREE,
+ finish_bound_expr (expr));
+ case num_args_plus:
+ return build_tree_list (get_identifier ("omp num args plus"),
+ finish_bound_expr (expr));
+ case num_args_minus:
+ return build_tree_list (get_identifier ("omp num args minus"),
+ finish_bound_expr (expr));
+ case num_args_no_offset:
+ /* Handled above. */
+ default:
+ gcc_unreachable ();
+ }
+ gcc_unreachable ();
+ };
+ /* I'm not happy with the state of diagnostics here, but I'm not sure how
+ to fix it so it's best to wait to see which cases end up giving really
+ unclear errors. */
+ location_t num_range_loc_begin
+ = cp_lexer_peek_token (parser->lexer)->location;
+ /* As stated above, empty bounds are handled here. */
+ tree lower_bound = cp_lexer_next_token_is (parser->lexer, CPP_COLON)
+ ? NULL_TREE : parse_bound ();
+ /* I wish we could error here saying that we expect an unqualified-id,
+ an integer, or an expression. Parsing the expression emits the error
+ right away though. Maybe we can do some tentative parsing? */
+ if (lower_bound && error_operand_p (lower_bound))
+ {
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/true,
+ /*consume_paren=*/false);
+ continue;
+ }
+ /* Tokens get consumed by parse_bound. */
+ if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON))
+ {
+ /* lower_bound can only be null if the next token was a colon. */
+ gcc_assert (lower_bound && !error_operand_p (lower_bound));
+ const cp_token *const next_tok = cp_lexer_peek_token (parser->lexer);
+
+ cp_parser_error (parser, "expected %<:%>");
+ if (tok_terminates_item_p (next_tok))
+ {
+ const location_t loc = make_location (num_range_loc_begin,
+ num_range_loc_begin,
+ input_location);
+ inform (loc, "an expression is only allowed in a numeric range");
+ }
+ /* Do not consume the close paren, this function does not handle
+ that part of the clause. */
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/true,
+ /*consume_paren=*/false);
+ continue;
+ }
+ location_t colon_loc = cp_lexer_consume_token (parser->lexer)->location;
+ tree upper_bound = tok_terminates_item_p
+ (cp_lexer_peek_token (parser->lexer))
+ ? NULL_TREE : parse_bound ();
+
+ /* I think we are supposed to have some sort of diagnostic here, I'm just
+ not sure what it should be. */
+ if (error_operand_p (lower_bound) || error_operand_p (upper_bound))
+ continue;
+
+ location_t num_range_loc_end
+ = upper_bound ? EXPR_LOCATION (TREE_VALUE (upper_bound)) : colon_loc;
+
+ auto build_default_bound = [] (tree num_args_marker, int val)
+ {
+ /* Unfortunately, we can't assume what the final value will be
+ because we don't know if we are in a member function or not. */
+ tree value = build_nop (integer_type_node,
+ build_int_cst (integer_type_node, val));
+ return build_tree_list (num_args_marker, value);
+ };
+ static constexpr int lb_default = 1;
+ /* Internally, 0 + omp_num_args refers to the last arg. */
+ static constexpr int ub_default = 0;
+ if (!lower_bound)
+ lower_bound = build_default_bound (NULL_TREE, lb_default);
+ if (!upper_bound)
+ upper_bound
+ = build_default_bound (get_identifier ("omp num args plus"),
+ ub_default);
+
+ append_to_list (build_tree_list (lower_bound, upper_bound),
+ make_location (num_range_loc_begin,
+ num_range_loc_begin,
+ num_range_loc_end));
+ } while (cp_lexer_next_token_is (parser->lexer, CPP_COMMA));
+ return list;
+}
+
/* OpenACC 2.0:
copy ( variable-list )
copyin ( variable-list )
@@ -39467,7 +40473,7 @@ cp_parser_oacc_data_clause (cp_parser *parser, pragma_omp_clause c_kind,
}
}
nl = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_MAP, list, NULL,
- false);
+ C_ORT_ACC, false);
}
for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
@@ -40831,7 +41837,7 @@ cp_parser_omp_clause_ordered (cp_parser *parser,
static tree
cp_parser_omp_clause_reduction (cp_parser *parser, enum omp_clause_code kind,
- bool is_omp, tree list)
+ enum c_omp_region_type ort, tree list)
{
enum tree_code code = ERROR_MARK;
tree nlist, c, id = NULL_TREE;
@@ -40841,7 +41847,7 @@ cp_parser_omp_clause_reduction (cp_parser *parser, enum omp_clause_code kind,
if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
return list;
- if (kind == OMP_CLAUSE_REDUCTION && is_omp)
+ if (kind == OMP_CLAUSE_REDUCTION && ort == C_ORT_OMP)
{
if (cp_lexer_next_token_is_keyword (parser->lexer, RID_DEFAULT)
&& cp_lexer_nth_token_is (parser->lexer, 2, CPP_COMMA))
@@ -40916,6 +41922,12 @@ cp_parser_omp_clause_reduction (cp_parser *parser, enum omp_clause_code kind,
code = TRUTH_ANDIF_EXPR;
else if (id == ovl_op_identifier (false, TRUTH_ORIF_EXPR))
code = TRUTH_ORIF_EXPR;
+ if (code == ERROR_MARK && ort == C_ORT_ACC)
+ {
+ cp_parser_error (parser, "expected %<+%>, %<*%>, %<-%>, %<&%>, "
+ "%<^%>, %<|%>, %<&&%>, %<||%>, %<min%> or %<max%>");
+ goto resync_fail;
+ }
id = omp_reduction_id (code, id, NULL_TREE);
tree scope = parser->scope;
if (scope)
@@ -40938,11 +41950,14 @@ cp_parser_omp_clause_reduction (cp_parser *parser, enum omp_clause_code kind,
if (!cp_parser_require (parser, CPP_COLON, RT_COLON))
goto resync_fail;
- nlist = cp_parser_omp_var_list_no_open (parser, kind, list,
- NULL);
+ nlist = cp_parser_omp_var_list_no_open (parser, kind, list, NULL, ort);
for (c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c))
{
OMP_CLAUSE_REDUCTION_CODE (c) = code;
+ /* OpenACC does not require anything below. */
+ if (ort == C_ORT_ACC)
+ continue;
+
if (task)
OMP_CLAUSE_REDUCTION_TASK (c) = 1;
else if (inscan)
@@ -41479,6 +42494,234 @@ cp_parser_omp_clause_allocate (cp_parser *parser, tree list)
return nlist;
}
+/* OpenMP 5.0:
+ uses_allocators ( allocator-list )
+
+ allocator-list:
+ allocator
+ allocator , allocator-list
+ allocator ( traits-array )
+ allocator ( traits-array ) , allocator-list
+
+ OpenMP 5.2:
+
+ uses_allocators ( modifier : allocator-list )
+ uses_allocators ( modifier , modifier : allocator-list )
+
+ modifier:
+ traits ( traits-array )
+ memspace ( mem-space-handle ) */
+
+static tree
+cp_parser_omp_clause_uses_allocators (cp_parser *parser, tree list)
+{
+ location_t clause_loc
+ = cp_lexer_peek_token (parser->lexer)->location;
+ tree t = NULL_TREE, nl = list;
+ matching_parens parens;
+ if (!parens.require_open (parser))
+ return list;
+
+ tree memspace_expr = NULL_TREE;
+ tree traits_var = NULL_TREE;
+
+ struct item_tok
+ {
+ location_t loc;
+ tree id;
+ item_tok (void) : loc (UNKNOWN_LOCATION), id (NULL_TREE) {}
+ };
+ struct item { item_tok name, arg; };
+ auto_vec<item> *modifiers = NULL, *allocators = NULL;
+ auto_vec<item> *cur_list = new auto_vec<item> (4);
+
+ while (true)
+ {
+ item it;
+
+ if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
+ {
+ cp_token *tok = cp_lexer_peek_token (parser->lexer);
+ it.name.id = tok->u.value;
+ it.name.loc = tok->location;
+ cp_lexer_consume_token (parser->lexer);
+
+ if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
+ {
+ matching_parens parens2;
+ parens2.consume_open (parser);
+
+ if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
+ {
+ tok = cp_lexer_peek_token (parser->lexer);
+ it.arg.id = tok->u.value;
+ it.arg.loc = tok->location;
+ cp_lexer_consume_token (parser->lexer);
+ }
+ else
+ {
+ cp_parser_error (parser, "expected identifier");
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+ goto end;
+ }
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/false,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+ }
+ }
+
+ cur_list->safe_push (it);
+
+ if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+ cp_lexer_consume_token (parser->lexer);
+ else if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+ {
+ if (modifiers)
+ {
+ cp_parser_error (parser, "expected %<)%>");
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+ goto end;
+ }
+ else
+ {
+ cp_lexer_consume_token (parser->lexer);
+ modifiers = cur_list;
+ cur_list = new auto_vec<item> (4);
+ }
+ }
+ else if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+ {
+ gcc_assert (allocators == NULL);
+ allocators = cur_list;
+ cur_list = NULL;
+ break;
+ }
+ else
+ {
+ cp_parser_error (parser, "expected %<)%>");
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+ goto end;
+ }
+ }
+
+ if (modifiers)
+ for (unsigned i = 0; i < modifiers->length (); i++)
+ {
+ item& it = (*modifiers)[i];
+ const char *p = IDENTIFIER_POINTER (it.name.id);
+ int strcmp_traits = 1, strcmp_memspace = 1;
+
+ if ((strcmp_traits = strcmp ("traits", p)) == 0
+ || (strcmp_memspace = strcmp ("memspace", p)) == 0)
+ {
+ if ((strcmp_traits == 0 && traits_var != NULL_TREE)
+ || (strcmp_memspace == 0 && memspace_expr != NULL_TREE))
+ {
+ error_at (it.name.loc, "duplicate %qs modifier", p);
+ goto end;
+ }
+ t = cp_parser_lookup_name_simple (parser, it.arg.id, it.arg.loc);
+ if (t == error_mark_node)
+ {
+ cp_parser_name_lookup_error (parser, it.arg.id, t, NLE_NULL,
+ it.arg.loc);
+ }
+ else if (strcmp_memspace == 0)
+ memspace_expr = t;
+ else if (strcmp_traits == 0)
+ traits_var = t;
+ else
+ gcc_unreachable ();
+ }
+ else
+ {
+ error_at (it.name.loc, "unknown modifier %qE", it.name.id);
+ goto end;
+ }
+ }
+
+ if (allocators)
+ {
+ if (modifiers)
+ {
+ if (allocators->length () > 1)
+ {
+ error_at ((*allocators)[1].name.loc,
+ "%<uses_allocators%> clause only accepts a single "
+ "allocator when using modifiers");
+ goto end;
+ }
+ else if ((*allocators)[0].arg.id)
+ {
+ error_at ((*allocators)[0].arg.loc,
+ "legacy %<%E(%E)%> traits syntax not allowed in "
+ "%<uses_allocators%> clause when using modifiers",
+ (*allocators)[0].name.id, (*allocators)[0].arg.id);
+ goto end;
+ }
+ }
+
+ for (unsigned i = 0; i < allocators->length (); i++)
+ {
+ item& it = (*allocators)[i];
+ t = cp_parser_lookup_name_simple (parser, it.name.id, it.name.loc);
+ if (t == error_mark_node)
+ {
+ cp_parser_name_lookup_error (parser, it.name.id, t, NLE_NULL,
+ it.name.loc);
+ goto end;
+ }
+ else if (t != error_mark_node)
+ {
+ tree t2 = NULL_TREE;
+ if (it.arg.id)
+ {
+ t2 = cp_parser_lookup_name_simple (parser, it.arg.id,
+ it.arg.loc);
+ if (t2 == error_mark_node)
+ {
+ cp_parser_name_lookup_error (parser, it.arg.id, t2,
+ NLE_NULL, it.arg.loc);
+ goto end;
+ }
+ }
+ else
+ t2 = traits_var;
+
+ tree c = build_omp_clause (clause_loc,
+ OMP_CLAUSE_USES_ALLOCATORS);
+ OMP_CLAUSE_USES_ALLOCATORS_ALLOCATOR (c) = t;
+ OMP_CLAUSE_USES_ALLOCATORS_MEMSPACE (c) = memspace_expr;
+ OMP_CLAUSE_USES_ALLOCATORS_TRAITS (c) = t2;
+ OMP_CLAUSE_CHAIN (c) = nl;
+ nl = c;
+ }
+ }
+ }
+ end:
+ if (cur_list)
+ delete cur_list;
+ if (modifiers)
+ delete modifiers;
+ if (allocators)
+ delete allocators;
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/false,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+ return nl;
+}
+
/* OpenMP 2.5:
lastprivate ( variable-list )
@@ -42030,10 +43273,10 @@ cp_parser_omp_iterators (cp_parser *parser)
pushdecl (iter_var);
*last = make_tree_vec (6);
- TREE_VEC_ELT (*last, 0) = iter_var;
- TREE_VEC_ELT (*last, 1) = begin;
- TREE_VEC_ELT (*last, 2) = end;
- TREE_VEC_ELT (*last, 3) = step;
+ OMP_ITERATORS_VAR (*last) = iter_var;
+ OMP_ITERATORS_BEGIN (*last) = begin;
+ OMP_ITERATORS_END (*last) = end;
+ OMP_ITERATORS_STEP (*last) = step;
last = &TREE_CHAIN (*last);
if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
@@ -42107,7 +43350,7 @@ cp_parser_omp_clause_affinity (cp_parser *parser, tree list)
tree block = poplevel (1, 1, 0);
if (iterators != error_mark_node)
{
- TREE_VEC_ELT (iterators, 5) = block;
+ OMP_ITERATORS_BLOCK (iterators) = block;
for (c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c))
OMP_CLAUSE_DECL (c) = build_tree_list (iterators,
OMP_CLAUSE_DECL (c));
@@ -42232,7 +43475,7 @@ cp_parser_omp_clause_depend (cp_parser *parser, tree list, location_t loc)
if (iterators == error_mark_node)
iterators = NULL_TREE;
else
- TREE_VEC_ELT (iterators, 5) = block;
+ OMP_ITERATORS_BLOCK (iterators) = block;
}
for (c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c))
@@ -42328,8 +43571,11 @@ cp_parser_omp_clause_doacross (cp_parser *parser, tree list, location_t loc)
to ( variable-list )
OpenMP 5.1:
- from ( [present :] variable-list )
- to ( [present :] variable-list ) */
+ from ( [motion-modifier[,] [motion-modifier[,]...]:] variable-list )
+ to ( [motion-modifier[,] [motion-modifier[,]...]:] variable-list )
+
+ motion-modifier:
+ present | iterator (iterators-definition) */
static tree
cp_parser_omp_clause_from_to (cp_parser *parser, enum omp_clause_code kind,
@@ -42338,23 +43584,196 @@ cp_parser_omp_clause_from_to (cp_parser *parser, enum omp_clause_code kind,
if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
return list;
- bool present = false;
- cp_token *token = cp_lexer_peek_token (parser->lexer);
+ int pos = 1;
+ int colon_pos = 0;
+ int iterator_length = 0;
- if (token->type == CPP_NAME
- && strcmp (IDENTIFIER_POINTER (token->u.value), "present") == 0
- && cp_lexer_nth_token_is (parser->lexer, 2, CPP_COLON))
+ while (cp_lexer_peek_nth_token (parser->lexer, pos)->type == CPP_NAME)
{
- present = true;
- cp_lexer_consume_token (parser->lexer);
- cp_lexer_consume_token (parser->lexer);
+ const char *identifier =
+ IDENTIFIER_POINTER (cp_lexer_peek_nth_token (parser->lexer,
+ pos)->u.value);
+ if (cp_lexer_nth_token_is (parser->lexer, pos + 1, CPP_OPEN_PAREN))
+ {
+ int n = cp_parser_skip_balanced_tokens (parser, pos + 1);
+ if (n != pos + 1)
+ {
+ if (strcmp (identifier, "iterator") == 0)
+ iterator_length = n - pos;
+ pos = n - 1;
+ }
+ }
+ if (cp_lexer_peek_nth_token (parser->lexer, pos + 1)->type == CPP_COMMA)
+ pos += 2;
+ else
+ pos++;
+ if (cp_lexer_peek_nth_token (parser->lexer, pos)->type == CPP_COLON)
+ {
+ colon_pos = pos;
+ break;
+ }
+ }
+
+ bool present_modifier = false;
+ bool mapper_modifier = false;
+ tree mapper_name = NULL_TREE;
+ tree iterators = NULL_TREE;
+
+ for (int pos = 1; pos < colon_pos; ++pos)
+ {
+ cp_token *tok = cp_lexer_peek_token (parser->lexer);
+ if (tok->type == CPP_COMMA)
+ {
+ cp_lexer_consume_token (parser->lexer);
+ continue;
+ }
+ const char *p = IDENTIFIER_POINTER (tok->u.value);
+ if (strcmp ("present", p) == 0)
+ {
+ if (present_modifier)
+ {
+ cp_parser_error (parser, "too many %<present%> modifiers");
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+ return list;
+ }
+ present_modifier = true;
+ cp_lexer_consume_token (parser->lexer);
+ }
+ else if (strcmp ("iterator", p) == 0)
+ {
+ if (iterators)
+ {
+ cp_parser_error (parser, "too many %<iterator%> modifiers");
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+ return list;
+ }
+ begin_scope (sk_omp, NULL);
+ iterators = cp_parser_omp_iterators (parser);
+ pos += iterator_length - 1;
+ }
+ else if (strcmp ("mapper", p) == 0)
+ {
+ cp_lexer_consume_token (parser->lexer);
+ matching_parens parens;
+ if (parens.require_open (parser))
+ {
+ if (mapper_modifier)
+ {
+ cp_parser_error (parser, "too many %<mapper%> modifiers");
+ /* Assume it's a well-formed mapper modifier, even if it
+ seems to be in the wrong place. */
+ cp_lexer_consume_token (parser->lexer);
+ parens.require_close (parser);
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/
+ true);
+ return list;
+ }
+ tok = cp_lexer_peek_token (parser->lexer);
+ switch (tok->type)
+ {
+ case CPP_NAME:
+ {
+ cp_expr e = cp_parser_identifier (parser);
+ if (e != error_mark_node)
+ mapper_name = e;
+ else
+ goto err;
+ }
+ break;
+ case CPP_KEYWORD:
+ if (tok->keyword == RID_DEFAULT)
+ {
+ cp_lexer_consume_token (parser->lexer);
+ break;
+ }
+ /* Fallthrough. */
+ default:
+ err:
+ cp_parser_error (parser,
+ "expected identifier or %<default%>");
+ return list;
+ }
+
+ if (!parens.require_close (parser))
+ {
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/
+ true);
+ return list;
+ }
+ mapper_modifier = true;
+ pos += 3;
+ }
+ }
+ else
+ {
+ cp_parser_error (parser, "%<to%> or %<from%> clause with "
+ "modifier other than %<iterator%>, "
+ "%<mapper%> or %<present%>");
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+ return list;
+ }
}
- tree nl = cp_parser_omp_var_list_no_open (parser, kind, list, NULL, true);
- if (present)
+ if (colon_pos)
+ cp_parser_require (parser, CPP_COLON, RT_COLON);
+
+ tree nl = cp_parser_omp_var_list_no_open (parser, kind, list, NULL, C_ORT_OMP,
+ true);
+ if (present_modifier)
for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
OMP_CLAUSE_MOTION_PRESENT (c) = 1;
+ if (mapper_name)
+ {
+ tree last_new = NULL_TREE;
+ for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
+ last_new = c;
+
+ tree name = build_omp_clause (input_location, OMP_CLAUSE_MAP);
+ OMP_CLAUSE_SET_MAP_KIND (name, GOMP_MAP_PUSH_MAPPER_NAME);
+ OMP_CLAUSE_DECL (name) = mapper_name;
+ OMP_CLAUSE_CHAIN (name) = nl;
+ nl = name;
+
+ gcc_assert (last_new);
+
+ name = build_omp_clause (input_location, OMP_CLAUSE_MAP);
+ OMP_CLAUSE_SET_MAP_KIND (name, GOMP_MAP_POP_MAPPER_NAME);
+ OMP_CLAUSE_DECL (name) = null_pointer_node;
+ if (iterators)
+ OMP_CLAUSE_ITERATORS (name) = iterators;
+ OMP_CLAUSE_CHAIN (name) = OMP_CLAUSE_CHAIN (last_new);
+ OMP_CLAUSE_CHAIN (last_new) = name;
+ }
+
+ if (iterators)
+ {
+ tree block = poplevel (1, 1, 0);
+ if (iterators == error_mark_node)
+ iterators = NULL_TREE;
+ else
+ OMP_ITERATORS_BLOCK (iterators) = block;
+ }
+
+ if (iterators)
+ for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
+ OMP_CLAUSE_ITERATORS (c) = iterators;
+
return nl;
}
@@ -42375,36 +43794,59 @@ cp_parser_omp_clause_from_to (cp_parser *parser, enum omp_clause_code kind,
map ( [map-type-modifier[,] ...] map-kind: variable-list )
map-type-modifier:
- always | close */
+ always | close | mapper ( mapper-name ) */
static tree
-cp_parser_omp_clause_map (cp_parser *parser, tree list)
+cp_parser_omp_clause_map (cp_parser *parser, tree list, enum gomp_map_kind kind)
{
tree nlist, c;
- enum gomp_map_kind kind = GOMP_MAP_TOFROM;
if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
return list;
int pos = 1;
int map_kind_pos = 0;
- while (cp_lexer_peek_nth_token (parser->lexer, pos)->type == CPP_NAME
- || cp_lexer_peek_nth_token (parser->lexer, pos)->keyword == RID_DELETE)
+ int iterator_length = 0;
+ for (;;)
{
- if (cp_lexer_peek_nth_token (parser->lexer, pos + 1)->type == CPP_COLON)
+ cp_token *tok = cp_lexer_peek_nth_token (parser->lexer, pos);
+ if (!(tok->type == CPP_NAME || tok->keyword == RID_DELETE))
+ break;
+
+ cp_token *next_tok = cp_lexer_peek_nth_token (parser->lexer, pos + 1);
+ if (tok->type == CPP_NAME
+ && strcmp (IDENTIFIER_POINTER (tok->u.value), "iterator") == 0
+ && next_tok->type == CPP_OPEN_PAREN)
+ {
+ int n = cp_parser_skip_balanced_tokens (parser, pos + 1);
+ if (n != pos + 1)
+ {
+ iterator_length = n - pos;
+ pos = n - 1;
+ next_tok = cp_lexer_peek_nth_token (parser->lexer, n);
+ }
+ }
+
+ if (next_tok->type == CPP_COLON)
{
map_kind_pos = pos;
break;
}
- if (cp_lexer_peek_nth_token (parser->lexer, pos + 1)->type == CPP_COMMA)
+ if (next_tok->type == CPP_COMMA)
pos++;
+ else if (cp_lexer_peek_nth_token (parser->lexer, pos + 1)->type
+ == CPP_OPEN_PAREN)
+ pos = cp_parser_skip_balanced_tokens (parser, pos + 1);
pos++;
}
bool always_modifier = false;
bool close_modifier = false;
bool present_modifier = false;
+ bool mapper_modifier = false;
+ tree mapper_name = NULL_TREE;
+ tree iterators = NULL_TREE;
for (int pos = 1; pos < map_kind_pos; ++pos)
{
cp_token *tok = cp_lexer_peek_token (parser->lexer);
@@ -42427,6 +43869,7 @@ cp_parser_omp_clause_map (cp_parser *parser, tree list)
return list;
}
always_modifier = true;
+ cp_lexer_consume_token (parser->lexer);
}
else if (strcmp ("close", p) == 0)
{
@@ -42440,6 +43883,71 @@ cp_parser_omp_clause_map (cp_parser *parser, tree list)
return list;
}
close_modifier = true;
+ cp_lexer_consume_token (parser->lexer);
+ }
+ else if (strcmp ("mapper", p) == 0)
+ {
+ cp_lexer_consume_token (parser->lexer);
+
+ matching_parens parens;
+ if (parens.require_open (parser))
+ {
+ if (mapper_modifier)
+ {
+ cp_parser_error (parser, "too many %<mapper%> modifiers");
+ /* Assume it's a well-formed mapper modifier, even if it
+ seems to be in the wrong place. */
+ cp_lexer_consume_token (parser->lexer);
+ parens.require_close (parser);
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/
+ true);
+ return list;
+ }
+
+ tok = cp_lexer_peek_token (parser->lexer);
+ switch (tok->type)
+ {
+ case CPP_NAME:
+ {
+ cp_expr e = cp_parser_identifier (parser);
+ if (e != error_mark_node)
+ mapper_name = e;
+ else
+ goto err;
+ }
+ break;
+
+ case CPP_KEYWORD:
+ if (tok->keyword == RID_DEFAULT)
+ {
+ cp_lexer_consume_token (parser->lexer);
+ break;
+ }
+ /* Fallthrough. */
+
+ default:
+ err:
+ cp_parser_error (parser,
+ "expected identifier or %<default%>");
+ return list;
+ }
+
+ if (!parens.require_close (parser))
+ {
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/
+ true);
+ return list;
+ }
+
+ mapper_modifier = true;
+ pos += 3;
+ }
}
else if (strcmp ("present", p) == 0)
{
@@ -42453,19 +43961,37 @@ cp_parser_omp_clause_map (cp_parser *parser, tree list)
return list;
}
present_modifier = true;
- }
+ cp_lexer_consume_token (parser->lexer);
+ }
+ else if (strcmp ("iterator", p) == 0
+ && cp_lexer_peek_nth_token (parser->lexer, 2)->type
+ == CPP_OPEN_PAREN)
+ {
+ if (iterators)
+ {
+ cp_parser_error (parser, "too many %<iterator%> modifiers");
+ cp_parser_skip_to_closing_parenthesis (parser,
+ /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+ return list;
+ }
+ begin_scope (sk_omp, NULL);
+ iterators = cp_parser_omp_iterators (parser);
+ pos += iterator_length - 1;
+ continue;
+ }
else
{
- cp_parser_error (parser, "%<map%> clause with map-type modifier other"
- " than %<always%>, %<close%> or %<present%>");
+ cp_parser_error (parser, "%<map%> clause with map-type modifier "
+ "other than %<always%>, %<close%>, "
+ "%<iterator%>, %<mapper%> or %<present%>");
cp_parser_skip_to_closing_parenthesis (parser,
/*recovering=*/true,
/*or_comma=*/false,
/*consume_paren=*/true);
return list;
}
-
- cp_lexer_consume_token (parser->lexer);
}
if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)
@@ -42518,11 +44044,47 @@ cp_parser_omp_clause_map (cp_parser *parser, tree list)
legally. */
begin_scope (sk_omp, NULL);
nlist = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_MAP, list,
- NULL, true);
+ NULL, (kind == GOMP_MAP_UNSET
+ ? C_ORT_OMP_DECLARE_MAPPER
+ : C_ORT_OMP), true);
finish_scope ();
+ tree last_new = NULL_TREE;
+
+ if (iterators)
+ {
+ tree block = poplevel (1, 1, 0);
+ if (iterators == error_mark_node)
+ iterators = NULL_TREE;
+ else
+ OMP_ITERATORS_BLOCK (iterators) = block;
+ }
+
for (c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c))
- OMP_CLAUSE_SET_MAP_KIND (c, kind);
+ {
+ OMP_CLAUSE_SET_MAP_KIND (c, kind);
+ OMP_CLAUSE_ITERATORS (c) = iterators;
+ last_new = c;
+ }
+
+ if (mapper_name)
+ {
+ tree name = build_omp_clause (input_location, OMP_CLAUSE_MAP);
+ OMP_CLAUSE_SET_MAP_KIND (name, GOMP_MAP_PUSH_MAPPER_NAME);
+ OMP_CLAUSE_DECL (name) = mapper_name;
+ if (iterators)
+ OMP_CLAUSE_ITERATORS (name) = iterators;
+ OMP_CLAUSE_CHAIN (name) = nlist;
+ nlist = name;
+
+ gcc_assert (last_new);
+
+ name = build_omp_clause (input_location, OMP_CLAUSE_MAP);
+ OMP_CLAUSE_SET_MAP_KIND (name, GOMP_MAP_POP_MAPPER_NAME);
+ OMP_CLAUSE_DECL (name) = null_pointer_node;
+ OMP_CLAUSE_CHAIN (name) = OMP_CLAUSE_CHAIN (last_new);
+ OMP_CLAUSE_CHAIN (last_new) = name;
+ }
return nlist;
}
@@ -43180,7 +44742,7 @@ cp_parser_omp_clause_init (cp_parser *parser, tree list)
"missing required %<target%> and/or %<targetsync%> modifier");
tree nl = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_INIT, list,
- NULL, false);
+ NULL);
for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
{
TREE_ADDRESSABLE (OMP_CLAUSE_DECL (c)) = 1;
@@ -43423,7 +44985,7 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
case PRAGMA_OACC_CLAUSE_REDUCTION:
clauses
= cp_parser_omp_clause_reduction (parser, OMP_CLAUSE_REDUCTION,
- false, clauses);
+ C_ORT_ACC, clauses);
c_name = "reduction";
break;
case PRAGMA_OACC_CLAUSE_SELF:
@@ -43621,7 +45183,7 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
case PRAGMA_OMP_CLAUSE_IN_REDUCTION:
clauses
= cp_parser_omp_clause_reduction (parser, OMP_CLAUSE_IN_REDUCTION,
- true, clauses);
+ C_ORT_OMP, clauses);
c_name = "in_reduction";
break;
case PRAGMA_OMP_CLAUSE_INDIRECT:
@@ -43676,7 +45238,7 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
case PRAGMA_OMP_CLAUSE_REDUCTION:
clauses
= cp_parser_omp_clause_reduction (parser, OMP_CLAUSE_REDUCTION,
- true, clauses);
+ C_ORT_OMP, clauses);
c_name = "reduction";
break;
case PRAGMA_OMP_CLAUSE_SCHEDULE:
@@ -43693,7 +45255,7 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
clauses
= cp_parser_omp_clause_reduction (parser,
OMP_CLAUSE_TASK_REDUCTION,
- true, clauses);
+ C_ORT_OMP, clauses);
c_name = "task_reduction";
break;
case PRAGMA_OMP_CLAUSE_UNTIED:
@@ -43796,6 +45358,10 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
clauses = cp_parser_omp_clause_allocate (parser, clauses);
c_name = "allocate";
break;
+ case PRAGMA_OMP_CLAUSE_USES_ALLOCATORS:
+ clauses = cp_parser_omp_clause_uses_allocators (parser, clauses);
+ c_name = "uses_allocators";
+ break;
case PRAGMA_OMP_CLAUSE_LINEAR:
{
bool declare_simd = false;
@@ -43850,7 +45416,7 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
c_name = "detach";
break;
case PRAGMA_OMP_CLAUSE_MAP:
- clauses = cp_parser_omp_clause_map (parser, clauses);
+ clauses = cp_parser_omp_clause_map (parser, clauses, GOMP_MAP_TOFROM);
c_name = "map";
break;
case PRAGMA_OMP_CLAUSE_DEVICE:
@@ -44011,11 +45577,149 @@ cp_parser_omp_structured_block (cp_parser *parser, bool *if_p)
static void
cp_parser_omp_allocate (cp_parser *parser, cp_token *pragma_tok)
{
- tree allocator = NULL_TREE;
- tree alignment = NULL_TREE;
- location_t loc = pragma_tok->location;
- tree nl = cp_parser_omp_var_list (parser, OMP_CLAUSE_ALLOCATE, NULL_TREE);
+ /* If there were errors nl might be NULL_TREE, we need to handle this case
+ where necessary to diagnose as much as possible. */
+ tree nl = cp_parser_omp_var_list (parser, OMP_CLAUSE_ERROR, NULL_TREE);
+ /* Reverse the chain so diagnostics are in forward order. */
+ nl = nreverse (nl);
+ /* The var declared first in this function of the variables passed into the
+ allocate directive, this gets assigned in the following loop. We can't
+ assign nl's decl to it, because it might have an error, and nl itself
+ might be NULL_TREE.
+
+ We use this to simplify checking the allocator clause's expr later. */
+ tree f_var = NULL_TREE;
+ {
+ /* The head might have an error and need to be replaced. */
+ tree *chain = &nl;
+ for (tree node = nl; node != NULL_TREE; node = TREE_CHAIN (node))
+ {
+ tree var = TREE_PURPOSE (node);
+ /* Do this before duplicates are diagnosed, parms are never valid so we
+ don't want to diagnose duplicate uses of them. */
+ if (TREE_CODE (var) == PARM_DECL)
+ {
+ auto_diagnostic_group d;
+ error_at (EXPR_LOCATION (TREE_VALUE (node)),
+ "function parameter %qD may not appear as list item in "
+ "an %<allocate%> directive", var);
+ inform (DECL_SOURCE_LOCATION (var),
+ "parameter %qD declared here", var);
+ /* Remove the node. */
+ *chain = TREE_CHAIN (node);
+ /* There is nothing else to diagnose for a parm. */
+ continue;
+ }
+ /* Diagnose duplicate vars passed to the allocate directive.
+ We could generate fixits for a number of these, but adding the
+ comma token to be removed to the fixit seems difficult.
+ This is O(n^2), we'll have to use a hash table if it ever becomes
+ a problem. */
+ {
+ auto_diagnostic_group d;
+ bool duplicate_entry = false;
+ /* Frontmost non duplicate node. */
+ tree node_prev = node;
+ /* The node we are checking as a potential duplicate. */
+ tree node_current = TREE_CHAIN (node);
+ while (node_current != NULL_TREE)
+ {
+ if (TREE_PURPOSE (node) == TREE_PURPOSE (node_current))
+ {
+ /* If we could just get the location of the comma token a
+ fixit hint would be viable here. */
+ error_at (EXPR_LOCATION (TREE_VALUE (node_current)),
+ "%qD already appeared as list item in this "
+ "directive",
+ TREE_PURPOSE (node));
+ duplicate_entry = true;
+ /* The current node is a duplicate of node, remove it. */
+ TREE_CHAIN (node_prev) = TREE_CHAIN (node_current);
+ }
+ else
+ node_prev = node_current;
+ node_current = TREE_CHAIN (node_current);
+ }
+ if (duplicate_entry)
+ inform (EXPR_LOCATION (TREE_VALUE (node)), "appeared first here");
+ /* Dupes of node doesn't mean we remove it, keep going. */
+ }
+ auto var_is_in_current_scope = [] (tree var)
+ {
+ tree v = current_binding_level->names;
+ for (; v != NULL_TREE; v = DECL_CHAIN (v))
+ if (v == var)
+ return true;
+ return false;
+ };
+ /* If we do this before checking if the var was used in another
+ allocate directive the diagnostic should be more clear if the user
+ intended to shadow another variable. */
+ if (!var_is_in_current_scope (var))
+ {
+ auto_diagnostic_group d;
+ error_at (EXPR_LOCATION (TREE_VALUE (node)),
+ "%<allocate%> directive must be in the same scope as "
+ "%qD", var);
+ inform (DECL_SOURCE_LOCATION (var), "declared here");
+ /* Remove the node. */
+ *chain = TREE_CHAIN (node);
+ continue;
+ }
+
+ tree attr = lookup_attribute ("omp allocate",
+ DECL_ATTRIBUTES (var));
+ if (attr)
+ {
+ auto_diagnostic_group d;
+ error_at (EXPR_LOCATION (TREE_VALUE (node)),
+ "%qD already appeared as list item in an "
+ "%<allocate%> directive", var);
+ /* The loc is stored in the chain member of the tree_list when the
+ directive is complete. Currently, it is only possible for a
+ directive to already be finished if we are parsing a
+ non-template. */
+ tree var_loc = TREE_CODE (TREE_VALUE (attr)) == NOP_EXPR
+ ? TREE_VALUE (attr)
+ : TREE_CHAIN (TREE_VALUE (attr));
+ inform (EXPR_LOCATION (var_loc),
+ "%qD previously appeared here", var);
+ /* Remove the node. */
+ *chain = TREE_CHAIN (node);
+ }
+ else
+ {
+ /* Mark the variable so we can diagnose this during parsing,
+ including before a template is instantiated.
+ This is necessary even if we diagnose errors for this directive
+ so we don't miss diagnosing secondary uses of a variable in
+ later allocate directives. Additionally, we take advantage of
+ this to store the loc the variable was used for diagnostics. */
+ DECL_ATTRIBUTES (var) = tree_cons (get_identifier ("omp allocate"),
+ TREE_VALUE (node),
+ DECL_ATTRIBUTES (var));
+ /* Everything is good, we are keeping this node now,
+ update the current chain pointer. */
+ chain = &TREE_CHAIN (node);
+
+ /* Nodes that contain a parm or a var that is not in this scope
+ were don't make it this far, we can to assign f_var now. */
+ if (f_var == NULL_TREE)
+ f_var = var;
+ /* I am not sure of a better way to do this. Maybe it would be
+ better to walk the declarations in the current scope? The
+ chain is backwards so it would probably be worse. */
+ else if (linemap_location_before_p (line_table,
+ DECL_SOURCE_LOCATION (var),
+ DECL_SOURCE_LOCATION (f_var)))
+ f_var = var;
+ }
+ }
+ }
+ /* cp_parser_assignment_expression wraps in a location by default. */
+ cp_expr allocator = NULL_TREE;
+ cp_expr alignment = NULL_TREE;
do
{
if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)
@@ -44036,70 +45740,202 @@ cp_parser_omp_allocate (cp_parser *parser, cp_token *pragma_tok)
}
if (!parens.require_open (parser))
break;
- tree expr = cp_parser_assignment_expression (parser);
+ cp_expr expr = cp_parser_assignment_expression (parser);
if (p[2] == 'i' && alignment)
{
error_at (cloc, "too many %qs clauses", "align");
break;
}
else if (p[2] == 'i')
- {
- if (expr != error_mark_node)
- alignment = expr;
- /* FIXME: Remove when adding check to semantics.cc; cf FIXME below. */
- if (alignment
- && !type_dependent_expression_p (alignment)
- && !INTEGRAL_TYPE_P (TREE_TYPE (alignment)))
- {
- error_at (cloc, "%<align%> clause argument needs to be "
- "positive constant power of two integer "
- "expression");
- alignment = NULL_TREE;
- }
- else if (alignment)
- {
- alignment = mark_rvalue_use (alignment);
- if (!processing_template_decl)
- {
- alignment = maybe_constant_value (alignment);
- if (TREE_CODE (alignment) != INTEGER_CST
- || !tree_fits_uhwi_p (alignment)
- || !integer_pow2p (alignment))
- {
- error_at (cloc, "%<align%> clause argument needs to be "
- "positive constant power of two integer "
- "expression");
- alignment = NULL_TREE;
- }
- }
- }
- }
+ alignment = expr;
else if (allocator)
{
error_at (cloc, "too many %qs clauses", "allocator");
break;
}
else
- {
- if (expr != error_mark_node)
- allocator = expr;
- }
+ allocator = expr;
parens.require_close (parser);
} while (true);
cp_parser_require_pragma_eol (parser, pragma_tok);
- if (allocator || alignment)
- for (tree c = nl; c != NULL_TREE; c = OMP_CLAUSE_CHAIN (c))
- {
- OMP_CLAUSE_ALLOCATE_ALLOCATOR (c) = allocator;
- OMP_CLAUSE_ALLOCATE_ALIGN (c) = alignment;
- }
+ /* Used to carry information into the below lambda through cp_walk_tree. */
+ struct cp_omp_loc_tree
+ {
+ location_t loc;
+ tree first_arg;
+ tree arg_list;
+ };
+ /* Check whether the expression used in the allocator clause is declared or
+ modified between the variable declaration and its allocate directive.
+
+ We could consider moving everything here to finish_omp_allocate but it's
+ convenient to keep it here until we opt to tackle the issues noted in the
+ comments below. */
+ auto check_omp_allocate_allocator_r = [] (tree *tp, int *, void *data)
+ {
+ /* We bail on the first error we find. Alternatively we could diagnose
+ as much as we can, keeping track of vars we have already diagnosed to
+ prevent duplicates errors, but it doesn't seem worth doing. */
+ tree var_in_expr = *tp;
+ if (!VAR_P (var_in_expr))
+ return NULL_TREE;
+
+ location_t alloc_expr_loc = ((cp_omp_loc_tree *) data)->loc;
+ tree first_declared_arg = ((cp_omp_loc_tree *) data)->first_arg;
+ tree all_args = ((cp_omp_loc_tree *) data)->arg_list;
+
+ /* For obvious reasons, you can't use a var used in an allocate directive
+ as part of the allocator clause's expression. We don't have to check
+ this specifically for each var because usage of a var declared after
+ the first declared var will also be rejected, but this would result in
+ a misleading diagnostic. It doesn't cost use much to do this check
+ explicitly for each arg so do it this way. */
+ for (tree arg = all_args; arg; arg = TREE_CHAIN (arg))
+ if (var_in_expr == TREE_PURPOSE (arg))
+ {
+ tree arg_decl = TREE_PURPOSE (arg);
+ /* It would be nice if there were an easy way to put the caret on
+ the use of the variable, but there doesn't appear to be. */
+ auto_diagnostic_group d;
+ error_at (alloc_expr_loc,
+ "variable %qD used in this %<allocate%> directive must "
+ "not be used in its %<allocator%> clause", arg_decl);
+ inform (DECL_SOURCE_LOCATION (arg_decl), "declared here");
+ inform (EXPR_LOCATION (TREE_VALUE (arg)),
+ "used in allocate directive here");
+ return error_mark_node;
+ }
+ /* We can't rely on locations to determine declaration order because var
+ decls that are implicit lambda captures have their location set to
+ their first use. This is probably a bug but relying on source
+ location for this seems incorrect anyway. */
+ for (tree v = current_binding_level->names; v; v = DECL_CHAIN (v))
+ {
+ /* If we find first_declared_arg before var_in_expr it must have
+ been declared before it. */
+ if (v == first_declared_arg)
+ break;
+ if (v == var_in_expr)
+ {
+ /* Captures don't make it here so we should be able to rely on
+ the DECL_SOURCE_LOCATION for var_in_expr. */
+ auto_diagnostic_group d;
+ error_at (alloc_expr_loc,
+ "variable %qD used in the %<allocator%> clause "
+ "must be declared before %qD",
+ var_in_expr, first_declared_arg);
+ inform (DECL_SOURCE_LOCATION (var_in_expr), "declared here");
+ inform (DECL_SOURCE_LOCATION (first_declared_arg),
+ "to be allocated variable declared here");
+ return error_mark_node;
+ }
+ }
+
+ /* It's super easy to hide mutations to variables used in the alloc
+ clause with our current error checking, the following is not currently
+ diagnosed.
+
+ void f() {
+ omp_allocator_handle_t alloc = omp_default_mem_alloc;
+ int a;
+ int hide_mutation = alloc = omp_large_cap_mem_alloc;
+ #pragma omp allocate(a) allocator(alloc)
+ }
+
+ But this is fairly representative of what we currently have in the C
+ front end, which has the same problem, so a problem for the future.
+
+ Alongside incomplete error diagnostics, there are also cases that
+ can't be diagnosed or warned until template instantiation.
+
+ template<typename T>
+ void f(T arg) {
+ omp_allocator_handle_t alloc = omp_default_mem_alloc;
+ int a;
+ foobar(arg, alloc);
+ #pragma omp allocate(a) allocator(alloc)
+ }
+ It's impossible to know what the signature of foobar is until overload
+ resolution completes. To diagnose (or at least warn) for these edge
+ cases, this section should be moved to finish_omp_allocate instead of
+ here. We can add full diagnostics to mutations of variables in the
+ allocator clause once we do that. */
+ gcc_assert (cur_stmt_list
+ && TREE_CODE (cur_stmt_list) == STATEMENT_LIST);
+ for (tree_stmt_iterator stmt_it = tsi_last (cur_stmt_list);
+ !tsi_end_p (stmt_it);
+ --stmt_it)
+ {
+ tree stmt = *stmt_it;
+ /* Don't check anything preceding first_declared_arg's decl. */
+ if (TREE_CODE (stmt) == DECL_EXPR
+ && DECL_EXPR_DECL (stmt) == first_declared_arg)
+ break;
+ /* Due to differences in the C++ AST, I don't believe this catches
+ any cases right now.*/
+ if (TREE_CODE (stmt) == MODIFY_EXPR
+ && TREE_OPERAND (stmt, 0) == var_in_expr)
+ {
+ auto_diagnostic_group d;
+ error_at (alloc_expr_loc,
+ "variable %qD used in the %<allocator%> clause must "
+ "not be modified between declaration of %qD and its "
+ "%<allocate%> directive",
+ var_in_expr, first_declared_arg);
+ inform (EXPR_LOCATION (stmt), "modified here");
+ inform (DECL_SOURCE_LOCATION (first_declared_arg),
+ "to be allocated variable declared here");
+ return error_mark_node;
+ }
+ }
+ return NULL_TREE;
+ };
+ /* This diagnostic is meaningless if we have no valid args. */
+ if (allocator != NULL_TREE && nl != NULL_TREE)
+ {
+ gcc_assert (f_var != NULL_TREE);
+ /* Declarations and mutations must happen before any of the vars are
+ declared. If this is satisfied for the first declaration, it will be
+ satisfied for all of them, so just check the first. */
+ cp_omp_loc_tree data = {allocator.get_location (), f_var, nl};
+ /* We can't take the address of an rvalue, so we need to do this. */
+ tree a = allocator.get_value ();
+ if (cp_walk_tree (&a, check_omp_allocate_allocator_r, &data, NULL))
+ allocator = cp_expr (error_mark_node, UNKNOWN_LOCATION);
+ }
+ /* I couldn't find a function that already does this, might be best to
+ add it instead of having it here.
+ Some codes, such as template parameters, don't get wrapped despite not
+ being able to carry a location. We need a location to issue correct
+ diagnostics in finish_omp_allocate. */
+ auto maybe_force_wrap_with_location = [](cp_expr expr_with_loc) -> tree
+ {
+ tree expr = expr_with_loc.get_value ();
+ if (!expr || error_operand_p (expr))
+ return expr;
+ /* In most situations, expr will already have been wrapped,
+ we don't need to do anything if that's the case. */
+ if (CAN_HAVE_LOCATION_P (expr))
+ return expr;
- /* FIXME: When implementing properly, delete the align/allocate expr error
- check above and add one in semantics.cc (to properly handle templates).
- Base this on the allocator/align modifiers check for the 'allocate' clause
- in semantics.cc's finish_omp_clauses. */
- sorry_at (loc, "%<#pragma omp allocate%> not yet supported");
+ location_t expr_loc = expr_with_loc.get_location ();
+ /* Copied from tree.cc:maybe_wrap_with_location. */
+ tree_code code
+ = (((CONSTANT_CLASS_P (expr) && TREE_CODE (expr) != STRING_CST)
+ || (TREE_CODE (expr) == CONST_DECL && !TREE_STATIC (expr)))
+ ? NON_LVALUE_EXPR : VIEW_CONVERT_EXPR);
+ tree wrapper = build1_loc (expr_loc, code, TREE_TYPE (expr), expr);
+ /* Mark this node as being a wrapper. */
+ EXPR_LOCATION_WRAPPER_P (wrapper) = 1;
+ return wrapper;
+ };
+ /* We can still diagnose some things about allocator/alignment even if nl
+ is empty. */
+ finish_omp_allocate (pragma_tok->location,
+ nl,
+ maybe_force_wrap_with_location (allocator),
+ maybe_force_wrap_with_location (alignment));
}
/* OpenMP 2.5:
@@ -48332,7 +50168,10 @@ cp_parser_omp_target_data (cp_parser *parser, cp_token *pragma_tok, bool *if_p)
tree clauses
= cp_parser_omp_all_clauses (parser, OMP_TARGET_DATA_CLAUSE_MASK,
- "#pragma omp target data", pragma_tok);
+ "#pragma omp target data", pragma_tok, false);
+ if (!processing_template_decl)
+ clauses = c_omp_instantiate_mappers (clauses, C_ORT_OMP);
+ clauses = finish_omp_clauses (clauses, C_ORT_OMP);
c_omp_adjust_map_clauses (clauses, false);
int map_seen = 0;
for (tree *pc = &clauses; *pc;)
@@ -48447,7 +50286,11 @@ cp_parser_omp_target_enter_data (cp_parser *parser, cp_token *pragma_tok,
tree clauses
= cp_parser_omp_all_clauses (parser, OMP_TARGET_ENTER_DATA_CLAUSE_MASK,
- "#pragma omp target enter data", pragma_tok);
+ "#pragma omp target enter data", pragma_tok,
+ false);
+ if (!processing_template_decl)
+ clauses = c_omp_instantiate_mappers (clauses, C_ORT_OMP);
+ clauses = finish_omp_clauses (clauses, C_ORT_OMP);
c_omp_adjust_map_clauses (clauses, false);
int map_seen = 0;
for (tree *pc = &clauses; *pc;)
@@ -48564,6 +50407,8 @@ cp_parser_omp_target_exit_data (cp_parser *parser, cp_token *pragma_tok,
= cp_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
"#pragma omp target exit data", pragma_tok,
false);
+ if (!processing_template_decl)
+ clauses = c_omp_instantiate_mappers (clauses, C_ORT_OMP_EXIT_DATA);
clauses = finish_omp_clauses (clauses, C_ORT_OMP_EXIT_DATA);
c_omp_adjust_map_clauses (clauses, false);
int map_seen = 0;
@@ -48657,9 +50502,43 @@ cp_parser_omp_target_update (cp_parser *parser, cp_token *pragma_tok,
tree clauses
= cp_parser_omp_all_clauses (parser, OMP_TARGET_UPDATE_CLAUSE_MASK,
- "#pragma omp target update", pragma_tok);
- if (omp_find_clause (clauses, OMP_CLAUSE_TO) == NULL_TREE
- && omp_find_clause (clauses, OMP_CLAUSE_FROM) == NULL_TREE)
+ "#pragma omp target update", pragma_tok,
+ false);
+ if (!processing_template_decl)
+ clauses = c_omp_instantiate_mappers (clauses, C_ORT_OMP_UPDATE);
+ clauses = finish_omp_clauses (clauses, C_ORT_OMP_UPDATE);
+ bool to_clause = false, from_clause = false;
+ for (tree c = clauses;
+ c && !to_clause && !from_clause;
+ c = OMP_CLAUSE_CHAIN (c))
+ {
+ switch (OMP_CLAUSE_CODE (c))
+ {
+ case OMP_CLAUSE_TO:
+ to_clause = true;
+ break;
+ case OMP_CLAUSE_FROM:
+ from_clause = true;
+ break;
+ case OMP_CLAUSE_MAP:
+ switch (OMP_CLAUSE_MAP_KIND (c))
+ {
+ case GOMP_MAP_TO_GRID:
+ to_clause = true;
+ break;
+ case GOMP_MAP_FROM_GRID:
+ from_clause = true;
+ break;
+ default:
+ ;
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ if (!to_clause && !from_clause)
{
error_at (pragma_tok->location,
"%<#pragma omp target update%> must contain at least one "
@@ -48696,7 +50575,8 @@ cp_parser_omp_target_update (cp_parser *parser, cp_token *pragma_tok,
| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IN_REDUCTION) \
| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_THREAD_LIMIT) \
| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR)\
- | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR))
+ | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR)\
+ | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_USES_ALLOCATORS))
static bool
cp_parser_omp_target (cp_parser *parser, cp_token *pragma_tok,
@@ -48856,6 +50736,8 @@ cp_parser_omp_target (cp_parser *parser, cp_token *pragma_tok,
OMP_CLAUSE_CHAIN (nc) = OMP_CLAUSE_CHAIN (c);
OMP_CLAUSE_CHAIN (c) = nc;
}
+ if (!processing_template_decl)
+ clauses = c_omp_instantiate_mappers (clauses, C_ORT_OMP_TARGET);
clauses = finish_omp_clauses (clauses, C_ORT_OMP_TARGET);
c_omp_adjust_map_clauses (clauses, true);
@@ -50332,7 +52214,7 @@ cp_parser_omp_dispatch (cp_parser *parser, cp_token *pragma_tok)
static tree
cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
- tree attrs, tree parms)
+ tree attrs)
{
matching_parens parens;
if (!parens.require_open (parser))
@@ -50392,13 +52274,27 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
tree append_args_tree = NULL_TREE;
tree append_args_last = NULL_TREE;
- vec<tree> adjust_args_list = vNULL;
bool has_match = false, has_adjust_args = false;
location_t adjust_args_loc = UNKNOWN_LOCATION;
location_t append_args_loc = UNKNOWN_LOCATION;
- tree need_device_ptr_list = NULL_TREE;
+
tree ctx = NULL_TREE;
+ tree adjust_args_list = NULL_TREE;
+ auto append_adjust_args
+ = [chain = &adjust_args_list] (tree list, tree clause_modifier) mutable
+ {
+ gcc_assert (chain && *chain == NULL_TREE);
+ *chain = list;
+ /* Just stick them all together in one list and process them all
+ at once later. */
+ for (tree node = list; node; node = TREE_CHAIN (node))
+ {
+ TREE_PURPOSE (node) = clause_modifier;
+ chain = &TREE_CHAIN (node);
+ }
+ };
+
do
{
if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)
@@ -50452,6 +52348,20 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
goto fail;
ctx = omp_check_context_selector (match_loc, ctx,
OMP_CTX_DECLARE_VARIANT);
+
+ /* The OpenMP spec says the merging rules for enclosing
+ "begin declare variant" contexts apply to "declare variant
+ directives" -- the term it uses to refer to both directive
+ forms. */
+ if (ctx != error_mark_node
+ && !vec_safe_is_empty (scope_chain->omp_declare_variant_attribute))
+ {
+ cp_omp_declare_variant_attr a
+ = scope_chain->omp_declare_variant_attribute->last ();
+ tree outer_ctx = a.selector;
+ ctx = omp_merge_context_selectors (match_loc, outer_ctx, ctx,
+ OMP_CTX_DECLARE_VARIANT);
+ }
if (ctx != error_mark_node && variant != error_mark_node)
{
tree match_loc_node
@@ -50479,78 +52389,49 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
{
const char *p = IDENTIFIER_POINTER (adjust_op_tok->u.value);
if (strcmp (p, "need_device_ptr") == 0
+ || strcmp (p, "need_device_addr") == 0
|| strcmp (p, "nothing") == 0)
{
cp_lexer_consume_token (parser->lexer); // need_device_ptr
cp_lexer_consume_token (parser->lexer); // :
- tree arg;
- tree list
- = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_ERROR,
- NULL_TREE, NULL);
-
- for (tree c = list; c != NULL_TREE; c = TREE_CHAIN (c))
+ tree list = cp_parser_omp_parm_list (parser);
+ if (list && list != error_mark_node)
+ /* It should be fine to just use the identifier node. */
+ append_adjust_args (list, adjust_op_tok->u.value);
+ else
{
- tree decl = TREE_PURPOSE (c);
- location_t arg_loc = EXPR_LOCATION (TREE_VALUE (c));
- int idx;
- for (arg = parms, idx = 0; arg != NULL;
- arg = TREE_CHAIN (arg), idx++)
- if (TREE_VALUE (arg) == decl)
- break;
- if (arg == NULL_TREE)
- {
- error_at (arg_loc, "%qD is not a function argument",
- decl);
- continue;
- }
- arg = TREE_VALUE (arg);
- if (adjust_args_list.contains (arg))
- {
- error_at (arg_loc, "%qD is specified more than once",
- decl);
- continue;
- }
- if (strcmp (p, "need_device_ptr") == 0)
- {
- bool is_ptr_or_template
- = TEMPLATE_PARM_P (TREE_TYPE (arg))
- || POINTER_TYPE_P (TREE_TYPE (arg));
- if (!is_ptr_or_template)
- {
- error_at (arg_loc, "%qD is not a C pointer",
- decl);
- continue;
- }
- }
- adjust_args_list.safe_push (arg);
- if (strcmp (p, "need_device_ptr") == 0)
- {
- need_device_ptr_list = chainon (
- need_device_ptr_list,
- build_tree_list (
- NULL_TREE,
- build_int_cst (
- integer_type_node,
- idx))); // Store 0-based argument index,
- // as in gimplify_call_expr
- }
+ /* Do we need a specific diagnostic here?
+ I don't like failing here, we should be skipping to
+ a close paren and continuing. */
+ goto fail;
}
}
else
{
error_at (adjust_op_tok->location,
- "expected %<nothing%> or %<need_device_ptr%>");
+ "expected %<nothing%>, %<need_device_ptr%> or "
+ "%<need_device_addr%>");
+ /* We should be trying to recover here instead of immediately
+ failing, skipping to close paren and continuing. */
goto fail;
}
}
else
{
+ /* We should be trying to recover here instead of immediately
+ failing, skipping to close paren and continuing. */
error_at (adjust_op_tok->location,
- "expected %<nothing%> or %<need_device_ptr%> followed "
- "by %<:%>");
+ "expected %<nothing%>, %<need_device_ptr%> or "
+ "%<need_device_addr%> followed by %<:%>");
goto fail;
}
+ /* cp_parser_omp_var_list_no_open used to handle this, we don't use
+ it anymore though. */
+ if (!parens.require_close (parser))
+ /* We should be trying to recover here instead of immediately
+ failing, I'm not sure what we skip to though. */
+ goto fail;
}
else if (ccode == append_args)
{
@@ -50614,14 +52495,12 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
cp_lexer_consume_token (parser->lexer); // ','
}
while (true);
- int nbase_args = 0;
- for (tree t = parms;
- t && TREE_VALUE (t) != void_type_node; t = TREE_CHAIN (t))
- nbase_args++;
- /* Store as purpose = arg number after which to append
- and value = list of interop items. */
- append_args_tree = build_tree_list (build_int_cst (integer_type_node,
- nbase_args),
+ /* This is where the number of args used to be inserted, it still
+ gets put here by omp_declare_variant_finalize_one once we know how
+ many parameters there are. Ideally we should refactor the way we
+ pass this data around, once we do that we can remove this bit from
+ here. Until then, leave it be. */
+ append_args_tree = build_tree_list (NULL_TREE,
append_args_tree);
}
} while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL));
@@ -50651,11 +52530,17 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
// We might not have a DECL for the variant yet. So we store the
// need_device_ptr list in the base function attribute, after loc
// nodes.
- tree t = build_tree_list (need_device_ptr_list,
- NULL_TREE /* need_device_addr */);
+ tree debug_idxs_node
+ = CHECKING_P ? get_identifier ("omp adjust args idxs")
+ : NULL_TREE;
+ tree t = build_tree_list (debug_idxs_node,
+ adjust_args_list);
TREE_CHAIN (t) = append_args_tree;
+ tree debug_tail_node
+ = CHECKING_P ? get_identifier ("omp variant clauses temp")
+ : NULL_TREE;
TREE_VALUE (attrs) = chainon (TREE_VALUE (attrs),
- build_tree_list ( NULL_TREE, t));
+ build_tree_list (debug_tail_node, t));
}
}
@@ -50668,8 +52553,7 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
been parsed, and put that into "omp declare simd" attribute. */
static tree
-cp_parser_late_parsing_omp_declare_simd (cp_parser *parser, tree attrs,
- tree parms)
+cp_parser_late_parsing_omp_declare_simd (cp_parser *parser, tree attrs)
{
struct cp_token_cache *ce;
cp_omp_declare_simd_data *data = parser->omp_declare_simd;
@@ -50713,7 +52597,7 @@ cp_parser_late_parsing_omp_declare_simd (cp_parser *parser, tree attrs,
{
gcc_assert (strcmp (kind, "variant") == 0);
attrs
- = cp_finish_omp_declare_variant (parser, pragma_tok, attrs, parms);
+ = cp_finish_omp_declare_variant (parser, pragma_tok, attrs);
}
cp_parser_pop_lexer (parser);
}
@@ -50845,7 +52729,7 @@ cp_parser_late_parsing_omp_declare_simd (cp_parser *parser, tree attrs,
{
gcc_assert (strcmp (kind, "variant") == 0);
attrs = cp_finish_omp_declare_variant (parser, pragma_tok,
- attrs, parms);
+ attrs);
}
gcc_assert (parser->lexer != lexer);
vec_safe_truncate (lexer->buffer, 0);
@@ -51113,7 +52997,9 @@ cp_parser_omp_declare_target (cp_parser *parser, cp_token *pragma_tok)
/* OpenMP 5.1
# pragma omp begin assumes clauses[optseq] new-line
- # pragma omp begin declare target clauses[optseq] new-line */
+ # pragma omp begin declare target clauses[optseq] new-line
+
+ # pragma omp begin declare variant (match context-selector) new-line */
#define OMP_BEGIN_DECLARE_TARGET_CLAUSE_MASK \
( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEVICE_TYPE) \
@@ -51159,9 +53045,73 @@ cp_parser_omp_begin (cp_parser *parser, cp_token *pragma_tok)
= { in_omp_attribute_pragma, device_type, indirect };
vec_safe_push (scope_chain->omp_declare_target_attribute, a);
}
+ else if (strcmp (p, "variant") == 0)
+ {
+ cp_lexer_consume_token (parser->lexer);
+ const char *clause = "";
+ matching_parens parens;
+ location_t match_loc = cp_lexer_peek_token (parser->lexer)->location;
+ if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
+ {
+ tree id = cp_lexer_peek_token (parser->lexer)->u.value;
+ clause = IDENTIFIER_POINTER (id);
+ }
+ if (strcmp (clause, "match") != 0)
+ {
+ cp_parser_error (parser, "expected %<match%>");
+ cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+ return;
+ }
+
+ cp_lexer_consume_token (parser->lexer);
+
+ if (!parens.require_open (parser))
+ {
+ cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+ return;
+ }
+
+ tree ctx = cp_parser_omp_context_selector_specification (parser,
+ true);
+ if (ctx != error_mark_node)
+ ctx = omp_check_context_selector (match_loc, ctx,
+ OMP_CTX_BEGIN_DECLARE_VARIANT);
+
+ if (ctx != error_mark_node
+ && !vec_safe_is_empty (scope_chain->omp_declare_variant_attribute))
+ {
+ cp_omp_declare_variant_attr a
+ = scope_chain->omp_declare_variant_attribute->last ();
+ tree outer_ctx = a.selector;
+ ctx = omp_merge_context_selectors (match_loc, outer_ctx, ctx,
+ OMP_CTX_BEGIN_DECLARE_VARIANT);
+ }
+
+ if (ctx == error_mark_node
+ || !omp_context_selector_matches (ctx, NULL_TREE, false, true))
+ {
+ /* The context is either invalid or cannot possibly match.
+ In the latter case the spec says all code in the begin/end
+ sequence will be elided. In the former case we'll get bogus
+ errors from trying to parse it without a valid context to
+ use for name-mangling, so elide that too. */
+ cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+ cp_parser_skip_to_pragma_omp_end_declare_variant (parser);
+ return;
+ }
+ else
+ {
+ cp_omp_declare_variant_attr a
+ = { parser->lexer->in_omp_attribute_pragma, ctx };
+ vec_safe_push (scope_chain->omp_declare_variant_attribute, a);
+ }
+
+ parens.require_close (parser);
+ cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+ }
else
{
- cp_parser_error (parser, "expected %<target%>");
+ cp_parser_error (parser, "expected %<target%> or %<variant%>");
cp_parser_skip_to_pragma_eol (parser, pragma_tok);
}
}
@@ -51174,7 +53124,8 @@ cp_parser_omp_begin (cp_parser *parser, cp_token *pragma_tok)
}
else
{
- cp_parser_error (parser, "expected %<declare target%> or %<assumes%>");
+ cp_parser_error (parser, "expected %<declare target%>, "
+ "%<declare variant%>, or %<assumes%>");
cp_parser_skip_to_pragma_eol (parser, pragma_tok);
}
}
@@ -51183,7 +53134,8 @@ cp_parser_omp_begin (cp_parser *parser, cp_token *pragma_tok)
# pragma omp end declare target new-line
OpenMP 5.1:
- # pragma omp end assumes new-line */
+ # pragma omp end assumes new-line
+ # pragma omp end declare variant new-line */
static void
cp_parser_omp_end (cp_parser *parser, cp_token *pragma_tok)
@@ -51205,41 +53157,70 @@ cp_parser_omp_end (cp_parser *parser, cp_token *pragma_tok)
p = IDENTIFIER_POINTER (id);
}
if (strcmp (p, "target") == 0)
- cp_lexer_consume_token (parser->lexer);
- else
{
- cp_parser_error (parser, "expected %<target%>");
- cp_parser_skip_to_pragma_eol (parser, pragma_tok);
- return;
+ cp_lexer_consume_token (parser->lexer);
+ cp_parser_require_pragma_eol (parser, pragma_tok);
+ if (!vec_safe_length (scope_chain->omp_declare_target_attribute))
+ error_at (pragma_tok->location,
+ "%<#pragma omp end declare target%> without "
+ "corresponding %<#pragma omp declare target%> or "
+ "%<#pragma omp begin declare target%>");
+ else
+ {
+ cp_omp_declare_target_attr
+ a = scope_chain->omp_declare_target_attribute->pop ();
+ if (a.attr_syntax != in_omp_attribute_pragma)
+ {
+ if (a.attr_syntax)
+ error_at (pragma_tok->location,
+ "%qs in attribute syntax terminated "
+ "with %qs in pragma syntax",
+ a.device_type >= 0 ? "begin declare target"
+ : "declare target",
+ "end declare target");
+ else
+ error_at (pragma_tok->location,
+ "%qs in pragma syntax terminated "
+ "with %qs in attribute syntax",
+ a.device_type >= 0 ? "begin declare target"
+ : "declare target",
+ "end declare target");
+ }
+ }
}
- cp_parser_require_pragma_eol (parser, pragma_tok);
- if (!vec_safe_length (scope_chain->omp_declare_target_attribute))
- error_at (pragma_tok->location,
- "%<#pragma omp end declare target%> without corresponding "
- "%<#pragma omp declare target%> or "
- "%<#pragma omp begin declare target%>");
- else
+ else if (strcmp (p, "variant") == 0)
{
- cp_omp_declare_target_attr
- a = scope_chain->omp_declare_target_attribute->pop ();
- if (a.attr_syntax != in_omp_attribute_pragma)
+ cp_lexer_consume_token (parser->lexer);
+ cp_parser_require_pragma_eol (parser, pragma_tok);
+ if (!vec_safe_length (scope_chain->omp_declare_variant_attribute))
+ error_at (pragma_tok->location,
+ "%<#pragma omp end declare variant%> without "
+ "corresponding %<#pragma omp begin declare variant%>");
+ else
{
- if (a.attr_syntax)
- error_at (pragma_tok->location,
- "%qs in attribute syntax terminated "
- "with %qs in pragma syntax",
- a.device_type >= 0 ? "begin declare target"
- : "declare target",
- "end declare target");
- else
- error_at (pragma_tok->location,
- "%qs in pragma syntax terminated "
- "with %qs in attribute syntax",
- a.device_type >= 0 ? "begin declare target"
- : "declare target",
- "end declare target");
+ cp_omp_declare_variant_attr
+ a = scope_chain->omp_declare_variant_attribute->pop ();
+ if (a.attr_syntax != in_omp_attribute_pragma)
+ {
+ if (a.attr_syntax)
+ error_at (pragma_tok->location,
+ "%<begin declare variant%> in attribute syntax "
+ "terminated with %<end declare variant%> in "
+ "pragma syntax");
+ else
+ error_at (pragma_tok->location,
+ "%<begin declare variant%> in pragma syntax "
+ "terminated with %<end declare variant%> in "
+ "attribute syntax");
+ }
}
}
+ else
+ {
+ cp_parser_error (parser, "expected %<target%>");
+ cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+ return;
+ }
}
else if (strcmp (p, "assumes") == 0)
{
@@ -52136,6 +54117,172 @@ cp_parser_omp_declare_reduction (cp_parser *parser, cp_token *pragma_tok,
obstack_free (&declarator_obstack, p);
}
+/* OpenMP 5.0
+ #pragma omp declare mapper([mapper-identifier:]type var) \
+ [clause[[,] clause] ... ] new-line */
+
+static void
+cp_parser_omp_declare_mapper (cp_parser *parser, cp_token *pragma_tok,
+ enum pragma_context)
+{
+ cp_token *token = NULL;
+ tree type = NULL_TREE, vardecl = NULL_TREE, block = NULL_TREE;
+ bool block_scope = false;
+ /* Don't create location wrapper nodes within "declare mapper"
+ directives. */
+ auto_suppress_location_wrappers sentinel;
+ tree mapper_name = NULL_TREE;
+ tree mapper_id, id, placeholder, mapper, maplist = NULL_TREE;
+
+ if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+ goto fail;
+
+ if (current_function_decl)
+ block_scope = true;
+
+ token = cp_lexer_peek_token (parser->lexer);
+
+ if (cp_lexer_nth_token_is (parser->lexer, 2, CPP_COLON))
+ {
+ switch (token->type)
+ {
+ case CPP_NAME:
+ {
+ cp_expr e = cp_parser_identifier (parser);
+ if (e != error_mark_node)
+ mapper_name = e;
+ else
+ goto fail;
+ }
+ break;
+
+ case CPP_KEYWORD:
+ if (token->keyword == RID_DEFAULT)
+ {
+ mapper_name = NULL_TREE;
+ cp_lexer_consume_token (parser->lexer);
+ break;
+ }
+ /* Fallthrough. */
+
+ default:
+ cp_parser_error (parser, "expected identifier or %<default%>");
+ }
+
+ if (!cp_parser_require (parser, CPP_COLON, RT_COLON))
+ goto fail;
+ }
+
+ {
+ const char *saved_message = parser->type_definition_forbidden_message;
+ parser->type_definition_forbidden_message
+ = G_("types may not be defined within %<declare mapper%>");
+ type_id_in_expr_sentinel s (parser);
+ type = cp_parser_type_id (parser);
+ parser->type_definition_forbidden_message = saved_message;
+ }
+
+ if (dependent_type_p (type))
+ mapper_id = omp_mapper_id (mapper_name, NULL_TREE);
+ else
+ mapper_id = omp_mapper_id (mapper_name, type);
+
+ vardecl = build_lang_decl (VAR_DECL, mapper_id, type);
+ DECL_ARTIFICIAL (vardecl) = 1;
+ TREE_STATIC (vardecl) = 1;
+ TREE_PUBLIC (vardecl) = 0;
+ DECL_EXTERNAL (vardecl) = 0;
+ DECL_DECLARED_CONSTEXPR_P (vardecl) = 1;
+ DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (vardecl) = 1;
+ DECL_OMP_DECLARE_MAPPER_P (vardecl) = 1;
+
+ keep_next_level (true);
+ block = begin_omp_structured_block ();
+
+ if (block_scope)
+ DECL_CONTEXT (vardecl) = current_function_decl;
+ else if (current_class_type)
+ DECL_CONTEXT (vardecl) = current_class_type;
+ else
+ DECL_CONTEXT (vardecl) = current_namespace;
+
+ if (processing_template_decl)
+ vardecl = push_template_decl (vardecl);
+
+ id = cp_parser_declarator_id (parser, false);
+
+ if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+ {
+ finish_omp_structured_block (block);
+ goto fail;
+ }
+
+ placeholder = build_lang_decl (VAR_DECL, id, type);
+ DECL_CONTEXT (placeholder) = DECL_CONTEXT (vardecl);
+ if (processing_template_decl)
+ placeholder = push_template_decl (placeholder);
+ pushdecl (placeholder);
+ cp_finish_decl (placeholder, NULL_TREE, 0, NULL_TREE, 0);
+ DECL_ARTIFICIAL (placeholder) = 1;
+ TREE_USED (placeholder) = 1;
+
+ while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL))
+ {
+ pragma_omp_clause c_kind = cp_parser_omp_clause_name (parser);
+ if (c_kind != PRAGMA_OMP_CLAUSE_MAP)
+ {
+ if (c_kind != PRAGMA_OMP_CLAUSE_NONE)
+ cp_parser_error (parser, "unexpected clause");
+ finish_omp_structured_block (block);
+ goto fail;
+ }
+ maplist = cp_parser_omp_clause_map (parser, maplist, GOMP_MAP_UNSET);
+ if (maplist == NULL_TREE)
+ break;
+ }
+
+ if (maplist == NULL_TREE)
+ {
+ cp_parser_error (parser, "missing %<map%> clause");
+ finish_omp_structured_block (block);
+ goto fail;
+ }
+
+ mapper = make_node (OMP_DECLARE_MAPPER);
+ TREE_TYPE (mapper) = type;
+ OMP_DECLARE_MAPPER_ID (mapper) = mapper_name;
+ OMP_DECLARE_MAPPER_DECL (mapper) = placeholder;
+ OMP_DECLARE_MAPPER_CLAUSES (mapper) = maplist;
+
+ finish_omp_structured_block (block);
+
+ DECL_INITIAL (vardecl) = mapper;
+
+ if (current_class_type)
+ {
+ if (processing_template_decl)
+ {
+ retrofit_lang_decl (vardecl);
+ SET_DECL_VAR_DECLARED_INLINE_P (vardecl);
+ }
+ finish_static_data_member_decl (vardecl, mapper,
+ /*init_const_expr_p=*/true, NULL_TREE, 0);
+ finish_member_declaration (vardecl);
+ }
+ else if (processing_template_decl && block_scope)
+ add_decl_expr (vardecl);
+ else
+ pushdecl (vardecl);
+
+ cp_check_omp_declare_mapper (vardecl);
+
+ cp_parser_require_pragma_eol (parser, pragma_tok);
+ return;
+
+fail:
+ cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+}
+
/* OpenMP 4.0
#pragma omp declare simd declare-simd-clauses[optseq] new-line
#pragma omp declare reduction (reduction-id : typename-list : expression) \
@@ -52180,6 +54327,12 @@ cp_parser_omp_declare (cp_parser *parser, cp_token *pragma_tok,
context);
return false;
}
+ if (strcmp (p, "mapper") == 0)
+ {
+ cp_lexer_consume_token (parser->lexer);
+ cp_parser_omp_declare_mapper (parser, pragma_tok, context);
+ return false;
+ }
if (!flag_openmp) /* flag_openmp_simd */
{
cp_parser_skip_to_pragma_eol (parser, pragma_tok);
@@ -52193,7 +54346,7 @@ cp_parser_omp_declare (cp_parser *parser, cp_token *pragma_tok,
}
}
cp_parser_error (parser, "expected %<simd%>, %<reduction%>, "
- "%<target%> or %<variant%>");
+ "%<target%>, %<mapper%> or %<variant%>");
cp_parser_require_pragma_eol (parser, pragma_tok);
return false;
}
@@ -52968,8 +55121,8 @@ cp_parser_omp_construct (cp_parser *parser, cp_token *pragma_tok, bool *if_p)
stmt = cp_parser_oacc_wait (parser, pragma_tok);
break;
case PRAGMA_OMP_ALLOCATE:
- cp_parser_omp_allocate (parser, pragma_tok);
- return;
+ /* This is a declarative directive, not a construct. */
+ gcc_unreachable ();
case PRAGMA_OMP_ATOMIC:
cp_parser_omp_atomic (parser, pragma_tok, false);
return;
@@ -53671,7 +55824,10 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p)
cp_parser_omp_construct (parser, pragma_tok, if_p);
return true;
case PRAGMA_OMP_ALLOCATE:
+ /* Don't go through cp_parser_omp_construct as this pragma is a
+ declarative directive, not a construct. */
cp_parser_omp_allocate (parser, pragma_tok);
+ /* EOL is handled in cp_parser_omp_allocate. */
return false;
case PRAGMA_OACC_ATOMIC:
case PRAGMA_OACC_CACHE: