diff options
Diffstat (limited to 'gcc/cp/parser.cc')
-rw-r--r-- | gcc/cp/parser.cc | 2630 |
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: |