| /* Perform the semantic phase of parsing, i.e., the process of |
| building tree structure, checking semantic consistency, and |
| building RTL. These routines are used both during actual parsing |
| and during the instantiation of template functions. |
| |
| Copyright (C) 1998-2017 Free Software Foundation, Inc. |
| Written by Mark Mitchell (mmitchell@usa.net) based on code found |
| formerly in parse.y and pt.c. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it |
| under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GCC is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "target.h" |
| #include "bitmap.h" |
| #include "cp-tree.h" |
| #include "stringpool.h" |
| #include "cgraph.h" |
| #include "stmt.h" |
| #include "varasm.h" |
| #include "stor-layout.h" |
| #include "c-family/c-objc.h" |
| #include "tree-inline.h" |
| #include "intl.h" |
| #include "tree-iterator.h" |
| #include "omp-general.h" |
| #include "convert.h" |
| #include "stringpool.h" |
| #include "attribs.h" |
| #include "gomp-constants.h" |
| #include "predict.h" |
| |
| /* There routines provide a modular interface to perform many parsing |
| operations. They may therefore be used during actual parsing, or |
| during template instantiation, which may be regarded as a |
| degenerate form of parsing. */ |
| |
| static tree maybe_convert_cond (tree); |
| static tree finalize_nrv_r (tree *, int *, void *); |
| static tree capture_decltype (tree); |
| |
| /* Used for OpenMP non-static data member privatization. */ |
| |
| static hash_map<tree, tree> *omp_private_member_map; |
| static vec<tree> omp_private_member_vec; |
| static bool omp_private_member_ignore_next; |
| |
| |
| /* Deferred Access Checking Overview |
| --------------------------------- |
| |
| Most C++ expressions and declarations require access checking |
| to be performed during parsing. However, in several cases, |
| this has to be treated differently. |
| |
| For member declarations, access checking has to be deferred |
| until more information about the declaration is known. For |
| example: |
| |
| class A { |
| typedef int X; |
| public: |
| X f(); |
| }; |
| |
| A::X A::f(); |
| A::X g(); |
| |
| When we are parsing the function return type `A::X', we don't |
| really know if this is allowed until we parse the function name. |
| |
| Furthermore, some contexts require that access checking is |
| never performed at all. These include class heads, and template |
| instantiations. |
| |
| Typical use of access checking functions is described here: |
| |
| 1. When we enter a context that requires certain access checking |
| mode, the function `push_deferring_access_checks' is called with |
| DEFERRING argument specifying the desired mode. Access checking |
| may be performed immediately (dk_no_deferred), deferred |
| (dk_deferred), or not performed (dk_no_check). |
| |
| 2. When a declaration such as a type, or a variable, is encountered, |
| the function `perform_or_defer_access_check' is called. It |
| maintains a vector of all deferred checks. |
| |
| 3. The global `current_class_type' or `current_function_decl' is then |
| setup by the parser. `enforce_access' relies on these information |
| to check access. |
| |
| 4. Upon exiting the context mentioned in step 1, |
| `perform_deferred_access_checks' is called to check all declaration |
| stored in the vector. `pop_deferring_access_checks' is then |
| called to restore the previous access checking mode. |
| |
| In case of parsing error, we simply call `pop_deferring_access_checks' |
| without `perform_deferred_access_checks'. */ |
| |
| struct GTY(()) deferred_access { |
| /* A vector representing name-lookups for which we have deferred |
| checking access controls. We cannot check the accessibility of |
| names used in a decl-specifier-seq until we know what is being |
| declared because code like: |
| |
| class A { |
| class B {}; |
| B* f(); |
| } |
| |
| A::B* A::f() { return 0; } |
| |
| is valid, even though `A::B' is not generally accessible. */ |
| vec<deferred_access_check, va_gc> * GTY(()) deferred_access_checks; |
| |
| /* The current mode of access checks. */ |
| enum deferring_kind deferring_access_checks_kind; |
| |
| }; |
| |
| /* Data for deferred access checking. */ |
| static GTY(()) vec<deferred_access, va_gc> *deferred_access_stack; |
| static GTY(()) unsigned deferred_access_no_check; |
| |
| /* Save the current deferred access states and start deferred |
| access checking iff DEFER_P is true. */ |
| |
| void |
| push_deferring_access_checks (deferring_kind deferring) |
| { |
| /* For context like template instantiation, access checking |
| disabling applies to all nested context. */ |
| if (deferred_access_no_check || deferring == dk_no_check) |
| deferred_access_no_check++; |
| else |
| { |
| deferred_access e = {NULL, deferring}; |
| vec_safe_push (deferred_access_stack, e); |
| } |
| } |
| |
| /* Save the current deferred access states and start deferred access |
| checking, continuing the set of deferred checks in CHECKS. */ |
| |
| void |
| reopen_deferring_access_checks (vec<deferred_access_check, va_gc> * checks) |
| { |
| push_deferring_access_checks (dk_deferred); |
| if (!deferred_access_no_check) |
| deferred_access_stack->last().deferred_access_checks = checks; |
| } |
| |
| /* Resume deferring access checks again after we stopped doing |
| this previously. */ |
| |
| void |
| resume_deferring_access_checks (void) |
| { |
| if (!deferred_access_no_check) |
| deferred_access_stack->last().deferring_access_checks_kind = dk_deferred; |
| } |
| |
| /* Stop deferring access checks. */ |
| |
| void |
| stop_deferring_access_checks (void) |
| { |
| if (!deferred_access_no_check) |
| deferred_access_stack->last().deferring_access_checks_kind = dk_no_deferred; |
| } |
| |
| /* Discard the current deferred access checks and restore the |
| previous states. */ |
| |
| void |
| pop_deferring_access_checks (void) |
| { |
| if (deferred_access_no_check) |
| deferred_access_no_check--; |
| else |
| deferred_access_stack->pop (); |
| } |
| |
| /* Returns a TREE_LIST representing the deferred checks. |
| The TREE_PURPOSE of each node is the type through which the |
| access occurred; the TREE_VALUE is the declaration named. |
| */ |
| |
| vec<deferred_access_check, va_gc> * |
| get_deferred_access_checks (void) |
| { |
| if (deferred_access_no_check) |
| return NULL; |
| else |
| return (deferred_access_stack->last().deferred_access_checks); |
| } |
| |
| /* Take current deferred checks and combine with the |
| previous states if we also defer checks previously. |
| Otherwise perform checks now. */ |
| |
| void |
| pop_to_parent_deferring_access_checks (void) |
| { |
| if (deferred_access_no_check) |
| deferred_access_no_check--; |
| else |
| { |
| vec<deferred_access_check, va_gc> *checks; |
| deferred_access *ptr; |
| |
| checks = (deferred_access_stack->last ().deferred_access_checks); |
| |
| deferred_access_stack->pop (); |
| ptr = &deferred_access_stack->last (); |
| if (ptr->deferring_access_checks_kind == dk_no_deferred) |
| { |
| /* Check access. */ |
| perform_access_checks (checks, tf_warning_or_error); |
| } |
| else |
| { |
| /* Merge with parent. */ |
| int i, j; |
| deferred_access_check *chk, *probe; |
| |
| FOR_EACH_VEC_SAFE_ELT (checks, i, chk) |
| { |
| FOR_EACH_VEC_SAFE_ELT (ptr->deferred_access_checks, j, probe) |
| { |
| if (probe->binfo == chk->binfo && |
| probe->decl == chk->decl && |
| probe->diag_decl == chk->diag_decl) |
| goto found; |
| } |
| /* Insert into parent's checks. */ |
| vec_safe_push (ptr->deferred_access_checks, *chk); |
| found:; |
| } |
| } |
| } |
| } |
| |
| /* Perform the access checks in CHECKS. The TREE_PURPOSE of each node |
| is the BINFO indicating the qualifying scope used to access the |
| DECL node stored in the TREE_VALUE of the node. If CHECKS is empty |
| or we aren't in SFINAE context or all the checks succeed return TRUE, |
| otherwise FALSE. */ |
| |
| bool |
| perform_access_checks (vec<deferred_access_check, va_gc> *checks, |
| tsubst_flags_t complain) |
| { |
| int i; |
| deferred_access_check *chk; |
| location_t loc = input_location; |
| bool ok = true; |
| |
| if (!checks) |
| return true; |
| |
| FOR_EACH_VEC_SAFE_ELT (checks, i, chk) |
| { |
| input_location = chk->loc; |
| ok &= enforce_access (chk->binfo, chk->decl, chk->diag_decl, complain); |
| } |
| |
| input_location = loc; |
| return (complain & tf_error) ? true : ok; |
| } |
| |
| /* Perform the deferred access checks. |
| |
| After performing the checks, we still have to keep the list |
| `deferred_access_stack->deferred_access_checks' since we may want |
| to check access for them again later in a different context. |
| For example: |
| |
| class A { |
| typedef int X; |
| static X a; |
| }; |
| A::X A::a, x; // No error for `A::a', error for `x' |
| |
| We have to perform deferred access of `A::X', first with `A::a', |
| next with `x'. Return value like perform_access_checks above. */ |
| |
| bool |
| perform_deferred_access_checks (tsubst_flags_t complain) |
| { |
| return perform_access_checks (get_deferred_access_checks (), complain); |
| } |
| |
| /* Defer checking the accessibility of DECL, when looked up in |
| BINFO. DIAG_DECL is the declaration to use to print diagnostics. |
| Return value like perform_access_checks above. |
| If non-NULL, report failures to AFI. */ |
| |
| bool |
| perform_or_defer_access_check (tree binfo, tree decl, tree diag_decl, |
| tsubst_flags_t complain, |
| access_failure_info *afi) |
| { |
| int i; |
| deferred_access *ptr; |
| deferred_access_check *chk; |
| |
| |
| /* Exit if we are in a context that no access checking is performed. |
| */ |
| if (deferred_access_no_check) |
| return true; |
| |
| gcc_assert (TREE_CODE (binfo) == TREE_BINFO); |
| |
| ptr = &deferred_access_stack->last (); |
| |
| /* If we are not supposed to defer access checks, just check now. */ |
| if (ptr->deferring_access_checks_kind == dk_no_deferred) |
| { |
| bool ok = enforce_access (binfo, decl, diag_decl, complain, afi); |
| return (complain & tf_error) ? true : ok; |
| } |
| |
| /* See if we are already going to perform this check. */ |
| FOR_EACH_VEC_SAFE_ELT (ptr->deferred_access_checks, i, chk) |
| { |
| if (chk->decl == decl && chk->binfo == binfo && |
| chk->diag_decl == diag_decl) |
| { |
| return true; |
| } |
| } |
| /* If not, record the check. */ |
| deferred_access_check new_access = {binfo, decl, diag_decl, input_location}; |
| vec_safe_push (ptr->deferred_access_checks, new_access); |
| |
| return true; |
| } |
| |
| /* Returns nonzero if the current statement is a full expression, |
| i.e. temporaries created during that statement should be destroyed |
| at the end of the statement. */ |
| |
| int |
| stmts_are_full_exprs_p (void) |
| { |
| return current_stmt_tree ()->stmts_are_full_exprs_p; |
| } |
| |
| /* T is a statement. Add it to the statement-tree. This is the C++ |
| version. The C/ObjC frontends have a slightly different version of |
| this function. */ |
| |
| tree |
| add_stmt (tree t) |
| { |
| enum tree_code code = TREE_CODE (t); |
| |
| if (EXPR_P (t) && code != LABEL_EXPR) |
| { |
| if (!EXPR_HAS_LOCATION (t)) |
| SET_EXPR_LOCATION (t, input_location); |
| |
| /* When we expand a statement-tree, we must know whether or not the |
| statements are full-expressions. We record that fact here. */ |
| STMT_IS_FULL_EXPR_P (t) = stmts_are_full_exprs_p (); |
| } |
| |
| if (code == LABEL_EXPR || code == CASE_LABEL_EXPR) |
| STATEMENT_LIST_HAS_LABEL (cur_stmt_list) = 1; |
| |
| /* Add T to the statement-tree. Non-side-effect statements need to be |
| recorded during statement expressions. */ |
| gcc_checking_assert (!stmt_list_stack->is_empty ()); |
| append_to_statement_list_force (t, &cur_stmt_list); |
| |
| return t; |
| } |
| |
| /* Returns the stmt_tree to which statements are currently being added. */ |
| |
| stmt_tree |
| current_stmt_tree (void) |
| { |
| return (cfun |
| ? &cfun->language->base.x_stmt_tree |
| : &scope_chain->x_stmt_tree); |
| } |
| |
| /* If statements are full expressions, wrap STMT in a CLEANUP_POINT_EXPR. */ |
| |
| static tree |
| maybe_cleanup_point_expr (tree expr) |
| { |
| if (!processing_template_decl && stmts_are_full_exprs_p ()) |
| expr = fold_build_cleanup_point_expr (TREE_TYPE (expr), expr); |
| return expr; |
| } |
| |
| /* Like maybe_cleanup_point_expr except have the type of the new expression be |
| void so we don't need to create a temporary variable to hold the inner |
| expression. The reason why we do this is because the original type might be |
| an aggregate and we cannot create a temporary variable for that type. */ |
| |
| tree |
| maybe_cleanup_point_expr_void (tree expr) |
| { |
| if (!processing_template_decl && stmts_are_full_exprs_p ()) |
| expr = fold_build_cleanup_point_expr (void_type_node, expr); |
| return expr; |
| } |
| |
| |
| |
| /* Create a declaration statement for the declaration given by the DECL. */ |
| |
| void |
| add_decl_expr (tree decl) |
| { |
| tree r = build_stmt (DECL_SOURCE_LOCATION (decl), DECL_EXPR, decl); |
| if (DECL_INITIAL (decl) |
| || (DECL_SIZE (decl) && TREE_SIDE_EFFECTS (DECL_SIZE (decl)))) |
| r = maybe_cleanup_point_expr_void (r); |
| add_stmt (r); |
| } |
| |
| /* Finish a scope. */ |
| |
| tree |
| do_poplevel (tree stmt_list) |
| { |
| tree block = NULL; |
| |
| if (stmts_are_full_exprs_p ()) |
| block = poplevel (kept_level_p (), 1, 0); |
| |
| stmt_list = pop_stmt_list (stmt_list); |
| |
| if (!processing_template_decl) |
| { |
| stmt_list = c_build_bind_expr (input_location, block, stmt_list); |
| /* ??? See c_end_compound_stmt re statement expressions. */ |
| } |
| |
| return stmt_list; |
| } |
| |
| /* Begin a new scope. */ |
| |
| static tree |
| do_pushlevel (scope_kind sk) |
| { |
| tree ret = push_stmt_list (); |
| if (stmts_are_full_exprs_p ()) |
| begin_scope (sk, NULL); |
| return ret; |
| } |
| |
| /* Queue a cleanup. CLEANUP is an expression/statement to be executed |
| when the current scope is exited. EH_ONLY is true when this is not |
| meant to apply to normal control flow transfer. */ |
| |
| void |
| push_cleanup (tree decl, tree cleanup, bool eh_only) |
| { |
| tree stmt = build_stmt (input_location, CLEANUP_STMT, NULL, cleanup, decl); |
| CLEANUP_EH_ONLY (stmt) = eh_only; |
| add_stmt (stmt); |
| CLEANUP_BODY (stmt) = push_stmt_list (); |
| } |
| |
| /* Simple infinite loop tracking for -Wreturn-type. We keep a stack of all |
| the current loops, represented by 'NULL_TREE' if we've seen a possible |
| exit, and 'error_mark_node' if not. This is currently used only to |
| suppress the warning about a function with no return statements, and |
| therefore we don't bother noting returns as possible exits. We also |
| don't bother with gotos. */ |
| |
| static void |
| begin_maybe_infinite_loop (tree cond) |
| { |
| /* Only track this while parsing a function, not during instantiation. */ |
| if (!cfun || (DECL_TEMPLATE_INSTANTIATION (current_function_decl) |
| && !processing_template_decl)) |
| return; |
| bool maybe_infinite = true; |
| if (cond) |
| { |
| cond = fold_non_dependent_expr (cond); |
| maybe_infinite = integer_nonzerop (cond); |
| } |
| vec_safe_push (cp_function_chain->infinite_loops, |
| maybe_infinite ? error_mark_node : NULL_TREE); |
| |
| } |
| |
| /* A break is a possible exit for the current loop. */ |
| |
| void |
| break_maybe_infinite_loop (void) |
| { |
| if (!cfun) |
| return; |
| cp_function_chain->infinite_loops->last() = NULL_TREE; |
| } |
| |
| /* If we reach the end of the loop without seeing a possible exit, we have |
| an infinite loop. */ |
| |
| static void |
| end_maybe_infinite_loop (tree cond) |
| { |
| if (!cfun || (DECL_TEMPLATE_INSTANTIATION (current_function_decl) |
| && !processing_template_decl)) |
| return; |
| tree current = cp_function_chain->infinite_loops->pop(); |
| if (current != NULL_TREE) |
| { |
| cond = fold_non_dependent_expr (cond); |
| if (integer_nonzerop (cond)) |
| current_function_infinite_loop = 1; |
| } |
| } |
| |
| |
| /* Begin a conditional that might contain a declaration. When generating |
| normal code, we want the declaration to appear before the statement |
| containing the conditional. When generating template code, we want the |
| conditional to be rendered as the raw DECL_EXPR. */ |
| |
| static void |
| begin_cond (tree *cond_p) |
| { |
| if (processing_template_decl) |
| *cond_p = push_stmt_list (); |
| } |
| |
| /* Finish such a conditional. */ |
| |
| static void |
| finish_cond (tree *cond_p, tree expr) |
| { |
| if (processing_template_decl) |
| { |
| tree cond = pop_stmt_list (*cond_p); |
| |
| if (expr == NULL_TREE) |
| /* Empty condition in 'for'. */ |
| gcc_assert (empty_expr_stmt_p (cond)); |
| else if (check_for_bare_parameter_packs (expr)) |
| expr = error_mark_node; |
| else if (!empty_expr_stmt_p (cond)) |
| expr = build2 (COMPOUND_EXPR, TREE_TYPE (expr), cond, expr); |
| } |
| *cond_p = expr; |
| } |
| |
| /* If *COND_P specifies a conditional with a declaration, transform the |
| loop such that |
| while (A x = 42) { } |
| for (; A x = 42;) { } |
| becomes |
| while (true) { A x = 42; if (!x) break; } |
| for (;;) { A x = 42; if (!x) break; } |
| The statement list for BODY will be empty if the conditional did |
| not declare anything. */ |
| |
| static void |
| simplify_loop_decl_cond (tree *cond_p, tree body) |
| { |
| tree cond, if_stmt; |
| |
| if (!TREE_SIDE_EFFECTS (body)) |
| return; |
| |
| cond = *cond_p; |
| *cond_p = boolean_true_node; |
| |
| if_stmt = begin_if_stmt (); |
| cond = cp_build_unary_op (TRUTH_NOT_EXPR, cond, false, tf_warning_or_error); |
| finish_if_stmt_cond (cond, if_stmt); |
| finish_break_stmt (); |
| finish_then_clause (if_stmt); |
| finish_if_stmt (if_stmt); |
| } |
| |
| /* Finish a goto-statement. */ |
| |
| tree |
| finish_goto_stmt (tree destination) |
| { |
| if (identifier_p (destination)) |
| destination = lookup_label (destination); |
| |
| /* We warn about unused labels with -Wunused. That means we have to |
| mark the used labels as used. */ |
| if (TREE_CODE (destination) == LABEL_DECL) |
| TREE_USED (destination) = 1; |
| else |
| { |
| if (check_no_cilk (destination, |
| "Cilk array notation cannot be used as a computed goto expression", |
| "%<_Cilk_spawn%> statement cannot be used as a computed goto expression")) |
| destination = error_mark_node; |
| destination = mark_rvalue_use (destination); |
| if (!processing_template_decl) |
| { |
| destination = cp_convert (ptr_type_node, destination, |
| tf_warning_or_error); |
| if (error_operand_p (destination)) |
| return NULL_TREE; |
| destination |
| = fold_build_cleanup_point_expr (TREE_TYPE (destination), |
| destination); |
| } |
| } |
| |
| check_goto (destination); |
| |
| add_stmt (build_predict_expr (PRED_GOTO, NOT_TAKEN)); |
| return add_stmt (build_stmt (input_location, GOTO_EXPR, destination)); |
| } |
| |
| /* COND is the condition-expression for an if, while, etc., |
| statement. Convert it to a boolean value, if appropriate. |
| In addition, verify sequence points if -Wsequence-point is enabled. */ |
| |
| static tree |
| maybe_convert_cond (tree cond) |
| { |
| /* Empty conditions remain empty. */ |
| if (!cond) |
| return NULL_TREE; |
| |
| /* Wait until we instantiate templates before doing conversion. */ |
| if (processing_template_decl) |
| return cond; |
| |
| if (warn_sequence_point) |
| verify_sequence_points (cond); |
| |
| /* Do the conversion. */ |
| cond = convert_from_reference (cond); |
| |
| if (TREE_CODE (cond) == MODIFY_EXPR |
| && !TREE_NO_WARNING (cond) |
| && warn_parentheses) |
| { |
| warning_at (EXPR_LOC_OR_LOC (cond, input_location), OPT_Wparentheses, |
| "suggest parentheses around assignment used as truth value"); |
| TREE_NO_WARNING (cond) = 1; |
| } |
| |
| return condition_conversion (cond); |
| } |
| |
| /* Finish an expression-statement, whose EXPRESSION is as indicated. */ |
| |
| tree |
| finish_expr_stmt (tree expr) |
| { |
| tree r = NULL_TREE; |
| location_t loc = EXPR_LOCATION (expr); |
| |
| if (expr != NULL_TREE) |
| { |
| /* If we ran into a problem, make sure we complained. */ |
| gcc_assert (expr != error_mark_node || seen_error ()); |
| |
| if (!processing_template_decl) |
| { |
| if (warn_sequence_point) |
| verify_sequence_points (expr); |
| expr = convert_to_void (expr, ICV_STATEMENT, tf_warning_or_error); |
| } |
| else if (!type_dependent_expression_p (expr)) |
| convert_to_void (build_non_dependent_expr (expr), ICV_STATEMENT, |
| tf_warning_or_error); |
| |
| if (check_for_bare_parameter_packs (expr)) |
| expr = error_mark_node; |
| |
| /* Simplification of inner statement expressions, compound exprs, |
| etc can result in us already having an EXPR_STMT. */ |
| if (TREE_CODE (expr) != CLEANUP_POINT_EXPR) |
| { |
| if (TREE_CODE (expr) != EXPR_STMT) |
| expr = build_stmt (loc, EXPR_STMT, expr); |
| expr = maybe_cleanup_point_expr_void (expr); |
| } |
| |
| r = add_stmt (expr); |
| } |
| |
| return r; |
| } |
| |
| |
| /* Begin an if-statement. Returns a newly created IF_STMT if |
| appropriate. */ |
| |
| tree |
| begin_if_stmt (void) |
| { |
| tree r, scope; |
| scope = do_pushlevel (sk_cond); |
| r = build_stmt (input_location, IF_STMT, NULL_TREE, |
| NULL_TREE, NULL_TREE, scope); |
| current_binding_level->this_entity = r; |
| begin_cond (&IF_COND (r)); |
| return r; |
| } |
| |
| /* Process the COND of an if-statement, which may be given by |
| IF_STMT. */ |
| |
| tree |
| finish_if_stmt_cond (tree cond, tree if_stmt) |
| { |
| cond = maybe_convert_cond (cond); |
| if (IF_STMT_CONSTEXPR_P (if_stmt) |
| && is_constant_expression (cond) |
| && !value_dependent_expression_p (cond)) |
| { |
| cond = instantiate_non_dependent_expr (cond); |
| cond = cxx_constant_value (cond, NULL_TREE); |
| } |
| finish_cond (&IF_COND (if_stmt), cond); |
| add_stmt (if_stmt); |
| THEN_CLAUSE (if_stmt) = push_stmt_list (); |
| return cond; |
| } |
| |
| /* Finish the then-clause of an if-statement, which may be given by |
| IF_STMT. */ |
| |
| tree |
| finish_then_clause (tree if_stmt) |
| { |
| THEN_CLAUSE (if_stmt) = pop_stmt_list (THEN_CLAUSE (if_stmt)); |
| return if_stmt; |
| } |
| |
| /* Begin the else-clause of an if-statement. */ |
| |
| void |
| begin_else_clause (tree if_stmt) |
| { |
| ELSE_CLAUSE (if_stmt) = push_stmt_list (); |
| } |
| |
| /* Finish the else-clause of an if-statement, which may be given by |
| IF_STMT. */ |
| |
| void |
| finish_else_clause (tree if_stmt) |
| { |
| ELSE_CLAUSE (if_stmt) = pop_stmt_list (ELSE_CLAUSE (if_stmt)); |
| } |
| |
| /* Finish an if-statement. */ |
| |
| void |
| finish_if_stmt (tree if_stmt) |
| { |
| tree scope = IF_SCOPE (if_stmt); |
| IF_SCOPE (if_stmt) = NULL; |
| add_stmt (do_poplevel (scope)); |
| } |
| |
| /* Begin a while-statement. Returns a newly created WHILE_STMT if |
| appropriate. */ |
| |
| tree |
| begin_while_stmt (void) |
| { |
| tree r; |
| r = build_stmt (input_location, WHILE_STMT, NULL_TREE, NULL_TREE); |
| add_stmt (r); |
| WHILE_BODY (r) = do_pushlevel (sk_block); |
| begin_cond (&WHILE_COND (r)); |
| return r; |
| } |
| |
| /* Process the COND of a while-statement, which may be given by |
| WHILE_STMT. */ |
| |
| void |
| finish_while_stmt_cond (tree cond, tree while_stmt, bool ivdep) |
| { |
| if (check_no_cilk (cond, |
| "Cilk array notation cannot be used as a condition for while statement", |
| "%<_Cilk_spawn%> statement cannot be used as a condition for while statement")) |
| cond = error_mark_node; |
| cond = maybe_convert_cond (cond); |
| finish_cond (&WHILE_COND (while_stmt), cond); |
| begin_maybe_infinite_loop (cond); |
| if (ivdep && cond != error_mark_node) |
| WHILE_COND (while_stmt) = build3 (ANNOTATE_EXPR, |
| TREE_TYPE (WHILE_COND (while_stmt)), |
| WHILE_COND (while_stmt), |
| build_int_cst (integer_type_node, |
| annot_expr_ivdep_kind), |
| integer_zero_node); |
| simplify_loop_decl_cond (&WHILE_COND (while_stmt), WHILE_BODY (while_stmt)); |
| } |
| |
| /* Finish a while-statement, which may be given by WHILE_STMT. */ |
| |
| void |
| finish_while_stmt (tree while_stmt) |
| { |
| end_maybe_infinite_loop (boolean_true_node); |
| WHILE_BODY (while_stmt) = do_poplevel (WHILE_BODY (while_stmt)); |
| } |
| |
| /* Begin a do-statement. Returns a newly created DO_STMT if |
| appropriate. */ |
| |
| tree |
| begin_do_stmt (void) |
| { |
| tree r = build_stmt (input_location, DO_STMT, NULL_TREE, NULL_TREE); |
| begin_maybe_infinite_loop (boolean_true_node); |
| add_stmt (r); |
| DO_BODY (r) = push_stmt_list (); |
| return r; |
| } |
| |
| /* Finish the body of a do-statement, which may be given by DO_STMT. */ |
| |
| void |
| finish_do_body (tree do_stmt) |
| { |
| tree body = DO_BODY (do_stmt) = pop_stmt_list (DO_BODY (do_stmt)); |
| |
| if (TREE_CODE (body) == STATEMENT_LIST && STATEMENT_LIST_TAIL (body)) |
| body = STATEMENT_LIST_TAIL (body)->stmt; |
| |
| if (IS_EMPTY_STMT (body)) |
| warning (OPT_Wempty_body, |
| "suggest explicit braces around empty body in %<do%> statement"); |
| } |
| |
| /* Finish a do-statement, which may be given by DO_STMT, and whose |
| COND is as indicated. */ |
| |
| void |
| finish_do_stmt (tree cond, tree do_stmt, bool ivdep) |
| { |
| if (check_no_cilk (cond, |
| "Cilk array notation cannot be used as a condition for a do-while statement", |
| "%<_Cilk_spawn%> statement cannot be used as a condition for a do-while statement")) |
| cond = error_mark_node; |
| cond = maybe_convert_cond (cond); |
| end_maybe_infinite_loop (cond); |
| if (ivdep && cond != error_mark_node) |
| cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond, |
| build_int_cst (integer_type_node, annot_expr_ivdep_kind), |
| integer_zero_node); |
| DO_COND (do_stmt) = cond; |
| } |
| |
| /* Finish a return-statement. The EXPRESSION returned, if any, is as |
| indicated. */ |
| |
| tree |
| finish_return_stmt (tree expr) |
| { |
| tree r; |
| bool no_warning; |
| |
| expr = check_return_expr (expr, &no_warning); |
| |
| if (error_operand_p (expr) |
| || (flag_openmp && !check_omp_return ())) |
| { |
| /* Suppress -Wreturn-type for this function. */ |
| if (warn_return_type) |
| TREE_NO_WARNING (current_function_decl) = true; |
| return error_mark_node; |
| } |
| |
| if (!processing_template_decl) |
| { |
| if (warn_sequence_point) |
| verify_sequence_points (expr); |
| |
| if (DECL_DESTRUCTOR_P (current_function_decl) |
| || (DECL_CONSTRUCTOR_P (current_function_decl) |
| && targetm.cxx.cdtor_returns_this ())) |
| { |
| /* Similarly, all destructors must run destructors for |
| base-classes before returning. So, all returns in a |
| destructor get sent to the DTOR_LABEL; finish_function emits |
| code to return a value there. */ |
| return finish_goto_stmt (cdtor_label); |
| } |
| } |
| |
| r = build_stmt (input_location, RETURN_EXPR, expr); |
| TREE_NO_WARNING (r) |= no_warning; |
| r = maybe_cleanup_point_expr_void (r); |
| r = add_stmt (r); |
| |
| return r; |
| } |
| |
| /* Begin the scope of a for-statement or a range-for-statement. |
| Both the returned trees are to be used in a call to |
| begin_for_stmt or begin_range_for_stmt. */ |
| |
| tree |
| begin_for_scope (tree *init) |
| { |
| tree scope = NULL_TREE; |
| if (flag_new_for_scope > 0) |
| scope = do_pushlevel (sk_for); |
| |
| if (processing_template_decl) |
| *init = push_stmt_list (); |
| else |
| *init = NULL_TREE; |
| |
| return scope; |
| } |
| |
| /* Begin a for-statement. Returns a new FOR_STMT. |
| SCOPE and INIT should be the return of begin_for_scope, |
| or both NULL_TREE */ |
| |
| tree |
| begin_for_stmt (tree scope, tree init) |
| { |
| tree r; |
| |
| r = build_stmt (input_location, FOR_STMT, NULL_TREE, NULL_TREE, |
| NULL_TREE, NULL_TREE, NULL_TREE); |
| |
| if (scope == NULL_TREE) |
| { |
| gcc_assert (!init || !(flag_new_for_scope > 0)); |
| if (!init) |
| scope = begin_for_scope (&init); |
| } |
| FOR_INIT_STMT (r) = init; |
| FOR_SCOPE (r) = scope; |
| |
| return r; |
| } |
| |
| /* Finish the init-statement of a for-statement, which may be |
| given by FOR_STMT. */ |
| |
| void |
| finish_init_stmt (tree for_stmt) |
| { |
| if (processing_template_decl) |
| FOR_INIT_STMT (for_stmt) = pop_stmt_list (FOR_INIT_STMT (for_stmt)); |
| add_stmt (for_stmt); |
| FOR_BODY (for_stmt) = do_pushlevel (sk_block); |
| begin_cond (&FOR_COND (for_stmt)); |
| } |
| |
| /* Finish the COND of a for-statement, which may be given by |
| FOR_STMT. */ |
| |
| void |
| finish_for_cond (tree cond, tree for_stmt, bool ivdep) |
| { |
| if (check_no_cilk (cond, |
| "Cilk array notation cannot be used in a condition for a for-loop", |
| "%<_Cilk_spawn%> statement cannot be used in a condition for a for-loop")) |
| cond = error_mark_node; |
| cond = maybe_convert_cond (cond); |
| finish_cond (&FOR_COND (for_stmt), cond); |
| begin_maybe_infinite_loop (cond); |
| if (ivdep && cond != error_mark_node) |
| FOR_COND (for_stmt) = build3 (ANNOTATE_EXPR, |
| TREE_TYPE (FOR_COND (for_stmt)), |
| FOR_COND (for_stmt), |
| build_int_cst (integer_type_node, |
| annot_expr_ivdep_kind), |
| integer_zero_node); |
| simplify_loop_decl_cond (&FOR_COND (for_stmt), FOR_BODY (for_stmt)); |
| } |
| |
| /* Finish the increment-EXPRESSION in a for-statement, which may be |
| given by FOR_STMT. */ |
| |
| void |
| finish_for_expr (tree expr, tree for_stmt) |
| { |
| if (!expr) |
| return; |
| /* If EXPR is an overloaded function, issue an error; there is no |
| context available to use to perform overload resolution. */ |
| if (type_unknown_p (expr)) |
| { |
| cxx_incomplete_type_error (expr, TREE_TYPE (expr)); |
| expr = error_mark_node; |
| } |
| if (!processing_template_decl) |
| { |
| if (warn_sequence_point) |
| verify_sequence_points (expr); |
| expr = convert_to_void (expr, ICV_THIRD_IN_FOR, |
| tf_warning_or_error); |
| } |
| else if (!type_dependent_expression_p (expr)) |
| convert_to_void (build_non_dependent_expr (expr), ICV_THIRD_IN_FOR, |
| tf_warning_or_error); |
| expr = maybe_cleanup_point_expr_void (expr); |
| if (check_for_bare_parameter_packs (expr)) |
| expr = error_mark_node; |
| FOR_EXPR (for_stmt) = expr; |
| } |
| |
| /* Finish the body of a for-statement, which may be given by |
| FOR_STMT. The increment-EXPR for the loop must be |
| provided. |
| It can also finish RANGE_FOR_STMT. */ |
| |
| void |
| finish_for_stmt (tree for_stmt) |
| { |
| end_maybe_infinite_loop (boolean_true_node); |
| |
| if (TREE_CODE (for_stmt) == RANGE_FOR_STMT) |
| RANGE_FOR_BODY (for_stmt) = do_poplevel (RANGE_FOR_BODY (for_stmt)); |
| else |
| FOR_BODY (for_stmt) = do_poplevel (FOR_BODY (for_stmt)); |
| |
| /* Pop the scope for the body of the loop. */ |
| if (flag_new_for_scope > 0) |
| { |
| tree scope; |
| tree *scope_ptr = (TREE_CODE (for_stmt) == RANGE_FOR_STMT |
| ? &RANGE_FOR_SCOPE (for_stmt) |
| : &FOR_SCOPE (for_stmt)); |
| scope = *scope_ptr; |
| *scope_ptr = NULL; |
| add_stmt (do_poplevel (scope)); |
| } |
| } |
| |
| /* Begin a range-for-statement. Returns a new RANGE_FOR_STMT. |
| SCOPE and INIT should be the return of begin_for_scope, |
| or both NULL_TREE . |
| To finish it call finish_for_stmt(). */ |
| |
| tree |
| begin_range_for_stmt (tree scope, tree init) |
| { |
| tree r; |
| |
| begin_maybe_infinite_loop (boolean_false_node); |
| |
| r = build_stmt (input_location, RANGE_FOR_STMT, |
| NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE); |
| |
| if (scope == NULL_TREE) |
| { |
| gcc_assert (!init || !(flag_new_for_scope > 0)); |
| if (!init) |
| scope = begin_for_scope (&init); |
| } |
| |
| /* RANGE_FOR_STMTs do not use nor save the init tree, so we |
| pop it now. */ |
| if (init) |
| pop_stmt_list (init); |
| RANGE_FOR_SCOPE (r) = scope; |
| |
| return r; |
| } |
| |
| /* Finish the head of a range-based for statement, which may |
| be given by RANGE_FOR_STMT. DECL must be the declaration |
| and EXPR must be the loop expression. */ |
| |
| void |
| finish_range_for_decl (tree range_for_stmt, tree decl, tree expr) |
| { |
| RANGE_FOR_DECL (range_for_stmt) = decl; |
| RANGE_FOR_EXPR (range_for_stmt) = expr; |
| add_stmt (range_for_stmt); |
| RANGE_FOR_BODY (range_for_stmt) = do_pushlevel (sk_block); |
| } |
| |
| /* Finish a break-statement. */ |
| |
| tree |
| finish_break_stmt (void) |
| { |
| /* In switch statements break is sometimes stylistically used after |
| a return statement. This can lead to spurious warnings about |
| control reaching the end of a non-void function when it is |
| inlined. Note that we are calling block_may_fallthru with |
| language specific tree nodes; this works because |
| block_may_fallthru returns true when given something it does not |
| understand. */ |
| if (!block_may_fallthru (cur_stmt_list)) |
| return void_node; |
| return add_stmt (build_stmt (input_location, BREAK_STMT)); |
| } |
| |
| /* Finish a continue-statement. */ |
| |
| tree |
| finish_continue_stmt (void) |
| { |
| return add_stmt (build_stmt (input_location, CONTINUE_STMT)); |
| } |
| |
| /* Begin a switch-statement. Returns a new SWITCH_STMT if |
| appropriate. */ |
| |
| tree |
| begin_switch_stmt (void) |
| { |
| tree r, scope; |
| |
| scope = do_pushlevel (sk_cond); |
| r = build_stmt (input_location, SWITCH_STMT, NULL_TREE, NULL_TREE, NULL_TREE, scope); |
| |
| begin_cond (&SWITCH_STMT_COND (r)); |
| |
| return r; |
| } |
| |
| /* Finish the cond of a switch-statement. */ |
| |
| void |
| finish_switch_cond (tree cond, tree switch_stmt) |
| { |
| tree orig_type = NULL; |
| |
| if (check_no_cilk (cond, |
| "Cilk array notation cannot be used as a condition for switch statement", |
| "%<_Cilk_spawn%> statement cannot be used as a condition for switch statement")) |
| cond = error_mark_node; |
| |
| if (!processing_template_decl) |
| { |
| /* Convert the condition to an integer or enumeration type. */ |
| cond = build_expr_type_conversion (WANT_INT | WANT_ENUM, cond, true); |
| if (cond == NULL_TREE) |
| { |
| error ("switch quantity not an integer"); |
| cond = error_mark_node; |
| } |
| /* We want unlowered type here to handle enum bit-fields. */ |
| orig_type = unlowered_expr_type (cond); |
| if (TREE_CODE (orig_type) != ENUMERAL_TYPE) |
| orig_type = TREE_TYPE (cond); |
| if (cond != error_mark_node) |
| { |
| /* [stmt.switch] |
| |
| Integral promotions are performed. */ |
| cond = perform_integral_promotions (cond); |
| cond = maybe_cleanup_point_expr (cond); |
| } |
| } |
| if (check_for_bare_parameter_packs (cond)) |
| cond = error_mark_node; |
| else if (!processing_template_decl && warn_sequence_point) |
| verify_sequence_points (cond); |
| |
| finish_cond (&SWITCH_STMT_COND (switch_stmt), cond); |
| SWITCH_STMT_TYPE (switch_stmt) = orig_type; |
| add_stmt (switch_stmt); |
| push_switch (switch_stmt); |
| SWITCH_STMT_BODY (switch_stmt) = push_stmt_list (); |
| } |
| |
| /* Finish the body of a switch-statement, which may be given by |
| SWITCH_STMT. The COND to switch on is indicated. */ |
| |
| void |
| finish_switch_stmt (tree switch_stmt) |
| { |
| tree scope; |
| |
| SWITCH_STMT_BODY (switch_stmt) = |
| pop_stmt_list (SWITCH_STMT_BODY (switch_stmt)); |
| pop_switch (); |
| |
| scope = SWITCH_STMT_SCOPE (switch_stmt); |
| SWITCH_STMT_SCOPE (switch_stmt) = NULL; |
| add_stmt (do_poplevel (scope)); |
| } |
| |
| /* Begin a try-block. Returns a newly-created TRY_BLOCK if |
| appropriate. */ |
| |
| tree |
| begin_try_block (void) |
| { |
| tree r = build_stmt (input_location, TRY_BLOCK, NULL_TREE, NULL_TREE); |
| add_stmt (r); |
| TRY_STMTS (r) = push_stmt_list (); |
| return r; |
| } |
| |
| /* Likewise, for a function-try-block. The block returned in |
| *COMPOUND_STMT is an artificial outer scope, containing the |
| function-try-block. */ |
| |
| tree |
| begin_function_try_block (tree *compound_stmt) |
| { |
| tree r; |
| /* This outer scope does not exist in the C++ standard, but we need |
| a place to put __FUNCTION__ and similar variables. */ |
| *compound_stmt = begin_compound_stmt (0); |
| r = begin_try_block (); |
| FN_TRY_BLOCK_P (r) = 1; |
| return r; |
| } |
| |
| /* Finish a try-block, which may be given by TRY_BLOCK. */ |
| |
| void |
| finish_try_block (tree try_block) |
| { |
| TRY_STMTS (try_block) = pop_stmt_list (TRY_STMTS (try_block)); |
| TRY_HANDLERS (try_block) = push_stmt_list (); |
| } |
| |
| /* Finish the body of a cleanup try-block, which may be given by |
| TRY_BLOCK. */ |
| |
| void |
| finish_cleanup_try_block (tree try_block) |
| { |
| TRY_STMTS (try_block) = pop_stmt_list (TRY_STMTS (try_block)); |
| } |
| |
| /* Finish an implicitly generated try-block, with a cleanup is given |
| by CLEANUP. */ |
| |
| void |
| finish_cleanup (tree cleanup, tree try_block) |
| { |
| TRY_HANDLERS (try_block) = cleanup; |
| CLEANUP_P (try_block) = 1; |
| } |
| |
| /* Likewise, for a function-try-block. */ |
| |
| void |
| finish_function_try_block (tree try_block) |
| { |
| finish_try_block (try_block); |
| /* FIXME : something queer about CTOR_INITIALIZER somehow following |
| the try block, but moving it inside. */ |
| in_function_try_handler = 1; |
| } |
| |
| /* Finish a handler-sequence for a try-block, which may be given by |
| TRY_BLOCK. */ |
| |
| void |
| finish_handler_sequence (tree try_block) |
| { |
| TRY_HANDLERS (try_block) = pop_stmt_list (TRY_HANDLERS (try_block)); |
| check_handlers (TRY_HANDLERS (try_block)); |
| } |
| |
| /* Finish the handler-seq for a function-try-block, given by |
| TRY_BLOCK. COMPOUND_STMT is the outer block created by |
| begin_function_try_block. */ |
| |
| void |
| finish_function_handler_sequence (tree try_block, tree compound_stmt) |
| { |
| in_function_try_handler = 0; |
| finish_handler_sequence (try_block); |
| finish_compound_stmt (compound_stmt); |
| } |
| |
| /* Begin a handler. Returns a HANDLER if appropriate. */ |
| |
| tree |
| begin_handler (void) |
| { |
| tree r; |
| |
| r = build_stmt (input_location, HANDLER, NULL_TREE, NULL_TREE); |
| add_stmt (r); |
| |
| /* Create a binding level for the eh_info and the exception object |
| cleanup. */ |
| HANDLER_BODY (r) = do_pushlevel (sk_catch); |
| |
| return r; |
| } |
| |
| /* Finish the handler-parameters for a handler, which may be given by |
| HANDLER. DECL is the declaration for the catch parameter, or NULL |
| if this is a `catch (...)' clause. */ |
| |
| void |
| finish_handler_parms (tree decl, tree handler) |
| { |
| tree type = NULL_TREE; |
| if (processing_template_decl) |
| { |
| if (decl) |
| { |
| decl = pushdecl (decl); |
| decl = push_template_decl (decl); |
| HANDLER_PARMS (handler) = decl; |
| type = TREE_TYPE (decl); |
| } |
| } |
| else |
| { |
| type = expand_start_catch_block (decl); |
| if (warn_catch_value |
| && type != NULL_TREE |
| && type != error_mark_node |
| && TREE_CODE (TREE_TYPE (decl)) != REFERENCE_TYPE) |
| { |
| tree orig_type = TREE_TYPE (decl); |
| if (CLASS_TYPE_P (orig_type)) |
| { |
| if (TYPE_POLYMORPHIC_P (orig_type)) |
| warning (OPT_Wcatch_value_, |
| "catching polymorphic type %q#T by value", orig_type); |
| else if (warn_catch_value > 1) |
| warning (OPT_Wcatch_value_, |
| "catching type %q#T by value", orig_type); |
| } |
| else if (warn_catch_value > 2) |
| warning (OPT_Wcatch_value_, |
| "catching non-reference type %q#T", orig_type); |
| } |
| } |
| HANDLER_TYPE (handler) = type; |
| } |
| |
| /* Finish a handler, which may be given by HANDLER. The BLOCKs are |
| the return value from the matching call to finish_handler_parms. */ |
| |
| void |
| finish_handler (tree handler) |
| { |
| if (!processing_template_decl) |
| expand_end_catch_block (); |
| HANDLER_BODY (handler) = do_poplevel (HANDLER_BODY (handler)); |
| } |
| |
| /* Begin a compound statement. FLAGS contains some bits that control the |
| behavior and context. If BCS_NO_SCOPE is set, the compound statement |
| does not define a scope. If BCS_FN_BODY is set, this is the outermost |
| block of a function. If BCS_TRY_BLOCK is set, this is the block |
| created on behalf of a TRY statement. Returns a token to be passed to |
| finish_compound_stmt. */ |
| |
| tree |
| begin_compound_stmt (unsigned int flags) |
| { |
| tree r; |
| |
| if (flags & BCS_NO_SCOPE) |
| { |
| r = push_stmt_list (); |
| STATEMENT_LIST_NO_SCOPE (r) = 1; |
| |
| /* Normally, we try hard to keep the BLOCK for a statement-expression. |
| But, if it's a statement-expression with a scopeless block, there's |
| nothing to keep, and we don't want to accidentally keep a block |
| *inside* the scopeless block. */ |
| keep_next_level (false); |
| } |
| else |
| { |
| scope_kind sk = sk_block; |
| if (flags & BCS_TRY_BLOCK) |
| sk = sk_try; |
| else if (flags & BCS_TRANSACTION) |
| sk = sk_transaction; |
| r = do_pushlevel (sk); |
| } |
| |
| /* When processing a template, we need to remember where the braces were, |
| so that we can set up identical scopes when instantiating the template |
| later. BIND_EXPR is a handy candidate for this. |
| Note that do_poplevel won't create a BIND_EXPR itself here (and thus |
| result in nested BIND_EXPRs), since we don't build BLOCK nodes when |
| processing templates. */ |
| if (processing_template_decl) |
| { |
| r = build3 (BIND_EXPR, NULL, NULL, r, NULL); |
| BIND_EXPR_TRY_BLOCK (r) = (flags & BCS_TRY_BLOCK) != 0; |
| BIND_EXPR_BODY_BLOCK (r) = (flags & BCS_FN_BODY) != 0; |
| TREE_SIDE_EFFECTS (r) = 1; |
| } |
| |
| return r; |
| } |
| |
| /* Finish a compound-statement, which is given by STMT. */ |
| |
| void |
| finish_compound_stmt (tree stmt) |
| { |
| if (TREE_CODE (stmt) == BIND_EXPR) |
| { |
| tree body = do_poplevel (BIND_EXPR_BODY (stmt)); |
| /* If the STATEMENT_LIST is empty and this BIND_EXPR isn't special, |
| discard the BIND_EXPR so it can be merged with the containing |
| STATEMENT_LIST. */ |
| if (TREE_CODE (body) == STATEMENT_LIST |
| && STATEMENT_LIST_HEAD (body) == NULL |
| && !BIND_EXPR_BODY_BLOCK (stmt) |
| && !BIND_EXPR_TRY_BLOCK (stmt)) |
| stmt = body; |
| else |
| BIND_EXPR_BODY (stmt) = body; |
| } |
| else if (STATEMENT_LIST_NO_SCOPE (stmt)) |
| stmt = pop_stmt_list (stmt); |
| else |
| { |
| /* Destroy any ObjC "super" receivers that may have been |
| created. */ |
| objc_clear_super_receiver (); |
| |
| stmt = do_poplevel (stmt); |
| } |
| |
| /* ??? See c_end_compound_stmt wrt statement expressions. */ |
| add_stmt (stmt); |
| } |
| |
| /* Finish an asm-statement, whose components are a STRING, some |
| OUTPUT_OPERANDS, some INPUT_OPERANDS, some CLOBBERS and some |
| LABELS. Also note whether the asm-statement should be |
| considered volatile. */ |
| |
| tree |
| finish_asm_stmt (int volatile_p, tree string, tree output_operands, |
| tree input_operands, tree clobbers, tree labels) |
| { |
| tree r; |
| tree t; |
| int ninputs = list_length (input_operands); |
| int noutputs = list_length (output_operands); |
| |
| if (!processing_template_decl) |
| { |
| const char *constraint; |
| const char **oconstraints; |
| bool allows_mem, allows_reg, is_inout; |
| tree operand; |
| int i; |
| |
| oconstraints = XALLOCAVEC (const char *, noutputs); |
| |
| string = resolve_asm_operand_names (string, output_operands, |
| input_operands, labels); |
| |
| for (i = 0, t = output_operands; t; t = TREE_CHAIN (t), ++i) |
| { |
| operand = TREE_VALUE (t); |
| |
| /* ??? Really, this should not be here. Users should be using a |
| proper lvalue, dammit. But there's a long history of using |
| casts in the output operands. In cases like longlong.h, this |
| becomes a primitive form of typechecking -- if the cast can be |
| removed, then the output operand had a type of the proper width; |
| otherwise we'll get an error. Gross, but ... */ |
| STRIP_NOPS (operand); |
| |
| operand = mark_lvalue_use (operand); |
| |
| if (!lvalue_or_else (operand, lv_asm, tf_warning_or_error)) |
| operand = error_mark_node; |
| |
| if (operand != error_mark_node |
| && (TREE_READONLY (operand) |
| || CP_TYPE_CONST_P (TREE_TYPE (operand)) |
| /* Functions are not modifiable, even though they are |
| lvalues. */ |
| || TREE_CODE (TREE_TYPE (operand)) == FUNCTION_TYPE |
| || TREE_CODE (TREE_TYPE (operand)) == METHOD_TYPE |
| /* If it's an aggregate and any field is const, then it is |
| effectively const. */ |
| || (CLASS_TYPE_P (TREE_TYPE (operand)) |
| && C_TYPE_FIELDS_READONLY (TREE_TYPE (operand))))) |
| cxx_readonly_error (operand, lv_asm); |
| |
| constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))); |
| oconstraints[i] = constraint; |
| |
| if (parse_output_constraint (&constraint, i, ninputs, noutputs, |
| &allows_mem, &allows_reg, &is_inout)) |
| { |
| /* If the operand is going to end up in memory, |
| mark it addressable. */ |
| if (!allows_reg && !cxx_mark_addressable (operand)) |
| operand = error_mark_node; |
| } |
| else |
| operand = error_mark_node; |
| |
| TREE_VALUE (t) = operand; |
| } |
| |
| for (i = 0, t = input_operands; t; ++i, t = TREE_CHAIN (t)) |
| { |
| constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))); |
| bool constraint_parsed |
| = parse_input_constraint (&constraint, i, ninputs, noutputs, 0, |
| oconstraints, &allows_mem, &allows_reg); |
| /* If the operand is going to end up in memory, don't call |
| decay_conversion. */ |
| if (constraint_parsed && !allows_reg && allows_mem) |
| operand = mark_lvalue_use (TREE_VALUE (t)); |
| else |
| operand = decay_conversion (TREE_VALUE (t), tf_warning_or_error); |
| |
| /* If the type of the operand hasn't been determined (e.g., |
| because it involves an overloaded function), then issue |
| an error message. There's no context available to |
| resolve the overloading. */ |
| if (TREE_TYPE (operand) == unknown_type_node) |
| { |
| error ("type of asm operand %qE could not be determined", |
| TREE_VALUE (t)); |
| operand = error_mark_node; |
| } |
| |
| if (constraint_parsed) |
| { |
| /* If the operand is going to end up in memory, |
| mark it addressable. */ |
| if (!allows_reg && allows_mem) |
| { |
| /* Strip the nops as we allow this case. FIXME, this really |
| should be rejected or made deprecated. */ |
| STRIP_NOPS (operand); |
| if (!cxx_mark_addressable (operand)) |
| operand = error_mark_node; |
| } |
| else if (!allows_reg && !allows_mem) |
| { |
| /* If constraint allows neither register nor memory, |
| try harder to get a constant. */ |
| tree constop = maybe_constant_value (operand); |
| if (TREE_CONSTANT (constop)) |
| operand = constop; |
| } |
| } |
| else |
| operand = error_mark_node; |
| |
| TREE_VALUE (t) = operand; |
| } |
| } |
| |
| r = build_stmt (input_location, ASM_EXPR, string, |
| output_operands, input_operands, |
| clobbers, labels); |
| ASM_VOLATILE_P (r) = volatile_p || noutputs == 0; |
| r = maybe_cleanup_point_expr_void (r); |
| return add_stmt (r); |
| } |
| |
| /* Finish a label with the indicated NAME. Returns the new label. */ |
| |
| tree |
| finish_label_stmt (tree name) |
| { |
| tree decl = define_label (input_location, name); |
| |
| if (decl == error_mark_node) |
| return error_mark_node; |
| |
| add_stmt (build_stmt (input_location, LABEL_EXPR, decl)); |
| |
| return decl; |
| } |
| |
| /* Finish a series of declarations for local labels. G++ allows users |
| to declare "local" labels, i.e., labels with scope. This extension |
| is useful when writing code involving statement-expressions. */ |
| |
| void |
| finish_label_decl (tree name) |
| { |
| if (!at_function_scope_p ()) |
| { |
| error ("__label__ declarations are only allowed in function scopes"); |
| return; |
| } |
| |
| add_decl_expr (declare_local_label (name)); |
| } |
| |
| /* When DECL goes out of scope, make sure that CLEANUP is executed. */ |
| |
| void |
| finish_decl_cleanup (tree decl, tree cleanup) |
| { |
| push_cleanup (decl, cleanup, false); |
| } |
| |
| /* If the current scope exits with an exception, run CLEANUP. */ |
| |
| void |
| finish_eh_cleanup (tree cleanup) |
| { |
| push_cleanup (NULL, cleanup, true); |
| } |
| |
| /* The MEM_INITS is a list of mem-initializers, in reverse of the |
| order they were written by the user. Each node is as for |
| emit_mem_initializers. */ |
| |
| void |
| finish_mem_initializers (tree mem_inits) |
| { |
| /* Reorder the MEM_INITS so that they are in the order they appeared |
| in the source program. */ |
| mem_inits = nreverse (mem_inits); |
| |
| if (processing_template_decl) |
| { |
| tree mem; |
| |
| for (mem = mem_inits; mem; mem = TREE_CHAIN (mem)) |
| { |
| /* If the TREE_PURPOSE is a TYPE_PACK_EXPANSION, skip the |
| check for bare parameter packs in the TREE_VALUE, because |
| any parameter packs in the TREE_VALUE have already been |
| bound as part of the TREE_PURPOSE. See |
| make_pack_expansion for more information. */ |
| if (TREE_CODE (TREE_PURPOSE (mem)) != TYPE_PACK_EXPANSION |
| && check_for_bare_parameter_packs (TREE_VALUE (mem))) |
| TREE_VALUE (mem) = error_mark_node; |
| } |
| |
| add_stmt (build_min_nt_loc (UNKNOWN_LOCATION, |
| CTOR_INITIALIZER, mem_inits)); |
| } |
| else |
| emit_mem_initializers (mem_inits); |
| } |
| |
| /* Obfuscate EXPR if it looks like an id-expression or member access so |
| that the call to finish_decltype in do_auto_deduction will give the |
| right result. */ |
| |
| tree |
| force_paren_expr (tree expr) |
| { |
| /* This is only needed for decltype(auto) in C++14. */ |
| if (cxx_dialect < cxx14) |
| return expr; |
| |
| /* If we're in unevaluated context, we can't be deducing a |
| return/initializer type, so we don't need to mess with this. */ |
| if (cp_unevaluated_operand) |
| return expr; |
| |
| if (!DECL_P (expr) && TREE_CODE (expr) != COMPONENT_REF |
| && TREE_CODE (expr) != SCOPE_REF) |
| return expr; |
| |
| if (TREE_CODE (expr) == COMPONENT_REF |
| || TREE_CODE (expr) == SCOPE_REF) |
| REF_PARENTHESIZED_P (expr) = true; |
| else if (type_dependent_expression_p (expr)) |
| expr = build1 (PAREN_EXPR, TREE_TYPE (expr), expr); |
| else if (VAR_P (expr) && DECL_HARD_REGISTER (expr)) |
| /* We can't bind a hard register variable to a reference. */; |
| else |
| { |
| cp_lvalue_kind kind = lvalue_kind (expr); |
| if ((kind & ~clk_class) != clk_none) |
| { |
| tree type = unlowered_expr_type (expr); |
| bool rval = !!(kind & clk_rvalueref); |
| type = cp_build_reference_type (type, rval); |
| /* This inhibits warnings in, eg, cxx_mark_addressable |
| (c++/60955). */ |
| warning_sentinel s (extra_warnings); |
| expr = build_static_cast (type, expr, tf_error); |
| if (expr != error_mark_node) |
| REF_PARENTHESIZED_P (expr) = true; |
| } |
| } |
| |
| return expr; |
| } |
| |
| /* If T is an id-expression obfuscated by force_paren_expr, undo the |
| obfuscation and return the underlying id-expression. Otherwise |
| return T. */ |
| |
| tree |
| maybe_undo_parenthesized_ref (tree t) |
| { |
| if (cxx_dialect >= cxx14 |
| && INDIRECT_REF_P (t) |
| && REF_PARENTHESIZED_P (t)) |
| { |
| t = TREE_OPERAND (t, 0); |
| while (TREE_CODE (t) == NON_LVALUE_EXPR |
| || TREE_CODE (t) == NOP_EXPR) |
| t = TREE_OPERAND (t, 0); |
| |
| gcc_assert (TREE_CODE (t) == ADDR_EXPR |
| || TREE_CODE (t) == STATIC_CAST_EXPR); |
| t = TREE_OPERAND (t, 0); |
| } |
| |
| return t; |
| } |
| |
| /* Finish a parenthesized expression EXPR. */ |
| |
| cp_expr |
| finish_parenthesized_expr (cp_expr expr) |
| { |
| if (EXPR_P (expr)) |
| /* This inhibits warnings in c_common_truthvalue_conversion. */ |
| TREE_NO_WARNING (expr) = 1; |
| |
| if (TREE_CODE (expr) == OFFSET_REF |
| || TREE_CODE (expr) == SCOPE_REF) |
| /* [expr.unary.op]/3 The qualified id of a pointer-to-member must not be |
| enclosed in parentheses. */ |
| PTRMEM_OK_P (expr) = 0; |
| |
| if (TREE_CODE (expr) == STRING_CST) |
| PAREN_STRING_LITERAL_P (expr) = 1; |
| |
| expr = cp_expr (force_paren_expr (expr), expr.get_location ()); |
| |
| return expr; |
| } |
| |
| /* Finish a reference to a non-static data member (DECL) that is not |
| preceded by `.' or `->'. */ |
| |
| tree |
| finish_non_static_data_member (tree decl, tree object, tree qualifying_scope) |
| { |
| gcc_assert (TREE_CODE (decl) == FIELD_DECL); |
| bool try_omp_private = !object && omp_private_member_map; |
| tree ret; |
| |
| if (!object) |
| { |
| tree scope = qualifying_scope; |
| if (scope == NULL_TREE) |
| scope = context_for_name_lookup (decl); |
| object = maybe_dummy_object (scope, NULL); |
| } |
| |
| object = maybe_resolve_dummy (object, true); |
| if (object == error_mark_node) |
| return error_mark_node; |
| |
| /* DR 613/850: Can use non-static data members without an associated |
| object in sizeof/decltype/alignof. */ |
| if (is_dummy_object (object) && cp_unevaluated_operand == 0 |
| && (!processing_template_decl || !current_class_ref)) |
| { |
| if (current_function_decl |
| && DECL_STATIC_FUNCTION_P (current_function_decl)) |
| error ("invalid use of member %qD in static member function", decl); |
| else |
| error ("invalid use of non-static data member %qD", decl); |
| inform (DECL_SOURCE_LOCATION (decl), "declared here"); |
| |
| return error_mark_node; |
| } |
| |
| if (current_class_ptr) |
| TREE_USED (current_class_ptr) = 1; |
| if (processing_template_decl && !qualifying_scope) |
| { |
| tree type = TREE_TYPE (decl); |
| |
| if (TREE_CODE (type) == REFERENCE_TYPE) |
| /* Quals on the object don't matter. */; |
| else if (PACK_EXPANSION_P (type)) |
| /* Don't bother trying to represent this. */ |
| type = NULL_TREE; |
| else |
| { |
| /* Set the cv qualifiers. */ |
| int quals = cp_type_quals (TREE_TYPE (object)); |
| |
| if (DECL_MUTABLE_P (decl)) |
| quals &= ~TYPE_QUAL_CONST; |
| |
| quals |= cp_type_quals (TREE_TYPE (decl)); |
| type = cp_build_qualified_type (type, quals); |
| } |
| |
| ret = (convert_from_reference |
| (build_min (COMPONENT_REF, type, object, decl, NULL_TREE))); |
| } |
| /* If PROCESSING_TEMPLATE_DECL is nonzero here, then |
| QUALIFYING_SCOPE is also non-null. Wrap this in a SCOPE_REF |
| for now. */ |
| else if (processing_template_decl) |
| ret = build_qualified_name (TREE_TYPE (decl), |
| qualifying_scope, |
| decl, |
| /*template_p=*/false); |
| else |
| { |
| tree access_type = TREE_TYPE (object); |
| |
| perform_or_defer_access_check (TYPE_BINFO (access_type), decl, |
| decl, tf_warning_or_error); |
| |
| /* If the data member was named `C::M', convert `*this' to `C' |
| first. */ |
| if (qualifying_scope) |
| { |
| tree binfo = NULL_TREE; |
| object = build_scoped_ref (object, qualifying_scope, |
| &binfo); |
| } |
| |
| ret = build_class_member_access_expr (object, decl, |
| /*access_path=*/NULL_TREE, |
| /*preserve_reference=*/false, |
| tf_warning_or_error); |
| } |
| if (try_omp_private) |
| { |
| tree *v = omp_private_member_map->get (decl); |
| if (v) |
| ret = convert_from_reference (*v); |
| } |
| return ret; |
| } |
| |
| /* If we are currently parsing a template and we encountered a typedef |
| TYPEDEF_DECL that is being accessed though CONTEXT, this function |
| adds the typedef to a list tied to the current template. |
| At template instantiation time, that list is walked and access check |
| performed for each typedef. |
| LOCATION is the location of the usage point of TYPEDEF_DECL. */ |
| |
| void |
| add_typedef_to_current_template_for_access_check (tree typedef_decl, |
| tree context, |
| location_t location) |
| { |
| tree template_info = NULL; |
| tree cs = current_scope (); |
| |
| if (!is_typedef_decl (typedef_decl) |
| || !context |
| || !CLASS_TYPE_P (context) |
| || !cs) |
| return; |
| |
| if (CLASS_TYPE_P (cs) || TREE_CODE (cs) == FUNCTION_DECL) |
| template_info = get_template_info (cs); |
| |
| if (template_info |
| && TI_TEMPLATE (template_info) |
| && !currently_open_class (context)) |
| append_type_to_template_for_access_check (cs, typedef_decl, |
| context, location); |
| } |
| |
| /* DECL was the declaration to which a qualified-id resolved. Issue |
| an error message if it is not accessible. If OBJECT_TYPE is |
| non-NULL, we have just seen `x->' or `x.' and OBJECT_TYPE is the |
| type of `*x', or `x', respectively. If the DECL was named as |
| `A::B' then NESTED_NAME_SPECIFIER is `A'. */ |
| |
| void |
| check_accessibility_of_qualified_id (tree decl, |
| tree object_type, |
| tree nested_name_specifier) |
| { |
| tree scope; |
| tree qualifying_type = NULL_TREE; |
| |
| /* If we are parsing a template declaration and if decl is a typedef, |
| add it to a list tied to the template. |
| At template instantiation time, that list will be walked and |
| access check performed. */ |
| add_typedef_to_current_template_for_access_check (decl, |
| nested_name_specifier |
| ? nested_name_specifier |
| : DECL_CONTEXT (decl), |
| input_location); |
| |
| /* If we're not checking, return immediately. */ |
| if (deferred_access_no_check) |
| return; |
| |
| /* Determine the SCOPE of DECL. */ |
| scope = context_for_name_lookup (decl); |
| /* If the SCOPE is not a type, then DECL is not a member. */ |
| if (!TYPE_P (scope)) |
| return; |
| /* Compute the scope through which DECL is being accessed. */ |
| if (object_type |
| /* OBJECT_TYPE might not be a class type; consider: |
| |
| class A { typedef int I; }; |
| I *p; |
| p->A::I::~I(); |
| |
| In this case, we will have "A::I" as the DECL, but "I" as the |
| OBJECT_TYPE. */ |
| && CLASS_TYPE_P (object_type) |
| && DERIVED_FROM_P (scope, object_type)) |
| /* If we are processing a `->' or `.' expression, use the type of the |
| left-hand side. */ |
| qualifying_type = object_type; |
| else if (nested_name_specifier) |
| { |
| /* If the reference is to a non-static member of the |
| current class, treat it as if it were referenced through |
| `this'. */ |
| tree ct; |
| if (DECL_NONSTATIC_MEMBER_P (decl) |
| && current_class_ptr |
| && DERIVED_FROM_P (scope, ct = current_nonlambda_class_type ())) |
| qualifying_type = ct; |
| /* Otherwise, use the type indicated by the |
| nested-name-specifier. */ |
| else |
| qualifying_type = nested_name_specifier; |
| } |
| else |
| /* Otherwise, the name must be from the current class or one of |
| its bases. */ |
| qualifying_type = currently_open_derived_class (scope); |
| |
| if (qualifying_type |
| /* It is possible for qualifying type to be a TEMPLATE_TYPE_PARM |
| or similar in a default argument value. */ |
| && CLASS_TYPE_P (qualifying_type) |
| && !dependent_type_p (qualifying_type)) |
| perform_or_defer_access_check (TYPE_BINFO (qualifying_type), decl, |
| decl, tf_warning_or_error); |
| } |
| |
| /* EXPR is the result of a qualified-id. The QUALIFYING_CLASS was the |
| class named to the left of the "::" operator. DONE is true if this |
| expression is a complete postfix-expression; it is false if this |
| expression is followed by '->', '[', '(', etc. ADDRESS_P is true |
| iff this expression is the operand of '&'. TEMPLATE_P is true iff |
| the qualified-id was of the form "A::template B". TEMPLATE_ARG_P |
| is true iff this qualified name appears as a template argument. */ |
| |
| tree |
| finish_qualified_id_expr (tree qualifying_class, |
| tree expr, |
| bool done, |
| bool address_p, |
| bool template_p, |
| bool template_arg_p, |
| tsubst_flags_t complain) |
| { |
| gcc_assert (TYPE_P (qualifying_class)); |
| |
| if (error_operand_p (expr)) |
| return error_mark_node; |
| |
| if ((DECL_P (expr) || BASELINK_P (expr)) |
| && !mark_used (expr, complain)) |
| return error_mark_node; |
| |
| if (template_p) |
| { |
| if (TREE_CODE (expr) == UNBOUND_CLASS_TEMPLATE) |
| /* cp_parser_lookup_name thought we were looking for a type, |
| but we're actually looking for a declaration. */ |
| expr = build_qualified_name (/*type*/NULL_TREE, |
| TYPE_CONTEXT (expr), |
| TYPE_IDENTIFIER (expr), |
| /*template_p*/true); |
| else |
| check_template_keyword (expr); |
| } |
| |
| /* If EXPR occurs as the operand of '&', use special handling that |
| permits a pointer-to-member. */ |
| if (address_p && done) |
| { |
| if (TREE_CODE (expr) == SCOPE_REF) |
| expr = TREE_OPERAND (expr, 1); |
| expr = build_offset_ref (qualifying_class, expr, |
| /*address_p=*/true, complain); |
| return expr; |
| } |
| |
| /* No need to check access within an enum. */ |
| if (TREE_CODE (qualifying_class) == ENUMERAL_TYPE) |
| return expr; |
| |
| /* Within the scope of a class, turn references to non-static |
| members into expression of the form "this->...". */ |
| if (template_arg_p) |
| /* But, within a template argument, we do not want make the |
| transformation, as there is no "this" pointer. */ |
| ; |
| else if (TREE_CODE (expr) == FIELD_DECL) |
| { |
| push_deferring_access_checks (dk_no_check); |
| expr = finish_non_static_data_member (expr, NULL_TREE, |
| qualifying_class); |
| pop_deferring_access_checks (); |
| } |
| else if (BASELINK_P (expr)) |
| { |
| /* See if any of the functions are non-static members. */ |
| /* If so, the expression may be relative to 'this'. */ |
| if (!shared_member_p (expr) |
| && current_class_ptr |
| && DERIVED_FROM_P (qualifying_class, |
| current_nonlambda_class_type ())) |
| expr = (build_class_member_access_expr |
| (maybe_dummy_object (qualifying_class, NULL), |
| expr, |
| BASELINK_ACCESS_BINFO (expr), |
| /*preserve_reference=*/false, |
| complain)); |
| else if (done) |
| /* The expression is a qualified name whose address is not |
| being taken. */ |
| expr = build_offset_ref (qualifying_class, expr, /*address_p=*/false, |
| complain); |
| } |
| else |
| { |
| /* In a template, return a SCOPE_REF for most qualified-ids |
| so that we can check access at instantiation time. But if |
| we're looking at a member of the current instantiation, we |
| know we have access and building up the SCOPE_REF confuses |
| non-type template argument handling. */ |
| if (processing_template_decl |
| && (!currently_open_class (qualifying_class) |
| || TREE_CODE (expr) == BIT_NOT_EXPR)) |
| expr = build_qualified_name (TREE_TYPE (expr), |
| qualifying_class, expr, |
| template_p); |
| |
| expr = convert_from_reference (expr); |
| } |
| |
| return expr; |
| } |
| |
| /* Begin a statement-expression. The value returned must be passed to |
| finish_stmt_expr. */ |
| |
| tree |
| begin_stmt_expr (void) |
| { |
| return push_stmt_list (); |
| } |
| |
| /* Process the final expression of a statement expression. EXPR can be |
| NULL, if the final expression is empty. Return a STATEMENT_LIST |
| containing all the statements in the statement-expression, or |
| ERROR_MARK_NODE if there was an error. */ |
| |
| tree |
| finish_stmt_expr_expr (tree expr, tree stmt_expr) |
| { |
| if (error_operand_p (expr)) |
| { |
| /* The type of the statement-expression is the type of the last |
| expression. */ |
| TREE_TYPE (stmt_expr) = error_mark_node; |
| return error_mark_node; |
| } |
| |
| /* If the last statement does not have "void" type, then the value |
| of the last statement is the value of the entire expression. */ |
| if (expr) |
| { |
| tree type = TREE_TYPE (expr); |
| |
| if (processing_template_decl) |
| { |
| expr = build_stmt (input_location, EXPR_STMT, expr); |
| expr = add_stmt (expr); |
| /* Mark the last statement so that we can recognize it as such at |
| template-instantiation time. */ |
| EXPR_STMT_STMT_EXPR_RESULT (expr) = 1; |
| } |
| else if (VOID_TYPE_P (type)) |
| { |
| /* Just treat this like an ordinary statement. */ |
| expr = finish_expr_stmt (expr); |
| } |
| else |
| { |
| /* It actually has a value we need to deal with. First, force it |
| to be an rvalue so that we won't need to build up a copy |
| constructor call later when we try to assign it to something. */ |
| expr = force_rvalue (expr, tf_warning_or_error); |
| if (error_operand_p (expr)) |
| return error_mark_node; |
| |
| /* Update for array-to-pointer decay. */ |
| type = TREE_TYPE (expr); |
| |
| /* Wrap it in a CLEANUP_POINT_EXPR and add it to the list like a |
| normal statement, but don't convert to void or actually add |
| the EXPR_STMT. */ |
| if (TREE_CODE (expr) != CLEANUP_POINT_EXPR) |
| expr = maybe_cleanup_point_expr (expr); |
| add_stmt (expr); |
| } |
| |
| /* The type of the statement-expression is the type of the last |
| expression. */ |
| TREE_TYPE (stmt_expr) = type; |
| } |
| |
| return stmt_expr; |
| } |
| |
| /* Finish a statement-expression. EXPR should be the value returned |
| by the previous begin_stmt_expr. Returns an expression |
| representing the statement-expression. */ |
| |
| tree |
| finish_stmt_expr (tree stmt_expr, bool has_no_scope) |
| { |
| tree type; |
| tree result; |
| |
| if (error_operand_p (stmt_expr)) |
| { |
| pop_stmt_list (stmt_expr); |
| return error_mark_node; |
| } |
| |
| gcc_assert (TREE_CODE (stmt_expr) == STATEMENT_LIST); |
| |
| type = TREE_TYPE (stmt_expr); |
| result = pop_stmt_list (stmt_expr); |
| TREE_TYPE (result) = type; |
| |
| if (processing_template_decl) |
| { |
| result = build_min (STMT_EXPR, type, result); |
| TREE_SIDE_EFFECTS (result) = 1; |
| STMT_EXPR_NO_SCOPE (result) = has_no_scope; |
| } |
| else if (CLASS_TYPE_P (type)) |
| { |
| /* Wrap the statement-expression in a TARGET_EXPR so that the |
| temporary object created by the final expression is destroyed at |
| the end of the full-expression containing the |
| statement-expression. */ |
| result = force_target_expr (type, result, tf_warning_or_error); |
| } |
| |
| return result; |
| } |
| |
| /* Returns the expression which provides the value of STMT_EXPR. */ |
| |
| tree |
| stmt_expr_value_expr (tree stmt_expr) |
| { |
| tree t = STMT_EXPR_STMT (stmt_expr); |
| |
| if (TREE_CODE (t) == BIND_EXPR) |
| t = BIND_EXPR_BODY (t); |
| |
| if (TREE_CODE (t) == STATEMENT_LIST && STATEMENT_LIST_TAIL (t)) |
| t = STATEMENT_LIST_TAIL (t)->stmt; |
| |
| if (TREE_CODE (t) == EXPR_STMT) |
| t = EXPR_STMT_EXPR (t); |
| |
| return t; |
| } |
| |
| /* Return TRUE iff EXPR_STMT is an empty list of |
| expression statements. */ |
| |
| bool |
| empty_expr_stmt_p (tree expr_stmt) |
| { |
| tree body = NULL_TREE; |
| |
| if (expr_stmt == void_node) |
| return true; |
| |
| if (expr_stmt) |
| { |
| if (TREE_CODE (expr_stmt) == EXPR_STMT) |
| body = EXPR_STMT_EXPR (expr_stmt); |
| else if (TREE_CODE (expr_stmt) == STATEMENT_LIST) |
| body = expr_stmt; |
| } |
| |
| if (body) |
| { |
| if (TREE_CODE (body) == STATEMENT_LIST) |
| return tsi_end_p (tsi_start (body)); |
| else |
| return empty_expr_stmt_p (body); |
| } |
| return false; |
| } |
| |
| /* Perform Koenig lookup. FN is the postfix-expression representing |
| the function (or functions) to call; ARGS are the arguments to the |
| call. Returns the functions to be considered by overload resolution. */ |
| |
| cp_expr |
| perform_koenig_lookup (cp_expr fn, vec<tree, va_gc> *args, |
| tsubst_flags_t complain) |
| { |
| tree identifier = NULL_TREE; |
| tree functions = NULL_TREE; |
| tree tmpl_args = NULL_TREE; |
| bool template_id = false; |
| location_t loc = fn.get_location (); |
| |
| if (TREE_CODE (fn) == TEMPLATE_ID_EXPR) |
| { |
| /* Use a separate flag to handle null args. */ |
| template_id = true; |
| tmpl_args = TREE_OPERAND (fn, 1); |
| fn = TREE_OPERAND (fn, 0); |
| } |
| |
| /* Find the name of the overloaded function. */ |
| if (identifier_p (fn)) |
| identifier = fn; |
| else |
| { |
| functions = fn; |
| identifier = OVL_NAME (functions); |
| } |
| |
| /* A call to a namespace-scope function using an unqualified name. |
| |
| Do Koenig lookup -- unless any of the arguments are |
| type-dependent. */ |
| if (!any_type_dependent_arguments_p (args) |
| && !any_dependent_template_arguments_p (tmpl_args)) |
| { |
| fn = lookup_arg_dependent (identifier, functions, args); |
| if (!fn) |
| { |
| /* The unqualified name could not be resolved. */ |
| if (complain & tf_error) |
| fn = unqualified_fn_lookup_error (cp_expr (identifier, loc)); |
| else |
| fn = identifier; |
| } |
| } |
| |
| if (fn && template_id && fn != error_mark_node) |
| fn = build2 (TEMPLATE_ID_EXPR, unknown_type_node, fn, tmpl_args); |
| |
| return fn; |
| } |
| |
| /* Generate an expression for `FN (ARGS)'. This may change the |
| contents of ARGS. |
| |
| If DISALLOW_VIRTUAL is true, the call to FN will be not generated |
| as a virtual call, even if FN is virtual. (This flag is set when |
| encountering an expression where the function name is explicitly |
| qualified. For example a call to `X::f' never generates a virtual |
| call.) |
| |
| Returns code for the call. */ |
| |
| tree |
| finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual, |
| bool koenig_p, tsubst_flags_t complain) |
| { |
| tree result; |
| tree orig_fn; |
| vec<tree, va_gc> *orig_args = NULL; |
| |
| if (fn == error_mark_node) |
| return error_mark_node; |
| |
| gcc_assert (!TYPE_P (fn)); |
| |
| /* If FN may be a FUNCTION_DECL obfuscated by force_paren_expr, undo |
| it so that we can tell this is a call to a known function. */ |
| fn = maybe_undo_parenthesized_ref (fn); |
| |
| orig_fn = fn; |
| |
| if (processing_template_decl) |
| { |
| /* If FN is a local extern declaration or set thereof, look them up |
| again at instantiation time. */ |
| if (is_overloaded_fn (fn)) |
| { |
| tree ifn = get_first_fn (fn); |
| if (TREE_CODE (ifn) == FUNCTION_DECL |
| && DECL_LOCAL_FUNCTION_P (ifn)) |
| orig_fn = DECL_NAME (ifn); |
| } |
| |
| /* If the call expression is dependent, build a CALL_EXPR node |
| with no type; type_dependent_expression_p recognizes |
| expressions with no type as being dependent. */ |
| if (type_dependent_expression_p (fn) |
| || any_type_dependent_arguments_p (*args)) |
| { |
| result = build_min_nt_call_vec (orig_fn, *args); |
| SET_EXPR_LOCATION (result, EXPR_LOC_OR_LOC (fn, input_location)); |
| KOENIG_LOOKUP_P (result) = koenig_p; |
| if (is_overloaded_fn (fn)) |
| { |
| fn = get_fns (fn); |
| lookup_keep (fn, true); |
| } |
| |
| if (cfun) |
| { |
| bool abnormal = true; |
| for (lkp_iterator iter (fn); abnormal && iter; ++iter) |
| { |
| tree fndecl = *iter; |
| if (TREE_CODE (fndecl) != FUNCTION_DECL |
| || !TREE_THIS_VOLATILE (fndecl)) |
| abnormal = false; |
| } |
| /* FIXME: Stop warning about falling off end of non-void |
| function. But this is wrong. Even if we only see |
| no-return fns at this point, we could select a |
| future-defined return fn during instantiation. Or |
| vice-versa. */ |
| if (abnormal) |
| current_function_returns_abnormally = 1; |
| } |
| return result; |
| } |
| orig_args = make_tree_vector_copy (*args); |
| if (!BASELINK_P (fn) |
| && TREE_CODE (fn) != PSEUDO_DTOR_EXPR |
| && TREE_TYPE (fn) != unknown_type_node) |
| fn = build_non_dependent_expr (fn); |
| make_args_non_dependent (*args); |
| } |
| |
| if (TREE_CODE (fn) == COMPONENT_REF) |
| { |
| tree member = TREE_OPERAND (fn, 1); |
| if (BASELINK_P (member)) |
| { |
| tree object = TREE_OPERAND (fn, 0); |
| return build_new_method_call (object, member, |
| args, NULL_TREE, |
| (disallow_virtual |
| ? LOOKUP_NORMAL | LOOKUP_NONVIRTUAL |
| : LOOKUP_NORMAL), |
| /*fn_p=*/NULL, |
| complain); |
| } |
| } |
| |
| /* Per 13.3.1.1, '(&f)(...)' is the same as '(f)(...)'. */ |
| if (TREE_CODE (fn) == ADDR_EXPR |
| && TREE_CODE (TREE_OPERAND (fn, 0)) == OVERLOAD) |
| fn = TREE_OPERAND (fn, 0); |
| |
| if (is_overloaded_fn (fn)) |
| fn = baselink_for_fns (fn); |
| |
| result = NULL_TREE; |
| if (BASELINK_P (fn)) |
| { |
| tree object; |
| |
| /* A call to a member function. From [over.call.func]: |
| |
| If the keyword this is in scope and refers to the class of |
| that member function, or a derived class thereof, then the |
| function call is transformed into a qualified function call |
| using (*this) as the postfix-expression to the left of the |
| . operator.... [Otherwise] a contrived object of type T |
| becomes the implied object argument. |
| |
| In this situation: |
| |
| struct A { void f(); }; |
| struct B : public A {}; |
| struct C : public A { void g() { B::f(); }}; |
| |
| "the class of that member function" refers to `A'. But 11.2 |
| [class.access.base] says that we need to convert 'this' to B* as |
| part of the access, so we pass 'B' to maybe_dummy_object. */ |
| |
| if (DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (get_first_fn (fn))) |
| { |
| /* A constructor call always uses a dummy object. (This constructor |
| call which has the form A::A () is actually invalid and we are |
| going to reject it later in build_new_method_call.) */ |
| object = build_dummy_object (BINFO_TYPE (BASELINK_ACCESS_BINFO (fn))); |
| } |
| else |
| object = maybe_dummy_object (BINFO_TYPE (BASELINK_ACCESS_BINFO (fn)), |
| NULL); |
| |
| result = build_new_method_call (object, fn, args, NULL_TREE, |
| (disallow_virtual |
| ? LOOKUP_NORMAL|LOOKUP_NONVIRTUAL |
| : LOOKUP_NORMAL), |
| /*fn_p=*/NULL, |
| complain); |
| } |
| else if (is_overloaded_fn (fn)) |
| { |
| /* If the function is an overloaded builtin, resolve it. */ |
| if (TREE_CODE (fn) == FUNCTION_DECL |
| && (DECL_BUILT_IN_CLASS (fn) == BUILT_IN_NORMAL |
| || DECL_BUILT_IN_CLASS (fn) == BUILT_IN_MD)) |
| result = resolve_overloaded_builtin (input_location, fn, *args); |
| |
| if (!result) |
| { |
| if (warn_sizeof_pointer_memaccess |
| && (complain & tf_warning) |
| && !vec_safe_is_empty (*args) |
| && !processing_template_decl) |
| { |
| location_t sizeof_arg_loc[3]; |
| tree sizeof_arg[3]; |
| unsigned int i; |
| for (i = 0; i < 3; i++) |
| { |
| tree t; |
| |
| sizeof_arg_loc[i] = UNKNOWN_LOCATION; |
| sizeof_arg[i] = NULL_TREE; |
| if (i >= (*args)->length ()) |
| continue; |
| t = (**args)[i]; |
| if (TREE_CODE (t) != SIZEOF_EXPR) |
| continue; |
| if (SIZEOF_EXPR_TYPE_P (t)) |
| sizeof_arg[i] = TREE_TYPE (TREE_OPERAND (t, 0)); |
| else |
| sizeof_arg[i] = TREE_OPERAND (t, 0); |
| sizeof_arg_loc[i] = EXPR_LOCATION (t); |
| } |
| sizeof_pointer_memaccess_warning |
| (sizeof_arg_loc, fn, *args, |
| sizeof_arg, same_type_ignoring_top_level_qualifiers_p); |
| } |
| |
| /* A call to a namespace-scope function. */ |
| result = build_new_function_call (fn, args, complain); |
| } |
| } |
| else if (TREE_CODE (fn) == PSEUDO_DTOR_EXPR) |
| { |
| if (!vec_safe_is_empty (*args)) |
| error ("arguments to destructor are not allowed"); |
| /* Mark the pseudo-destructor call as having side-effects so |
| that we do not issue warnings about its use. */ |
| result = build1 (NOP_EXPR, |
| void_type_node, |
| TREE_OPERAND (fn, 0)); |
| TREE_SIDE_EFFECTS (result) = 1; |
| } |
| else if (CLASS_TYPE_P (TREE_TYPE (fn))) |
| /* If the "function" is really an object of class type, it might |
| have an overloaded `operator ()'. */ |
| result = build_op_call (fn, args, complain); |
| |
| if (!result) |
| /* A call where the function is unknown. */ |
| result = cp_build_function_call_vec (fn, args, complain); |
| |
| if (processing_template_decl && result != error_mark_node) |
| { |
| if (INDIRECT_REF_P (result)) |
| result = TREE_OPERAND (result, 0); |
| result = build_call_vec (TREE_TYPE (result), orig_fn, orig_args); |
| SET_EXPR_LOCATION (result, input_location); |
| KOENIG_LOOKUP_P (result) = koenig_p; |
| release_tree_vector (orig_args); |
| result = convert_from_reference (result); |
| } |
| |
| /* Free or retain OVERLOADs from lookup. */ |
| if (is_overloaded_fn (orig_fn)) |
| lookup_keep (get_fns (orig_fn), processing_template_decl); |
| |
| return result; |
| } |
| |
| /* Finish a call to a postfix increment or decrement or EXPR. (Which |
| is indicated by CODE, which should be POSTINCREMENT_EXPR or |
| POSTDECREMENT_EXPR.) */ |
| |
| cp_expr |
| finish_increment_expr (cp_expr expr, enum tree_code code) |
| { |
| /* input_location holds the location of the trailing operator token. |
| Build a location of the form: |
| expr++ |
| ~~~~^~ |
| with the caret at the operator token, ranging from the start |
| of EXPR to the end of the operator token. */ |
| location_t combined_loc = make_location (input_location, |
| expr.get_start (), |
| get_finish (input_location)); |
| cp_expr result = build_x_unary_op (combined_loc, code, expr, |
| tf_warning_or_error); |
| /* TODO: build_x_unary_op doesn't honor the location, so set it here. */ |
| result.set_location (combined_loc); |
| return result; |
| } |
| |
| /* Finish a use of `this'. Returns an expression for `this'. */ |
| |
| tree |
| finish_this_expr (void) |
| { |
| tree result = NULL_TREE; |
| |
| if (current_class_ptr) |
| { |
| tree type = TREE_TYPE (current_class_ref); |
| |
| /* In a lambda expression, 'this' refers to the captured 'this'. */ |
| if (LAMBDA_TYPE_P (type)) |
| result = lambda_expr_this_capture (CLASSTYPE_LAMBDA_EXPR (type), true); |
| else |
| result = current_class_ptr; |
| } |
| |
| if (result) |
| /* The keyword 'this' is a prvalue expression. */ |
| return rvalue (result); |
| |
| tree fn = current_nonlambda_function (); |
| if (fn && DECL_STATIC_FUNCTION_P (fn)) |
| error ("%<this%> is unavailable for static member functions"); |
| else if (fn) |
| error ("invalid use of %<this%> in non-member function"); |
| else |
| error ("invalid use of %<this%> at top level"); |
| return error_mark_node; |
| } |
| |
| /* Finish a pseudo-destructor expression. If SCOPE is NULL, the |
| expression was of the form `OBJECT.~DESTRUCTOR' where DESTRUCTOR is |
| the TYPE for the type given. If SCOPE is non-NULL, the expression |
| was of the form `OBJECT.SCOPE::~DESTRUCTOR'. */ |
| |
| tree |
| finish_pseudo_destructor_expr (tree object, tree scope, tree destructor, |
| location_t loc) |
| { |
| if (object == error_mark_node || destructor == error_mark_node) |
| return error_mark_node; |
| |
| gcc_assert (TYPE_P (destructor)); |
| |
| if (!processing_template_decl) |
| { |
| if (scope == error_mark_node) |
| { |
| error_at (loc, "invalid qualifying scope in pseudo-destructor name"); |
| return error_mark_node; |
| } |
| if (is_auto (destructor)) |
| destructor = TREE_TYPE (object); |
| if (scope && TYPE_P (scope) && !check_dtor_name (scope, destructor)) |
| { |
| error_at (loc, |
| "qualified type %qT does not match destructor name ~%qT", |
| scope, destructor); |
| return error_mark_node; |
| } |
| |
| |
| /* [expr.pseudo] says both: |
| |
| The type designated by the pseudo-destructor-name shall be |
| the same as the object type. |
| |
| and: |
| |
| The cv-unqualified versions of the object type and of the |
| type designated by the pseudo-destructor-name shall be the |
| same type. |
| |
| We implement the more generous second sentence, since that is |
| what most other compilers do. */ |
| if (!same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (object), |
| destructor)) |
| { |
| error_at (loc, "%qE is not of type %qT", object, destructor); |
| return error_mark_node; |
| } |
| } |
| |
| return build3_loc (loc, PSEUDO_DTOR_EXPR, void_type_node, object, |
| scope, destructor); |
| } |
| |
| /* Finish an expression of the form CODE EXPR. */ |
| |
| cp_expr |
| finish_unary_op_expr (location_t op_loc, enum tree_code code, cp_expr expr, |
| tsubst_flags_t complain) |
| { |
| /* Build a location of the form: |
| ++expr |
| ^~~~~~ |
| with the caret at the operator token, ranging from the start |
| of the operator token to the end of EXPR. */ |
| location_t combined_loc = make_location (op_loc, |
| op_loc, expr.get_finish ()); |
| cp_expr result = build_x_unary_op (combined_loc, code, expr, complain); |
| /* TODO: build_x_unary_op doesn't always honor the location. */ |
| result.set_location (combined_loc); |
| |
| tree result_ovl, expr_ovl; |
| |
| if (!(complain & tf_warning)) |
| return result; |
| |
| result_ovl = result; |
| expr_ovl = expr; |
| |
| if (!processing_template_decl) |
| expr_ovl = cp_fully_fold (expr_ovl); |
| |
| if (!CONSTANT_CLASS_P (expr_ovl) |
| || TREE_OVERFLOW_P (expr_ovl)) |
| return result; |
| |
| if (!processing_template_decl) |
| result_ovl = cp_fully_fold (result_ovl); |
| |
| if (CONSTANT_CLASS_P (result_ovl) && TREE_OVERFLOW_P (result_ovl)) |
| overflow_warning (combined_loc, result_ovl); |
| |
| return result; |
| } |
| |
| /* Finish a compound-literal expression or C++11 functional cast with aggregate |
| initializer. TYPE is the type to which the CONSTRUCTOR in COMPOUND_LITERAL |
| is being cast. */ |
| |
| tree |
| finish_compound_literal (tree type, tree compound_literal, |
| tsubst_flags_t complain, |
| fcl_t fcl_context) |
| { |
| if (type == error_mark_node) |
| return error_mark_node; |
| |
| if (TREE_CODE (type) == REFERENCE_TYPE) |
| { |
| compound_literal |
| = finish_compound_literal (TREE_TYPE (type), compound_literal, |
| complain, fcl_context); |
| return cp_build_c_cast (type, compound_literal, complain); |
| } |
| |
| if (!TYPE_OBJ_P (type)) |
| { |
| if (complain & tf_error) |
| error ("compound literal of non-object type %qT", type); |
| return error_mark_node; |
| } |
| |
| if (tree anode = type_uses_auto (type)) |
| if (CLASS_PLACEHOLDER_TEMPLATE (anode)) |
| { |
| type = do_auto_deduction (type, compound_literal, anode, complain, |
| adc_variable_type); |
| if (type == error_mark_node) |
| return error_mark_node; |
| } |
| |
| if (processing_template_decl) |
| { |
| TREE_TYPE (compound_literal) = type; |
| /* Mark the expression as a compound literal. */ |
| TREE_HAS_CONSTRUCTOR (compound_literal) = 1; |
| if (fcl_context == fcl_c99) |
| CONSTRUCTOR_C99_COMPOUND_LITERAL (compound_literal) = 1; |
| return compound_literal; |
| } |
| |
| type = complete_type (type); |
| |
| if (TYPE_NON_AGGREGATE_CLASS (type)) |
| { |
| /* Trying to deal with a CONSTRUCTOR instead of a TREE_LIST |
| everywhere that deals with function arguments would be a pain, so |
| just wrap it in a TREE_LIST. The parser set a flag so we know |
| that it came from T{} rather than T({}). */ |
| CONSTRUCTOR_IS_DIRECT_INIT (compound_literal) = 1; |
| compound_literal = build_tree_list (NULL_TREE, compound_literal); |
| return build_functional_cast (type, compound_literal, complain); |
| } |
| |
| if (TREE_CODE (type) == ARRAY_TYPE |
| && check_array_initializer (NULL_TREE, type, compound_literal)) |
| return error_mark_node; |
| compound_literal = reshape_init (type, compound_literal, complain); |
| if (SCALAR_TYPE_P (type) |
| && !BRACE_ENCLOSED_INITIALIZER_P (compound_literal) |
| && !check_narrowing (type, compound_literal, complain)) |
| return error_mark_node; |
| if (TREE_CODE (type) == ARRAY_TYPE |
| && TYPE_DOMAIN (type) == NULL_TREE) |
| { |
| cp_complete_array_type_or_error (&type, compound_literal, |
| false, complain); |
| if (type == error_mark_node) |
| return error_mark_node; |
| } |
| compound_literal = digest_init_flags (type, compound_literal, LOOKUP_NORMAL, |
| complain); |
| if (TREE_CODE (compound_literal) == CONSTRUCTOR) |
| { |
| TREE_HAS_CONSTRUCTOR (compound_literal) = true; |
| if (fcl_context == fcl_c99) |
| CONSTRUCTOR_C99_COMPOUND_LITERAL (compound_literal) = 1; |
| } |
| |
| /* Put static/constant array temporaries in static variables. */ |
| /* FIXME all C99 compound literals should be variables rather than C++ |
| temporaries, unless they are used as an aggregate initializer. */ |
| if ((!at_function_scope_p () || CP_TYPE_CONST_P (type)) |
| && fcl_context == fcl_c99 |
| && TREE_CODE (type) == ARRAY_TYPE |
| && !TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type) |
| && initializer_constant_valid_p (compound_literal, type)) |
| { |
| tree decl = create_temporary_var (type); |
| DECL_INITIAL (decl) = compound_literal; |
| TREE_STATIC (decl) = 1; |
| if (literal_type_p (type) && CP_TYPE_CONST_NON_VOLATILE_P (type)) |
| { |
| /* 5.19 says that a constant expression can include an |
| lvalue-rvalue conversion applied to "a glvalue of literal type |
| that refers to a non-volatile temporary object initialized |
| with a constant expression". Rather than try to communicate |
| that this VAR_DECL is a temporary, just mark it constexpr. */ |
| DECL_DECLARED_CONSTEXPR_P (decl) = true; |
| DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = true; |
| TREE_CONSTANT (decl) = true; |
| } |
| cp_apply_type_quals_to_decl (cp_type_quals (type), decl); |
| decl = pushdecl_top_level (decl); |
| DECL_NAME (decl) = make_anon_name (); |
| SET_DECL_ASSEMBLER_NAME (decl, DECL_NAME (decl)); |
| /* Make sure the destructor is callable. */ |
| tree clean = cxx_maybe_build_cleanup (decl, complain); |
| if (clean == error_mark_node) |
| return error_mark_node; |
| return decl; |
| } |
| |
| /* Represent other compound literals with TARGET_EXPR so we produce |
| an lvalue, but can elide copies. */ |
| if (!VECTOR_TYPE_P (type)) |
| compound_literal = get_target_expr_sfinae (compound_literal, complain); |
| |
| return compound_literal; |
| } |
| |
| /* Return the declaration for the function-name variable indicated by |
| ID. */ |
| |
| tree |
| finish_fname (tree id) |
| { |
| tree decl; |
| |
| decl = fname_decl (input_location, C_RID_CODE (id), id); |
| if (processing_template_decl && current_function_decl |
| && decl != error_mark_node) |
| decl = DECL_NAME (decl); |
| return decl; |
| } |
| |
| /* Finish a translation unit. */ |
| |
| void |
| finish_translation_unit (void) |
| { |
| /* In case there were missing closebraces, |
| get us back to the global binding level. */ |
| pop_everything (); |
| while (current_namespace != global_namespace) |
| pop_namespace (); |
| |
| /* Do file scope __FUNCTION__ et al. */ |
| finish_fname_decls (); |
| } |
| |
| /* Finish a template type parameter, specified as AGGR IDENTIFIER. |
| Returns the parameter. */ |
| |
| tree |
| finish_template_type_parm (tree aggr, tree identifier) |
| { |
| if (aggr != class_type_node) |
| { |
| permerror (input_location, "template type parameters must use the keyword %<class%> or %<typename%>"); |
| aggr = class_type_node; |
| } |
| |
| return build_tree_list (aggr, identifier); |
| } |
| |
| /* Finish a template template parameter, specified as AGGR IDENTIFIER. |
| Returns the parameter. */ |
| |
| tree |
| finish_template_template_parm (tree aggr, tree identifier) |
| { |
| tree decl = build_decl (input_location, |
| TYPE_DECL, identifier, NULL_TREE); |
| |
| tree tmpl = build_lang_decl (TEMPLATE_DECL, identifier, NULL_TREE); |
| DECL_TEMPLATE_PARMS (tmpl) = current_template_parms; |
| DECL_TEMPLATE_RESULT (tmpl) = decl; |
| DECL_ARTIFICIAL (decl) = 1; |
| |
| // Associate the constraints with the underlying declaration, |
| // not the template. |
| tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms); |
| tree constr = build_constraints (reqs, NULL_TREE); |
| set_constraints (decl, constr); |
| |
| end_template_decl (); |
| |
| gcc_assert (DECL_TEMPLATE_PARMS (tmpl)); |
| |
| check_default_tmpl_args (decl, DECL_TEMPLATE_PARMS (tmpl), |
| /*is_primary=*/true, /*is_partial=*/false, |
| /*is_friend=*/0); |
| |
| return finish_template_type_parm (aggr, tmpl); |
| } |
| |
| /* ARGUMENT is the default-argument value for a template template |
| parameter. If ARGUMENT is invalid, issue error messages and return |
| the ERROR_MARK_NODE. Otherwise, ARGUMENT itself is returned. */ |
| |
| tree |
| check_template_template_default_arg (tree argument) |
| { |
| if (TREE_CODE (argument) != TEMPLATE_DECL |
| && TREE_CODE (argument) != TEMPLATE_TEMPLATE_PARM |
| && TREE_CODE (argument) != UNBOUND_CLASS_TEMPLATE) |
| { |
| if (TREE_CODE (argument) == TYPE_DECL) |
| error ("invalid use of type %qT as a default value for a template " |
| "template-parameter", TREE_TYPE (argument)); |
| else |
| error ("invalid default argument for a template template parameter"); |
| return error_mark_node; |
| } |
| |
| return argument; |
| } |
| |
| /* Begin a class definition, as indicated by T. */ |
| |
| tree |
| begin_class_definition (tree t) |
| { |
| if (error_operand_p (t) || error_operand_p (TYPE_MAIN_DECL (t))) |
| return error_mark_node; |
| |
| if (processing_template_parmlist) |
| { |
| error ("definition of %q#T inside template parameter list", t); |
| return error_mark_node; |
| } |
| |
| /* According to the C++ ABI, decimal classes defined in ISO/IEC TR 24733 |
| are passed the same as decimal scalar types. */ |
| if (TREE_CODE (t) == RECORD_TYPE |
| && !processing_template_decl) |
| { |
| tree ns = TYPE_CONTEXT (t); |
| if (ns && TREE_CODE (ns) == NAMESPACE_DECL |
| && DECL_CONTEXT (ns) == std_node |
| && DECL_NAME (ns) |
| && id_equal (DECL_NAME (ns), "decimal")) |
| { |
| const char *n = TYPE_NAME_STRING (t); |
| if ((strcmp (n, "decimal32") == 0) |
| || (strcmp (n, "decimal64") == 0) |
| || (strcmp (n, "decimal128") == 0)) |
| TYPE_TRANSPARENT_AGGR (t) = 1; |
| } |
| } |
| |
| /* A non-implicit typename comes from code like: |
| |
| template <typename T> struct A { |
| template <typename U> struct A<T>::B ... |
| |
| This is erroneous. */ |
| else if (TREE_CODE (t) == TYPENAME_TYPE) |
| { |
| error ("invalid definition of qualified type %qT", t); |
| t = error_mark_node; |
| } |
| |
| if (t == error_mark_node || ! MAYBE_CLASS_TYPE_P (t)) |
| { |
| t = make_class_type (RECORD_TYPE); |
| pushtag (make_anon_name (), t, /*tag_scope=*/ts_current); |
| } |
| |
| if (TYPE_BEING_DEFINED (t)) |
| { |
| t = make_class_type (TREE_CODE (t)); |
| pushtag (TYPE_IDENTIFIER (t), t, /*tag_scope=*/ts_current); |
| } |
| maybe_process_partial_specialization (t); |
| pushclass (t); |
| TYPE_BEING_DEFINED (t) = 1; |
| class_binding_level->defining_class_p = 1; |
| |
| if (flag_pack_struct) |
| { |
| tree v; |
| TYPE_PACKED (t) = 1; |
| /* Even though the type is being defined for the first time |
| here, there might have been a forward declaration, so there |
| might be cv-qualified variants of T. */ |
| for (v = TYPE_NEXT_VARIANT (t); v; v = TYPE_NEXT_VARIANT (v)) |
| TYPE_PACKED (v) = 1; |
| } |
| /* Reset the interface data, at the earliest possible |
| moment, as it might have been set via a class foo; |
| before. */ |
| if (! TYPE_UNNAMED_P (t)) |
| { |
| struct c_fileinfo *finfo = \ |
| get_fileinfo (LOCATION_FILE (input_location)); |
| CLASSTYPE_INTERFACE_ONLY (t) = finfo->interface_only; |
| SET_CLASSTYPE_INTERFACE_UNKNOWN_X |
| (t, finfo->interface_unknown); |
| } |
| reset_specialization(); |
| |
| /* Make a declaration for this class in its own scope. */ |
| build_self_reference (); |
| |
| return t; |
| } |
| |
| /* Finish the member declaration given by DECL. */ |
| |
| void |
| finish_member_declaration (tree decl) |
| { |
| if (decl == error_mark_node || decl == NULL_TREE) |
| return; |
| |
| if (decl == void_type_node) |
| /* The COMPONENT was a friend, not a member, and so there's |
| nothing for us to do. */ |
| return; |
| |
| /* We should see only one DECL at a time. */ |
| gcc_assert (DECL_CHAIN (decl) == NULL_TREE); |
| |
| /* Don't add decls after definition. */ |
| gcc_assert (TYPE_BEING_DEFINED (current_class_type) |
| /* We can add lambda types when late parsing default |
| arguments. */ |
| || LAMBDA_TYPE_P (TREE_TYPE (decl))); |
| |
| /* Set up access control for DECL. */ |
| TREE_PRIVATE (decl) |
| = (current_access_specifier == access_private_node); |
| TREE_PROTECTED (decl) |
| = (current_access_specifier == access_protected_node); |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| { |
| TREE_PRIVATE (DECL_TEMPLATE_RESULT (decl)) = TREE_PRIVATE (decl); |
| TREE_PROTECTED (DECL_TEMPLATE_RESULT (decl)) = TREE_PROTECTED (decl); |
| } |
| |
| /* Mark the DECL as a member of the current class, unless it's |
| a member of an enumeration. */ |
| if (TREE_CODE (decl) != CONST_DECL) |
| DECL_CONTEXT (decl) = current_class_type; |
| |
| if (TREE_CODE (decl) == USING_DECL) |
| /* For now, ignore class-scope USING_DECLS, so that debugging |
| backends do not see them. */ |
| DECL_IGNORED_P (decl) = 1; |
| |
| /* Check for bare parameter packs in the non-static data member |
| declaration. */ |
| if (TREE_CODE (decl) == FIELD_DECL) |
| { |
| if (check_for_bare_parameter_packs (TREE_TYPE (decl))) |
| TREE_TYPE (decl) = error_mark_node; |
| if (check_for_bare_parameter_packs (DECL_ATTRIBUTES (decl))) |
| DECL_ATTRIBUTES (decl) = NULL_TREE; |
| } |
| |
| /* [dcl.link] |
| |
| A C language linkage is ignored for the names of class members |
| and the member function type of class member functions. */ |
| if (DECL_LANG_SPECIFIC (decl)) |
| SET_DECL_LANGUAGE (decl, lang_cplusplus); |
| |
| bool add = false; |
| |
| /* Functions and non-functions are added differently. */ |
| if (DECL_DECLARES_FUNCTION_P (decl)) |
| add = add_method (current_class_type, decl, false); |
| /* Enter the DECL into the scope of the class, if the class |
| isn't a closure (whose fields are supposed to be unnamed). */ |
| else if (CLASSTYPE_LAMBDA_EXPR (current_class_type) |
| || pushdecl_class_level (decl)) |
| add = true; |
| |
| if (add) |
| { |
| /* All TYPE_DECLs go at the end of TYPE_FIELDS. Ordinary fields |
| go at the beginning. The reason is that |
| legacy_nonfn_member_lookup searches the list in order, and we |
| want a field name to override a type name so that the "struct |
| stat hack" will work. In particular: |
| |
| struct S { enum E { }; static const int E = 5; int ary[S::E]; } s; |
| |
| is valid. */ |
| |
| if (TREE_CODE (decl) == TYPE_DECL) |
| TYPE_FIELDS (current_class_type) |
| = chainon (TYPE_FIELDS (current_class_type), decl); |
| else |
| { |
| DECL_CHAIN (decl) = TYPE_FIELDS (current_class_type); |
| TYPE_FIELDS (current_class_type) = decl; |
| } |
| |
| maybe_add_class_template_decl_list (current_class_type, decl, |
| /*friend_p=*/0); |
| } |
| } |
| |
| /* Finish processing a complete template declaration. The PARMS are |
| the template parameters. */ |
| |
| void |
| finish_template_decl (tree parms) |
| { |
| if (parms) |
| end_template_decl (); |
| else |
| end_specialization (); |
| } |
| |
| // Returns the template type of the class scope being entered. If we're |
| // entering a constrained class scope. TYPE is the class template |
| // scope being entered and we may need to match the intended type with |
| // a constrained specialization. For example: |
| // |
| // template<Object T> |
| // struct S { void f(); }; #1 |
| // |
| // template<Object T> |
| // void S<T>::f() { } #2 |
| // |
| // We check, in #2, that S<T> refers precisely to the type declared by |
| // #1 (i.e., that the constraints match). Note that the following should |
| // be an error since there is no specialization of S<T> that is |
| // unconstrained, but this is not diagnosed here. |
| // |
| // template<typename T> |
| // void S<T>::f() { } |
| // |
| // We cannot diagnose this problem here since this function also matches |
| // qualified template names that are not part of a definition. For example: |
| // |
| // template<Integral T, Floating_point U> |
| // typename pair<T, U>::first_type void f(T, U); |
| // |
| // Here, it is unlikely that there is a partial specialization of |
| // pair constrained for for Integral and Floating_point arguments. |
| // |
| // The general rule is: if a constrained specialization with matching |
| // constraints is found return that type. Also note that if TYPE is not a |
| // class-type (e.g. a typename type), then no fixup is needed. |
| |
| static tree |
| fixup_template_type (tree type) |
| { |
| // Find the template parameter list at the a depth appropriate to |
| // the scope we're trying to enter. |
| tree parms = current_template_parms; |
| int depth = template_class_depth (type); |
| for (int n = processing_template_decl; n > depth && parms; --n) |
| parms = TREE_CHAIN (parms); |
| if (!parms) |
| return type; |
| tree cur_reqs = TEMPLATE_PARMS_CONSTRAINTS (parms); |
| tree cur_constr = build_constraints (cur_reqs, NULL_TREE); |
| |
| // Search for a specialization whose type and constraints match. |
| tree tmpl = CLASSTYPE_TI_TEMPLATE (type); |
| tree specs = DECL_TEMPLATE_SPECIALIZATIONS (tmpl); |
| while (specs) |
| { |
| tree spec_constr = get_constraints (TREE_VALUE (specs)); |
| |
| // If the type and constraints match a specialization, then we |
| // are entering that type. |
| if (same_type_p (type, TREE_TYPE (specs)) |
| && equivalent_constraints (cur_constr, spec_constr)) |
| return TREE_TYPE (specs); |
| specs = TREE_CHAIN (specs); |
| } |
| |
| // If no specialization matches, then must return the type |
| // previously found. |
| return type; |
| } |
| |
| /* Finish processing a template-id (which names a type) of the form |
| NAME < ARGS >. Return the TYPE_DECL for the type named by the |
| template-id. If ENTERING_SCOPE is nonzero we are about to enter |
| the scope of template-id indicated. */ |
| |
| tree |
| finish_template_type (tree name, tree args, int entering_scope) |
| { |
| tree type; |
| |
| type = lookup_template_class (name, args, |
| NULL_TREE, NULL_TREE, entering_scope, |
| tf_warning_or_error | tf_user); |
| |
| /* If we might be entering the scope of a partial specialization, |
| find the one with the right constraints. */ |
| if (flag_concepts |
| && entering_scope |
| && CLASS_TYPE_P (type) |
| && CLASSTYPE_TEMPLATE_INFO (type) |
| && dependent_type_p (type) |
| && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (type))) |
| type = fixup_template_type (type); |
| |
| if (type == error_mark_node) |
| return type; |
| else if (CLASS_TYPE_P (type) && !alias_type_or_template_p (type)) |
| return TYPE_STUB_DECL (type); |
| else |
| return TYPE_NAME (type); |
| } |
| |
| /* Finish processing a BASE_CLASS with the indicated ACCESS_SPECIFIER. |
| Return a TREE_LIST containing the ACCESS_SPECIFIER and the |
| BASE_CLASS, or NULL_TREE if an error occurred. The |
| ACCESS_SPECIFIER is one of |
| access_{default,public,protected_private}_node. For a virtual base |
| we set TREE_TYPE. */ |
| |
| tree |
| finish_base_specifier (tree base, tree access, bool virtual_p) |
| { |
| tree result; |
| |
| if (base == error_mark_node) |
| { |
| error ("invalid base-class specification"); |
| result = NULL_TREE; |
| } |
| else if (! MAYBE_CLASS_TYPE_P (base)) |
| { |
| error ("%qT is not a class type", base); |
| result = NULL_TREE; |
| } |
| else |
| { |
| if (cp_type_quals (base) != 0) |
| { |
| /* DR 484: Can a base-specifier name a cv-qualified |
| class type? */ |
| base = TYPE_MAIN_VARIANT (base); |
| } |
| result = build_tree_list (access, base); |
| if (virtual_p) |
| TREE_TYPE (result) = integer_type_node; |
| } |
| |
| return result; |
| } |
| |
| /* If FNS is a member function, a set of member functions, or a |
| template-id referring to one or more member functions, return a |
| BASELINK for FNS, incorporating the current access context. |
| Otherwise, return FNS unchanged. */ |
| |
| tree |
| baselink_for_fns (tree fns) |
| { |
| tree scope; |
| tree cl; |
| |
| if (BASELINK_P (fns) |
| || error_operand_p (fns)) |
| return fns; |
| |
| scope = ovl_scope (fns); |
| if (!CLASS_TYPE_P (scope)) |
| return fns; |
| |
| cl = currently_open_derived_class (scope); |
| if (!cl) |
| cl = scope; |
| cl = TYPE_BINFO (cl); |
| return build_baselink (cl, cl, fns, /*optype=*/NULL_TREE); |
| } |
| |
| /* Returns true iff DECL is a variable from a function outside |
| the current one. */ |
| |
| static bool |
| outer_var_p (tree decl) |
| { |
| return ((VAR_P (decl) || TREE_CODE (decl) == PARM_DECL) |
| && DECL_FUNCTION_SCOPE_P (decl) |
| /* Don't get confused by temporaries. */ |
| && DECL_NAME (decl) |
| && (DECL_CONTEXT (decl) != current_function_decl |
| || parsing_nsdmi ())); |
| } |
| |
| /* As above, but also checks that DECL is automatic. */ |
| |
| bool |
| outer_automatic_var_p (tree decl) |
| { |
| return (outer_var_p (decl) |
| && !TREE_STATIC (decl)); |
| } |
| |
| /* DECL satisfies outer_automatic_var_p. Possibly complain about it or |
| rewrite it for lambda capture. |
| |
| If ODR_USE is true, we're being called from mark_use, and we complain about |
| use of constant variables. If ODR_USE is false, we're being called for the |
| id-expression, and we do lambda capture. */ |
| |
| tree |
| process_outer_var_ref (tree decl, tsubst_flags_t complain, bool odr_use) |
| { |
| if (cp_unevaluated_operand) |
| /* It's not a use (3.2) if we're in an unevaluated context. */ |
| return decl; |
| if (decl == error_mark_node) |
| return decl; |
| |
| tree context = DECL_CONTEXT (decl); |
| tree containing_function = current_function_decl; |
| tree lambda_stack = NULL_TREE; |
| tree lambda_expr = NULL_TREE; |
| tree initializer = convert_from_reference (decl); |
| |
| /* Mark it as used now even if the use is ill-formed. */ |
| if (!mark_used (decl, complain)) |
| return error_mark_node; |
| |
| if (parsing_nsdmi ()) |
| containing_function = NULL_TREE; |
| |
| if (containing_function && LAMBDA_FUNCTION_P (containing_function)) |
| { |
| /* Check whether we've already built a proxy. */ |
| tree var = decl; |
| while (is_normal_capture_proxy (var)) |
| var = DECL_CAPTURED_VARIABLE (var); |
| tree d = retrieve_local_specialization (var); |
| |
| if (d && d != decl && is_capture_proxy (d)) |
| { |
| if (DECL_CONTEXT (d) == containing_function) |
| /* We already have an inner proxy. */ |
| return d; |
| else |
| /* We need to capture an outer proxy. */ |
| return process_outer_var_ref (d, complain, odr_use); |
| } |
| } |
| |
| /* If we are in a lambda function, we can move out until we hit |
| 1. the context, |
| 2. a non-lambda function, or |
| 3. a non-default capturing lambda function. */ |
| while (context != containing_function |
| /* containing_function can be null with invalid generic lambdas. */ |
| && containing_function |
| && LAMBDA_FUNCTION_P (containing_function)) |
| { |
| tree closure = DECL_CONTEXT (containing_function); |
| lambda_expr = CLASSTYPE_LAMBDA_EXPR (closure); |
| |
| if (TYPE_CLASS_SCOPE_P (closure)) |
| /* A lambda in an NSDMI (c++/64496). */ |
| break; |
| |
| if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) |
| == CPLD_NONE) |
| break; |
| |
| lambda_stack = tree_cons (NULL_TREE, |
| lambda_expr, |
| lambda_stack); |
| |
| containing_function |
| = decl_function_context (containing_function); |
| } |
| |
| /* In a lambda within a template, wait until instantiation |
| time to implicitly capture. */ |
| if (context == containing_function |
| && DECL_TEMPLATE_INFO (containing_function) |
| && uses_template_parms (DECL_TI_ARGS (containing_function))) |
| return decl; |
| |
| if (lambda_expr && VAR_P (decl) |
| && DECL_ANON_UNION_VAR_P (decl)) |
| { |
| if (complain & tf_error) |
| error ("cannot capture member %qD of anonymous union", decl); |
| return error_mark_node; |
| } |
| /* Do lambda capture when processing the id-expression, not when |
| odr-using a variable. */ |
| if (!odr_use && context == containing_function) |
| { |
| decl = add_default_capture (lambda_stack, |
| /*id=*/DECL_NAME (decl), |
| initializer); |
| } |
| /* Only an odr-use of an outer automatic variable causes an |
| error, and a constant variable can decay to a prvalue |
| constant without odr-use. So don't complain yet. */ |
| else if (!odr_use && decl_constant_var_p (decl)) |
| return decl; |
| else if (lambda_expr) |
| { |
| if (complain & tf_error) |
| { |
| error ("%qD is not captured", decl); |
| tree closure = LAMBDA_EXPR_CLOSURE (lambda_expr); |
| if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) |
| == CPLD_NONE) |
| inform (location_of (closure), |
| "the lambda has no capture-default"); |
| else if (TYPE_CLASS_SCOPE_P (closure)) |
| inform (UNKNOWN_LOCATION, "lambda in local class %q+T cannot " |
| "capture variables from the enclosing context", |
| TYPE_CONTEXT (closure)); |
| inform (DECL_SOURCE_LOCATION (decl), "%q#D declared here", decl); |
| } |
| return error_mark_node; |
| } |
| else |
| { |
| if (complain & tf_error) |
| { |
| error (VAR_P (decl) |
| ? G_("use of local variable with automatic storage from " |
| "containing function") |
| : G_("use of parameter from containing function")); |
| inform (DECL_SOURCE_LOCATION (decl), "%q#D declared here", decl); |
| } |
| return error_mark_node; |
| } |
| return decl; |
| } |
| |
| /* ID_EXPRESSION is a representation of parsed, but unprocessed, |
| id-expression. (See cp_parser_id_expression for details.) SCOPE, |
| if non-NULL, is the type or namespace used to explicitly qualify |
| ID_EXPRESSION. DECL is the entity to which that name has been |
| resolved. |
| |
| *CONSTANT_EXPRESSION_P is true if we are presently parsing a |
| constant-expression. In that case, *NON_CONSTANT_EXPRESSION_P will |
| be set to true if this expression isn't permitted in a |
| constant-expression, but it is otherwise not set by this function. |
| *ALLOW_NON_CONSTANT_EXPRESSION_P is true if we are parsing a |
| constant-expression, but a non-constant expression is also |
| permissible. |
| |
| DONE is true if this expression is a complete postfix-expression; |
| it is false if this expression is followed by '->', '[', '(', etc. |
| ADDRESS_P is true iff this expression is the operand of '&'. |
| TEMPLATE_P is true iff the qualified-id was of the form |
| "A::template B". TEMPLATE_ARG_P is true iff this qualified name |
| appears as a template argument. |
| |
| If an error occurs, and it is the kind of error that might cause |
| the parser to abort a tentative parse, *ERROR_MSG is filled in. It |
| is the caller's responsibility to issue the message. *ERROR_MSG |
| will be a string with static storage duration, so the caller need |
| not "free" it. |
| |
| Return an expression for the entity, after issuing appropriate |
| diagnostics. This function is also responsible for transforming a |
| reference to a non-static member into a COMPONENT_REF that makes |
| the use of "this" explicit. |
| |
| Upon return, *IDK will be filled in appropriately. */ |
| cp_expr |
| finish_id_expression (tree id_expression, |
| tree decl, |
| tree scope, |
| cp_id_kind *idk, |
| bool integral_constant_expression_p, |
| bool allow_non_integral_constant_expression_p, |
| bool *non_integral_constant_expression_p, |
| bool template_p, |
| bool done, |
| bool address_p, |
| bool template_arg_p, |
| const char **error_msg, |
| location_t location) |
| { |
| decl = strip_using_decl (decl); |
| |
| /* Initialize the output parameters. */ |
| *idk = CP_ID_KIND_NONE; |
| *error_msg = NULL; |
| |
| if (id_expression == error_mark_node) |
| return error_mark_node; |
| /* If we have a template-id, then no further lookup is |
| required. If the template-id was for a template-class, we |
| will sometimes have a TYPE_DECL at this point. */ |
| else if (TREE_CODE (decl) == TEMPLATE_ID_EXPR |
| || TREE_CODE (decl) == TYPE_DECL) |
| ; |
| /* Look up the name. */ |
| else |
| { |
| if (decl == error_mark_node) |
| { |
| /* Name lookup failed. */ |
| if (scope |
| && (!TYPE_P (scope) |
| || (!dependent_type_p (scope) |
| && !(identifier_p (id_expression) |
| && IDENTIFIER_CONV_OP_P (id_expression) |
| && dependent_type_p (TREE_TYPE (id_expression)))))) |
| { |
| /* If the qualifying type is non-dependent (and the name |
| does not name a conversion operator to a dependent |
| type), issue an error. */ |
| qualified_name_lookup_error (scope, id_expression, decl, location); |
| return error_mark_node; |
| } |
| else if (!scope) |
| { |
| /* It may be resolved via Koenig lookup. */ |
| *idk = CP_ID_KIND_UNQUALIFIED; |
| return id_expression; |
| } |
| else |
| decl = id_expression; |
| } |
| /* If DECL is a variable that would be out of scope under |
| ANSI/ISO rules, but in scope in the ARM, name lookup |
| will succeed. Issue a diagnostic here. */ |
| else |
| decl = check_for_out_of_scope_variable (decl); |
| |
| /* Remember that the name was used in the definition of |
| the current class so that we can check later to see if |
| the meaning would have been different after the class |
| was entirely defined. */ |
| if (!scope && decl != error_mark_node && identifier_p (id_expression)) |
| maybe_note_name_used_in_class (id_expression, decl); |
| |
| /* A use in unevaluated operand might not be instantiated appropriately |
| if tsubst_copy builds a dummy parm, or if we never instantiate a |
| generic lambda, so mark it now. */ |
| if (processing_template_decl && cp_unevaluated_operand) |
| mark_type_use (decl); |
| |
| /* Disallow uses of local variables from containing functions, except |
| within lambda-expressions. */ |
| if (outer_automatic_var_p (decl)) |
| { |
| decl = process_outer_var_ref (decl, tf_warning_or_error); |
| if (decl == error_mark_node) |
| return error_mark_node; |
| } |
| |
| /* Also disallow uses of function parameters outside the function |
| body, except inside an unevaluated context (i.e. decltype). */ |
| if (TREE_CODE (decl) == PARM_DECL |
| && DECL_CONTEXT (decl) == NULL_TREE |
| && !cp_unevaluated_operand) |
| { |
| *error_msg = G_("use of parameter outside function body"); |
| return error_mark_node; |
| } |
| } |
| |
| /* If we didn't find anything, or what we found was a type, |
| then this wasn't really an id-expression. */ |
| if (TREE_CODE (decl) == TEMPLATE_DECL |
| && !DECL_FUNCTION_TEMPLATE_P (decl)) |
| { |
| *error_msg = G_("missing template arguments"); |
| return error_mark_node; |
| } |
| else if (TREE_CODE (decl) == TYPE_DECL |
| || TREE_CODE (decl) == NAMESPACE_DECL) |
| { |
| *error_msg = G_("expected primary-expression"); |
| return error_mark_node; |
| } |
| |
| /* If the name resolved to a template parameter, there is no |
| need to look it up again later. */ |
| if ((TREE_CODE (decl) == CONST_DECL && DECL_TEMPLATE_PARM_P (decl)) |
| || TREE_CODE (decl) == TEMPLATE_PARM_INDEX) |
| { |
| tree r; |
| |
| *idk = CP_ID_KIND_NONE; |
| if (TREE_CODE (decl) == TEMPLATE_PARM_INDEX) |
| decl = TEMPLATE_PARM_DECL (decl); |
| r = convert_from_reference (DECL_INITIAL (decl)); |
| |
| if (integral_constant_expression_p |
| && !dependent_type_p (TREE_TYPE (decl)) |
| && !(INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (r)))) |
| { |
| if (!allow_non_integral_constant_expression_p) |
| error ("template parameter %qD of type %qT is not allowed in " |
| "an integral constant expression because it is not of " |
| "integral or enumeration type", decl, TREE_TYPE (decl)); |
| *non_integral_constant_expression_p = true; |
| } |
| return r; |
| } |
| else |
| { |
| bool dependent_p = type_dependent_expression_p (decl); |
| |
| /* If the declaration was explicitly qualified indicate |
| that. The semantics of `A::f(3)' are different than |
| `f(3)' if `f' is virtual. */ |
| *idk = (scope |
| ? CP_ID_KIND_QUALIFIED |
| : (TREE_CODE (decl) == TEMPLATE_ID_EXPR |
| ? CP_ID_KIND_TEMPLATE_ID |
| : (dependent_p |
| ? CP_ID_KIND_UNQUALIFIED_DEPENDENT |
| : CP_ID_KIND_UNQUALIFIED))); |
| |
| if (dependent_p |
| && DECL_P (decl) |
| && any_dependent_type_attributes_p (DECL_ATTRIBUTES (decl))) |
| /* Dependent type attributes on the decl mean that the TREE_TYPE is |
| wrong, so just return the identifier. */ |
| return id_expression; |
| |
| if (TREE_CODE (decl) == NAMESPACE_DECL) |
| { |
| error ("use of namespace %qD as expression", decl); |
| return error_mark_node; |
| } |
| else if (DECL_CLASS_TEMPLATE_P (decl)) |
| { |
| error ("use of class template %qT as expression", decl); |
| return error_mark_node; |
| } |
| else if (TREE_CODE (decl) == TREE_LIST) |
| { |
| /* Ambiguous reference to base members. */ |
| error ("request for member %qD is ambiguous in " |
| "multiple inheritance lattice", id_expression); |
| print_candidates (decl); |
| return error_mark_node; |
| } |
| |
| /* Mark variable-like entities as used. Functions are similarly |
| marked either below or after overload resolution. */ |
| if ((VAR_P (decl) |
| || TREE_CODE (decl) == PARM_DECL |
| || TREE_CODE (decl) == CONST_DECL |
| || TREE_CODE (decl) == RESULT_DECL) |
| && !mark_used (decl)) |
| return error_mark_node; |
| |
| /* Only certain kinds of names are allowed in constant |
| expression. Template parameters have already |
| been handled above. */ |
| if (! error_operand_p (decl) |
| && !dependent_p |
| && integral_constant_expression_p |
| && ! decl_constant_var_p (decl) |
| && TREE_CODE (decl) != CONST_DECL |
| && ! builtin_valid_in_constant_expr_p (decl)) |
| { |
| if (!allow_non_integral_constant_expression_p) |
| { |
| error ("%qD cannot appear in a constant-expression", decl); |
| return error_mark_node; |
| } |
| *non_integral_constant_expression_p = true; |
| } |
| |
| tree wrap; |
| if (VAR_P (decl) |
| && !cp_unevaluated_operand |
| && !processing_template_decl |
| && (TREE_STATIC (decl) || DECL_EXTERNAL (decl)) |
| && CP_DECL_THREAD_LOCAL_P (decl) |
| && (wrap = get_tls_wrapper_fn (decl))) |
| { |
| /* Replace an evaluated use of the thread_local variable with |
| a call to its wrapper. */ |
| decl = build_cxx_call (wrap, 0, NULL, tf_warning_or_error); |
| } |
| else if (TREE_CODE (decl) == TEMPLATE_ID_EXPR |
| && !dependent_p |
| && variable_template_p (TREE_OPERAND (decl, 0))) |
| { |
| decl = finish_template_variable (decl); |
| mark_used (decl); |
| decl = convert_from_reference (decl); |
| } |
| else if (scope) |
| { |
| if (TREE_CODE (decl) == SCOPE_REF) |
| { |
| gcc_assert (same_type_p (scope, TREE_OPERAND (decl, 0))); |
| decl = TREE_OPERAND (decl, 1); |
| } |
| |
| decl = (adjust_result_of_qualified_name_lookup |
| (decl, scope, current_nonlambda_class_type())); |
| |
| if (TREE_CODE (decl) == FUNCTION_DECL) |
| mark_used (decl); |
| |
| if (TYPE_P (scope)) |
| decl = finish_qualified_id_expr (scope, |
| decl, |
| done, |
| address_p, |
| template_p, |
| template_arg_p, |
| tf_warning_or_error); |
| else |
| decl = convert_from_reference (decl); |
| } |
| else if (TREE_CODE (decl) == FIELD_DECL) |
| { |
| /* Since SCOPE is NULL here, this is an unqualified name. |
| Access checking has been performed during name lookup |
| already. Turn off checking to avoid duplicate errors. */ |
| push_deferring_access_checks (dk_no_check); |
| decl = finish_non_static_data_member (decl, NULL_TREE, |
| /*qualifying_scope=*/NULL_TREE); |
| pop_deferring_access_checks (); |
| } |
| else if (is_overloaded_fn (decl)) |
| { |
| tree first_fn = get_first_fn (decl); |
| |
| if (TREE_CODE (first_fn) == TEMPLATE_DECL) |
| first_fn = DECL_TEMPLATE_RESULT (first_fn); |
| |
| /* [basic.def.odr]: "A function whose name appears as a |
| potentially-evaluated expression is odr-used if it is the unique |
| lookup result". |
| |
| But only mark it if it's a complete postfix-expression; in a call, |
| ADL might select a different function, and we'll call mark_used in |
| build_over_call. */ |
| if (done |
| && !really_overloaded_fn (decl) |
| && !mark_used (first_fn)) |
| return error_mark_node; |
| |
| if (!template_arg_p |
| && TREE_CODE (first_fn) == FUNCTION_DECL |
| && DECL_FUNCTION_MEMBER_P (first_fn) |
| && !shared_member_p (decl)) |
| { |
| /* A set of member functions. */ |
| decl = maybe_dummy_object (DECL_CONTEXT (first_fn), 0); |
| return finish_class_member_access_expr (decl, id_expression, |
| /*template_p=*/false, |
| tf_warning_or_error); |
| } |
| |
| decl = baselink_for_fns (decl); |
| } |
| else |
| { |
| if (DECL_P (decl) && DECL_NONLOCAL (decl) |
| && DECL_CLASS_SCOPE_P (decl)) |
| { |
| tree context = context_for_name_lookup (decl); |
| if (context != current_class_type) |
| { |
| tree path = currently_open_derived_class (context); |
| perform_or_defer_access_check (TYPE_BINFO (path), |
| decl, decl, |
| tf_warning_or_error); |
| } |
| } |
| |
| decl = convert_from_reference (decl); |
| } |
| } |
| |
| return cp_expr (decl, location); |
| } |
| |
| /* Implement the __typeof keyword: Return the type of EXPR, suitable for |
| use as a type-specifier. */ |
| |
| tree |
| finish_typeof (tree expr) |
| { |
| tree type; |
| |
| if (type_dependent_expression_p (expr)) |
| { |
| type = cxx_make_type (TYPEOF_TYPE); |
| TYPEOF_TYPE_EXPR (type) = expr; |
| SET_TYPE_STRUCTURAL_EQUALITY (type); |
| |
| return type; |
| } |
| |
| expr = mark_type_use (expr); |
| |
| type = unlowered_expr_type (expr); |
| |
| if (!type || type == unknown_type_node) |
| { |
| error ("type of %qE is unknown", expr); |
| return error_mark_node; |
| } |
| |
| return type; |
| } |
| |
| /* Implement the __underlying_type keyword: Return the underlying |
| type of TYPE, suitable for use as a type-specifier. */ |
| |
| tree |
| finish_underlying_type (tree type) |
| { |
| tree underlying_type; |
| |
| if (processing_template_decl) |
| { |
| underlying_type = cxx_make_type (UNDERLYING_TYPE); |
| UNDERLYING_TYPE_TYPE (underlying_type) = type; |
| SET_TYPE_STRUCTURAL_EQUALITY (underlying_type); |
| |
| return underlying_type; |
| } |
| |
| if (!complete_type_or_else (type, NULL_TREE)) |
| return error_mark_node; |
| |
| if (TREE_CODE (type) != ENUMERAL_TYPE) |
| { |
| error ("%qT is not an enumeration type", type); |
| return error_mark_node; |
| } |
| |
| underlying_type = ENUM_UNDERLYING_TYPE (type); |
| |
| /* Fixup necessary in this case because ENUM_UNDERLYING_TYPE |
| includes TYPE_MIN_VALUE and TYPE_MAX_VALUE information. |
| See finish_enum_value_list for details. */ |
| if (!ENUM_FIXED_UNDERLYING_TYPE_P (type)) |
| underlying_type |
| = c_common_type_for_mode (TYPE_MODE (underlying_type), |
| TYPE_UNSIGNED (underlying_type)); |
| |
| return underlying_type; |
| } |
| |
| /* Implement the __direct_bases keyword: Return the direct base classes |
| of type */ |
| |
| tree |
| calculate_direct_bases (tree type) |
| { |
| vec<tree, va_gc> *vector = make_tree_vector(); |
| tree bases_vec = NULL_TREE; |
| vec<tree, va_gc> *base_binfos; |
| tree binfo; |
| unsigned i; |
| |
| complete_type (type); |
| |
| if (!NON_UNION_CLASS_TYPE_P (type)) |
| return make_tree_vec (0); |
| |
| base_binfos = BINFO_BASE_BINFOS (TYPE_BINFO (type)); |
| |
| /* Virtual bases are initialized first */ |
| for (i = 0; base_binfos->iterate (i, &binfo); i++) |
| { |
| if (BINFO_VIRTUAL_P (binfo)) |
| { |
| vec_safe_push (vector, binfo); |
| } |
| } |
| |
| /* Now non-virtuals */ |
| for (i = 0; base_binfos->iterate (i, &binfo); i++) |
| { |
| if (!BINFO_VIRTUAL_P (binfo)) |
| { |
| vec_safe_push (vector, binfo); |
| } |
| } |
| |
| |
| bases_vec = make_tree_vec (vector->length ()); |
| |
| for (i = 0; i < vector->length (); ++i) |
| { |
| TREE_VEC_ELT (bases_vec, i) = BINFO_TYPE ((*vector)[i]); |
| } |
| return bases_vec; |
| } |
| |
| /* Implement the __bases keyword: Return the base classes |
| of type */ |
| |
| /* Find morally non-virtual base classes by walking binfo hierarchy */ |
| /* Virtual base classes are handled separately in finish_bases */ |
| |
| static tree |
| dfs_calculate_bases_pre (tree binfo, void * /*data_*/) |
| { |
| /* Don't walk bases of virtual bases */ |
| return BINFO_VIRTUAL_P (binfo) ? dfs_skip_bases : NULL_TREE; |
| } |
| |
| static tree |
| dfs_calculate_bases_post (tree binfo, void *data_) |
| { |
| vec<tree, va_gc> **data = ((vec<tree, va_gc> **) data_); |
| if (!BINFO_VIRTUAL_P (binfo)) |
| { |
| vec_safe_push (*data, BINFO_TYPE (binfo)); |
| } |
| return NULL_TREE; |
| } |
| |
| /* Calculates the morally non-virtual base classes of a class */ |
| static vec<tree, va_gc> * |
| calculate_bases_helper (tree type) |
| { |
| vec<tree, va_gc> *vector = make_tree_vector(); |
| |
| /* Now add non-virtual base classes in order of construction */ |
| if (TYPE_BINFO (type)) |
| dfs_walk_all (TYPE_BINFO (type), |
| dfs_calculate_bases_pre, dfs_calculate_bases_post, &vector); |
| return vector; |
| } |
| |
| tree |
| calculate_bases (tree type) |
| { |
| vec<tree, va_gc> *vector = make_tree_vector(); |
| tree bases_vec = NULL_TREE; |
| unsigned i; |
| vec<tree, va_gc> *vbases; |
| vec<tree, va_gc> *nonvbases; |
| tree binfo; |
| |
| complete_type (type); |
| |
| if (!NON_UNION_CLASS_TYPE_P (type)) |
| return make_tree_vec (0); |
| |
| /* First go through virtual base classes */ |
| for (vbases = CLASSTYPE_VBASECLASSES (type), i = 0; |
| vec_safe_iterate (vbases, i, &binfo); i++) |
| { |
| vec<tree, va_gc> *vbase_bases; |
| vbase_bases = calculate_bases_helper (BINFO_TYPE (binfo)); |
| vec_safe_splice (vector, vbase_bases); |
| release_tree_vector (vbase_bases); |
| } |
| |
| /* Now for the non-virtual bases */ |
| nonvbases = calculate_bases_helper (type); |
| vec_safe_splice (vector, nonvbases); |
| release_tree_vector (nonvbases); |
| |
| /* Note that during error recovery vector->length can even be zero. */ |
| if (vector->length () > 1) |
| { |
| /* Last element is entire class, so don't copy */ |
| bases_vec = make_tree_vec (vector->length() - 1); |
| |
| for (i = 0; i < vector->length () - 1; ++i) |
| TREE_VEC_ELT (bases_vec, i) = (*vector)[i]; |
| } |
| else |
| bases_vec = make_tree_vec (0); |
| |
| release_tree_vector (vector); |
| return bases_vec; |
| } |
| |
| tree |
| finish_bases (tree type, bool direct) |
| { |
| tree bases = NULL_TREE; |
| |
| if (!processing_template_decl) |
| { |
| /* Parameter packs can only be used in templates */ |
| error ("Parameter pack __bases only valid in template declaration"); |
| return error_mark_node; |
| } |
| |
| bases = cxx_make_type (BASES); |
| BASES_TYPE (bases) = type; |
| BASES_DIRECT (bases) = direct; |
| SET_TYPE_STRUCTURAL_EQUALITY (bases); |
| |
| return bases; |
| } |
| |
| /* Perform C++-specific checks for __builtin_offsetof before calling |
| fold_offsetof. */ |
| |
| tree |
| finish_offsetof (tree object_ptr, tree expr, location_t loc) |
| { |
| /* If we're processing a template, we can't finish the semantics yet. |
| Otherwise we can fold the entire expression now. */ |
| if (processing_template_decl) |
| { |
| expr = build2 (OFFSETOF_EXPR, size_type_node, expr, object_ptr); |
| SET_EXPR_LOCATION (expr, loc); |
| return expr; |
| } |
| |
| if (TREE_CODE (expr) == PSEUDO_DTOR_EXPR) |
| { |
| error ("cannot apply %<offsetof%> to destructor %<~%T%>", |
| TREE_OPERAND (expr, 2)); |
| return error_mark_node; |
| } |
| if (TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE |
| || TREE_CODE (TREE_TYPE (expr)) == METHOD_TYPE |
| || TREE_TYPE (expr) == unknown_type_node) |
| { |
| if (INDIRECT_REF_P (expr)) |
| error ("second operand of %<offsetof%> is neither a single " |
| "identifier nor a sequence of member accesses and " |
| "array references"); |
| else |
| { |
| if (TREE_CODE (expr) == COMPONENT_REF |
| || TREE_CODE (expr) == COMPOUND_EXPR) |
| expr = TREE_OPERAND (expr, 1); |
| error ("cannot apply %<offsetof%> to member function %qD", expr); |
| } |
| return error_mark_node; |
| } |
| if (REFERENCE_REF_P (expr)) |
| expr = TREE_OPERAND (expr, 0); |
| if (!complete_type_or_else (TREE_TYPE (TREE_TYPE (object_ptr)), object_ptr)) |
| return error_mark_node; |
| if (warn_invalid_offsetof |
| && CLASS_TYPE_P (TREE_TYPE (TREE_TYPE (object_ptr))) |
| && CLASSTYPE_NON_STD_LAYOUT (TREE_TYPE (TREE_TYPE (object_ptr))) |
| && cp_unevaluated_operand == 0) |
| pedwarn (loc, OPT_Winvalid_offsetof, |
| "offsetof within non-standard-layout type %qT is undefined", |
| TREE_TYPE (TREE_TYPE (object_ptr))); |
| return fold_offsetof (expr); |
| } |
| |
| /* Replace the AGGR_INIT_EXPR at *TP with an equivalent CALL_EXPR. This |
| function is broken out from the above for the benefit of the tree-ssa |
| project. */ |
| |
| void |
| simplify_aggr_init_expr (tree *tp) |
| { |
| tree aggr_init_expr = *tp; |
| |
| /* Form an appropriate CALL_EXPR. */ |
| tree fn = AGGR_INIT_EXPR_FN (aggr_init_expr); |
| tree slot = AGGR_INIT_EXPR_SLOT (aggr_init_expr); |
| tree type = TREE_TYPE (slot); |
| |
| tree call_expr; |
| enum style_t { ctor, arg, pcc } style; |
| |
| if (AGGR_INIT_VIA_CTOR_P (aggr_init_expr)) |
| style = ctor; |
| #ifdef PCC_STATIC_STRUCT_RETURN |
| else if (1) |
| style = pcc; |
| #endif |
| else |
| { |
| gcc_assert (TREE_ADDRESSABLE (type)); |
| style = arg; |
| } |
| |
| call_expr = build_call_array_loc (input_location, |
| TREE_TYPE (TREE_TYPE (TREE_TYPE (fn))), |
| fn, |
| aggr_init_expr_nargs (aggr_init_expr), |
| AGGR_INIT_EXPR_ARGP (aggr_init_expr)); |
| TREE_NOTHROW (call_expr) = TREE_NOTHROW (aggr_init_expr); |
| CALL_FROM_THUNK_P (call_expr) = AGGR_INIT_FROM_THUNK_P (aggr_init_expr); |
| CALL_EXPR_OPERATOR_SYNTAX (call_expr) |
| = CALL_EXPR_OPERATOR_SYNTAX (aggr_init_expr); |
| CALL_EXPR_ORDERED_ARGS (call_expr) = CALL_EXPR_ORDERED_ARGS (aggr_init_expr); |
| CALL_EXPR_REVERSE_ARGS (call_expr) = CALL_EXPR_REVERSE_ARGS (aggr_init_expr); |
| /* Preserve CILK_SPAWN flag. */ |
| EXPR_CILK_SPAWN (call_expr) = EXPR_CILK_SPAWN (aggr_init_expr); |
| |
| if (style == ctor) |
| { |
| /* Replace the first argument to the ctor with the address of the |
| slot. */ |
| cxx_mark_addressable (slot); |
| CALL_EXPR_ARG (call_expr, 0) = |
| build1 (ADDR_EXPR, build_pointer_type (type), slot); |
| } |
| else if (style == arg) |
| { |
| /* Just mark it addressable here, and leave the rest to |
| expand_call{,_inline}. */ |
| cxx_mark_addressable (slot); |
| CALL_EXPR_RETURN_SLOT_OPT (call_expr) = true; |
| call_expr = build2 (INIT_EXPR, TREE_TYPE (call_expr), slot, call_expr); |
| } |
| else if (style == pcc) |
| { |
| /* If we're using the non-reentrant PCC calling convention, then we |
| need to copy the returned value out of the static buffer into the |
| SLOT. */ |
| push_deferring_access_checks (dk_no_check); |
| call_expr = build_aggr_init (slot, call_expr, |
| DIRECT_BIND | LOOKUP_ONLYCONVERTING, |
| tf_warning_or_error); |
| pop_deferring_access_checks (); |
| call_expr = build2 (COMPOUND_EXPR, TREE_TYPE (slot), call_expr, slot); |
| } |
| |
| if (AGGR_INIT_ZERO_FIRST (aggr_init_expr)) |
| { |
| tree init = build_zero_init (type, NULL_TREE, |
| /*static_storage_p=*/false); |
| init = build2 (INIT_EXPR, void_type_node, slot, init); |
| call_expr = build2 (COMPOUND_EXPR, TREE_TYPE (call_expr), |
| init, call_expr); |
| } |
| |
| *tp = call_expr; |
| } |
| |
| /* Emit all thunks to FN that should be emitted when FN is emitted. */ |
| |
| void |
| emit_associated_thunks (tree fn) |
| { |
| /* When we use vcall offsets, we emit thunks with the virtual |
| functions to which they thunk. The whole point of vcall offsets |
| is so that you can know statically the entire set of thunks that |
| will ever be needed for a given virtual function, thereby |
| enabling you to output all the thunks with the function itself. */ |
| if (DECL_VIRTUAL_P (fn) |
| /* Do not emit thunks for extern template instantiations. */ |
| && ! DECL_REALLY_EXTERN (fn)) |
| { |
| tree thunk; |
| |
| for (thunk = DECL_THUNKS (fn); thunk; thunk = DECL_CHAIN (thunk)) |
| { |
| if (!THUNK_ALIAS (thunk)) |
| { |
| use_thunk (thunk, /*emit_p=*/1); |
| if (DECL_RESULT_THUNK_P (thunk)) |
| { |
| tree probe; |
| |
| for (probe = DECL_THUNKS (thunk); |
| probe; probe = DECL_CHAIN (probe)) |
| use_thunk (probe, /*emit_p=*/1); |
| } |
| } |
| else |
| gcc_assert (!DECL_THUNKS (thunk)); |
| } |
| } |
| } |
| |
| /* Generate RTL for FN. */ |
| |
| bool |
| expand_or_defer_fn_1 (tree fn) |
| { |
| /* When the parser calls us after finishing the body of a template |
| function, we don't really want to expand the body. */ |
| if (processing_template_decl) |
| { |
| /* Normally, collection only occurs in rest_of_compilation. So, |
| if we don't collect here, we never collect junk generated |
| during the processing of templates until we hit a |
| non-template function. It's not safe to do this inside a |
| nested class, though, as the parser may have local state that |
| is not a GC root. */ |
| if (!function_depth) |
| ggc_collect (); |
| return false; |
| } |
| |
| gcc_assert (DECL_SAVED_TREE (fn)); |
| |
| /* We make a decision about linkage for these functions at the end |
| of the compilation. Until that point, we do not want the back |
| end to output them -- but we do want it to see the bodies of |
| these functions so that it can inline them as appropriate. */ |
| if (DECL_DECLARED_INLINE_P (fn) || DECL_IMPLICIT_INSTANTIATION (fn)) |
| { |
| if (DECL_INTERFACE_KNOWN (fn)) |
| /* We've already made a decision as to how this function will |
| be handled. */; |
| else if (!at_eof) |
| tentative_decl_linkage (fn); |
| else |
| import_export_decl (fn); |
| |
| /* If the user wants us to keep all inline functions, then mark |
| this function as needed so that finish_file will make sure to |
| output it later. Similarly, all dllexport'd functions must |
| be emitted; there may be callers in other DLLs. */ |
| if (DECL_DECLARED_INLINE_P (fn) |
| && !DECL_REALLY_EXTERN (fn) |
| && (flag_keep_inline_functions |
| || (flag_keep_inline_dllexport |
| && lookup_attribute ("dllexport", DECL_ATTRIBUTES (fn))))) |
| { |
| mark_needed (fn); |
| DECL_EXTERNAL (fn) = 0; |
| } |
| } |
| |
| /* If this is a constructor or destructor body, we have to clone |
| it. */ |
| if (maybe_clone_body (fn)) |
| { |
| /* We don't want to process FN again, so pretend we've written |
| it out, even though we haven't. */ |
| TREE_ASM_WRITTEN (fn) = 1; |
| /* If this is a constexpr function, keep DECL_SAVED_TREE. */ |
| if (!DECL_DECLARED_CONSTEXPR_P (fn)) |
| DECL_SAVED_TREE (fn) = NULL_TREE; |
| return false; |
| } |
| |
| /* There's no reason to do any of the work here if we're only doing |
| semantic analysis; this code just generates RTL. */ |
| if (flag_syntax_only) |
| return false; |
| |
| return true; |
| } |
| |
| void |
| expand_or_defer_fn (tree fn) |
| { |
| if (expand_or_defer_fn_1 (fn)) |
| { |
| function_depth++; |
| |
| /* Expand or defer, at the whim of the compilation unit manager. */ |
| cgraph_node::finalize_function (fn, function_depth > 1); |
| emit_associated_thunks (fn); |
| |
| function_depth--; |
| } |
| } |
| |
| struct nrv_data |
| { |
| nrv_data () : visited (37) {} |
| |
| tree var; |
| tree result; |
| hash_table<nofree_ptr_hash <tree_node> > visited; |
| }; |
| |
| /* Helper function for walk_tree, used by finalize_nrv below. */ |
| |
| static tree |
| finalize_nrv_r (tree* tp, int* walk_subtrees, void* data) |
| { |
| struct nrv_data *dp = (struct nrv_data *)data; |
| tree_node **slot; |
| |
| /* No need to walk into types. There wouldn't be any need to walk into |
| non-statements, except that we have to consider STMT_EXPRs. */ |
| if (TYPE_P (*tp)) |
| *walk_subtrees = 0; |
| /* Change all returns to just refer to the RESULT_DECL; this is a nop, |
| but differs from using NULL_TREE in that it indicates that we care |
| about the value of the RESULT_DECL. */ |
| else if (TREE_CODE (*tp) == RETURN_EXPR) |
| TREE_OPERAND (*tp, 0) = dp->result; |
| /* Change all cleanups for the NRV to only run when an exception is |
| thrown. */ |
| else if (TREE_CODE (*tp) == CLEANUP_STMT |
| && CLEANUP_DECL (*tp) == dp->var) |
| CLEANUP_EH_ONLY (*tp) = 1; |
| /* Replace the DECL_EXPR for the NRV with an initialization of the |
| RESULT_DECL, if needed. */ |
| else if (TREE_CODE (*tp) == DECL_EXPR |
| && DECL_EXPR_DECL (*tp) == dp->var) |
| { |
| tree init; |
| if (DECL_INITIAL (dp->var) |
| && DECL_INITIAL (dp->var) != error_mark_node) |
| init = build2 (INIT_EXPR, void_type_node, dp->result, |
| DECL_INITIAL (dp->var)); |
| else |
| init = build_empty_stmt (EXPR_LOCATION (*tp)); |
| DECL_INITIAL (dp->var) = NULL_TREE; |
| SET_EXPR_LOCATION (init, EXPR_LOCATION (*tp)); |
| *tp = init; |
| } |
| /* And replace all uses of the NRV with the RESULT_DECL. */ |
| else if (*tp == dp->var) |
| *tp = dp->result; |
| |
| /* Avoid walking into the same tree more than once. Unfortunately, we |
| can't just use walk_tree_without duplicates because it would only call |
| us for the first occurrence of dp->var in the function body. */ |
| slot = dp->visited.find_slot (*tp, INSERT); |
| if (*slot) |
| *walk_subtrees = 0; |
| else |
| *slot = *tp; |
| |
| /* Keep iterating. */ |
| return NULL_TREE; |
| } |
| |
| /* Called from finish_function to implement the named return value |
| optimization by overriding all the RETURN_EXPRs and pertinent |
| CLEANUP_STMTs and replacing all occurrences of VAR with RESULT, the |
| RESULT_DECL for the function. */ |
| |
| void |
| finalize_nrv (tree *tp, tree var, tree result) |
| { |
| struct nrv_data data; |
| |
| /* Copy name from VAR to RESULT. */ |
| DECL_NAME (result) = DECL_NAME (var); |
| /* Don't forget that we take its address. */ |
| TREE_ADDRESSABLE (result) = TREE_ADDRESSABLE (var); |
| /* Finally set DECL_VALUE_EXPR to avoid assigning |
| a stack slot at -O0 for the original var and debug info |
| uses RESULT location for VAR. */ |
| SET_DECL_VALUE_EXPR (var, result); |
| DECL_HAS_VALUE_EXPR_P (var) = 1; |
| |
| data.var = var; |
| data.result = result; |
| cp_walk_tree (tp, finalize_nrv_r, &data, 0); |
| } |
| |
| /* Create CP_OMP_CLAUSE_INFO for clause C. Returns true if it is invalid. */ |
| |
| bool |
| cxx_omp_create_clause_info (tree c, tree type, bool need_default_ctor, |
| bool need_copy_ctor, bool need_copy_assignment, |
| bool need_dtor) |
| { |
| int save_errorcount = errorcount; |
| tree info, t; |
| |
| /* Always allocate 3 elements for simplicity. These are the |
| function decls for the ctor, dtor, and assignment op. |
| This layout is known to the three lang hooks, |
| cxx_omp_clause_default_init, cxx_omp_clause_copy_init, |
| and cxx_omp_clause_assign_op. */ |
| info = make_tree_vec (3); |
| CP_OMP_CLAUSE_INFO (c) = info; |
| |
| if (need_default_ctor || need_copy_ctor) |
| { |
| if (need_default_ctor) |
| t = get_default_ctor (type); |
| else |
| t = get_copy_ctor (type, tf_warning_or_error); |
| |
| if (t && !trivial_fn_p (t)) |
| TREE_VEC_ELT (info, 0) = t; |
| } |
| |
| if (need_dtor && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type)) |
| TREE_VEC_ELT (info, 1) = get_dtor (type, tf_warning_or_error); |
| |
| if (need_copy_assignment) |
| { |
| t = get_copy_assign (type); |
| |
| if (t && !trivial_fn_p (t)) |
| TREE_VEC_ELT (info, 2) = t; |
| } |
| |
| return errorcount != save_errorcount; |
| } |
| |
| /* If DECL is DECL_OMP_PRIVATIZED_MEMBER, return corresponding |
| FIELD_DECL, otherwise return DECL itself. */ |
| |
| static tree |
| omp_clause_decl_field (tree decl) |
| { |
| if (VAR_P (decl) |
| && DECL_HAS_VALUE_EXPR_P (decl) |
| && DECL_ARTIFICIAL (decl) |
| && DECL_LANG_SPECIFIC (decl) |
| && DECL_OMP_PRIVATIZED_MEMBER (decl)) |
| { |
| tree f = DECL_VALUE_EXPR (decl); |
| if (TREE_CODE (f) == INDIRECT_REF) |
| f = TREE_OPERAND (f, 0); |
| if (TREE_CODE (f) == COMPONENT_REF) |
| { |
| f = TREE_OPERAND (f, 1); |
| gcc_assert (TREE_CODE (f) == FIELD_DECL); |
| return f; |
| } |
| } |
| return NULL_TREE; |
| } |
| |
| /* Adjust DECL if needed for printing using %qE. */ |
| |
| static tree |
| omp_clause_printable_decl (tree decl) |
| { |
| tree t = omp_clause_decl_field (decl); |
| if (t) |
| return t; |
| return decl; |
| } |
| |
| /* For a FIELD_DECL F and corresponding DECL_OMP_PRIVATIZED_MEMBER |
| VAR_DECL T that doesn't need a DECL_EXPR added, record it for |
| privatization. */ |
| |
| static void |
| omp_note_field_privatization (tree f, tree t) |
| { |
| if (!omp_private_member_map) |
| omp_private_member_map = new hash_map<tree, tree>; |
| tree &v = omp_private_member_map->get_or_insert (f); |
| if (v == NULL_TREE) |
| { |
| v = t; |
| omp_private_member_vec.safe_push (f); |
| /* Signal that we don't want to create DECL_EXPR for this dummy var. */ |
| omp_private_member_vec.safe_push (integer_zero_node); |
| } |
| } |
| |
| /* Privatize FIELD_DECL T, return corresponding DECL_OMP_PRIVATIZED_MEMBER |
| dummy VAR_DECL. */ |
| |
| tree |
| omp_privatize_field (tree t, bool shared) |
| { |
| tree m = finish_non_static_data_member (t, NULL_TREE, NULL_TREE); |
| if (m == error_mark_node) |
| return error_mark_node; |
| if (!omp_private_member_map && !shared) |
| omp_private_member_map = new hash_map<tree, tree>; |
| if (TREE_CODE (TREE_TYPE (t)) == REFERENCE_TYPE) |
| { |
| gcc_assert (TREE_CODE (m) == INDIRECT_REF); |
| m = TREE_OPERAND (m, 0); |
| } |
| tree vb = NULL_TREE; |
| tree &v = shared ? vb : omp_private_member_map->get_or_insert (t); |
| if (v == NULL_TREE) |
| { |
| v = create_temporary_var (TREE_TYPE (m)); |
| retrofit_lang_decl (v); |
| DECL_OMP_PRIVATIZED_MEMBER (v) = 1; |
| SET_DECL_VALUE_EXPR (v, m); |
| DECL_HAS_VALUE_EXPR_P (v) = 1; |
| if (!shared) |
| omp_private_member_vec.safe_push (t); |
| } |
| return v; |
| } |
| |
| /* Helper function for handle_omp_array_sections. Called recursively |
| to handle multiple array-section-subscripts. C is the clause, |
| T current expression (initially OMP_CLAUSE_DECL), which is either |
| a TREE_LIST for array-section-subscript (TREE_PURPOSE is low-bound |
| expression if specified, TREE_VALUE length expression if specified, |
| TREE_CHAIN is what it has been specified after, or some decl. |
| TYPES vector is populated with array section types, MAYBE_ZERO_LEN |
| set to true if any of the array-section-subscript could have length |
| of zero (explicit or implicit), FIRST_NON_ONE is the index of the |
| first array-section-subscript which is known not to have length |
| of one. Given say: |
| map(a[:b][2:1][:c][:2][:d][e:f][2:5]) |
| FIRST_NON_ONE will be 3, array-section-subscript [:b], [2:1] and [:c] |
| all are or may have length of 1, array-section-subscript [:2] is the |
| first one known not to have length 1. For array-section-subscript |
| <= FIRST_NON_ONE we diagnose non-contiguous arrays if low bound isn't |
| 0 or length isn't the array domain max + 1, for > FIRST_NON_ONE we |
| can if MAYBE_ZERO_LEN is false. MAYBE_ZERO_LEN will be true in the above |
| case though, as some lengths could be zero. */ |
| |
| static tree |
| handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types, |
| bool &maybe_zero_len, unsigned int &first_non_one, |
| enum c_omp_region_type ort) |
| { |
| tree ret, low_bound, length, type; |
| if (TREE_CODE (t) != TREE_LIST) |
| { |
| if (error_operand_p (t)) |
| return error_mark_node; |
| if (REFERENCE_REF_P (t) |
| && TREE_CODE (TREE_OPERAND (t, 0)) == COMPONENT_REF) |
| t = TREE_OPERAND (t, 0); |
| ret = t; |
| if (TREE_CODE (t) == COMPONENT_REF |
| && ort == C_ORT_OMP |
| && (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP |
| || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TO |
| || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM) |
| && !type_dependent_expression_p (t)) |
| { |
| if (TREE_CODE (TREE_OPERAND (t, 1)) == FIELD_DECL |
| && DECL_BIT_FIELD (TREE_OPERAND (t, 1))) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "bit-field %qE in %qs clause", |
| t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| return error_mark_node; |
| } |
| while (TREE_CODE (t) == COMPONENT_REF) |
| { |
| if (TREE_TYPE (TREE_OPERAND (t, 0)) |
| && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == UNION_TYPE) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%qE is a member of a union", t); |
| return error_mark_node; |
| } |
| t = TREE_OPERAND (t, 0); |
| } |
| if (REFERENCE_REF_P (t)) |
| t = TREE_OPERAND (t, 0); |
| } |
| if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL) |
| { |
| if (processing_template_decl && TREE_CODE (t) != OVERLOAD) |
| return NULL_TREE; |
| if (DECL_P (t)) |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%qD is not a variable in %qs clause", t, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| else |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%qE is not a variable in %qs clause", t, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| return error_mark_node; |
| } |
| else if (TREE_CODE (t) == PARM_DECL |
| && DECL_ARTIFICIAL (t) |
| && DECL_NAME (t) == this_identifier) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%<this%> allowed in OpenMP only in %<declare simd%>" |
| " clauses"); |
| return error_mark_node; |
| } |
| else if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND |
| && VAR_P (t) && CP_DECL_THREAD_LOCAL_P (t)) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%qD is threadprivate variable in %qs clause", t, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| return error_mark_node; |
| } |
| if (type_dependent_expression_p (ret)) |
| return NULL_TREE; |
| ret = convert_from_reference (ret); |
| return ret; |
| } |
| |
| if (ort == C_ORT_OMP |
| && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION |
| && TREE_CODE (TREE_CHAIN (t)) == FIELD_DECL) |
| TREE_CHAIN (t) = omp_privatize_field (TREE_CHAIN (t), false); |
| ret = handle_omp_array_sections_1 (c, TREE_CHAIN (t), types, |
| maybe_zero_len, first_non_one, ort); |
| if (ret == error_mark_node || ret == NULL_TREE) |
| return ret; |
| |
| type = TREE_TYPE (ret); |
| low_bound = TREE_PURPOSE (t); |
| length = TREE_VALUE (t); |
| if ((low_bound && type_dependent_expression_p (low_bound)) |
| || (length && type_dependent_expression_p (length))) |
| return NULL_TREE; |
| |
| if (low_bound == error_mark_node || length == error_mark_node) |
| return error_mark_node; |
| |
| if (low_bound && !INTEGRAL_TYPE_P (TREE_TYPE (low_bound))) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "low bound %qE of array section does not have integral type", |
| low_bound); |
| return error_mark_node; |
| } |
| if (length && !INTEGRAL_TYPE_P (TREE_TYPE (length))) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "length %qE of array section does not have integral type", |
| length); |
| return error_mark_node; |
| } |
| if (low_bound) |
| low_bound = mark_rvalue_use (low_bound); |
| if (length) |
| length = mark_rvalue_use (length); |
| /* We need to reduce to real constant-values for checks below. */ |
| if (length) |
| length = fold_simple (length); |
| if (low_bound) |
| low_bound = fold_simple (low_bound); |
| if (low_bound |
| && TREE_CODE (low_bound) == INTEGER_CST |
| && TYPE_PRECISION (TREE_TYPE (low_bound)) |
| > TYPE_PRECISION (sizetype)) |
| low_bound = fold_convert (sizetype, low_bound); |
| if (length |
| && TREE_CODE (length) == INTEGER_CST |
| && TYPE_PRECISION (TREE_TYPE (length)) |
| > TYPE_PRECISION (sizetype)) |
| length = fold_convert (sizetype, length); |
| if (low_bound == NULL_TREE) |
| low_bound = integer_zero_node; |
| |
| if (length != NULL_TREE) |
| { |
| if (!integer_nonzerop (length)) |
| { |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND |
| || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION) |
| { |
| if (integer_zerop (length)) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "zero length array section in %qs clause", |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| return error_mark_node; |
| } |
| } |
| else |
| maybe_zero_len = true; |
| } |
| if (first_non_one == types.length () |
| && (TREE_CODE (length) != INTEGER_CST || integer_onep (length))) |
| first_non_one++; |
| } |
| if (TREE_CODE (type) == ARRAY_TYPE) |
| { |
| if (length == NULL_TREE |
| && (TYPE_DOMAIN (type) == NULL_TREE |
| || TYPE_MAX_VALUE (TYPE_DOMAIN (type)) == NULL_TREE)) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "for unknown bound array type length expression must " |
| "be specified"); |
| return error_mark_node; |
| } |
| if (TREE_CODE (low_bound) == INTEGER_CST |
| && tree_int_cst_sgn (low_bound) == -1) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "negative low bound in array section in %qs clause", |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| return error_mark_node; |
| } |
| if (length != NULL_TREE |
| && TREE_CODE (length) == INTEGER_CST |
| && tree_int_cst_sgn (length) == -1) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "negative length in array section in %qs clause", |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| return error_mark_node; |
| } |
| if (TYPE_DOMAIN (type) |
| && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) |
| && TREE_CODE (TYPE_MAX_VALUE (TYPE_DOMAIN (type))) |
| == INTEGER_CST) |
| { |
| tree size |
| = fold_convert (sizetype, TYPE_MAX_VALUE (TYPE_DOMAIN (type))); |
| size = size_binop (PLUS_EXPR, size, size_one_node); |
| if (TREE_CODE (low_bound) == INTEGER_CST) |
| { |
| if (tree_int_cst_lt (size, low_bound)) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "low bound %qE above array section size " |
| "in %qs clause", low_bound, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| return error_mark_node; |
| } |
| if (tree_int_cst_equal (size, low_bound)) |
| { |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND |
| || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "zero length array section in %qs clause", |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| return error_mark_node; |
| } |
| maybe_zero_len = true; |
| } |
| else if (length == NULL_TREE |
| && first_non_one == types.length () |
| && tree_int_cst_equal |
| (TYPE_MAX_VALUE (TYPE_DOMAIN (type)), |
| low_bound)) |
| first_non_one++; |
| } |
| else if (length == NULL_TREE) |
| { |
| if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND |
| && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_REDUCTION) |
| maybe_zero_len = true; |
| if (first_non_one == types.length ()) |
| first_non_one++; |
| } |
| if (length && TREE_CODE (length) == INTEGER_CST) |
| { |
| if (tree_int_cst_lt (size, length)) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "length %qE above array section size " |
| "in %qs clause", length, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| return error_mark_node; |
| } |
| if (TREE_CODE (low_bound) == INTEGER_CST) |
| { |
| tree lbpluslen |
| = size_binop (PLUS_EXPR, |
| fold_convert (sizetype, low_bound), |
| fold_convert (sizetype, length)); |
| if (TREE_CODE (lbpluslen) == INTEGER_CST |
| && tree_int_cst_lt (size, lbpluslen)) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "high bound %qE above array section size " |
| "in %qs clause", lbpluslen, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| return error_mark_node; |
| } |
| } |
| } |
| } |
| else if (length == NULL_TREE) |
| { |
| if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND |
| && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_REDUCTION) |
| maybe_zero_len = true; |
| if (first_non_one == types.length ()) |
| first_non_one++; |
| } |
| |
| /* For [lb:] we will need to evaluate lb more than once. */ |
| if (length == NULL_TREE && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND) |
| { |
| tree lb = cp_save_expr (low_bound); |
| if (lb != low_bound) |
| { |
| TREE_PURPOSE (t) = lb; |
| low_bound = lb; |
| } |
| } |
| } |
| else if (TREE_CODE (type) == POINTER_TYPE) |
| { |
| if (length == NULL_TREE) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "for pointer type length expression must be specified"); |
| return error_mark_node; |
| } |
| if (length != NULL_TREE |
| && TREE_CODE (length) == INTEGER_CST |
| && tree_int_cst_sgn (length) == -1) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "negative length in array section in %qs clause", |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| return error_mark_node; |
| } |
| /* If there is a pointer type anywhere but in the very first |
| array-section-subscript, the array section can't be contiguous. */ |
| if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND |
| && TREE_CODE (TREE_CHAIN (t)) == TREE_LIST) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "array section is not contiguous in %qs clause", |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| return error_mark_node; |
| } |
| } |
| else |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%qE does not have pointer or array type", ret); |
| return error_mark_node; |
| } |
| if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND) |
| types.safe_push (TREE_TYPE (ret)); |
| /* We will need to evaluate lb more than once. */ |
| tree lb = cp_save_expr (low_bound); |
| if (lb != low_bound) |
| { |
| TREE_PURPOSE (t) = lb; |
| low_bound = lb; |
| } |
| ret = grok_array_decl (OMP_CLAUSE_LOCATION (c), ret, low_bound, false); |
| return ret; |
| } |
| |
| /* Handle array sections for clause C. */ |
| |
| static bool |
| handle_omp_array_sections (tree c, enum c_omp_region_type ort) |
| { |
| bool maybe_zero_len = false; |
| unsigned int first_non_one = 0; |
| auto_vec<tree, 10> types; |
| tree first = handle_omp_array_sections_1 (c, OMP_CLAUSE_DECL (c), types, |
| maybe_zero_len, first_non_one, |
| ort); |
| if (first == error_mark_node) |
| return true; |
| if (first == NULL_TREE) |
| return false; |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND) |
| { |
| tree t = OMP_CLAUSE_DECL (c); |
| tree tem = NULL_TREE; |
| if (processing_template_decl) |
| return false; |
| /* Need to evaluate side effects in the length expressions |
| if any. */ |
| while (TREE_CODE (t) == TREE_LIST) |
| { |
| if (TREE_VALUE (t) && TREE_SIDE_EFFECTS (TREE_VALUE (t))) |
| { |
| if (tem == NULL_TREE) |
| tem = TREE_VALUE (t); |
| else |
| tem = build2 (COMPOUND_EXPR, TREE_TYPE (tem), |
| TREE_VALUE (t), tem); |
| } |
| t = TREE_CHAIN (t); |
| } |
| if (tem) |
| first = build2 (COMPOUND_EXPR, TREE_TYPE (first), tem, first); |
| OMP_CLAUSE_DECL (c) = first; |
| } |
| else |
| { |
| unsigned int num = types.length (), i; |
| tree t, side_effects = NULL_TREE, size = NULL_TREE; |
| tree condition = NULL_TREE; |
| |
| if (int_size_in_bytes (TREE_TYPE (first)) <= 0) |
| maybe_zero_len = true; |
| if (processing_template_decl && maybe_zero_len) |
| return false; |
| |
| for (i = num, t = OMP_CLAUSE_DECL (c); i > 0; |
| t = TREE_CHAIN (t)) |
| { |
| tree low_bound = TREE_PURPOSE (t); |
| tree length = TREE_VALUE (t); |
| |
| i--; |
| if (low_bound |
| && TREE_CODE (low_bound) == INTEGER_CST |
| && TYPE_PRECISION (TREE_TYPE (low_bound)) |
| > TYPE_PRECISION (sizetype)) |
| low_bound = fold_convert (sizetype, low_bound); |
| if (length |
| && TREE_CODE (length) == INTEGER_CST |
| && TYPE_PRECISION (TREE_TYPE (length)) |
| > TYPE_PRECISION (sizetype)) |
| length = fold_convert (sizetype, length); |
| if (low_bound == NULL_TREE) |
| low_bound = integer_zero_node; |
| if (!maybe_zero_len && i > first_non_one) |
| { |
| if (integer_nonzerop (low_bound)) |
| goto do_warn_noncontiguous; |
| if (length != NULL_TREE |
| && TREE_CODE (length) == INTEGER_CST |
| && TYPE_DOMAIN (types[i]) |
| && TYPE_MAX_VALUE (TYPE_DOMAIN (types[i])) |
| && TREE_CODE (TYPE_MAX_VALUE (TYPE_DOMAIN (types[i]))) |
| == INTEGER_CST) |
| { |
| tree size; |
| size = size_binop (PLUS_EXPR, |
| TYPE_MAX_VALUE (TYPE_DOMAIN (types[i])), |
| size_one_node); |
| if (!tree_int_cst_equal (length, size)) |
| { |
| do_warn_noncontiguous: |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "array section is not contiguous in %qs " |
| "clause", |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| return true; |
| } |
| } |
| if (!processing_template_decl |
| && length != NULL_TREE |
| && TREE_SIDE_EFFECTS (length)) |
| { |
| if (side_effects == NULL_TREE) |
| side_effects = length; |
| else |
| side_effects = build2 (COMPOUND_EXPR, |
| TREE_TYPE (side_effects), |
| length, side_effects); |
| } |
| } |
| else if (processing_template_decl) |
| continue; |
| else |
| { |
| tree l; |
| |
| if (i > first_non_one |
| && ((length && integer_nonzerop (length)) |
| || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION)) |
| continue; |
| if (length) |
| l = fold_convert (sizetype, length); |
| else |
| { |
| l = size_binop (PLUS_EXPR, |
| TYPE_MAX_VALUE (TYPE_DOMAIN (types[i])), |
| size_one_node); |
| l = size_binop (MINUS_EXPR, l, |
| fold_convert (sizetype, low_bound)); |
| } |
| if (i > first_non_one) |
| { |
| l = fold_build2 (NE_EXPR, boolean_type_node, l, |
| size_zero_node); |
| if (condition == NULL_TREE) |
| condition = l; |
| else |
| condition = fold_build2 (BIT_AND_EXPR, boolean_type_node, |
| l, condition); |
| } |
| else if (size == NULL_TREE) |
| { |
| size = size_in_bytes (TREE_TYPE (types[i])); |
| tree eltype = TREE_TYPE (types[num - 1]); |
| while (TREE_CODE (eltype) == ARRAY_TYPE) |
| eltype = TREE_TYPE (eltype); |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION) |
| size = size_binop (EXACT_DIV_EXPR, size, |
| size_in_bytes (eltype)); |
| size = size_binop (MULT_EXPR, size, l); |
| if (condition) |
| size = fold_build3 (COND_EXPR, sizetype, condition, |
| size, size_zero_node); |
| } |
| else |
| size = size_binop (MULT_EXPR, size, l); |
| } |
| } |
| if (!processing_template_decl) |
| { |
| if (side_effects) |
| size = build2 (COMPOUND_EXPR, sizetype, side_effects, size); |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION) |
| { |
| size = size_binop (MINUS_EXPR, size, size_one_node); |
| tree index_type = build_index_type (size); |
| tree eltype = TREE_TYPE (first); |
| while (TREE_CODE (eltype) == ARRAY_TYPE) |
| eltype = TREE_TYPE (eltype); |
| tree type = build_array_type (eltype, index_type); |
| tree ptype = build_pointer_type (eltype); |
| if (TREE_CODE (TREE_TYPE (t)) == REFERENCE_TYPE |
| && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (t)))) |
| t = convert_from_reference (t); |
| else if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE) |
| t = build_fold_addr_expr (t); |
| tree t2 = build_fold_addr_expr (first); |
| t2 = fold_convert_loc (OMP_CLAUSE_LOCATION (c), |
| ptrdiff_type_node, t2); |
| t2 = fold_build2_loc (OMP_CLAUSE_LOCATION (c), MINUS_EXPR, |
| ptrdiff_type_node, t2, |
| fold_convert_loc (OMP_CLAUSE_LOCATION (c), |
| ptrdiff_type_node, t)); |
| if (tree_fits_shwi_p (t2)) |
| t = build2 (MEM_REF, type, t, |
| build_int_cst (ptype, tree_to_shwi (t2))); |
| else |
| { |
| t2 = fold_convert_loc (OMP_CLAUSE_LOCATION (c), |
| sizetype, t2); |
| t = build2_loc (OMP_CLAUSE_LOCATION (c), POINTER_PLUS_EXPR, |
| TREE_TYPE (t), t, t2); |
| t = build2 (MEM_REF, type, t, build_int_cst (ptype, 0)); |
| } |
| OMP_CLAUSE_DECL (c) = t; |
| return false; |
| } |
| OMP_CLAUSE_DECL (c) = first; |
| OMP_CLAUSE_SIZE (c) = size; |
| if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP |
| || (TREE_CODE (t) == COMPONENT_REF |
| && TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)) |
| return false; |
| if (ort == C_ORT_OMP || ort == C_ORT_ACC) |
| switch (OMP_CLAUSE_MAP_KIND (c)) |
| { |
| case GOMP_MAP_ALLOC: |
| case GOMP_MAP_TO: |
| case GOMP_MAP_FROM: |
| case GOMP_MAP_TOFROM: |
| case GOMP_MAP_ALWAYS_TO: |
| case GOMP_MAP_ALWAYS_FROM: |
| case GOMP_MAP_ALWAYS_TOFROM: |
| case GOMP_MAP_RELEASE: |
| case GOMP_MAP_DELETE: |
| case GOMP_MAP_FORCE_TO: |
| case GOMP_MAP_FORCE_FROM: |
| case GOMP_MAP_FORCE_TOFROM: |
| case GOMP_MAP_FORCE_PRESENT: |
| OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1; |
| break; |
| default: |
| break; |
| } |
| tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c), |
| OMP_CLAUSE_MAP); |
| if ((ort & C_ORT_OMP_DECLARE_SIMD) != C_ORT_OMP && ort != C_ORT_ACC) |
| OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_POINTER); |
| else if (TREE_CODE (t) == COMPONENT_REF) |
| OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ALWAYS_POINTER); |
| else if (REFERENCE_REF_P (t) |
| && TREE_CODE (TREE_OPERAND (t, 0)) == COMPONENT_REF) |
| { |
| t = TREE_OPERAND (t, 0); |
| OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ALWAYS_POINTER); |
| } |
| else |
| OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_POINTER); |
| if (OMP_CLAUSE_MAP_KIND (c2) != GOMP_MAP_FIRSTPRIVATE_POINTER |
| && !cxx_mark_addressable (t)) |
| return false; |
| OMP_CLAUSE_DECL (c2) = t; |
| t = build_fold_addr_expr (first); |
| t = fold_convert_loc (OMP_CLAUSE_LOCATION (c), |
| ptrdiff_type_node, t); |
| tree ptr = OMP_CLAUSE_DECL (c2); |
| ptr = convert_from_reference (ptr); |
| if (!POINTER_TYPE_P (TREE_TYPE (ptr))) |
| ptr = build_fold_addr_expr (ptr); |
| t = fold_build2_loc (OMP_CLAUSE_LOCATION (c), MINUS_EXPR, |
| ptrdiff_type_node, t, |
| fold_convert_loc (OMP_CLAUSE_LOCATION (c), |
| ptrdiff_type_node, ptr)); |
| OMP_CLAUSE_SIZE (c2) = t; |
| OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c); |
| OMP_CLAUSE_CHAIN (c) = c2; |
| ptr = OMP_CLAUSE_DECL (c2); |
| if (OMP_CLAUSE_MAP_KIND (c2) != GOMP_MAP_FIRSTPRIVATE_POINTER |
| && TREE_CODE (TREE_TYPE (ptr)) == REFERENCE_TYPE |
| && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (ptr)))) |
| { |
| tree c3 = build_omp_clause (OMP_CLAUSE_LOCATION (c), |
| OMP_CLAUSE_MAP); |
| OMP_CLAUSE_SET_MAP_KIND (c3, OMP_CLAUSE_MAP_KIND (c2)); |
| OMP_CLAUSE_DECL (c3) = ptr; |
| if (OMP_CLAUSE_MAP_KIND (c2) == GOMP_MAP_ALWAYS_POINTER) |
| OMP_CLAUSE_DECL (c2) = build_simple_mem_ref (ptr); |
| else |
| OMP_CLAUSE_DECL (c2) = convert_from_reference (ptr); |
| OMP_CLAUSE_SIZE (c3) = size_zero_node; |
| OMP_CLAUSE_CHAIN (c3) = OMP_CLAUSE_CHAIN (c2); |
| OMP_CLAUSE_CHAIN (c2) = c3; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /* Return identifier to look up for omp declare reduction. */ |
| |
| tree |
| omp_reduction_id (enum tree_code reduction_code, tree reduction_id, tree type) |
| { |
| const char *p = NULL; |
| const char *m = NULL; |
| switch (reduction_code) |
| { |
| case PLUS_EXPR: |
| case MULT_EXPR: |
| case MINUS_EXPR: |
| case BIT_AND_EXPR: |
| case BIT_XOR_EXPR: |
| case BIT_IOR_EXPR: |
| case TRUTH_ANDIF_EXPR: |
| case TRUTH_ORIF_EXPR: |
| reduction_id = ovl_op_identifier (false, reduction_code); |
| break; |
| case MIN_EXPR: |
| p = "min"; |
| break; |
| case MAX_EXPR: |
| p = "max"; |
| break; |
| default: |
| break; |
| } |
| |
| if (p == NULL) |
| { |
| if (TREE_CODE (reduction_id) != IDENTIFIER_NODE) |
| return error_mark_node; |
| p = IDENTIFIER_POINTER (reduction_id); |
| } |
| |
| if (type != NULL_TREE) |
| m = mangle_type_string (TYPE_MAIN_VARIANT (type)); |
| |
| const char prefix[] = "omp declare reduction "; |
| size_t lenp = sizeof (prefix); |
| if (strncmp (p, prefix, lenp - 1) == 0) |
| lenp = 1; |
| size_t len = strlen (p); |
| size_t lenm = m ? strlen (m) + 1 : 0; |
| char *name = XALLOCAVEC (char, lenp + len + lenm); |
| if (lenp > 1) |
| memcpy (name, prefix, lenp - 1); |
| memcpy (name + lenp - 1, p, len + 1); |
| if (m) |
| { |
| name[lenp + len - 1] = '~'; |
| memcpy (name + lenp + len, m, lenm); |
| } |
| return get_identifier (name); |
| } |
| |
| /* Lookup OpenMP UDR ID for TYPE, return the corresponding artificial |
| FUNCTION_DECL or NULL_TREE if not found. */ |
| |
| static tree |
| omp_reduction_lookup (location_t loc, tree id, tree type, tree *baselinkp, |
| vec<tree> *ambiguousp) |
| { |
| tree orig_id = id; |
| tree baselink = NULL_TREE; |
| if (identifier_p (id)) |
| { |
| cp_id_kind idk; |
| bool nonint_cst_expression_p; |
| const char *error_msg; |
| id = omp_reduction_id (ERROR_MARK, id, type); |
| tree decl = lookup_name (id); |
| if (decl == NULL_TREE) |
| decl = error_mark_node; |
| id = finish_id_expression (id, decl, NULL_TREE, &idk, false, true, |
| &nonint_cst_expression_p, false, true, false, |
| false, &error_msg, loc); |
| if (idk == CP_ID_KIND_UNQUALIFIED |
| && identifier_p (id)) |
| { |
| vec<tree, va_gc> *args = NULL; |
| vec_safe_push (args, build_reference_type (type)); |
| id = perform_koenig_lookup (id, args, tf_none); |
| } |
| } |
| else if (TREE_CODE (id) == SCOPE_REF) |
| id = lookup_qualified_name (TREE_OPERAND (id, 0), |
| omp_reduction_id (ERROR_MARK, |
| TREE_OPERAND (id, 1), |
| type), |
| false, false); |
| tree fns = id; |
| id = NULL_TREE; |
| if (fns && is_overloaded_fn (fns)) |
| { |
| for (lkp_iterator iter (get_fns (fns)); iter; ++iter) |
| { |
| tree fndecl = *iter; |
| if (TREE_CODE (fndecl) == FUNCTION_DECL) |
| { |
| tree argtype = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fndecl))); |
| if (same_type_p (TREE_TYPE (argtype), type)) |
| { |
| id = fndecl; |
| break; |
| } |
| } |
| } |
| |
| if (id && BASELINK_P (fns)) |
| { |
| if (baselinkp) |
| *baselinkp = fns; |
| else |
| baselink = fns; |
| } |
| } |
| |
| if (!id && CLASS_TYPE_P (type) && TYPE_BINFO (type)) |
| { |
| vec<tree> ambiguous = vNULL; |
| tree binfo = TYPE_BINFO (type), base_binfo, ret = NULL_TREE; |
| unsigned int ix; |
| if (ambiguousp == NULL) |
| ambiguousp = &ambiguous; |
| for (ix = 0; BINFO_BASE_ITERATE (binfo, ix, base_binfo); ix++) |
| { |
| id = omp_reduction_lookup (loc, orig_id, BINFO_TYPE (base_binfo), |
| baselinkp ? baselinkp : &baselink, |
| ambiguousp); |
| if (id == NULL_TREE) |
| continue; |
| if (!ambiguousp->is_empty ()) |
| ambiguousp->safe_push (id); |
| else if (ret != NULL_TREE) |
| { |
| ambiguousp->safe_push (ret); |
| ambiguousp->safe_push (id); |
| ret = NULL_TREE; |
| } |
| else |
| ret = id; |
| } |
| if (ambiguousp != &ambiguous) |
| return ret; |
| if (!ambiguous.is_empty ()) |
| { |
| const char *str = _("candidates are:"); |
| unsigned int idx; |
| tree udr; |
| error_at (loc, "user defined reduction lookup is ambiguous"); |
| FOR_EACH_VEC_ELT (ambiguous, idx, udr) |
| { |
| inform (DECL_SOURCE_LOCATION (udr), "%s %#qD", str, udr); |
| if (idx == 0) |
| str = get_spaces (str); |
| } |
| ambiguous.release (); |
| ret = error_mark_node; |
| baselink = NULL_TREE; |
| } |
| id = ret; |
| } |
| if (id && baselink) |
| perform_or_defer_access_check (BASELINK_BINFO (baselink), |
| id, id, tf_warning_or_error); |
| return id; |
| } |
| |
| /* Helper function for cp_parser_omp_declare_reduction_exprs |
| and tsubst_omp_udr. |
| Remove CLEANUP_STMT for data (omp_priv variable). |
| Also append INIT_EXPR for DECL_INITIAL of omp_priv after its |
| DECL_EXPR. */ |
| |
| tree |
| cp_remove_omp_priv_cleanup_stmt (tree *tp, int *walk_subtrees, void *data) |
| { |
| if (TYPE_P (*tp)) |
| *walk_subtrees = 0; |
| else if (TREE_CODE (*tp) == CLEANUP_STMT && CLEANUP_DECL (*tp) == (tree) data) |
| *tp = CLEANUP_BODY (*tp); |
| else if (TREE_CODE (*tp) == DECL_EXPR) |
| { |
| tree decl = DECL_EXPR_DECL (*tp); |
| if (!processing_template_decl |
| && decl == (tree) data |
| && DECL_INITIAL (decl) |
| && DECL_INITIAL (decl) != error_mark_node) |
| { |
| tree list = NULL_TREE; |
| append_to_statement_list_force (*tp, &list); |
| tree init_expr = build2 (INIT_EXPR, void_type_node, |
| decl, DECL_INITIAL (decl)); |
| DECL_INITIAL (decl) = NULL_TREE; |
| append_to_statement_list_force (init_expr, &list); |
| *tp = list; |
| } |
| } |
| return NULL_TREE; |
| } |
| |
| /* Data passed from cp_check_omp_declare_reduction to |
| cp_check_omp_declare_reduction_r. */ |
| |
| struct cp_check_omp_declare_reduction_data |
| { |
| location_t loc; |
| tree stmts[7]; |
| bool combiner_p; |
| }; |
| |
| /* Helper function for cp_check_omp_declare_reduction, called via |
| cp_walk_tree. */ |
| |
| static tree |
| cp_check_omp_declare_reduction_r (tree *tp, int *, void *data) |
| { |
| struct cp_check_omp_declare_reduction_data *udr_data |
| = (struct cp_check_omp_declare_reduction_data *) data; |
| if (SSA_VAR_P (*tp) |
| && !DECL_ARTIFICIAL (*tp) |
| && *tp != DECL_EXPR_DECL (udr_data->stmts[udr_data->combiner_p ? 0 : 3]) |
| && *tp != DECL_EXPR_DECL (udr_data->stmts[udr_data->combiner_p ? 1 : 4])) |
| { |
| location_t loc = udr_data->loc; |
| if (udr_data->combiner_p) |
| error_at (loc, "%<#pragma omp declare reduction%> combiner refers to " |
| "variable %qD which is not %<omp_out%> nor %<omp_in%>", |
| *tp); |
| else |
| error_at (loc, "%<#pragma omp declare reduction%> initializer refers " |
| "to variable %qD which is not %<omp_priv%> nor " |
| "%<omp_orig%>", |
| *tp); |
| return *tp; |
| } |
| return NULL_TREE; |
| } |
| |
| /* Diagnose violation of OpenMP #pragma omp declare reduction restrictions. */ |
| |
| void |
| cp_check_omp_declare_reduction (tree udr) |
| { |
| tree type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (udr))); |
| gcc_assert (TREE_CODE (type) == REFERENCE_TYPE); |
| type = TREE_TYPE (type); |
| int i; |
| location_t loc = DECL_SOURCE_LOCATION (udr); |
| |
| if (type == error_mark_node) |
| return; |
| if (ARITHMETIC_TYPE_P (type)) |
| { |
| static enum tree_code predef_codes[] |
| = { PLUS_EXPR, MULT_EXPR, MINUS_EXPR, BIT_AND_EXPR, BIT_XOR_EXPR, |
| BIT_IOR_EXPR, TRUTH_ANDIF_EXPR, TRUTH_ORIF_EXPR }; |
| for (i = 0; i < 8; i++) |
| { |
| tree id = omp_reduction_id (predef_codes[i], NULL_TREE, NULL_TREE); |
| const char *n1 = IDENTIFIER_POINTER (DECL_NAME (udr)); |
| const char *n2 = IDENTIFIER_POINTER (id); |
| if (strncmp (n1, n2, IDENTIFIER_LENGTH (id)) == 0 |
| && (n1[IDENTIFIER_LENGTH (id)] == '~' |
| || n1[IDENTIFIER_LENGTH (id)] == '\0')) |
| break; |
| } |
| |
| if (i == 8 |
| && TREE_CODE (type) != COMPLEX_EXPR) |
| { |
| const char prefix_minmax[] = "omp declare reduction m"; |
| size_t prefix_size = sizeof (prefix_minmax) - 1; |
| const char *n = IDENTIFIER_POINTER (DECL_NAME (udr)); |
| if (strncmp (IDENTIFIER_POINTER (DECL_NAME (udr)), |
| prefix_minmax, prefix_size) == 0 |
| && ((n[prefix_size] == 'i' && n[prefix_size + 1] == 'n') |
| || (n[prefix_size] == 'a' && n[prefix_size + 1] == 'x')) |
| && (n[prefix_size + 2] == '~' || n[prefix_size + 2] == '\0')) |
| i = 0; |
| } |
| if (i < 8) |
| { |
| error_at (loc, "predeclared arithmetic type %qT in " |
| "%<#pragma omp declare reduction%>", type); |
| return; |
| } |
| } |
| else if (TREE_CODE (type) == FUNCTION_TYPE |
| || TREE_CODE (type) == METHOD_TYPE |
| || TREE_CODE (type) == ARRAY_TYPE) |
| { |
| error_at (loc, "function or array type %qT in " |
| "%<#pragma omp declare reduction%>", type); |
| return; |
| } |
| else if (TREE_CODE (type) == REFERENCE_TYPE) |
| { |
| error_at (loc, "reference type %qT in %<#pragma omp declare reduction%>", |
| type); |
| return; |
| } |
| else if (TYPE_QUALS_NO_ADDR_SPACE (type)) |
| { |
| error_at (loc, "const, volatile or __restrict qualified type %qT in " |
| "%<#pragma omp declare reduction%>", type); |
| return; |
| } |
| |
| tree body = DECL_SAVED_TREE (udr); |
| if (body == NULL_TREE || TREE_CODE (body) != STATEMENT_LIST) |
| return; |
| |
| tree_stmt_iterator tsi; |
| struct cp_check_omp_declare_reduction_data data; |
| memset (data.stmts, 0, sizeof data.stmts); |
| for (i = 0, tsi = tsi_start (body); |
| i < 7 && !tsi_end_p (tsi); |
| i++, tsi_next (&tsi)) |
| data.stmts[i] = tsi_stmt (tsi); |
| data.loc = loc; |
| gcc_assert (tsi_end_p (tsi)); |
| if (i >= 3) |
| { |
| gcc_assert (TREE_CODE (data.stmts[0]) == DECL_EXPR |
| && TREE_CODE (data.stmts[1]) == DECL_EXPR); |
| if (TREE_NO_WARNING (DECL_EXPR_DECL (data.stmts[0]))) |
| return; |
| data.combiner_p = true; |
| if (cp_walk_tree (&data.stmts[2], cp_check_omp_declare_reduction_r, |
| &data, NULL)) |
| TREE_NO_WARNING (DECL_EXPR_DECL (data.stmts[0])) = 1; |
| } |
| if (i >= 6) |
| { |
| gcc_assert (TREE_CODE (data.stmts[3]) == DECL_EXPR |
| && TREE_CODE (data.stmts[4]) == DECL_EXPR); |
| data.combiner_p = false; |
| if (cp_walk_tree (&data.stmts[5], cp_check_omp_declare_reduction_r, |
| &data, NULL) |
| || cp_walk_tree (&DECL_INITIAL (DECL_EXPR_DECL (data.stmts[3])), |
| cp_check_omp_declare_reduction_r, &data, NULL)) |
| TREE_NO_WARNING (DECL_EXPR_DECL (data.stmts[0])) = 1; |
| if (i == 7) |
| gcc_assert (TREE_CODE (data.stmts[6]) == DECL_EXPR); |
| } |
| } |
| |
| /* Helper function of finish_omp_clauses. Clone STMT as if we were making |
| an inline call. But, remap |
| the OMP_DECL1 VAR_DECL (omp_out resp. omp_orig) to PLACEHOLDER |
| and OMP_DECL2 VAR_DECL (omp_in resp. omp_priv) to DECL. */ |
| |
| static tree |
| clone_omp_udr (tree stmt, tree omp_decl1, tree omp_decl2, |
| tree decl, tree placeholder) |
| { |
| copy_body_data id; |
| hash_map<tree, tree> decl_map; |
| |
| decl_map.put (omp_decl1, placeholder); |
| decl_map.put (omp_decl2, decl); |
| memset (&id, 0, sizeof (id)); |
| id.src_fn = DECL_CONTEXT (omp_decl1); |
| id.dst_fn = current_function_decl; |
| id.src_cfun = DECL_STRUCT_FUNCTION (id.src_fn); |
| id.decl_map = &decl_map; |
| |
| id.copy_decl = copy_decl_no_change; |
| id.transform_call_graph_edges = CB_CGE_DUPLICATE; |
| id.transform_new_cfg = true; |
| id.transform_return_to_modify = false; |
| id.transform_lang_insert_block = NULL; |
| id.eh_lp_nr = 0; |
| walk_tree (&stmt, copy_tree_body_r, &id, NULL); |
| return stmt; |
| } |
| |
| /* Helper function of finish_omp_clauses, called via cp_walk_tree. |
| Find OMP_CLAUSE_PLACEHOLDER (passed in DATA) in *TP. */ |
| |
| static tree |
| find_omp_placeholder_r (tree *tp, int *, void *data) |
| { |
| if (*tp == (tree) data) |
| return *tp; |
| return NULL_TREE; |
| } |
| |
| /* Helper function of finish_omp_clauses. Handle OMP_CLAUSE_REDUCTION C. |
| Return true if there is some error and the clause should be removed. */ |
| |
| static bool |
| finish_omp_reduction_clause (tree c, bool *need_default_ctor, bool *need_dtor) |
| { |
| tree t = OMP_CLAUSE_DECL (c); |
| bool predefined = false; |
| if (TREE_CODE (t) == TREE_LIST) |
| { |
| gcc_assert (processing_template_decl); |
| return false; |
| } |
| tree type = TREE_TYPE (t); |
| if (TREE_CODE (t) == MEM_REF) |
| type = TREE_TYPE (type); |
| if (TREE_CODE (type) == REFERENCE_TYPE) |
| type = TREE_TYPE (type); |
| if (TREE_CODE (type) == ARRAY_TYPE) |
| { |
| tree oatype = type; |
| gcc_assert (TREE_CODE (t) != MEM_REF); |
| while (TREE_CODE (type) == ARRAY_TYPE) |
| type = TREE_TYPE (type); |
| if (!processing_template_decl) |
| { |
| t = require_complete_type (t); |
| if (t == error_mark_node) |
| return true; |
| tree size = size_binop (EXACT_DIV_EXPR, TYPE_SIZE_UNIT (oatype), |
| TYPE_SIZE_UNIT (type)); |
| if (integer_zerop (size)) |
| { |
| error ("%qE in %<reduction%> clause is a zero size array", |
| omp_clause_printable_decl (t)); |
| return true; |
| } |
| size = size_binop (MINUS_EXPR, size, size_one_node); |
| tree index_type = build_index_type (size); |
| tree atype = build_array_type (type, index_type); |
| tree ptype = build_pointer_type (type); |
| if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE) |
| t = build_fold_addr_expr (t); |
| t = build2 (MEM_REF, atype, t, build_int_cst (ptype, 0)); |
| OMP_CLAUSE_DECL (c) = t; |
| } |
| } |
| if (type == error_mark_node) |
| return true; |
| else if (ARITHMETIC_TYPE_P (type)) |
| switch (OMP_CLAUSE_REDUCTION_CODE (c)) |
| { |
| case PLUS_EXPR: |
| case MULT_EXPR: |
| case MINUS_EXPR: |
| predefined = true; |
| break; |
| case MIN_EXPR: |
| case MAX_EXPR: |
| if (TREE_CODE (type) == COMPLEX_TYPE) |
| break; |
| predefined = true; |
| break; |
| case BIT_AND_EXPR: |
| case BIT_IOR_EXPR: |
| case BIT_XOR_EXPR: |
| if (FLOAT_TYPE_P (type) || TREE_CODE (type) == COMPLEX_TYPE) |
| break; |
| predefined = true; |
| break; |
| case TRUTH_ANDIF_EXPR: |
| case TRUTH_ORIF_EXPR: |
| if (FLOAT_TYPE_P (type)) |
| break; |
| predefined = true; |
| break; |
| default: |
| break; |
| } |
| else if (TYPE_READONLY (type)) |
| { |
| error ("%qE has const type for %<reduction%>", |
| omp_clause_printable_decl (t)); |
| return true; |
| } |
| else if (!processing_template_decl) |
| { |
| t = require_complete_type (t); |
| if (t == error_mark_node) |
| return true; |
| OMP_CLAUSE_DECL (c) = t; |
| } |
| |
| if (predefined) |
| { |
| OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = NULL_TREE; |
| return false; |
| } |
| else if (processing_template_decl) |
| return false; |
| |
| tree id = OMP_CLAUSE_REDUCTION_PLACEHOLDER (c); |
| |
| type = TYPE_MAIN_VARIANT (type); |
| OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = NULL_TREE; |
| if (id == NULL_TREE) |
| id = omp_reduction_id (OMP_CLAUSE_REDUCTION_CODE (c), |
| NULL_TREE, NULL_TREE); |
| id = omp_reduction_lookup (OMP_CLAUSE_LOCATION (c), id, type, NULL, NULL); |
| if (id) |
| { |
| if (id == error_mark_node) |
| return true; |
| mark_used (id); |
| tree body = DECL_SAVED_TREE (id); |
| if (!body) |
| return true; |
| if (TREE_CODE (body) == STATEMENT_LIST) |
| { |
| tree_stmt_iterator tsi; |
| tree placeholder = NULL_TREE, decl_placeholder = NULL_TREE; |
| int i; |
| tree stmts[7]; |
| tree atype = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (id))); |
| atype = TREE_TYPE (atype); |
| bool need_static_cast = !same_type_p (type, atype); |
| memset (stmts, 0, sizeof stmts); |
| for (i = 0, tsi = tsi_start (body); |
| i < 7 && !tsi_end_p (tsi); |
| i++, tsi_next (&tsi)) |
| stmts[i] = tsi_stmt (tsi); |
| gcc_assert (tsi_end_p (tsi)); |
| |
| if (i >= 3) |
| { |
| gcc_assert (TREE_CODE (stmts[0]) == DECL_EXPR |
| && TREE_CODE (stmts[1]) == DECL_EXPR); |
| placeholder = build_lang_decl (VAR_DECL, NULL_TREE, type); |
| DECL_ARTIFICIAL (placeholder) = 1; |
| DECL_IGNORED_P (placeholder) = 1; |
| OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = placeholder; |
| if (TREE_CODE (t) == MEM_REF) |
| { |
| decl_placeholder = build_lang_decl (VAR_DECL, NULL_TREE, |
| type); |
| DECL_ARTIFICIAL (decl_placeholder) = 1; |
| DECL_IGNORED_P (decl_placeholder) = 1; |
| OMP_CLAUSE_REDUCTION_DECL_PLACEHOLDER (c) = decl_placeholder; |
| } |
| if (TREE_ADDRESSABLE (DECL_EXPR_DECL (stmts[0]))) |
| cxx_mark_addressable (placeholder); |
| if (TREE_ADDRESSABLE (DECL_EXPR_DECL (stmts[1])) |
| && TREE_CODE (TREE_TYPE (OMP_CLAUSE_DECL (c))) |
| != REFERENCE_TYPE) |
| cxx_mark_addressable (decl_placeholder ? decl_placeholder |
| : OMP_CLAUSE_DECL (c)); |
| tree omp_out = placeholder; |
| tree omp_in = decl_placeholder ? decl_placeholder |
| : convert_from_reference (OMP_CLAUSE_DECL (c)); |
| if (need_static_cast) |
| { |
| tree rtype = build_reference_type (atype); |
| omp_out = build_static_cast (rtype, omp_out, |
| tf_warning_or_error); |
| omp_in = build_static_cast (rtype, omp_in, |
| tf_warning_or_error); |
| if (omp_out == error_mark_node || omp_in == error_mark_node) |
| return true; |
| omp_out = convert_from_reference (omp_out); |
| omp_in = convert_from_reference (omp_in); |
| } |
| OMP_CLAUSE_REDUCTION_MERGE (c) |
| = clone_omp_udr (stmts[2], DECL_EXPR_DECL (stmts[0]), |
| DECL_EXPR_DECL (stmts[1]), omp_in, omp_out); |
| } |
| if (i >= 6) |
| { |
| gcc_assert (TREE_CODE (stmts[3]) == DECL_EXPR |
| && TREE_CODE (stmts[4]) == DECL_EXPR); |
| if (TREE_ADDRESSABLE (DECL_EXPR_DECL (stmts[3]))) |
| cxx_mark_addressable (decl_placeholder ? decl_placeholder |
| : OMP_CLAUSE_DECL (c)); |
| if (TREE_ADDRESSABLE (DECL_EXPR_DECL (stmts[4]))) |
| cxx_mark_addressable (placeholder); |
| tree omp_priv = decl_placeholder ? decl_placeholder |
| : convert_from_reference (OMP_CLAUSE_DECL (c)); |
| tree omp_orig = placeholder; |
| if (need_static_cast) |
| { |
| if (i == 7) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "user defined reduction with constructor " |
| "initializer for base class %qT", atype); |
| return true; |
| } |
| tree rtype = build_reference_type (atype); |
| omp_priv = build_static_cast (rtype, omp_priv, |
| tf_warning_or_error); |
| omp_orig = build_static_cast (rtype, omp_orig, |
| tf_warning_or_error); |
| if (omp_priv == error_mark_node |
| || omp_orig == error_mark_node) |
| return true; |
| omp_priv = convert_from_reference (omp_priv); |
| omp_orig = convert_from_reference (omp_orig); |
| } |
| if (i == 6) |
| *need_default_ctor = true; |
| OMP_CLAUSE_REDUCTION_INIT (c) |
| = clone_omp_udr (stmts[5], DECL_EXPR_DECL (stmts[4]), |
| DECL_EXPR_DECL (stmts[3]), |
| omp_priv, omp_orig); |
| if (cp_walk_tree (&OMP_CLAUSE_REDUCTION_INIT (c), |
| find_omp_placeholder_r, placeholder, NULL)) |
| OMP_CLAUSE_REDUCTION_OMP_ORIG_REF (c) = 1; |
| } |
| else if (i >= 3) |
| { |
| if (CLASS_TYPE_P (type) && !pod_type_p (type)) |
| *need_default_ctor = true; |
| else |
| { |
| tree init; |
| tree v = decl_placeholder ? decl_placeholder |
| : convert_from_reference (t); |
| if (AGGREGATE_TYPE_P (TREE_TYPE (v))) |
| init = build_constructor (TREE_TYPE (v), NULL); |
| else |
| init = fold_convert (TREE_TYPE (v), integer_zero_node); |
| OMP_CLAUSE_REDUCTION_INIT (c) |
| = build2 (INIT_EXPR, TREE_TYPE (v), v, init); |
| } |
| } |
| } |
| } |
| if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (c)) |
| *need_dtor = true; |
| else |
| { |
| error ("user defined reduction not found for %qE", |
| omp_clause_printable_decl (t)); |
| return true; |
| } |
| if (TREE_CODE (OMP_CLAUSE_DECL (c)) == MEM_REF) |
| gcc_assert (TYPE_SIZE_UNIT (type) |
| && TREE_CODE (TYPE_SIZE_UNIT (type)) == INTEGER_CST); |
| return false; |
| } |
| |
| /* Called from finish_struct_1. linear(this) or linear(this:step) |
| clauses might not be finalized yet because the class has been incomplete |
| when parsing #pragma omp declare simd methods. Fix those up now. */ |
| |
| void |
| finish_omp_declare_simd_methods (tree t) |
| { |
| if (processing_template_decl) |
| return; |
| |
| for (tree x = TYPE_FIELDS (t); x; x = DECL_CHAIN (x)) |
| { |
| if (TREE_CODE (TREE_TYPE (x)) != METHOD_TYPE) |
| continue; |
| tree ods = lookup_attribute ("omp declare simd", DECL_ATTRIBUTES (x)); |
| if (!ods || !TREE_VALUE (ods)) |
| continue; |
| for (tree c = TREE_VALUE (TREE_VALUE (ods)); c; c = OMP_CLAUSE_CHAIN (c)) |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LINEAR |
| && integer_zerop (OMP_CLAUSE_DECL (c)) |
| && OMP_CLAUSE_LINEAR_STEP (c) |
| && TREE_CODE (TREE_TYPE (OMP_CLAUSE_LINEAR_STEP (c))) |
| == POINTER_TYPE) |
| { |
| tree s = OMP_CLAUSE_LINEAR_STEP (c); |
| s = fold_convert_loc (OMP_CLAUSE_LOCATION (c), sizetype, s); |
| s = fold_build2_loc (OMP_CLAUSE_LOCATION (c), MULT_EXPR, |
| sizetype, s, TYPE_SIZE_UNIT (t)); |
| OMP_CLAUSE_LINEAR_STEP (c) = s; |
| } |
| } |
| } |
| |
| /* Adjust sink depend clause to take into account pointer offsets. |
| |
| Return TRUE if there was a problem processing the offset, and the |
| whole clause should be removed. */ |
| |
| static bool |
| cp_finish_omp_clause_depend_sink (tree sink_clause) |
| { |
| tree t = OMP_CLAUSE_DECL (sink_clause); |
| gcc_assert (TREE_CODE (t) == TREE_LIST); |
| |
| /* Make sure we don't adjust things twice for templates. */ |
| if (processing_template_decl) |
| return false; |
| |
| for (; t; t = TREE_CHAIN (t)) |
| { |
| tree decl = TREE_VALUE (t); |
| if (TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE) |
| { |
| tree offset = TREE_PURPOSE (t); |
| bool neg = wi::neg_p (wi::to_wide (offset)); |
| offset = fold_unary (ABS_EXPR, TREE_TYPE (offset), offset); |
| decl = mark_rvalue_use (decl); |
| decl = convert_from_reference (decl); |
| tree t2 = pointer_int_sum (OMP_CLAUSE_LOCATION (sink_clause), |
| neg ? MINUS_EXPR : PLUS_EXPR, |
| decl, offset); |
| t2 = fold_build2_loc (OMP_CLAUSE_LOCATION (sink_clause), |
| MINUS_EXPR, sizetype, |
| fold_convert (sizetype, t2), |
| fold_convert (sizetype, decl)); |
| if (t2 == error_mark_node) |
| return true; |
| TREE_PURPOSE (t) = t2; |
| } |
| } |
| return false; |
| } |
| |
| /* For all elements of CLAUSES, validate them vs OpenMP constraints. |
| Remove any elements from the list that are invalid. */ |
| |
| tree |
| finish_omp_clauses (tree clauses, enum c_omp_region_type ort) |
| { |
| bitmap_head generic_head, firstprivate_head, lastprivate_head; |
| bitmap_head aligned_head, map_head, map_field_head, oacc_reduction_head; |
| tree c, t, *pc; |
| tree safelen = NULL_TREE; |
| bool branch_seen = false; |
| bool copyprivate_seen = false; |
| bool ordered_seen = false; |
| bool oacc_async = false; |
| |
| bitmap_obstack_initialize (NULL); |
| bitmap_initialize (&generic_head, &bitmap_default_obstack); |
| bitmap_initialize (&firstprivate_head, &bitmap_default_obstack); |
| bitmap_initialize (&lastprivate_head, &bitmap_default_obstack); |
| bitmap_initialize (&aligned_head, &bitmap_default_obstack); |
| bitmap_initialize (&map_head, &bitmap_default_obstack); |
| bitmap_initialize (&map_field_head, &bitmap_default_obstack); |
| bitmap_initialize (&oacc_reduction_head, &bitmap_default_obstack); |
| |
| if (ort & C_ORT_ACC) |
| for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_ASYNC) |
| { |
| oacc_async = true; |
| break; |
| } |
| |
| for (pc = &clauses, c = clauses; c ; c = *pc) |
| { |
| bool remove = false; |
| bool field_ok = false; |
| |
| switch (OMP_CLAUSE_CODE (c)) |
| { |
| case OMP_CLAUSE_SHARED: |
| field_ok = ((ort & C_ORT_OMP_DECLARE_SIMD) == C_ORT_OMP); |
| goto check_dup_generic; |
| case OMP_CLAUSE_PRIVATE: |
| field_ok = ((ort & C_ORT_OMP_DECLARE_SIMD) == C_ORT_OMP); |
| goto check_dup_generic; |
| case OMP_CLAUSE_REDUCTION: |
| field_ok = ((ort & C_ORT_OMP_DECLARE_SIMD) == C_ORT_OMP); |
| t = OMP_CLAUSE_DECL (c); |
| if (TREE_CODE (t) == TREE_LIST) |
| { |
| if (handle_omp_array_sections (c, ort)) |
| { |
| remove = true; |
| break; |
| } |
| if (TREE_CODE (t) == TREE_LIST) |
| { |
| while (TREE_CODE (t) == TREE_LIST) |
| t = TREE_CHAIN (t); |
| } |
| else |
| { |
| gcc_assert (TREE_CODE (t) == MEM_REF); |
| t = TREE_OPERAND (t, 0); |
| if (TREE_CODE (t) == POINTER_PLUS_EXPR) |
| t = TREE_OPERAND (t, 0); |
| if (TREE_CODE (t) == ADDR_EXPR |
| || TREE_CODE (t) == INDIRECT_REF) |
| t = TREE_OPERAND (t, 0); |
| } |
| tree n = omp_clause_decl_field (t); |
| if (n) |
| t = n; |
| goto check_dup_generic_t; |
| } |
| if (oacc_async) |
| cxx_mark_addressable (t); |
| goto check_dup_generic; |
| case OMP_CLAUSE_COPYPRIVATE: |
| copyprivate_seen = true; |
| field_ok = ((ort & C_ORT_OMP_DECLARE_SIMD) == C_ORT_OMP); |
| goto check_dup_generic; |
| case OMP_CLAUSE_COPYIN: |
| goto check_dup_generic; |
| case OMP_CLAUSE_LINEAR: |
| field_ok = ((ort & C_ORT_OMP_DECLARE_SIMD) == C_ORT_OMP); |
| t = OMP_CLAUSE_DECL (c); |
| if (ort != C_ORT_OMP_DECLARE_SIMD |
| && OMP_CLAUSE_LINEAR_KIND (c) != OMP_CLAUSE_LINEAR_DEFAULT) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "modifier should not be specified in %<linear%> " |
| "clause on %<simd%> or %<for%> constructs"); |
| OMP_CLAUSE_LINEAR_KIND (c) = OMP_CLAUSE_LINEAR_DEFAULT; |
| } |
| if ((VAR_P (t) || TREE_CODE (t) == PARM_DECL) |
| && !type_dependent_expression_p (t)) |
| { |
| tree type = TREE_TYPE (t); |
| if ((OMP_CLAUSE_LINEAR_KIND (c) == OMP_CLAUSE_LINEAR_REF |
| || OMP_CLAUSE_LINEAR_KIND (c) == OMP_CLAUSE_LINEAR_UVAL) |
| && TREE_CODE (type) != REFERENCE_TYPE) |
| { |
| error ("linear clause with %qs modifier applied to " |
| "non-reference variable with %qT type", |
| OMP_CLAUSE_LINEAR_KIND (c) == OMP_CLAUSE_LINEAR_REF |
| ? "ref" : "uval", TREE_TYPE (t)); |
| remove = true; |
| break; |
| } |
| if (TREE_CODE (type) == REFERENCE_TYPE) |
| type = TREE_TYPE (type); |
| if (ort == C_ORT_CILK) |
| { |
| if (!INTEGRAL_TYPE_P (type) |
| && !SCALAR_FLOAT_TYPE_P (type) |
| && TREE_CODE (type) != POINTER_TYPE) |
| { |
| error ("linear clause applied to non-integral, " |
| "non-floating, non-pointer variable with %qT type", |
| TREE_TYPE (t)); |
| remove = true; |
| break; |
| } |
| } |
| else if (OMP_CLAUSE_LINEAR_KIND (c) != OMP_CLAUSE_LINEAR_REF) |
| { |
| if (!INTEGRAL_TYPE_P (type) |
| && TREE_CODE (type) != POINTER_TYPE) |
| { |
| error ("linear clause applied to non-integral non-pointer" |
| " variable with %qT type", TREE_TYPE (t)); |
| remove = true; |
| break; |
| } |
| } |
| } |
| t = OMP_CLAUSE_LINEAR_STEP (c); |
| if (t == NULL_TREE) |
| t = integer_one_node; |
| if (t == error_mark_node) |
| { |
| remove = true; |
| break; |
| } |
| else if (!type_dependent_expression_p (t) |
| && !INTEGRAL_TYPE_P (TREE_TYPE (t)) |
| && (ort != C_ORT_OMP_DECLARE_SIMD |
| || TREE_CODE (t) != PARM_DECL |
| || TREE_CODE (TREE_TYPE (t)) != REFERENCE_TYPE |
| || !INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (t))))) |
| { |
| error ("linear step expression must be integral"); |
| remove = true; |
| break; |
| } |
| else |
| { |
| t = mark_rvalue_use (t); |
| if (ort == C_ORT_OMP_DECLARE_SIMD && TREE_CODE (t) == PARM_DECL) |
| { |
| OMP_CLAUSE_LINEAR_VARIABLE_STRIDE (c) = 1; |
| goto check_dup_generic; |
| } |
| if (!processing_template_decl |
| && (VAR_P (OMP_CLAUSE_DECL (c)) |
| || TREE_CODE (OMP_CLAUSE_DECL (c)) == PARM_DECL)) |
| { |
| if (ort == C_ORT_OMP_DECLARE_SIMD) |
| { |
| t = maybe_constant_value (t); |
| if (TREE_CODE (t) != INTEGER_CST) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%<linear%> clause step %qE is neither " |
| "constant nor a parameter", t); |
| remove = true; |
| break; |
| } |
| } |
| t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| tree type = TREE_TYPE (OMP_CLAUSE_DECL (c)); |
| if (TREE_CODE (type) == REFERENCE_TYPE) |
| type = TREE_TYPE (type); |
| if (OMP_CLAUSE_LINEAR_KIND (c) == OMP_CLAUSE_LINEAR_REF) |
| { |
| type = build_pointer_type (type); |
| tree d = fold_convert (type, OMP_CLAUSE_DECL (c)); |
| t = pointer_int_sum (OMP_CLAUSE_LOCATION (c), PLUS_EXPR, |
| d, t); |
| t = fold_build2_loc (OMP_CLAUSE_LOCATION (c), |
| MINUS_EXPR, sizetype, |
| fold_convert (sizetype, t), |
| fold_convert (sizetype, d)); |
| if (t == error_mark_node) |
| { |
| remove = true; |
| break; |
| } |
| } |
| else if (TREE_CODE (type) == POINTER_TYPE |
| /* Can't multiply the step yet if *this |
| is still incomplete type. */ |
| && (ort != C_ORT_OMP_DECLARE_SIMD |
| || TREE_CODE (OMP_CLAUSE_DECL (c)) != PARM_DECL |
| || !DECL_ARTIFICIAL (OMP_CLAUSE_DECL (c)) |
| || DECL_NAME (OMP_CLAUSE_DECL (c)) |
| != this_identifier |
| || !TYPE_BEING_DEFINED (TREE_TYPE (type)))) |
| { |
| tree d = convert_from_reference (OMP_CLAUSE_DECL (c)); |
| t = pointer_int_sum (OMP_CLAUSE_LOCATION (c), PLUS_EXPR, |
| d, t); |
| t = fold_build2_loc (OMP_CLAUSE_LOCATION (c), |
| MINUS_EXPR, sizetype, |
| fold_convert (sizetype, t), |
| fold_convert (sizetype, d)); |
| if (t == error_mark_node) |
| { |
| remove = true; |
| break; |
| } |
| } |
| else |
| t = fold_convert (type, t); |
| } |
| OMP_CLAUSE_LINEAR_STEP (c) = t; |
| } |
| goto check_dup_generic; |
| check_dup_generic: |
| t = omp_clause_decl_field (OMP_CLAUSE_DECL (c)); |
| if (t) |
| { |
| if (!remove && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_SHARED) |
| omp_note_field_privatization (t, OMP_CLAUSE_DECL (c)); |
| } |
| else |
| t = OMP_CLAUSE_DECL (c); |
| check_dup_generic_t: |
| if (t == current_class_ptr |
| && (ort != C_ORT_OMP_DECLARE_SIMD |
| || (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_LINEAR |
| && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_UNIFORM))) |
| { |
| error ("%<this%> allowed in OpenMP only in %<declare simd%>" |
| " clauses"); |
| remove = true; |
| break; |
| } |
| if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL |
| && (!field_ok || TREE_CODE (t) != FIELD_DECL)) |
| { |
| if (processing_template_decl && TREE_CODE (t) != OVERLOAD) |
| break; |
| if (DECL_P (t)) |
| error ("%qD is not a variable in clause %qs", t, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| else |
| error ("%qE is not a variable in clause %qs", t, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| remove = true; |
| } |
| else if (ort == C_ORT_ACC |
| && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION) |
| { |
| if (bitmap_bit_p (&oacc_reduction_head, DECL_UID (t))) |
| { |
| error ("%qD appears more than once in reduction clauses", t); |
| remove = true; |
| } |
| else |
| bitmap_set_bit (&oacc_reduction_head, DECL_UID (t)); |
| } |
| else if (bitmap_bit_p (&generic_head, DECL_UID (t)) |
| || bitmap_bit_p (&firstprivate_head, DECL_UID (t)) |
| || bitmap_bit_p (&lastprivate_head, DECL_UID (t))) |
| { |
| error ("%qD appears more than once in data clauses", t); |
| remove = true; |
| } |
| else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE |
| && bitmap_bit_p (&map_head, DECL_UID (t))) |
| { |
| if (ort == C_ORT_ACC) |
| error ("%qD appears more than once in data clauses", t); |
| else |
| error ("%qD appears both in data and map clauses", t); |
| remove = true; |
| } |
| else |
| bitmap_set_bit (&generic_head, DECL_UID (t)); |
| if (!field_ok) |
| break; |
| handle_field_decl: |
| if (!remove |
| && TREE_CODE (t) == FIELD_DECL |
| && t == OMP_CLAUSE_DECL (c) |
| && ort != C_ORT_ACC) |
| { |
| OMP_CLAUSE_DECL (c) |
| = omp_privatize_field (t, (OMP_CLAUSE_CODE (c) |
| == OMP_CLAUSE_SHARED)); |
| if (OMP_CLAUSE_DECL (c) == error_mark_node) |
| remove = true; |
| } |
| break; |
| |
| case OMP_CLAUSE_FIRSTPRIVATE: |
| t = omp_clause_decl_field (OMP_CLAUSE_DECL (c)); |
| if (t) |
| omp_note_field_privatization (t, OMP_CLAUSE_DECL (c)); |
| else |
| t = OMP_CLAUSE_DECL (c); |
| if (ort != C_ORT_ACC && t == current_class_ptr) |
| { |
| error ("%<this%> allowed in OpenMP only in %<declare simd%>" |
| " clauses"); |
| remove = true; |
| break; |
| } |
| if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL |
| && ((ort & C_ORT_OMP_DECLARE_SIMD) != C_ORT_OMP |
| || TREE_CODE (t) != FIELD_DECL)) |
| { |
| if (processing_template_decl && TREE_CODE (t) != OVERLOAD) |
| break; |
| if (DECL_P (t)) |
| error ("%qD is not a variable in clause %<firstprivate%>", t); |
| else |
| error ("%qE is not a variable in clause %<firstprivate%>", t); |
| remove = true; |
| } |
| else if (bitmap_bit_p (&generic_head, DECL_UID (t)) |
| || bitmap_bit_p (&firstprivate_head, DECL_UID (t))) |
| { |
| error ("%qD appears more than once in data clauses", t); |
| remove = true; |
| } |
| else if (bitmap_bit_p (&map_head, DECL_UID (t))) |
| { |
| if (ort == C_ORT_ACC) |
| error ("%qD appears more than once in data clauses", t); |
| else |
| error ("%qD appears both in data and map clauses", t); |
| remove = true; |
| } |
| else |
| bitmap_set_bit (&firstprivate_head, DECL_UID (t)); |
| goto handle_field_decl; |
| |
| case OMP_CLAUSE_LASTPRIVATE: |
| t = omp_clause_decl_field (OMP_CLAUSE_DECL (c)); |
| if (t) |
| omp_note_field_privatization (t, OMP_CLAUSE_DECL (c)); |
| else |
| t = OMP_CLAUSE_DECL (c); |
| if (t == current_class_ptr) |
| { |
| error ("%<this%> allowed in OpenMP only in %<declare simd%>" |
| " clauses"); |
| remove = true; |
| break; |
| } |
| if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL |
| && ((ort & C_ORT_OMP_DECLARE_SIMD) != C_ORT_OMP |
| || TREE_CODE (t) != FIELD_DECL)) |
| { |
| if (processing_template_decl && TREE_CODE (t) != OVERLOAD) |
| break; |
| if (DECL_P (t)) |
| error ("%qD is not a variable in clause %<lastprivate%>", t); |
| else |
| error ("%qE is not a variable in clause %<lastprivate%>", t); |
| remove = true; |
| } |
| else if (bitmap_bit_p (&generic_head, DECL_UID (t)) |
| || bitmap_bit_p (&lastprivate_head, DECL_UID (t))) |
| { |
| error ("%qD appears more than once in data clauses", t); |
| remove = true; |
| } |
| else |
| bitmap_set_bit (&lastprivate_head, DECL_UID (t)); |
| goto handle_field_decl; |
| |
| case OMP_CLAUSE_IF: |
| t = OMP_CLAUSE_IF_EXPR (c); |
| t = maybe_convert_cond (t); |
| if (t == error_mark_node) |
| remove = true; |
| else if (!processing_template_decl) |
| t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| OMP_CLAUSE_IF_EXPR (c) = t; |
| break; |
| |
| case OMP_CLAUSE_FINAL: |
| t = OMP_CLAUSE_FINAL_EXPR (c); |
| t = maybe_convert_cond (t); |
| if (t == error_mark_node) |
| remove = true; |
| else if (!processing_template_decl) |
| t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| OMP_CLAUSE_FINAL_EXPR (c) = t; |
| break; |
| |
| case OMP_CLAUSE_GANG: |
| /* Operand 1 is the gang static: argument. */ |
| t = OMP_CLAUSE_OPERAND (c, 1); |
| if (t != NULL_TREE) |
| { |
| if (t == error_mark_node) |
| remove = true; |
| else if (!type_dependent_expression_p (t) |
| && !INTEGRAL_TYPE_P (TREE_TYPE (t))) |
| { |
| error ("%<gang%> static expression must be integral"); |
| remove = true; |
| } |
| else |
| { |
| t = mark_rvalue_use (t); |
| if (!processing_template_decl) |
| { |
| t = maybe_constant_value (t); |
| if (TREE_CODE (t) == INTEGER_CST |
| && tree_int_cst_sgn (t) != 1 |
| && t != integer_minus_one_node) |
| { |
| warning_at (OMP_CLAUSE_LOCATION (c), 0, |
| "%<gang%> static value must be " |
| "positive"); |
| t = integer_one_node; |
| } |
| t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| } |
| } |
| OMP_CLAUSE_OPERAND (c, 1) = t; |
| } |
| /* Check operand 0, the num argument. */ |
| /* FALLTHRU */ |
| |
| case OMP_CLAUSE_WORKER: |
| case OMP_CLAUSE_VECTOR: |
| if (OMP_CLAUSE_OPERAND (c, 0) == NULL_TREE) |
| break; |
| /* FALLTHRU */ |
| |
| case OMP_CLAUSE_NUM_TASKS: |
| case OMP_CLAUSE_NUM_TEAMS: |
| case OMP_CLAUSE_NUM_THREADS: |
| case OMP_CLAUSE_NUM_GANGS: |
| case OMP_CLAUSE_NUM_WORKERS: |
| case OMP_CLAUSE_VECTOR_LENGTH: |
| t = OMP_CLAUSE_OPERAND (c, 0); |
| if (t == error_mark_node) |
| remove = true; |
| else if (!type_dependent_expression_p (t) |
| && !INTEGRAL_TYPE_P (TREE_TYPE (t))) |
| { |
| switch (OMP_CLAUSE_CODE (c)) |
| { |
| case OMP_CLAUSE_GANG: |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%<gang%> num expression must be integral"); break; |
| case OMP_CLAUSE_VECTOR: |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%<vector%> length expression must be integral"); |
| break; |
| case OMP_CLAUSE_WORKER: |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%<worker%> num expression must be integral"); |
| break; |
| default: |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%qs expression must be integral", |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| } |
| remove = true; |
| } |
| else |
| { |
| t = mark_rvalue_use (t); |
| if (!processing_template_decl) |
| { |
| t = maybe_constant_value (t); |
| if (TREE_CODE (t) == INTEGER_CST |
| && tree_int_cst_sgn (t) != 1) |
| { |
| switch (OMP_CLAUSE_CODE (c)) |
| { |
| case OMP_CLAUSE_GANG: |
| warning_at (OMP_CLAUSE_LOCATION (c), 0, |
| "%<gang%> num value must be positive"); |
| break; |
| case OMP_CLAUSE_VECTOR: |
| warning_at (OMP_CLAUSE_LOCATION (c), 0, |
| "%<vector%> length value must be " |
| "positive"); |
| break; |
| case OMP_CLAUSE_WORKER: |
| warning_at (OMP_CLAUSE_LOCATION (c), 0, |
| "%<worker%> num value must be " |
| "positive"); |
| break; |
| default: |
| warning_at (OMP_CLAUSE_LOCATION (c), 0, |
| "%qs value must be positive", |
| omp_clause_code_name |
| [OMP_CLAUSE_CODE (c)]); |
| } |
| t = integer_one_node; |
| } |
| t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| } |
| OMP_CLAUSE_OPERAND (c, 0) = t; |
| } |
| break; |
| |
| case OMP_CLAUSE_SCHEDULE: |
| if (OMP_CLAUSE_SCHEDULE_KIND (c) & OMP_CLAUSE_SCHEDULE_NONMONOTONIC) |
| { |
| const char *p = NULL; |
| switch (OMP_CLAUSE_SCHEDULE_KIND (c) & OMP_CLAUSE_SCHEDULE_MASK) |
| { |
| case OMP_CLAUSE_SCHEDULE_STATIC: p = "static"; break; |
| case OMP_CLAUSE_SCHEDULE_DYNAMIC: break; |
| case OMP_CLAUSE_SCHEDULE_GUIDED: break; |
| case OMP_CLAUSE_SCHEDULE_AUTO: p = "auto"; break; |
| case OMP_CLAUSE_SCHEDULE_RUNTIME: p = "runtime"; break; |
| default: gcc_unreachable (); |
| } |
| if (p) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%<nonmonotonic%> modifier specified for %qs " |
| "schedule kind", p); |
| OMP_CLAUSE_SCHEDULE_KIND (c) |
| = (enum omp_clause_schedule_kind) |
| (OMP_CLAUSE_SCHEDULE_KIND (c) |
| & ~OMP_CLAUSE_SCHEDULE_NONMONOTONIC); |
| } |
| } |
| |
| t = OMP_CLAUSE_SCHEDULE_CHUNK_EXPR (c); |
| if (t == NULL) |
| ; |
| else if (t == error_mark_node) |
| remove = true; |
| else if (!type_dependent_expression_p (t) |
| && (OMP_CLAUSE_SCHEDULE_KIND (c) |
| != OMP_CLAUSE_SCHEDULE_CILKFOR) |
| && !INTEGRAL_TYPE_P (TREE_TYPE (t))) |
| { |
| error ("schedule chunk size expression must be integral"); |
| remove = true; |
| } |
| else |
| { |
| t = mark_rvalue_use (t); |
| if (!processing_template_decl) |
| { |
| if (OMP_CLAUSE_SCHEDULE_KIND (c) |
| == OMP_CLAUSE_SCHEDULE_CILKFOR) |
| { |
| t = convert_to_integer (long_integer_type_node, t); |
| if (t == error_mark_node) |
| { |
| remove = true; |
| break; |
| } |
| } |
| else |
| { |
| t = maybe_constant_value (t); |
| if (TREE_CODE (t) == INTEGER_CST |
| && tree_int_cst_sgn (t) != 1) |
| { |
| warning_at (OMP_CLAUSE_LOCATION (c), 0, |
| "chunk size value must be positive"); |
| t = integer_one_node; |
| } |
| } |
| t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| } |
| OMP_CLAUSE_SCHEDULE_CHUNK_EXPR (c) = t; |
| } |
| break; |
| |
| case OMP_CLAUSE_SIMDLEN: |
| case OMP_CLAUSE_SAFELEN: |
| t = OMP_CLAUSE_OPERAND (c, 0); |
| if (t == error_mark_node) |
| remove = true; |
| else if (!type_dependent_expression_p (t) |
| && !INTEGRAL_TYPE_P (TREE_TYPE (t))) |
| { |
| error ("%qs length expression must be integral", |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| remove = true; |
| } |
| else |
| { |
| t = mark_rvalue_use (t); |
| if (!processing_template_decl) |
| { |
| t = maybe_constant_value (t); |
| if (TREE_CODE (t) != INTEGER_CST |
| || tree_int_cst_sgn (t) != 1) |
| { |
| error ("%qs length expression must be positive constant" |
| " integer expression", |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| remove = true; |
| } |
| } |
| OMP_CLAUSE_OPERAND (c, 0) = t; |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_SAFELEN) |
| safelen = c; |
| } |
| break; |
| |
| case OMP_CLAUSE_ASYNC: |
| t = OMP_CLAUSE_ASYNC_EXPR (c); |
| if (t == error_mark_node) |
| remove = true; |
| else if (!type_dependent_expression_p (t) |
| && !INTEGRAL_TYPE_P (TREE_TYPE (t))) |
| { |
| error ("%<async%> expression must be integral"); |
| remove = true; |
| } |
| else |
| { |
| t = mark_rvalue_use (t); |
| if (!processing_template_decl) |
| t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| OMP_CLAUSE_ASYNC_EXPR (c) = t; |
| } |
| break; |
| |
| case OMP_CLAUSE_WAIT: |
| t = OMP_CLAUSE_WAIT_EXPR (c); |
| if (t == error_mark_node) |
| remove = true; |
| else if (!processing_template_decl) |
| t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| OMP_CLAUSE_WAIT_EXPR (c) = t; |
| break; |
| |
| case OMP_CLAUSE_THREAD_LIMIT: |
| t = OMP_CLAUSE_THREAD_LIMIT_EXPR (c); |
| if (t == error_mark_node) |
| remove = true; |
| else if (!type_dependent_expression_p (t) |
| && !INTEGRAL_TYPE_P (TREE_TYPE (t))) |
| { |
| error ("%<thread_limit%> expression must be integral"); |
| remove = true; |
| } |
| else |
| { |
| t = mark_rvalue_use (t); |
| if (!processing_template_decl) |
| { |
| t = maybe_constant_value (t); |
| if (TREE_CODE (t) == INTEGER_CST |
| && tree_int_cst_sgn (t) != 1) |
| { |
| warning_at (OMP_CLAUSE_LOCATION (c), 0, |
| "%<thread_limit%> value must be positive"); |
| t = integer_one_node; |
| } |
| t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| } |
| OMP_CLAUSE_THREAD_LIMIT_EXPR (c) = t; |
| } |
| break; |
| |
| case OMP_CLAUSE_DEVICE: |
| t = OMP_CLAUSE_DEVICE_ID (c); |
| if (t == error_mark_node) |
| remove = true; |
| else if (!type_dependent_expression_p (t) |
| && !INTEGRAL_TYPE_P (TREE_TYPE (t))) |
| { |
| error ("%<device%> id must be integral"); |
| remove = true; |
| } |
| else |
| { |
| t = mark_rvalue_use (t); |
| if (!processing_template_decl) |
| t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| OMP_CLAUSE_DEVICE_ID (c) = t; |
| } |
| break; |
| |
| case OMP_CLAUSE_DIST_SCHEDULE: |
| t = OMP_CLAUSE_DIST_SCHEDULE_CHUNK_EXPR (c); |
| if (t == NULL) |
| ; |
| else if (t == error_mark_node) |
| remove = true; |
| else if (!type_dependent_expression_p (t) |
| && !INTEGRAL_TYPE_P (TREE_TYPE (t))) |
| { |
| error ("%<dist_schedule%> chunk size expression must be " |
| "integral"); |
| remove = true; |
| } |
| else |
| { |
| t = mark_rvalue_use (t); |
| if (!processing_template_decl) |
| t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| OMP_CLAUSE_DIST_SCHEDULE_CHUNK_EXPR (c) = t; |
| } |
| break; |
| |
| case OMP_CLAUSE_ALIGNED: |
| t = OMP_CLAUSE_DECL (c); |
| if (t == current_class_ptr && ort != C_ORT_OMP_DECLARE_SIMD) |
| { |
| error ("%<this%> allowed in OpenMP only in %<declare simd%>" |
| " clauses"); |
| remove = true; |
| break; |
| } |
| if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL) |
| { |
| if (processing_template_decl && TREE_CODE (t) != OVERLOAD) |
| break; |
| if (DECL_P (t)) |
| error ("%qD is not a variable in %<aligned%> clause", t); |
| else |
| error ("%qE is not a variable in %<aligned%> clause", t); |
| remove = true; |
| } |
| else if (!type_dependent_expression_p (t) |
| && TREE_CODE (TREE_TYPE (t)) != POINTER_TYPE |
| && TREE_CODE (TREE_TYPE (t)) != ARRAY_TYPE |
| && (TREE_CODE (TREE_TYPE (t)) != REFERENCE_TYPE |
| || (!POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (t))) |
| && (TREE_CODE (TREE_TYPE (TREE_TYPE (t))) |
| != ARRAY_TYPE)))) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%qE in %<aligned%> clause is neither a pointer nor " |
| "an array nor a reference to pointer or array", t); |
| remove = true; |
| } |
| else if (bitmap_bit_p (&aligned_head, DECL_UID (t))) |
| { |
| error ("%qD appears more than once in %<aligned%> clauses", t); |
| remove = true; |
| } |
| else |
| bitmap_set_bit (&aligned_head, DECL_UID (t)); |
| t = OMP_CLAUSE_ALIGNED_ALIGNMENT (c); |
| if (t == error_mark_node) |
| remove = true; |
| else if (t == NULL_TREE) |
| break; |
| else if (!type_dependent_expression_p (t) |
| && !INTEGRAL_TYPE_P (TREE_TYPE (t))) |
| { |
| error ("%<aligned%> clause alignment expression must " |
| "be integral"); |
| remove = true; |
| } |
| else |
| { |
| t = mark_rvalue_use (t); |
| if (!processing_template_decl) |
| { |
| t = maybe_constant_value (t); |
| if (TREE_CODE (t) != INTEGER_CST |
| || tree_int_cst_sgn (t) != 1) |
| { |
| error ("%<aligned%> clause alignment expression must be " |
| "positive constant integer expression"); |
| remove = true; |
| } |
| } |
| OMP_CLAUSE_ALIGNED_ALIGNMENT (c) = t; |
| } |
| break; |
| |
| case OMP_CLAUSE_DEPEND: |
| t = OMP_CLAUSE_DECL (c); |
| if (t == NULL_TREE) |
| { |
| gcc_assert (OMP_CLAUSE_DEPEND_KIND (c) |
| == OMP_CLAUSE_DEPEND_SOURCE); |
| break; |
| } |
| if (OMP_CLAUSE_DEPEND_KIND (c) == OMP_CLAUSE_DEPEND_SINK) |
| { |
| if (cp_finish_omp_clause_depend_sink (c)) |
| remove = true; |
| break; |
| } |
| if (TREE_CODE (t) == TREE_LIST) |
| { |
| if (handle_omp_array_sections (c, ort)) |
| remove = true; |
| break; |
| } |
| if (t == error_mark_node) |
| remove = true; |
| else if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL) |
| { |
| if (processing_template_decl && TREE_CODE (t) != OVERLOAD) |
| break; |
| if (DECL_P (t)) |
| error ("%qD is not a variable in %<depend%> clause", t); |
| else |
| error ("%qE is not a variable in %<depend%> clause", t); |
| remove = true; |
| } |
| else if (t == current_class_ptr) |
| { |
| error ("%<this%> allowed in OpenMP only in %<declare simd%>" |
| " clauses"); |
| remove = true; |
| } |
| else if (!processing_template_decl |
| && !cxx_mark_addressable (t)) |
| remove = true; |
| break; |
| |
| case OMP_CLAUSE_MAP: |
| case OMP_CLAUSE_TO: |
| case OMP_CLAUSE_FROM: |
| case OMP_CLAUSE__CACHE_: |
| t = OMP_CLAUSE_DECL (c); |
| if (TREE_CODE (t) == TREE_LIST) |
| { |
| if (handle_omp_array_sections (c, ort)) |
| remove = true; |
| else |
| { |
| t = OMP_CLAUSE_DECL (c); |
| if (TREE_CODE (t) != TREE_LIST |
| && !type_dependent_expression_p (t) |
| && !cp_omp_mappable_type (TREE_TYPE (t))) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "array section does not have mappable type " |
| "in %qs clause", |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| remove = true; |
| } |
| while (TREE_CODE (t) == ARRAY_REF) |
| t = TREE_OPERAND (t, 0); |
| if (TREE_CODE (t) == COMPONENT_REF |
| && TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE) |
| { |
| while (TREE_CODE (t) == COMPONENT_REF) |
| t = TREE_OPERAND (t, 0); |
| if (REFERENCE_REF_P (t)) |
| t = TREE_OPERAND (t, 0); |
| if (bitmap_bit_p (&map_field_head, DECL_UID (t))) |
| break; |
| if (bitmap_bit_p (&map_head, DECL_UID (t))) |
| { |
| if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP) |
| error ("%qD appears more than once in motion" |
| " clauses", t); |
| else if (ort == C_ORT_ACC) |
| error ("%qD appears more than once in data" |
| " clauses", t); |
| else |
| error ("%qD appears more than once in map" |
| " clauses", t); |
| remove = true; |
| } |
| else |
| { |
| bitmap_set_bit (&map_head, DECL_UID (t)); |
| bitmap_set_bit (&map_field_head, DECL_UID (t)); |
| } |
| } |
| } |
| break; |
| } |
| if (t == error_mark_node) |
| { |
| remove = true; |
| break; |
| } |
| if (REFERENCE_REF_P (t) |
| && TREE_CODE (TREE_OPERAND (t, 0)) == COMPONENT_REF) |
| { |
| t = TREE_OPERAND (t, 0); |
| OMP_CLAUSE_DECL (c) = t; |
| } |
| if (TREE_CODE (t) == COMPONENT_REF |
| && (ort & C_ORT_OMP_DECLARE_SIMD) == C_ORT_OMP |
| && OMP_CLAUSE_CODE (c) != OMP_CLAUSE__CACHE_) |
| { |
| if (type_dependent_expression_p (t)) |
| break; |
| if (TREE_CODE (TREE_OPERAND (t, 1)) == FIELD_DECL |
| && DECL_BIT_FIELD (TREE_OPERAND (t, 1))) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "bit-field %qE in %qs clause", |
| t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| remove = true; |
| } |
| else if (!cp_omp_mappable_type (TREE_TYPE (t))) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%qE does not have a mappable type in %qs clause", |
| t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| remove = true; |
| } |
| while (TREE_CODE (t) == COMPONENT_REF) |
| { |
| if (TREE_TYPE (TREE_OPERAND (t, 0)) |
| && (TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) |
| == UNION_TYPE)) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%qE is a member of a union", t); |
| remove = true; |
| break; |
| } |
| t = TREE_OPERAND (t, 0); |
| } |
| if (remove) |
| break; |
| if (REFERENCE_REF_P (t)) |
| t = TREE_OPERAND (t, 0); |
| if (VAR_P (t) || TREE_CODE (t) == PARM_DECL) |
| { |
| if (bitmap_bit_p (&map_field_head, DECL_UID (t))) |
| goto handle_map_references; |
| } |
| } |
| if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL) |
| { |
| if (processing_template_decl && TREE_CODE (t) != OVERLOAD) |
| break; |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP |
| && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER |
| || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_POINTER)) |
| break; |
| if (DECL_P (t)) |
| error ("%qD is not a variable in %qs clause", t, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| else |
| error ("%qE is not a variable in %qs clause", t, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| remove = true; |
| } |
| else if (VAR_P (t) && CP_DECL_THREAD_LOCAL_P (t)) |
| { |
| error ("%qD is threadprivate variable in %qs clause", t, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| remove = true; |
| } |
| else if (ort != C_ORT_ACC && t == current_class_ptr) |
| { |
| error ("%<this%> allowed in OpenMP only in %<declare simd%>" |
| " clauses"); |
| remove = true; |
| break; |
| } |
| else if (!processing_template_decl |
| && TREE_CODE (TREE_TYPE (t)) != REFERENCE_TYPE |
| && (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP |
| || (OMP_CLAUSE_MAP_KIND (c) |
| != GOMP_MAP_FIRSTPRIVATE_POINTER)) |
| && !cxx_mark_addressable (t)) |
| remove = true; |
| else if (!(OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP |
| && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER |
| || (OMP_CLAUSE_MAP_KIND (c) |
| == GOMP_MAP_FIRSTPRIVATE_POINTER))) |
| && t == OMP_CLAUSE_DECL (c) |
| && !type_dependent_expression_p (t) |
| && !cp_omp_mappable_type ((TREE_CODE (TREE_TYPE (t)) |
| == REFERENCE_TYPE) |
| ? TREE_TYPE (TREE_TYPE (t)) |
| : TREE_TYPE (t))) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%qD does not have a mappable type in %qs clause", t, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| remove = true; |
| } |
| else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP |
| && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FORCE_DEVICEPTR |
| && !type_dependent_expression_p (t) |
| && !POINTER_TYPE_P (TREE_TYPE (t))) |
| { |
| error ("%qD is not a pointer variable", t); |
| remove = true; |
| } |
| else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP |
| && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER) |
| { |
| if (bitmap_bit_p (&generic_head, DECL_UID (t)) |
| || bitmap_bit_p (&firstprivate_head, DECL_UID (t))) |
| { |
| error ("%qD appears more than once in data clauses", t); |
| remove = true; |
| } |
| else if (bitmap_bit_p (&map_head, DECL_UID (t))) |
| { |
| if (ort == C_ORT_ACC) |
| error ("%qD appears more than once in data clauses", t); |
| else |
| error ("%qD appears both in data and map clauses", t); |
| remove = true; |
| } |
| else |
| bitmap_set_bit (&generic_head, DECL_UID (t)); |
| } |
| else if (bitmap_bit_p (&map_head, DECL_UID (t))) |
| { |
| if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP) |
| error ("%qD appears more than once in motion clauses", t); |
| if (ort == C_ORT_ACC) |
| error ("%qD appears more than once in data clauses", t); |
| else |
| error ("%qD appears more than once in map clauses", t); |
| remove = true; |
| } |
| else if (bitmap_bit_p (&generic_head, DECL_UID (t)) |
| || bitmap_bit_p (&firstprivate_head, DECL_UID (t))) |
| { |
| if (ort == C_ORT_ACC) |
| error ("%qD appears more than once in data clauses", t); |
| else |
| error ("%qD appears both in data and map clauses", t); |
| remove = true; |
| } |
| else |
| { |
| bitmap_set_bit (&map_head, DECL_UID (t)); |
| if (t != OMP_CLAUSE_DECL (c) |
| && TREE_CODE (OMP_CLAUSE_DECL (c)) == COMPONENT_REF) |
| bitmap_set_bit (&map_field_head, DECL_UID (t)); |
| } |
| handle_map_references: |
| if (!remove |
| && !processing_template_decl |
| && (ort & C_ORT_OMP_DECLARE_SIMD) == C_ORT_OMP |
| && TREE_CODE (TREE_TYPE (OMP_CLAUSE_DECL (c))) == REFERENCE_TYPE) |
| { |
| t = OMP_CLAUSE_DECL (c); |
| if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP) |
| { |
| OMP_CLAUSE_DECL (c) = build_simple_mem_ref (t); |
| if (OMP_CLAUSE_SIZE (c) == NULL_TREE) |
| OMP_CLAUSE_SIZE (c) |
| = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (t))); |
| } |
| else if (OMP_CLAUSE_MAP_KIND (c) |
| != GOMP_MAP_FIRSTPRIVATE_POINTER |
| && (OMP_CLAUSE_MAP_KIND (c) |
| != GOMP_MAP_FIRSTPRIVATE_REFERENCE) |
| && (OMP_CLAUSE_MAP_KIND (c) |
| != GOMP_MAP_ALWAYS_POINTER)) |
| { |
| tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c), |
| OMP_CLAUSE_MAP); |
| if (TREE_CODE (t) == COMPONENT_REF) |
| OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ALWAYS_POINTER); |
| else |
| OMP_CLAUSE_SET_MAP_KIND (c2, |
| GOMP_MAP_FIRSTPRIVATE_REFERENCE); |
| OMP_CLAUSE_DECL (c2) = t; |
| OMP_CLAUSE_SIZE (c2) = size_zero_node; |
| OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c); |
| OMP_CLAUSE_CHAIN (c) = c2; |
| OMP_CLAUSE_DECL (c) = build_simple_mem_ref (t); |
| if (OMP_CLAUSE_SIZE (c) == NULL_TREE) |
| OMP_CLAUSE_SIZE (c) |
| = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (t))); |
| c = c2; |
| } |
| } |
| break; |
| |
| case OMP_CLAUSE_TO_DECLARE: |
| case OMP_CLAUSE_LINK: |
| t = OMP_CLAUSE_DECL (c); |
| if (TREE_CODE (t) == FUNCTION_DECL |
| && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TO_DECLARE) |
| ; |
| else if (!VAR_P (t)) |
| { |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TO_DECLARE) |
| { |
| if (TREE_CODE (t) == TEMPLATE_ID_EXPR) |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "template %qE in clause %qs", t, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| else if (really_overloaded_fn (t)) |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "overloaded function name %qE in clause %qs", t, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| else |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%qE is neither a variable nor a function name " |
| "in clause %qs", t, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| } |
| else |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%qE is not a variable in clause %qs", t, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| remove = true; |
| } |
| else if (DECL_THREAD_LOCAL_P (t)) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%qD is threadprivate variable in %qs clause", t, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| remove = true; |
| } |
| else if (!cp_omp_mappable_type (TREE_TYPE (t))) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%qD does not have a mappable type in %qs clause", t, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| remove = true; |
| } |
| if (remove) |
| break; |
| if (bitmap_bit_p (&generic_head, DECL_UID (t))) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%qE appears more than once on the same " |
| "%<declare target%> directive", t); |
| remove = true; |
| } |
| else |
| bitmap_set_bit (&generic_head, DECL_UID (t)); |
| break; |
| |
| case OMP_CLAUSE_UNIFORM: |
| t = OMP_CLAUSE_DECL (c); |
| if (TREE_CODE (t) != PARM_DECL) |
| { |
| if (processing_template_decl) |
| break; |
| if (DECL_P (t)) |
| error ("%qD is not an argument in %<uniform%> clause", t); |
| else |
| error ("%qE is not an argument in %<uniform%> clause", t); |
| remove = true; |
| break; |
| } |
| /* map_head bitmap is used as uniform_head if declare_simd. */ |
| bitmap_set_bit (&map_head, DECL_UID (t)); |
| goto check_dup_generic; |
| |
| case OMP_CLAUSE_GRAINSIZE: |
| t = OMP_CLAUSE_GRAINSIZE_EXPR (c); |
| if (t == error_mark_node) |
| remove = true; |
| else if (!type_dependent_expression_p (t) |
| && !INTEGRAL_TYPE_P (TREE_TYPE (t))) |
| { |
| error ("%<grainsize%> expression must be integral"); |
| remove = true; |
| } |
| else |
| { |
| t = mark_rvalue_use (t); |
| if (!processing_template_decl) |
| { |
| t = maybe_constant_value (t); |
| if (TREE_CODE (t) == INTEGER_CST |
| && tree_int_cst_sgn (t) != 1) |
| { |
| warning_at (OMP_CLAUSE_LOCATION (c), 0, |
| "%<grainsize%> value must be positive"); |
| t = integer_one_node; |
| } |
| t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| } |
| OMP_CLAUSE_GRAINSIZE_EXPR (c) = t; |
| } |
| break; |
| |
| case OMP_CLAUSE_PRIORITY: |
| t = OMP_CLAUSE_PRIORITY_EXPR (c); |
| if (t == error_mark_node) |
| remove = true; |
| else if (!type_dependent_expression_p (t) |
| && !INTEGRAL_TYPE_P (TREE_TYPE (t))) |
| { |
| error ("%<priority%> expression must be integral"); |
| remove = true; |
| } |
| else |
| { |
| t = mark_rvalue_use (t); |
| if (!processing_template_decl) |
| { |
| t = maybe_constant_value (t); |
| if (TREE_CODE (t) == INTEGER_CST |
| && tree_int_cst_sgn (t) == -1) |
| { |
| warning_at (OMP_CLAUSE_LOCATION (c), 0, |
| "%<priority%> value must be non-negative"); |
| t = integer_one_node; |
| } |
| t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| } |
| OMP_CLAUSE_PRIORITY_EXPR (c) = t; |
| } |
| break; |
| |
| case OMP_CLAUSE_HINT: |
| t = OMP_CLAUSE_HINT_EXPR (c); |
| if (t == error_mark_node) |
| remove = true; |
| else if (!type_dependent_expression_p (t) |
| && !INTEGRAL_TYPE_P (TREE_TYPE (t))) |
| { |
| error ("%<num_tasks%> expression must be integral"); |
| remove = true; |
| } |
| else |
| { |
| t = mark_rvalue_use (t); |
| if (!processing_template_decl) |
| { |
| t = maybe_constant_value (t); |
| t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| } |
| OMP_CLAUSE_HINT_EXPR (c) = t; |
| } |
| break; |
| |
| case OMP_CLAUSE_IS_DEVICE_PTR: |
| case OMP_CLAUSE_USE_DEVICE_PTR: |
| field_ok = (ort & C_ORT_OMP_DECLARE_SIMD) == C_ORT_OMP; |
| t = OMP_CLAUSE_DECL (c); |
| if (!type_dependent_expression_p (t)) |
| { |
| tree type = TREE_TYPE (t); |
| if (TREE_CODE (type) != POINTER_TYPE |
| && TREE_CODE (type) != ARRAY_TYPE |
| && (TREE_CODE (type) != REFERENCE_TYPE |
| || (TREE_CODE (TREE_TYPE (type)) != POINTER_TYPE |
| && TREE_CODE (TREE_TYPE (type)) != ARRAY_TYPE))) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%qs variable is neither a pointer, nor an array " |
| "nor reference to pointer or array", |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| remove = true; |
| } |
| } |
| goto check_dup_generic; |
| |
| case OMP_CLAUSE_NOWAIT: |
| case OMP_CLAUSE_DEFAULT: |
| case OMP_CLAUSE_UNTIED: |
| case OMP_CLAUSE_COLLAPSE: |
| case OMP_CLAUSE_MERGEABLE: |
| case OMP_CLAUSE_PARALLEL: |
| case OMP_CLAUSE_FOR: |
| case OMP_CLAUSE_SECTIONS: |
| case OMP_CLAUSE_TASKGROUP: |
| case OMP_CLAUSE_PROC_BIND: |
| case OMP_CLAUSE_NOGROUP: |
| case OMP_CLAUSE_THREADS: |
| case OMP_CLAUSE_SIMD: |
| case OMP_CLAUSE_DEFAULTMAP: |
| case OMP_CLAUSE__CILK_FOR_COUNT_: |
| case OMP_CLAUSE_AUTO: |
| case OMP_CLAUSE_INDEPENDENT: |
| case OMP_CLAUSE_SEQ: |
| break; |
| |
| case OMP_CLAUSE_TILE: |
| for (tree list = OMP_CLAUSE_TILE_LIST (c); !remove && list; |
| list = TREE_CHAIN (list)) |
| { |
| t = TREE_VALUE (list); |
| |
| if (t == error_mark_node) |
| remove = true; |
| else if (!type_dependent_expression_p (t) |
| && !INTEGRAL_TYPE_P (TREE_TYPE (t))) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%<tile%> argument needs integral type"); |
| remove = true; |
| } |
| else |
| { |
| t = mark_rvalue_use (t); |
| if (!processing_template_decl) |
| { |
| /* Zero is used to indicate '*', we permit you |
| to get there via an ICE of value zero. */ |
| t = maybe_constant_value (t); |
| if (!tree_fits_shwi_p (t) |
| || tree_to_shwi (t) < 0) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%<tile%> argument needs positive " |
| "integral constant"); |
| remove = true; |
| } |
| t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| } |
| } |
| |
| /* Update list item. */ |
| TREE_VALUE (list) = t; |
| } |
| break; |
| |
| case OMP_CLAUSE_ORDERED: |
| ordered_seen = true; |
| break; |
| |
| case OMP_CLAUSE_INBRANCH: |
| case OMP_CLAUSE_NOTINBRANCH: |
| if (branch_seen) |
| { |
| error ("%<inbranch%> clause is incompatible with " |
| "%<notinbranch%>"); |
| remove = true; |
| } |
| branch_seen = true; |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (remove) |
| *pc = OMP_CLAUSE_CHAIN (c); |
| else |
| pc = &OMP_CLAUSE_CHAIN (c); |
| } |
| |
| for (pc = &clauses, c = clauses; c ; c = *pc) |
| { |
| enum omp_clause_code c_kind = OMP_CLAUSE_CODE (c); |
| bool remove = false; |
| bool need_complete_type = false; |
| bool need_default_ctor = false; |
| bool need_copy_ctor = false; |
| bool need_copy_assignment = false; |
| bool need_implicitly_determined = false; |
| bool need_dtor = false; |
| tree type, inner_type; |
| |
| switch (c_kind) |
| { |
| case OMP_CLAUSE_SHARED: |
| need_implicitly_determined = true; |
| break; |
| case OMP_CLAUSE_PRIVATE: |
| need_complete_type = true; |
| need_default_ctor = true; |
| need_dtor = true; |
| need_implicitly_determined = true; |
| break; |
| case OMP_CLAUSE_FIRSTPRIVATE: |
| need_complete_type = true; |
| need_copy_ctor = true; |
| need_dtor = true; |
| need_implicitly_determined = true; |
| break; |
| case OMP_CLAUSE_LASTPRIVATE: |
| need_complete_type = true; |
| need_copy_assignment = true; |
| need_implicitly_determined = true; |
| break; |
| case OMP_CLAUSE_REDUCTION: |
| need_implicitly_determined = true; |
| break; |
| case OMP_CLAUSE_LINEAR: |
| if (ort != C_ORT_OMP_DECLARE_SIMD) |
| need_implicitly_determined = true; |
| else if (OMP_CLAUSE_LINEAR_VARIABLE_STRIDE (c) |
| && !bitmap_bit_p (&map_head, |
| DECL_UID (OMP_CLAUSE_LINEAR_STEP (c)))) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%<linear%> clause step is a parameter %qD not " |
| "specified in %<uniform%> clause", |
| OMP_CLAUSE_LINEAR_STEP (c)); |
| *pc = OMP_CLAUSE_CHAIN (c); |
| continue; |
| } |
| break; |
| case OMP_CLAUSE_COPYPRIVATE: |
| need_copy_assignment = true; |
| break; |
| case OMP_CLAUSE_COPYIN: |
| need_copy_assignment = true; |
| break; |
| case OMP_CLAUSE_SIMDLEN: |
| if (safelen |
| && !processing_template_decl |
| && tree_int_cst_lt (OMP_CLAUSE_SAFELEN_EXPR (safelen), |
| OMP_CLAUSE_SIMDLEN_EXPR (c))) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%<simdlen%> clause value is bigger than " |
| "%<safelen%> clause value"); |
| OMP_CLAUSE_SIMDLEN_EXPR (c) |
| = OMP_CLAUSE_SAFELEN_EXPR (safelen); |
| } |
| pc = &OMP_CLAUSE_CHAIN (c); |
| continue; |
| case OMP_CLAUSE_SCHEDULE: |
| if (ordered_seen |
| && (OMP_CLAUSE_SCHEDULE_KIND (c) |
| & OMP_CLAUSE_SCHEDULE_NONMONOTONIC)) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%<nonmonotonic%> schedule modifier specified " |
| "together with %<ordered%> clause"); |
| OMP_CLAUSE_SCHEDULE_KIND (c) |
| = (enum omp_clause_schedule_kind) |
| (OMP_CLAUSE_SCHEDULE_KIND (c) |
| & ~OMP_CLAUSE_SCHEDULE_NONMONOTONIC); |
| } |
| pc = &OMP_CLAUSE_CHAIN (c); |
| continue; |
| case OMP_CLAUSE_NOWAIT: |
| if (copyprivate_seen) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%<nowait%> clause must not be used together " |
| "with %<copyprivate%>"); |
| *pc = OMP_CLAUSE_CHAIN (c); |
| continue; |
| } |
| /* FALLTHRU */ |
| default: |
| pc = &OMP_CLAUSE_CHAIN (c); |
| continue; |
| } |
| |
| t = OMP_CLAUSE_DECL (c); |
| if (processing_template_decl |
| && !VAR_P (t) && TREE_CODE (t) != PARM_DECL) |
| { |
| pc = &OMP_CLAUSE_CHAIN (c); |
| continue; |
| } |
| |
| switch (c_kind) |
| { |
| case OMP_CLAUSE_LASTPRIVATE: |
| if (!bitmap_bit_p (&firstprivate_head, DECL_UID (t))) |
| { |
| need_default_ctor = true; |
| need_dtor = true; |
| } |
| break; |
| |
| case OMP_CLAUSE_REDUCTION: |
| if (finish_omp_reduction_clause (c, &need_default_ctor, |
| &need_dtor)) |
| remove = true; |
| else |
| t = OMP_CLAUSE_DECL (c); |
| break; |
| |
| case OMP_CLAUSE_COPYIN: |
| if (!VAR_P (t) || !CP_DECL_THREAD_LOCAL_P (t)) |
| { |
| error ("%qE must be %<threadprivate%> for %<copyin%>", t); |
| remove = true; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| if (need_complete_type || need_copy_assignment) |
| { |
| t = require_complete_type (t); |
| if (t == error_mark_node) |
| remove = true; |
| else if (TREE_CODE (TREE_TYPE (t)) == REFERENCE_TYPE |
| && !complete_type_or_else (TREE_TYPE (TREE_TYPE (t)), t)) |
| remove = true; |
| } |
| if (need_implicitly_determined) |
| { |
| const char *share_name = NULL; |
| |
| if (VAR_P (t) && CP_DECL_THREAD_LOCAL_P (t)) |
| share_name = "threadprivate"; |
| else switch (cxx_omp_predetermined_sharing (t)) |
| { |
| case OMP_CLAUSE_DEFAULT_UNSPECIFIED: |
| break; |
| case OMP_CLAUSE_DEFAULT_SHARED: |
| /* const vars may be specified in firstprivate clause. */ |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE |
| && cxx_omp_const_qual_no_mutable (t)) |
| break; |
| share_name = "shared"; |
| break; |
| case OMP_CLAUSE_DEFAULT_PRIVATE: |
| share_name = "private"; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| if (share_name) |
| { |
| error ("%qE is predetermined %qs for %qs", |
| omp_clause_printable_decl (t), share_name, |
| omp_clause_code_name[OMP_CLAUSE_CODE (c)]); |
| remove = true; |
| } |
| } |
| |
| /* We're interested in the base element, not arrays. */ |
| inner_type = type = TREE_TYPE (t); |
| if ((need_complete_type |
| || need_copy_assignment |
| || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION) |
| && TREE_CODE (inner_type) == REFERENCE_TYPE) |
| inner_type = TREE_TYPE (inner_type); |
| while (TREE_CODE (inner_type) == ARRAY_TYPE) |
| inner_type = TREE_TYPE (inner_type); |
| |
| /* Check for special function availability by building a call to one. |
| Save the results, because later we won't be in the right context |
| for making these queries. */ |
| if (CLASS_TYPE_P (inner_type) |
| && COMPLETE_TYPE_P (inner_type) |
| && (need_default_ctor || need_copy_ctor |
| || need_copy_assignment || need_dtor) |
| && !type_dependent_expression_p (t) |
| && cxx_omp_create_clause_info (c, inner_type, need_default_ctor, |
| need_copy_ctor, need_copy_assignment, |
| need_dtor)) |
| remove = true; |
| |
| if (!remove |
| && c_kind == OMP_CLAUSE_SHARED |
| && processing_template_decl) |
| { |
| t = omp_clause_decl_field (OMP_CLAUSE_DECL (c)); |
| if (t) |
| OMP_CLAUSE_DECL (c) = t; |
| } |
| |
| if (remove) |
| *pc = OMP_CLAUSE_CHAIN (c); |
| else |
| pc = &OMP_CLAUSE_CHAIN (c); |
| } |
| |
| bitmap_obstack_release (NULL); |
| return clauses; |
| } |
| |
| /* Start processing OpenMP clauses that can include any |
| privatization clauses for non-static data members. */ |
| |
| tree |
| push_omp_privatization_clauses (bool ignore_next) |
| { |
| if (omp_private_member_ignore_next) |
| { |
| omp_private_member_ignore_next = ignore_next; |
| return NULL_TREE; |
| } |
| omp_private_member_ignore_next = ignore_next; |
| if (omp_private_member_map) |
| omp_private_member_vec.safe_push (error_mark_node); |
| return push_stmt_list (); |
| } |
| |
| /* Revert remapping of any non-static data members since |
| the last push_omp_privatization_clauses () call. */ |
| |
| void |
| pop_omp_privatization_clauses (tree stmt) |
| { |
| if (stmt == NULL_TREE) |
| return; |
| stmt = pop_stmt_list (stmt); |
| if (omp_private_member_map) |
| { |
| while (!omp_private_member_vec.is_empty ()) |
| { |
| tree t = omp_private_member_vec.pop (); |
| if (t == error_mark_node) |
| { |
| add_stmt (stmt); |
| return; |
| } |
| bool no_decl_expr = t == integer_zero_node; |
| if (no_decl_expr) |
| t = omp_private_member_vec.pop (); |
| tree *v = omp_private_member_map->get (t); |
| gcc_assert (v); |
| if (!no_decl_expr) |
| add_decl_expr (*v); |
| omp_private_member_map->remove (t); |
| } |
| delete omp_private_member_map; |
| omp_private_member_map = NULL; |
| } |
| add_stmt (stmt); |
| } |
| |
| /* Remember OpenMP privatization clauses mapping and clear it. |
| Used for lambdas. */ |
| |
| void |
| save_omp_privatization_clauses (vec<tree> &save) |
| { |
| save = vNULL; |
| if (omp_private_member_ignore_next) |
| save.safe_push (integer_one_node); |
| omp_private_member_ignore_next = false; |
| if (!omp_private_member_map) |
| return; |
| |
| while (!omp_private_member_vec.is_empty ()) |
| { |
| tree t = omp_private_member_vec.pop (); |
| if (t == error_mark_node) |
| { |
| save.safe_push (t); |
| continue; |
| } |
| tree n = t; |
| if (t == integer_zero_node) |
| t = omp_private_member_vec.pop (); |
| tree *v = omp_private_member_map->get (t); |
| gcc_assert (v); |
| save.safe_push (*v); |
| save.safe_push (t); |
| if (n != t) |
| save.safe_push (n); |
| } |
| delete omp_private_member_map; |
| omp_private_member_map = NULL; |
| } |
| |
| /* Restore OpenMP privatization clauses mapping saved by the |
| above function. */ |
| |
| void |
| restore_omp_privatization_clauses (vec<tree> &save) |
| { |
| gcc_assert (omp_private_member_vec.is_empty ()); |
| omp_private_member_ignore_next = false; |
| if (save.is_empty ()) |
| return; |
| if (save.length () == 1 && save[0] == integer_one_node) |
| { |
| omp_private_member_ignore_next = true; |
| save.release (); |
| return; |
| } |
| |
| omp_private_member_map = new hash_map <tree, tree>; |
| while (!save.is_empty ()) |
| { |
| tree t = save.pop (); |
| tree n = t; |
| if (t != error_mark_node) |
| { |
| if (t == integer_one_node) |
| { |
| omp_private_member_ignore_next = true; |
| gcc_assert (save.is_empty ()); |
| break; |
| } |
| if (t == integer_zero_node) |
| t = save.pop (); |
| tree &v = omp_private_member_map->get_or_insert (t); |
| v = save.pop (); |
| } |
| omp_private_member_vec.safe_push (t); |
| if (n != t) |
| omp_private_member_vec.safe_push (n); |
| } |
| save.release (); |
| } |
| |
| /* For all variables in the tree_list VARS, mark them as thread local. */ |
| |
| void |
| finish_omp_threadprivate (tree vars) |
| { |
| tree t; |
| |
| /* Mark every variable in VARS to be assigned thread local storage. */ |
| for (t = vars; t; t = TREE_CHAIN (t)) |
| { |
| tree v = TREE_PURPOSE (t); |
| |
| if (error_operand_p (v)) |
| ; |
| else if (!VAR_P (v)) |
| error ("%<threadprivate%> %qD is not file, namespace " |
| "or block scope variable", v); |
| /* If V had already been marked threadprivate, it doesn't matter |
| whether it had been used prior to this point. */ |
| else if (TREE_USED (v) |
| && (DECL_LANG_SPECIFIC (v) == NULL |
| || !CP_DECL_THREADPRIVATE_P (v))) |
| error ("%qE declared %<threadprivate%> after first use", v); |
| else if (! TREE_STATIC (v) && ! DECL_EXTERNAL (v)) |
| error ("automatic variable %qE cannot be %<threadprivate%>", v); |
| else if (! COMPLETE_TYPE_P (complete_type (TREE_TYPE (v)))) |
| error ("%<threadprivate%> %qE has incomplete type", v); |
| else if (TREE_STATIC (v) && TYPE_P (CP_DECL_CONTEXT (v)) |
| && CP_DECL_CONTEXT (v) != current_class_type) |
| error ("%<threadprivate%> %qE directive not " |
| "in %qT definition", v, CP_DECL_CONTEXT (v)); |
| else |
| { |
| /* Allocate a LANG_SPECIFIC structure for V, if needed. */ |
| if (DECL_LANG_SPECIFIC (v) == NULL) |
| { |
| retrofit_lang_decl (v); |
| |
| /* Make sure that DECL_DISCRIMINATOR_P continues to be true |
| after the allocation of the lang_decl structure. */ |
| if (DECL_DISCRIMINATOR_P (v)) |
| DECL_LANG_SPECIFIC (v)->u.base.u2sel = 1; |
| } |
| |
| if (! CP_DECL_THREAD_LOCAL_P (v)) |
| { |
| CP_DECL_THREAD_LOCAL_P (v) = true; |
| set_decl_tls_model (v, decl_default_tls_model (v)); |
| /* If rtl has been already set for this var, call |
| make_decl_rtl once again, so that encode_section_info |
| has a chance to look at the new decl flags. */ |
| if (DECL_RTL_SET_P (v)) |
| make_decl_rtl (v); |
| } |
| CP_DECL_THREADPRIVATE_P (v) = 1; |
| } |
| } |
| } |
| |
| /* Build an OpenMP structured block. */ |
| |
| tree |
| begin_omp_structured_block (void) |
| { |
| return do_pushlevel (sk_omp); |
| } |
| |
| tree |
| finish_omp_structured_block (tree block) |
| { |
| return do_poplevel (block); |
| } |
| |
| /* Similarly, except force the retention of the BLOCK. */ |
| |
| tree |
| begin_omp_parallel (void) |
| { |
| keep_next_level (true); |
| return begin_omp_structured_block (); |
| } |
| |
| /* Generate OACC_DATA, with CLAUSES and BLOCK as its compound |
| statement. */ |
| |
| tree |
| finish_oacc_data (tree clauses, tree block) |
| { |
| tree stmt; |
| |
| block = finish_omp_structured_block (block); |
| |
| stmt = make_node (OACC_DATA); |
| TREE_TYPE (stmt) = void_type_node; |
| OACC_DATA_CLAUSES (stmt) = clauses; |
| OACC_DATA_BODY (stmt) = block; |
| |
| return add_stmt (stmt); |
| } |
| |
| /* Generate OACC_HOST_DATA, with CLAUSES and BLOCK as its compound |
| statement. */ |
| |
| tree |
| finish_oacc_host_data (tree clauses, tree block) |
| { |
| tree stmt; |
| |
| block = finish_omp_structured_block (block); |
| |
| stmt = make_node (OACC_HOST_DATA); |
| TREE_TYPE (stmt) = void_type_node; |
| OACC_HOST_DATA_CLAUSES (stmt) = clauses; |
| OACC_HOST_DATA_BODY (stmt) = block; |
| |
| return add_stmt (stmt); |
| } |
| |
| /* Generate OMP construct CODE, with BODY and CLAUSES as its compound |
| statement. */ |
| |
| tree |
| finish_omp_construct (enum tree_code code, tree body, tree clauses) |
| { |
| body = finish_omp_structured_block (body); |
| |
| tree stmt = make_node (code); |
| TREE_TYPE (stmt) = void_type_node; |
| OMP_BODY (stmt) = body; |
| OMP_CLAUSES (stmt) = clauses; |
| |
| return add_stmt (stmt); |
| } |
| |
| tree |
| finish_omp_parallel (tree clauses, tree body) |
| { |
| tree stmt; |
| |
| body = finish_omp_structured_block (body); |
| |
| stmt = make_node (OMP_PARALLEL); |
| TREE_TYPE (stmt) = void_type_node; |
| OMP_PARALLEL_CLAUSES (stmt) = clauses; |
| OMP_PARALLEL_BODY (stmt) = body; |
| |
| return add_stmt (stmt); |
| } |
| |
| tree |
| begin_omp_task (void) |
| { |
| keep_next_level (true); |
| return begin_omp_structured_block (); |
| } |
| |
| tree |
| finish_omp_task (tree clauses, tree body) |
| { |
| tree stmt; |
| |
| body = finish_omp_structured_block (body); |
| |
| stmt = make_node (OMP_TASK); |
| TREE_TYPE (stmt) = void_type_node; |
| OMP_TASK_CLAUSES (stmt) = clauses; |
| OMP_TASK_BODY (stmt) = body; |
| |
| return add_stmt (stmt); |
| } |
| |
| /* Helper function for finish_omp_for. Convert Ith random access iterator |
| into integral iterator. Return FALSE if successful. */ |
| |
| static bool |
| handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, |
| tree declv, tree orig_declv, tree initv, |
| tree condv, tree incrv, tree *body, |
| tree *pre_body, tree &clauses, tree *lastp, |
| int collapse, int ordered) |
| { |
| tree diff, iter_init, iter_incr = NULL, last; |
| tree incr_var = NULL, orig_pre_body, orig_body, c; |
| tree decl = TREE_VEC_ELT (declv, i); |
| tree init = TREE_VEC_ELT (initv, i); |
| tree cond = TREE_VEC_ELT (condv, i); |
| tree incr = TREE_VEC_ELT (incrv, i); |
| tree iter = decl; |
| location_t elocus = locus; |
| |
| if (init && EXPR_HAS_LOCATION (init)) |
| elocus = EXPR_LOCATION (init); |
| |
| cond = cp_fully_fold (cond); |
| switch (TREE_CODE (cond)) |
| { |
| case GT_EXPR: |
| case GE_EXPR: |
| case LT_EXPR: |
| case LE_EXPR: |
| case NE_EXPR: |
| if (TREE_OPERAND (cond, 1) == iter) |
| cond = build2 (swap_tree_comparison (TREE_CODE (cond)), |
| TREE_TYPE (cond), iter, TREE_OPERAND (cond, 0)); |
| if (TREE_OPERAND (cond, 0) != iter) |
| cond = error_mark_node; |
| else |
| { |
| tree tem = build_x_binary_op (EXPR_LOCATION (cond), |
| TREE_CODE (cond), |
| iter, ERROR_MARK, |
| TREE_OPERAND (cond, 1), ERROR_MARK, |
| NULL, tf_warning_or_error); |
| if (error_operand_p (tem)) |
| return true; |
| } |
| break; |
| default: |
| cond = error_mark_node; |
| break; |
| } |
| if (cond == error_mark_node) |
| { |
| error_at (elocus, "invalid controlling predicate"); |
| return true; |
| } |
| diff = build_x_binary_op (elocus, MINUS_EXPR, TREE_OPERAND (cond, 1), |
| ERROR_MARK, iter, ERROR_MARK, NULL, |
| tf_warning_or_error); |
| diff = cp_fully_fold (diff); |
| if (error_operand_p (diff)) |
| return true; |
| if (TREE_CODE (TREE_TYPE (diff)) != INTEGER_TYPE) |
| { |
| error_at (elocus, "difference between %qE and %qD does not have integer type", |
| TREE_OPERAND (cond, 1), iter); |
| return true; |
| } |
| if (!c_omp_check_loop_iv_exprs (locus, orig_declv, |
| TREE_VEC_ELT (declv, i), NULL_TREE, |
| cond, cp_walk_subtrees)) |
| return true; |
| |
| switch (TREE_CODE (incr)) |
| { |
| case PREINCREMENT_EXPR: |
| case PREDECREMENT_EXPR: |
| case POSTINCREMENT_EXPR: |
| case POSTDECREMENT_EXPR: |
| if (TREE_OPERAND (incr, 0) != iter) |
| { |
| incr = error_mark_node; |
| break; |
| } |
| iter_incr = build_x_unary_op (EXPR_LOCATION (incr), |
| TREE_CODE (incr), iter, |
| tf_warning_or_error); |
| if (error_operand_p (iter_incr)) |
| return true; |
| else if (TREE_CODE (incr) == PREINCREMENT_EXPR |
| || TREE_CODE (incr) == POSTINCREMENT_EXPR) |
| incr = integer_one_node; |
| else |
| incr = integer_minus_one_node; |
| break; |
| case MODIFY_EXPR: |
| if (TREE_OPERAND (incr, 0) != iter) |
| incr = error_mark_node; |
| else if (TREE_CODE (TREE_OPERAND (incr, 1)) == PLUS_EXPR |
| || TREE_CODE (TREE_OPERAND (incr, 1)) == MINUS_EXPR) |
| { |
| tree rhs = TREE_OPERAND (incr, 1); |
| if (TREE_OPERAND (rhs, 0) == iter) |
| { |
| if (TREE_CODE (TREE_TYPE (TREE_OPERAND (rhs, 1))) |
| != INTEGER_TYPE) |
| incr = error_mark_node; |
| else |
| { |
| iter_incr = build_x_modify_expr (EXPR_LOCATION (rhs), |
| iter, TREE_CODE (rhs), |
| TREE_OPERAND (rhs, 1), |
| tf_warning_or_error); |
| if (error_operand_p (iter_incr)) |
| return true; |
| incr = TREE_OPERAND (rhs, 1); |
| incr = cp_convert (TREE_TYPE (diff), incr, |
| tf_warning_or_error); |
| if (TREE_CODE (rhs) == MINUS_EXPR) |
| { |
| incr = build1 (NEGATE_EXPR, TREE_TYPE (diff), incr); |
| incr = fold_simple (incr); |
| } |
| if (TREE_CODE (incr) != INTEGER_CST |
| && (TREE_CODE (incr) != NOP_EXPR |
| || (TREE_CODE (TREE_OPERAND (incr, 0)) |
| != INTEGER_CST))) |
| iter_incr = NULL; |
| } |
| } |
| else if (TREE_OPERAND (rhs, 1) == iter) |
| { |
| if (TREE_CODE (TREE_TYPE (TREE_OPERAND (rhs, 0))) != INTEGER_TYPE |
| || TREE_CODE (rhs) != PLUS_EXPR) |
| incr = error_mark_node; |
| else |
| { |
| iter_incr = build_x_binary_op (EXPR_LOCATION (rhs), |
| PLUS_EXPR, |
| TREE_OPERAND (rhs, 0), |
| ERROR_MARK, iter, |
| ERROR_MARK, NULL, |
| tf_warning_or_error); |
| if (error_operand_p (iter_incr)) |
| return true; |
| iter_incr = build_x_modify_expr (EXPR_LOCATION (rhs), |
| iter, NOP_EXPR, |
| iter_incr, |
| tf_warning_or_error); |
| if (error_operand_p (iter_incr)) |
| return true; |
| incr = TREE_OPERAND (rhs, 0); |
| iter_incr = NULL; |
| } |
| } |
| else |
| incr = error_mark_node; |
| } |
| else |
| incr = error_mark_node; |
| break; |
| default: |
| incr = error_mark_node; |
| break; |
| } |
| |
| if (incr == error_mark_node) |
| { |
| error_at (elocus, "invalid increment expression"); |
| return true; |
| } |
| |
| incr = cp_convert (TREE_TYPE (diff), incr, tf_warning_or_error); |
| bool taskloop_iv_seen = false; |
| for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE |
| && OMP_CLAUSE_DECL (c) == iter) |
| { |
| if (code == OMP_TASKLOOP) |
| { |
| taskloop_iv_seen = true; |
| OMP_CLAUSE_LASTPRIVATE_TASKLOOP_IV (c) = 1; |
| } |
| break; |
| } |
| else if (code == OMP_TASKLOOP |
| && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE |
| && OMP_CLAUSE_DECL (c) == iter) |
| { |
| taskloop_iv_seen = true; |
| OMP_CLAUSE_PRIVATE_TASKLOOP_IV (c) = 1; |
| } |
| |
| decl = create_temporary_var (TREE_TYPE (diff)); |
| pushdecl (decl); |
| add_decl_expr (decl); |
| last = create_temporary_var (TREE_TYPE (diff)); |
| pushdecl (last); |
| add_decl_expr (last); |
| if (c && iter_incr == NULL && TREE_CODE (incr) != INTEGER_CST |
| && (!ordered || (i < collapse && collapse > 1))) |
| { |
| incr_var = create_temporary_var (TREE_TYPE (diff)); |
| pushdecl (incr_var); |
| add_decl_expr (incr_var); |
| } |
| gcc_assert (stmts_are_full_exprs_p ()); |
| tree diffvar = NULL_TREE; |
| if (code == OMP_TASKLOOP) |
| { |
| if (!taskloop_iv_seen) |
| { |
| tree ivc = build_omp_clause (locus, OMP_CLAUSE_FIRSTPRIVATE); |
| OMP_CLAUSE_DECL (ivc) = iter; |
| cxx_omp_finish_clause (ivc, NULL); |
| OMP_CLAUSE_CHAIN (ivc) = clauses; |
| clauses = ivc; |
| } |
| tree lvc = build_omp_clause (locus, OMP_CLAUSE_FIRSTPRIVATE); |
| OMP_CLAUSE_DECL (lvc) = last; |
| OMP_CLAUSE_CHAIN (lvc) = clauses; |
| clauses = lvc; |
| diffvar = create_temporary_var (TREE_TYPE (diff)); |
| pushdecl (diffvar); |
| add_decl_expr (diffvar); |
| } |
| |
| orig_pre_body = *pre_body; |
| *pre_body = push_stmt_list (); |
| if (orig_pre_body) |
| add_stmt (orig_pre_body); |
| if (init != NULL) |
| finish_expr_stmt (build_x_modify_expr (elocus, |
| iter, NOP_EXPR, init, |
| tf_warning_or_error)); |
| init = build_int_cst (TREE_TYPE (diff), 0); |
| if (c && iter_incr == NULL |
| && (!ordered || (i < collapse && collapse > 1))) |
| { |
| if (incr_var) |
| { |
| finish_expr_stmt (build_x_modify_expr (elocus, |
| incr_var, NOP_EXPR, |
| incr, tf_warning_or_error)); |
| incr = incr_var; |
| } |
| iter_incr = build_x_modify_expr (elocus, |
| iter, PLUS_EXPR, incr, |
| tf_warning_or_error); |
| } |
| if (c && ordered && i < collapse && collapse > 1) |
| iter_incr = incr; |
| finish_expr_stmt (build_x_modify_expr (elocus, |
| last, NOP_EXPR, init, |
| tf_warning_or_error)); |
| if (diffvar) |
| { |
| finish_expr_stmt (build_x_modify_expr (elocus, |
| diffvar, NOP_EXPR, |
| diff, tf_warning_or_error)); |
| diff = diffvar; |
| } |
| *pre_body = pop_stmt_list (*pre_body); |
| |
| cond = cp_build_binary_op (elocus, |
| TREE_CODE (cond), decl, diff, |
| tf_warning_or_error); |
| incr = build_modify_expr (elocus, decl, NULL_TREE, PLUS_EXPR, |
| elocus, incr, NULL_TREE); |
| |
| orig_body = *body; |
| *body = push_stmt_list (); |
| iter_init = build2 (MINUS_EXPR, TREE_TYPE (diff), decl, last); |
| iter_init = build_x_modify_expr (elocus, |
| iter, PLUS_EXPR, iter_init, |
| tf_warning_or_error); |
| if (iter_init != error_mark_node) |
| iter_init = build1 (NOP_EXPR, void_type_node, iter_init); |
| finish_expr_stmt (iter_init); |
| finish_expr_stmt (build_x_modify_expr (elocus, |
| last, NOP_EXPR, decl, |
| tf_warning_or_error)); |
| add_stmt (orig_body); |
| *body = pop_stmt_list (*body); |
| |
| if (c) |
| { |
| OMP_CLAUSE_LASTPRIVATE_STMT (c) = push_stmt_list (); |
| if (!ordered) |
| finish_expr_stmt (iter_incr); |
| else |
| { |
| iter_init = decl; |
| if (i < collapse && collapse > 1 && !error_operand_p (iter_incr)) |
| iter_init = build2 (PLUS_EXPR, TREE_TYPE (diff), |
| iter_init, iter_incr); |
| iter_init = build2 (MINUS_EXPR, TREE_TYPE (diff), iter_init, last); |
| iter_init = build_x_modify_expr (elocus, |
| iter, PLUS_EXPR, iter_init, |
| tf_warning_or_error); |
| if (iter_init != error_mark_node) |
| iter_init = build1 (NOP_EXPR, void_type_node, iter_init); |
| finish_expr_stmt (iter_init); |
| } |
| OMP_CLAUSE_LASTPRIVATE_STMT (c) |
| = pop_stmt_list (OMP_CLAUSE_LASTPRIVATE_STMT (c)); |
| } |
| |
| TREE_VEC_ELT (declv, i) = decl; |
| TREE_VEC_ELT (initv, i) = init; |
| TREE_VEC_ELT (condv, i) = cond; |
| TREE_VEC_ELT (incrv, i) = incr; |
| *lastp = last; |
| |
| return false; |
| } |
| |
| /* Build and validate an OMP_FOR statement. CLAUSES, BODY, COND, INCR |
| are directly for their associated operands in the statement. DECL |
| and INIT are a combo; if DECL is NULL then INIT ought to be a |
| MODIFY_EXPR, and the DECL should be extracted. PRE_BODY are |
| optional statements that need to go before the loop into its |
| sk_omp scope. */ |
| |
| tree |
| finish_omp_for (location_t locus, enum tree_code code, tree declv, |
| tree orig_declv, tree initv, tree condv, tree incrv, |
| tree body, tree pre_body, vec<tree> *orig_inits, tree clauses) |
| { |
| tree omp_for = NULL, orig_incr = NULL; |
| tree decl = NULL, init, cond, incr, orig_decl = NULL_TREE, block = NULL_TREE; |
| tree last = NULL_TREE; |
| location_t elocus; |
| int i; |
| int collapse = 1; |
| int ordered = 0; |
| |
| gcc_assert (TREE_VEC_LENGTH (declv) == TREE_VEC_LENGTH (initv)); |
| gcc_assert (TREE_VEC_LENGTH (declv) == TREE_VEC_LENGTH (condv)); |
| gcc_assert (TREE_VEC_LENGTH (declv) == TREE_VEC_LENGTH (incrv)); |
| if (TREE_VEC_LENGTH (declv) > 1) |
| { |
| tree c; |
| |
| c = omp_find_clause (clauses, OMP_CLAUSE_TILE); |
| if (c) |
| collapse = list_length (OMP_CLAUSE_TILE_LIST (c)); |
| else |
| { |
| c = omp_find_clause (clauses, OMP_CLAUSE_COLLAPSE); |
| if (c) |
| collapse = tree_to_shwi (OMP_CLAUSE_COLLAPSE_EXPR (c)); |
| if (collapse != TREE_VEC_LENGTH (declv)) |
| ordered = TREE_VEC_LENGTH (declv); |
| } |
| } |
| for (i = 0; i < TREE_VEC_LENGTH (declv); i++) |
| { |
| decl = TREE_VEC_ELT (declv, i); |
| init = TREE_VEC_ELT (initv, i); |
| cond = TREE_VEC_ELT (condv, i); |
| incr = TREE_VEC_ELT (incrv, i); |
| elocus = locus; |
| |
| if (decl == NULL) |
| { |
| if (init != NULL) |
| switch (TREE_CODE (init)) |
| { |
| case MODIFY_EXPR: |
| decl = TREE_OPERAND (init, 0); |
| init = TREE_OPERAND (init, 1); |
| break; |
| case MODOP_EXPR: |
| if (TREE_CODE (TREE_OPERAND (init, 1)) == NOP_EXPR) |
| { |
| decl = TREE_OPERAND (init, 0); |
| init = TREE_OPERAND (init, 2); |
| } |
| break; |
| default: |
| break; |
| } |
| |
| if (decl == NULL) |
| { |
| error_at (locus, |
| "expected iteration declaration or initialization"); |
| return NULL; |
| } |
| } |
| |
| if (init && EXPR_HAS_LOCATION (init)) |
| elocus = EXPR_LOCATION (init); |
| |
| if (cond == NULL) |
| { |
| error_at (elocus, "missing controlling predicate"); |
| return NULL; |
| } |
| |
| if (incr == NULL) |
| { |
| error_at (elocus, "missing increment expression"); |
| return NULL; |
| } |
| |
| TREE_VEC_ELT (declv, i) = decl; |
| TREE_VEC_ELT (initv, i) = init; |
| } |
| |
| if (orig_inits) |
| { |
| bool fail = false; |
| tree orig_init; |
| FOR_EACH_VEC_ELT (*orig_inits, i, orig_init) |
| if (orig_init |
| && !c_omp_check_loop_iv_exprs (locus, declv, |
| TREE_VEC_ELT (declv, i), orig_init, |
| NULL_TREE, cp_walk_subtrees)) |
| fail = true; |
| if (fail) |
| return NULL; |
| } |
| |
| if (dependent_omp_for_p (declv, initv, condv, incrv)) |
| { |
| tree stmt; |
| |
| stmt = make_node (code); |
| |
| for (i = 0; i < TREE_VEC_LENGTH (declv); i++) |
| { |
| /* This is really just a place-holder. We'll be decomposing this |
| again and going through the cp_build_modify_expr path below when |
| we instantiate the thing. */ |
| TREE_VEC_ELT (initv, i) |
| = build2 (MODIFY_EXPR, void_type_node, TREE_VEC_ELT (declv, i), |
| TREE_VEC_ELT (initv, i)); |
| } |
| |
| TREE_TYPE (stmt) = void_type_node; |
| OMP_FOR_INIT (stmt) = initv; |
| OMP_FOR_COND (stmt) = condv; |
| OMP_FOR_INCR (stmt) = incrv; |
| OMP_FOR_BODY (stmt) = body; |
| OMP_FOR_PRE_BODY (stmt) = pre_body; |
| OMP_FOR_CLAUSES (stmt) = clauses; |
| |
| SET_EXPR_LOCATION (stmt, locus); |
| return add_stmt (stmt); |
| } |
| |
| if (!orig_declv) |
| orig_declv = copy_node (declv); |
| |
| if (processing_template_decl) |
| orig_incr = make_tree_vec (TREE_VEC_LENGTH (incrv)); |
| |
| for (i = 0; i < TREE_VEC_LENGTH (declv); ) |
| { |
| decl = TREE_VEC_ELT (declv, i); |
| init = TREE_VEC_ELT (initv, i); |
| cond = TREE_VEC_ELT (condv, i); |
| incr = TREE_VEC_ELT (incrv, i); |
| if (orig_incr) |
| TREE_VEC_ELT (orig_incr, i) = incr; |
| elocus = locus; |
| |
| if (init && EXPR_HAS_LOCATION (init)) |
| elocus = EXPR_LOCATION (init); |
| |
| if (!DECL_P (decl)) |
| { |
| error_at (elocus, "expected iteration declaration or initialization"); |
| return NULL; |
| } |
| |
| if (incr && TREE_CODE (incr) == MODOP_EXPR) |
| { |
| if (orig_incr) |
| TREE_VEC_ELT (orig_incr, i) = incr; |
| incr = cp_build_modify_expr (elocus, TREE_OPERAND (incr, 0), |
| TREE_CODE (TREE_OPERAND (incr, 1)), |
| TREE_OPERAND (incr, 2), |
| tf_warning_or_error); |
| } |
| |
| if (CLASS_TYPE_P (TREE_TYPE (decl))) |
| { |
| if (code == OMP_SIMD) |
| { |
| error_at (elocus, "%<#pragma omp simd%> used with class " |
| "iteration variable %qE", decl); |
| return NULL; |
| } |
| if (code == CILK_FOR && i == 0) |
| orig_decl = decl; |
| if (handle_omp_for_class_iterator (i, locus, code, declv, orig_declv, |
| initv, condv, incrv, &body, |
| &pre_body, clauses, &last, |
| collapse, ordered)) |
| return NULL; |
| continue; |
| } |
| |
| if (!INTEGRAL_TYPE_P (TREE_TYPE (decl)) |
| && !TYPE_PTR_P (TREE_TYPE (decl))) |
| { |
| error_at (elocus, "invalid type for iteration variable %qE", decl); |
| return NULL; |
| } |
| |
| if (!processing_template_decl) |
| { |
| init = fold_build_cleanup_point_expr (TREE_TYPE (init), init); |
| init = cp_build_modify_expr (elocus, decl, NOP_EXPR, init, |
| tf_warning_or_error); |
| } |
| else |
| init = build2 (MODIFY_EXPR, void_type_node, decl, init); |
| if (cond |
| && TREE_SIDE_EFFECTS (cond) |
| && COMPARISON_CLASS_P (cond) |
| && !processing_template_decl) |
| { |
| tree t = TREE_OPERAND (cond, 0); |
| if (TREE_SIDE_EFFECTS (t) |
| && t != decl |
| && (TREE_CODE (t) != NOP_EXPR |
| || TREE_OPERAND (t, 0) != decl)) |
| TREE_OPERAND (cond, 0) |
| = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| |
| t = TREE_OPERAND (cond, 1); |
| if (TREE_SIDE_EFFECTS (t) |
| && t != decl |
| && (TREE_CODE (t) != NOP_EXPR |
| || TREE_OPERAND (t, 0) != decl)) |
| TREE_OPERAND (cond, 1) |
| = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| } |
| if (decl == error_mark_node || init == error_mark_node) |
| return NULL; |
| |
| TREE_VEC_ELT (declv, i) = decl; |
| TREE_VEC_ELT (initv, i) = init; |
| TREE_VEC_ELT (condv, i) = cond; |
| TREE_VEC_ELT (incrv, i) = incr; |
| i++; |
| } |
| |
| if (IS_EMPTY_STMT (pre_body)) |
| pre_body = NULL; |
| |
| if (code == CILK_FOR && !processing_template_decl) |
| block = push_stmt_list (); |
| |
| omp_for = c_finish_omp_for (locus, code, declv, orig_declv, initv, condv, |
| incrv, body, pre_body); |
| |
| /* Check for iterators appearing in lb, b or incr expressions. */ |
| if (omp_for && !c_omp_check_loop_iv (omp_for, orig_declv, cp_walk_subtrees)) |
| omp_for = NULL_TREE; |
| |
| if (omp_for == NULL) |
| { |
| if (block) |
| pop_stmt_list (block); |
| return NULL; |
| } |
| |
| add_stmt (omp_for); |
| |
| for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INCR (omp_for)); i++) |
| { |
| decl = TREE_OPERAND (TREE_VEC_ELT (OMP_FOR_INIT (omp_for), i), 0); |
| incr = TREE_VEC_ELT (OMP_FOR_INCR (omp_for), i); |
| |
| if (TREE_CODE (incr) != MODIFY_EXPR) |
| continue; |
| |
| if (TREE_SIDE_EFFECTS (TREE_OPERAND (incr, 1)) |
| && BINARY_CLASS_P (TREE_OPERAND (incr, 1)) |
| && !processing_template_decl) |
| { |
| tree t = TREE_OPERAND (TREE_OPERAND (incr, 1), 0); |
| if (TREE_SIDE_EFFECTS (t) |
| && t != decl |
| && (TREE_CODE (t) != NOP_EXPR |
| || TREE_OPERAND (t, 0) != decl)) |
| TREE_OPERAND (TREE_OPERAND (incr, 1), 0) |
| = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| |
| t = TREE_OPERAND (TREE_OPERAND (incr, 1), 1); |
| if (TREE_SIDE_EFFECTS (t) |
| && t != decl |
| && (TREE_CODE (t) != NOP_EXPR |
| || TREE_OPERAND (t, 0) != decl)) |
| TREE_OPERAND (TREE_OPERAND (incr, 1), 1) |
| = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| } |
| |
| if (orig_incr) |
| TREE_VEC_ELT (OMP_FOR_INCR (omp_for), i) = TREE_VEC_ELT (orig_incr, i); |
| } |
| OMP_FOR_CLAUSES (omp_for) = clauses; |
| |
| /* For simd loops with non-static data member iterators, we could have added |
| OMP_CLAUSE_LINEAR clauses without OMP_CLAUSE_LINEAR_STEP. As we know the |
| step at this point, fill it in. */ |
| if (code == OMP_SIMD && !processing_template_decl |
| && TREE_VEC_LENGTH (OMP_FOR_INCR (omp_for)) == 1) |
| for (tree c = omp_find_clause (clauses, OMP_CLAUSE_LINEAR); c; |
| c = omp_find_clause (OMP_CLAUSE_CHAIN (c), OMP_CLAUSE_LINEAR)) |
| if (OMP_CLAUSE_LINEAR_STEP (c) == NULL_TREE) |
| { |
| decl = TREE_OPERAND (TREE_VEC_ELT (OMP_FOR_INIT (omp_for), 0), 0); |
| gcc_assert (decl == OMP_CLAUSE_DECL (c)); |
| incr = TREE_VEC_ELT (OMP_FOR_INCR (omp_for), 0); |
| tree step, stept; |
| switch (TREE_CODE (incr)) |
| { |
| case PREINCREMENT_EXPR: |
| case POSTINCREMENT_EXPR: |
| /* c_omp_for_incr_canonicalize_ptr() should have been |
| called to massage things appropriately. */ |
| gcc_assert (!POINTER_TYPE_P (TREE_TYPE (decl))); |
| OMP_CLAUSE_LINEAR_STEP (c) = build_int_cst (TREE_TYPE (decl), 1); |
| break; |
| case PREDECREMENT_EXPR: |
| case POSTDECREMENT_EXPR: |
| /* c_omp_for_incr_canonicalize_ptr() should have been |
| called to massage things appropriately. */ |
| gcc_assert (!POINTER_TYPE_P (TREE_TYPE (decl))); |
| OMP_CLAUSE_LINEAR_STEP (c) |
| = build_int_cst (TREE_TYPE (decl), -1); |
| break; |
| case MODIFY_EXPR: |
| gcc_assert (TREE_OPERAND (incr, 0) == decl); |
| incr = TREE_OPERAND (incr, 1); |
| switch (TREE_CODE (incr)) |
| { |
| case PLUS_EXPR: |
| if (TREE_OPERAND (incr, 1) == decl) |
| step = TREE_OPERAND (incr, 0); |
| else |
| step = TREE_OPERAND (incr, 1); |
| break; |
| case MINUS_EXPR: |
| case POINTER_PLUS_EXPR: |
| gcc_assert (TREE_OPERAND (incr, 0) == decl); |
| step = TREE_OPERAND (incr, 1); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| stept = TREE_TYPE (decl); |
| if (POINTER_TYPE_P (stept)) |
| stept = sizetype; |
| step = fold_convert (stept, step); |
| if (TREE_CODE (incr) == MINUS_EXPR) |
| step = fold_build1 (NEGATE_EXPR, stept, step); |
| OMP_CLAUSE_LINEAR_STEP (c) = step; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| if (block) |
| { |
| tree omp_par = make_node (OMP_PARALLEL); |
| TREE_TYPE (omp_par) = void_type_node; |
| OMP_PARALLEL_CLAUSES (omp_par) = NULL_TREE; |
| tree bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL); |
| TREE_SIDE_EFFECTS (bind) = 1; |
| BIND_EXPR_BODY (bind) = pop_stmt_list (block); |
| OMP_PARALLEL_BODY (omp_par) = bind; |
| if (OMP_FOR_PRE_BODY (omp_for)) |
| { |
| add_stmt (OMP_FOR_PRE_BODY (omp_for)); |
| OMP_FOR_PRE_BODY (omp_for) = NULL_TREE; |
| } |
| init = TREE_VEC_ELT (OMP_FOR_INIT (omp_for), 0); |
| decl = TREE_OPERAND (init, 0); |
| cond = TREE_VEC_ELT (OMP_FOR_COND (omp_for), 0); |
| incr = TREE_VEC_ELT (OMP_FOR_INCR (omp_for), 0); |
| tree t = TREE_OPERAND (cond, 1), c, clauses, *pc; |
| clauses = OMP_FOR_CLAUSES (omp_for); |
| OMP_FOR_CLAUSES (omp_for) = NULL_TREE; |
| for (pc = &clauses; *pc; ) |
| if (OMP_CLAUSE_CODE (*pc) == OMP_CLAUSE_SCHEDULE) |
| { |
| gcc_assert (OMP_FOR_CLAUSES (omp_for) == NULL_TREE); |
| OMP_FOR_CLAUSES (omp_for) = *pc; |
| *pc = OMP_CLAUSE_CHAIN (*pc); |
| OMP_CLAUSE_CHAIN (OMP_FOR_CLAUSES (omp_for)) = NULL_TREE; |
| } |
| else |
| { |
| gcc_assert (OMP_CLAUSE_CODE (*pc) == OMP_CLAUSE_FIRSTPRIVATE); |
| pc = &OMP_CLAUSE_CHAIN (*pc); |
| } |
| if (TREE_CODE (t) != INTEGER_CST) |
| { |
| TREE_OPERAND (cond, 1) = get_temp_regvar (TREE_TYPE (t), t); |
| c = build_omp_clause (input_location, OMP_CLAUSE_FIRSTPRIVATE); |
| OMP_CLAUSE_DECL (c) = TREE_OPERAND (cond, 1); |
| OMP_CLAUSE_CHAIN (c) = clauses; |
| clauses = c; |
| } |
| if (TREE_CODE (incr) == MODIFY_EXPR) |
| { |
| t = TREE_OPERAND (TREE_OPERAND (incr, 1), 1); |
| if (TREE_CODE (t) != INTEGER_CST) |
| { |
| TREE_OPERAND (TREE_OPERAND (incr, 1), 1) |
| = get_temp_regvar (TREE_TYPE (t), t); |
| c = build_omp_clause (input_location, OMP_CLAUSE_FIRSTPRIVATE); |
| OMP_CLAUSE_DECL (c) = TREE_OPERAND (TREE_OPERAND (incr, 1), 1); |
| OMP_CLAUSE_CHAIN (c) = clauses; |
| clauses = c; |
| } |
| } |
| t = TREE_OPERAND (init, 1); |
| if (TREE_CODE (t) != INTEGER_CST) |
| { |
| TREE_OPERAND (init, 1) = get_temp_regvar (TREE_TYPE (t), t); |
| c = build_omp_clause (input_location, OMP_CLAUSE_FIRSTPRIVATE); |
| OMP_CLAUSE_DECL (c) = TREE_OPERAND (init, 1); |
| OMP_CLAUSE_CHAIN (c) = clauses; |
| clauses = c; |
| } |
| if (orig_decl && orig_decl != decl) |
| { |
| c = build_omp_clause (input_location, OMP_CLAUSE_FIRSTPRIVATE); |
| OMP_CLAUSE_DECL (c) = orig_decl; |
| OMP_CLAUSE_CHAIN (c) = clauses; |
| clauses = c; |
| } |
| if (last) |
| { |
| c = build_omp_clause (input_location, OMP_CLAUSE_FIRSTPRIVATE); |
| OMP_CLAUSE_DECL (c) = last; |
| OMP_CLAUSE_CHAIN (c) = clauses; |
| clauses = c; |
| } |
| c = build_omp_clause (input_location, OMP_CLAUSE_PRIVATE); |
| OMP_CLAUSE_DECL (c) = decl; |
| OMP_CLAUSE_CHAIN (c) = clauses; |
| clauses = c; |
| c = build_omp_clause (input_location, OMP_CLAUSE__CILK_FOR_COUNT_); |
| OMP_CLAUSE_OPERAND (c, 0) |
| = cilk_for_number_of_iterations (omp_for); |
| OMP_CLAUSE_CHAIN (c) = clauses; |
| OMP_PARALLEL_CLAUSES (omp_par) = finish_omp_clauses (c, C_ORT_CILK); |
| add_stmt (omp_par); |
| return omp_par; |
| } |
| else if (code == CILK_FOR && processing_template_decl) |
| { |
| tree c, clauses = OMP_FOR_CLAUSES (omp_for); |
| if (orig_decl && orig_decl != decl) |
| { |
| c = build_omp_clause (input_location, OMP_CLAUSE_FIRSTPRIVATE); |
| OMP_CLAUSE_DECL (c) = orig_decl; |
| OMP_CLAUSE_CHAIN (c) = clauses; |
| clauses = c; |
| } |
| if (last) |
| { |
| c = build_omp_clause (input_location, OMP_CLAUSE_FIRSTPRIVATE); |
| OMP_CLAUSE_DECL (c) = last; |
| OMP_CLAUSE_CHAIN (c) = clauses; |
| clauses = c; |
| } |
| OMP_FOR_CLAUSES (omp_for) = clauses; |
| } |
| return omp_for; |
| } |
| |
| void |
| finish_omp_atomic (enum tree_code code, enum tree_code opcode, tree lhs, |
| tree rhs, tree v, tree lhs1, tree rhs1, bool seq_cst) |
| { |
| tree orig_lhs; |
| tree orig_rhs; |
| tree orig_v; |
| tree orig_lhs1; |
| tree orig_rhs1; |
| bool dependent_p; |
| tree stmt; |
| |
| orig_lhs = lhs; |
| orig_rhs = rhs; |
| orig_v = v; |
| orig_lhs1 = lhs1; |
| orig_rhs1 = rhs1; |
| dependent_p = false; |
| stmt = NULL_TREE; |
| |
| /* Even in a template, we can detect invalid uses of the atomic |
| pragma if neither LHS nor RHS is type-dependent. */ |
| if (processing_template_decl) |
| { |
| dependent_p = (type_dependent_expression_p (lhs) |
| || (rhs && type_dependent_expression_p (rhs)) |
| || (v && type_dependent_expression_p (v)) |
| || (lhs1 && type_dependent_expression_p (lhs1)) |
| || (rhs1 && type_dependent_expression_p (rhs1))); |
| if (!dependent_p) |
| { |
| lhs = build_non_dependent_expr (lhs); |
| if (rhs) |
| rhs = build_non_dependent_expr (rhs); |
| if (v) |
| v = build_non_dependent_expr (v); |
| if (lhs1) |
| lhs1 = build_non_dependent_expr (lhs1); |
| if (rhs1) |
| rhs1 = build_non_dependent_expr (rhs1); |
| } |
| } |
| if (!dependent_p) |
| { |
| bool swapped = false; |
| if (rhs1 && cp_tree_equal (lhs, rhs)) |
| { |
| std::swap (rhs, rhs1); |
| swapped = !commutative_tree_code (opcode); |
| } |
| if (rhs1 && !cp_tree_equal (lhs, rhs1)) |
| { |
| if (code == OMP_ATOMIC) |
| error ("%<#pragma omp atomic update%> uses two different " |
| "expressions for memory"); |
| else |
| error ("%<#pragma omp atomic capture%> uses two different " |
| "expressions for memory"); |
| return; |
| } |
| if (lhs1 && !cp_tree_equal (lhs, lhs1)) |
| { |
| if (code == OMP_ATOMIC) |
| error ("%<#pragma omp atomic update%> uses two different " |
| "expressions for memory"); |
| else |
| error ("%<#pragma omp atomic capture%> uses two different " |
| "expressions for memory"); |
| return; |
| } |
| stmt = c_finish_omp_atomic (input_location, code, opcode, lhs, rhs, |
| v, lhs1, rhs1, swapped, seq_cst, |
| processing_template_decl != 0); |
| if (stmt == error_mark_node) |
| return; |
| } |
| if (processing_template_decl) |
| { |
| if (code == OMP_ATOMIC_READ) |
| { |
| stmt = build_min_nt_loc (EXPR_LOCATION (orig_lhs), |
| OMP_ATOMIC_READ, orig_lhs); |
| OMP_ATOMIC_SEQ_CST (stmt) = seq_cst; |
| stmt = build2 (MODIFY_EXPR, void_type_node, orig_v, stmt); |
| } |
| else |
| { |
| if (opcode == NOP_EXPR) |
| stmt = build2 (MODIFY_EXPR, void_type_node, orig_lhs, orig_rhs); |
| else |
| stmt = build2 (opcode, void_type_node, orig_lhs, orig_rhs); |
| if (orig_rhs1) |
| stmt = build_min_nt_loc (EXPR_LOCATION (orig_rhs1), |
| COMPOUND_EXPR, orig_rhs1, stmt); |
| if (code != OMP_ATOMIC) |
| { |
| stmt = build_min_nt_loc (EXPR_LOCATION (orig_lhs1), |
| code, orig_lhs1, stmt); |
| OMP_ATOMIC_SEQ_CST (stmt) = seq_cst; |
| stmt = build2 (MODIFY_EXPR, void_type_node, orig_v, stmt); |
| } |
| } |
| stmt = build2 (OMP_ATOMIC, void_type_node, integer_zero_node, stmt); |
| OMP_ATOMIC_SEQ_CST (stmt) = seq_cst; |
| } |
| finish_expr_stmt (stmt); |
| } |
| |
| void |
| finish_omp_barrier (void) |
| { |
| tree fn = builtin_decl_explicit (BUILT_IN_GOMP_BARRIER); |
| vec<tree, va_gc> *vec = make_tree_vector (); |
| tree stmt = finish_call_expr (fn, &vec, false, false, tf_warning_or_error); |
| release_tree_vector (vec); |
| finish_expr_stmt (stmt); |
| } |
| |
| void |
| finish_omp_flush (void) |
| { |
| tree fn = builtin_decl_explicit (BUILT_IN_SYNC_SYNCHRONIZE); |
| vec<tree, va_gc> *vec = make_tree_vector (); |
| tree stmt = finish_call_expr (fn, &vec, false, false, tf_warning_or_error); |
| release_tree_vector (vec); |
| finish_expr_stmt (stmt); |
| } |
| |
| void |
| finish_omp_taskwait (void) |
| { |
| tree fn = builtin_decl_explicit (BUILT_IN_GOMP_TASKWAIT); |
| vec<tree, va_gc> *vec = make_tree_vector (); |
| tree stmt = finish_call_expr (fn, &vec, false, false, tf_warning_or_error); |
| release_tree_vector (vec); |
| finish_expr_stmt (stmt); |
| } |
| |
| void |
| finish_omp_taskyield (void) |
| { |
| tree fn = builtin_decl_explicit (BUILT_IN_GOMP_TASKYIELD); |
| vec<tree, va_gc> *vec = make_tree_vector (); |
| tree stmt = finish_call_expr (fn, &vec, false, false, tf_warning_or_error); |
| release_tree_vector (vec); |
| finish_expr_stmt (stmt); |
| } |
| |
| void |
| finish_omp_cancel (tree clauses) |
| { |
| tree fn = builtin_decl_explicit (BUILT_IN_GOMP_CANCEL); |
| int mask = 0; |
| if (omp_find_clause (clauses, OMP_CLAUSE_PARALLEL)) |
| mask = 1; |
| else if (omp_find_clause (clauses, OMP_CLAUSE_FOR)) |
| mask = 2; |
| else if (omp_find_clause (clauses, OMP_CLAUSE_SECTIONS)) |
| mask = 4; |
| else if (omp_find_clause (clauses, OMP_CLAUSE_TASKGROUP)) |
| mask = 8; |
| else |
| { |
| error ("%<#pragma omp cancel%> must specify one of " |
| "%<parallel%>, %<for%>, %<sections%> or %<taskgroup%> clauses"); |
| return; |
| } |
| vec<tree, va_gc> *vec = make_tree_vector (); |
| tree ifc = omp_find_clause (clauses, OMP_CLAUSE_IF); |
| if (ifc != NULL_TREE) |
| { |
| tree type = TREE_TYPE (OMP_CLAUSE_IF_EXPR (ifc)); |
| ifc = fold_build2_loc (OMP_CLAUSE_LOCATION (ifc), NE_EXPR, |
| boolean_type_node, OMP_CLAUSE_IF_EXPR (ifc), |
| build_zero_cst (type)); |
| } |
| else |
| ifc = boolean_true_node; |
| vec->quick_push (build_int_cst (integer_type_node, mask)); |
| vec->quick_push (ifc); |
| tree stmt = finish_call_expr (fn, &vec, false, false, tf_warning_or_error); |
| release_tree_vector (vec); |
| finish_expr_stmt (stmt); |
| } |
| |
| void |
| finish_omp_cancellation_point (tree clauses) |
| { |
| tree fn = builtin_decl_explicit (BUILT_IN_GOMP_CANCELLATION_POINT); |
| int mask = 0; |
| if (omp_find_clause (clauses, OMP_CLAUSE_PARALLEL)) |
| mask = 1; |
| else if (omp_find_clause (clauses, OMP_CLAUSE_FOR)) |
| mask = 2; |
| else if (omp_find_clause (clauses, OMP_CLAUSE_SECTIONS)) |
| mask = 4; |
| else if (omp_find_clause (clauses, OMP_CLAUSE_TASKGROUP)) |
| mask = 8; |
| else |
| { |
| error ("%<#pragma omp cancellation point%> must specify one of " |
| "%<parallel%>, %<for%>, %<sections%> or %<taskgroup%> clauses"); |
| return; |
| } |
| vec<tree, va_gc> *vec |
| = make_tree_vector_single (build_int_cst (integer_type_node, mask)); |
| tree stmt = finish_call_expr (fn, &vec, false, false, tf_warning_or_error); |
| release_tree_vector (vec); |
| finish_expr_stmt (stmt); |
| } |
| |
| /* Begin a __transaction_atomic or __transaction_relaxed statement. |
| If PCOMPOUND is non-null, this is for a function-transaction-block, and we |
| should create an extra compound stmt. */ |
| |
| tree |
| begin_transaction_stmt (location_t loc, tree *pcompound, int flags) |
| { |
| tree r; |
| |
| if (pcompound) |
| *pcompound = begin_compound_stmt (0); |
| |
| r = build_stmt (loc, TRANSACTION_EXPR, NULL_TREE); |
| |
| /* Only add the statement to the function if support enabled. */ |
| if (flag_tm) |
| add_stmt (r); |
| else |
| error_at (loc, ((flags & TM_STMT_ATTR_RELAXED) != 0 |
| ? G_("%<__transaction_relaxed%> without " |
| "transactional memory support enabled") |
| : G_("%<__transaction_atomic%> without " |
| "transactional memory support enabled"))); |
| |
| TRANSACTION_EXPR_BODY (r) = push_stmt_list (); |
| TREE_SIDE_EFFECTS (r) = 1; |
| return r; |
| } |
| |
| /* End a __transaction_atomic or __transaction_relaxed statement. |
| If COMPOUND_STMT is non-null, this is for a function-transaction-block, |
| and we should end the compound. If NOEX is non-NULL, we wrap the body in |
| a MUST_NOT_THROW_EXPR with NOEX as condition. */ |
| |
| void |
| finish_transaction_stmt (tree stmt, tree compound_stmt, int flags, tree noex) |
| { |
| TRANSACTION_EXPR_BODY (stmt) = pop_stmt_list (TRANSACTION_EXPR_BODY (stmt)); |
| TRANSACTION_EXPR_OUTER (stmt) = (flags & TM_STMT_ATTR_OUTER) != 0; |
| TRANSACTION_EXPR_RELAXED (stmt) = (flags & TM_STMT_ATTR_RELAXED) != 0; |
| TRANSACTION_EXPR_IS_STMT (stmt) = 1; |
| |
| /* noexcept specifications are not allowed for function transactions. */ |
| gcc_assert (!(noex && compound_stmt)); |
| if (noex) |
| { |
| tree body = build_must_not_throw_expr (TRANSACTION_EXPR_BODY (stmt), |
| noex); |
| protected_set_expr_location |
| (body, EXPR_LOCATION (TRANSACTION_EXPR_BODY (stmt))); |
| TREE_SIDE_EFFECTS (body) = 1; |
| TRANSACTION_EXPR_BODY (stmt) = body; |
| } |
| |
| if (compound_stmt) |
| finish_compound_stmt (compound_stmt); |
| } |
| |
| /* Build a __transaction_atomic or __transaction_relaxed expression. If |
| NOEX is non-NULL, we wrap the body in a MUST_NOT_THROW_EXPR with NOEX as |
| condition. */ |
| |
| tree |
| build_transaction_expr (location_t loc, tree expr, int flags, tree noex) |
| { |
| tree ret; |
| if (noex) |
| { |
| expr = build_must_not_throw_expr (expr, noex); |
| protected_set_expr_location (expr, loc); |
| TREE_SIDE_EFFECTS (expr) = 1; |
| } |
| ret = build1 (TRANSACTION_EXPR, TREE_TYPE (expr), expr); |
| if (flags & TM_STMT_ATTR_RELAXED) |
| TRANSACTION_EXPR_RELAXED (ret) = 1; |
| TREE_SIDE_EFFECTS (ret) = 1; |
| SET_EXPR_LOCATION (ret, loc); |
| return ret; |
| } |
| |
| void |
| init_cp_semantics (void) |
| { |
| } |
| |
| /* Build a STATIC_ASSERT for a static assertion with the condition |
| CONDITION and the message text MESSAGE. LOCATION is the location |
| of the static assertion in the source code. When MEMBER_P, this |
| static assertion is a member of a class. */ |
| void |
| finish_static_assert (tree condition, tree message, location_t location, |
| bool member_p) |
| { |
| if (message == NULL_TREE |
| || message == error_mark_node |
| || condition == NULL_TREE |
| || condition == error_mark_node) |
| return; |
| |
| if (check_for_bare_parameter_packs (condition)) |
| condition = error_mark_node; |
| |
| if (type_dependent_expression_p (condition) |
| || value_dependent_expression_p (condition)) |
| { |
| /* We're in a template; build a STATIC_ASSERT and put it in |
| the right place. */ |
| tree assertion; |
| |
| assertion = make_node (STATIC_ASSERT); |
| STATIC_ASSERT_CONDITION (assertion) = condition; |
| STATIC_ASSERT_MESSAGE (assertion) = message; |
| STATIC_ASSERT_SOURCE_LOCATION (assertion) = location; |
| |
| if (member_p) |
| maybe_add_class_template_decl_list (current_class_type, |
| assertion, |
| /*friend_p=*/0); |
| else |
| add_stmt (assertion); |
| |
| return; |
| } |
| |
| /* Fold the expression and convert it to a boolean value. */ |
| condition = instantiate_non_dependent_expr (condition); |
| condition = cp_convert (boolean_type_node, condition, tf_warning_or_error); |
| condition = maybe_constant_value (condition); |
| |
| if (TREE_CODE (condition) == INTEGER_CST && !integer_zerop (condition)) |
| /* Do nothing; the condition is satisfied. */ |
| ; |
| else |
| { |
| location_t saved_loc = input_location; |
| |
| input_location = location; |
| if (TREE_CODE (condition) == INTEGER_CST |
| && integer_zerop (condition)) |
| { |
| int sz = TREE_INT_CST_LOW (TYPE_SIZE_UNIT |
| (TREE_TYPE (TREE_TYPE (message)))); |
| int len = TREE_STRING_LENGTH (message) / sz - 1; |
| /* Report the error. */ |
| if (len == 0) |
| error ("static assertion failed"); |
| else |
| error ("static assertion failed: %s", |
| TREE_STRING_POINTER (message)); |
| } |
| else if (condition && condition != error_mark_node) |
| { |
| error ("non-constant condition for static assertion"); |
| if (require_potential_rvalue_constant_expression (condition)) |
| cxx_constant_value (condition); |
| } |
| input_location = saved_loc; |
| } |
| } |
| |
| /* Implements the C++0x decltype keyword. Returns the type of EXPR, |
| suitable for use as a type-specifier. |
| |
| ID_EXPRESSION_OR_MEMBER_ACCESS_P is true when EXPR was parsed as an |
| id-expression or a class member access, FALSE when it was parsed as |
| a full expression. */ |
| |
| tree |
| finish_decltype_type (tree expr, bool id_expression_or_member_access_p, |
| tsubst_flags_t complain) |
| { |
| tree type = NULL_TREE; |
| |
| if (!expr || error_operand_p (expr)) |
| return error_mark_node; |
| |
| if (TYPE_P (expr) |
| || TREE_CODE (expr) == TYPE_DECL |
| || (TREE_CODE (expr) == BIT_NOT_EXPR |
| && TYPE_P (TREE_OPERAND (expr, 0)))) |
| { |
| if (complain & tf_error) |
| error ("argument to decltype must be an expression"); |
| return error_mark_node; |
| } |
| |
| /* Depending on the resolution of DR 1172, we may later need to distinguish |
| instantiation-dependent but not type-dependent expressions so that, say, |
| A<decltype(sizeof(T))>::U doesn't require 'typename'. */ |
| if (instantiation_dependent_uneval_expression_p (expr)) |
| { |
| type = cxx_make_type (DECLTYPE_TYPE); |
| DECLTYPE_TYPE_EXPR (type) = expr; |
| DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (type) |
| = id_expression_or_member_access_p; |
| SET_TYPE_STRUCTURAL_EQUALITY (type); |
| |
| return type; |
| } |
| |
| /* The type denoted by decltype(e) is defined as follows: */ |
| |
| expr = resolve_nondeduced_context (expr, complain); |
| |
| if (invalid_nonstatic_memfn_p (input_location, expr, complain)) |
| return error_mark_node; |
| |
| if (type_unknown_p (expr)) |
| { |
| if (complain & tf_error) |
| error ("decltype cannot resolve address of overloaded function"); |
| return error_mark_node; |
| } |
| |
| /* To get the size of a static data member declared as an array of |
| unknown bound, we need to instantiate it. */ |
| if (VAR_P (expr) |
| && VAR_HAD_UNKNOWN_BOUND (expr) |
| && DECL_TEMPLATE_INSTANTIATION (expr)) |
| instantiate_decl (expr, /*defer_ok*/true, /*expl_inst_mem*/false); |
| |
| if (id_expression_or_member_access_p) |
| { |
| /* If e is an id-expression or a class member access (5.2.5 |
| [expr.ref]), decltype(e) is defined as the type of the entity |
| named by e. If there is no such entity, or e names a set of |
| overloaded functions, the program is ill-formed. */ |
| if (identifier_p (expr)) |
| expr = lookup_name (expr); |
| |
| if (INDIRECT_REF_P (expr)) |
| /* This can happen when the expression is, e.g., "a.b". Just |
| look at the underlying operand. */ |
| expr = TREE_OPERAND (expr, 0); |
| |
| if (TREE_CODE (expr) == OFFSET_REF |
| || TREE_CODE (expr) == MEMBER_REF |
| || TREE_CODE (expr) == SCOPE_REF) |
| /* We're only interested in the field itself. If it is a |
| BASELINK, we will need to see through it in the next |
| step. */ |
| expr = TREE_OPERAND (expr, 1); |
| |
| if (BASELINK_P (expr)) |
| /* See through BASELINK nodes to the underlying function. */ |
| expr = BASELINK_FUNCTIONS (expr); |
| |
| /* decltype of a decomposition name drops references in the tuple case |
| (unlike decltype of a normal variable) and keeps cv-qualifiers from |
| the containing object in the other cases (unlike decltype of a member |
| access expression). */ |
| if (DECL_DECOMPOSITION_P (expr)) |
| { |
| if (DECL_HAS_VALUE_EXPR_P (expr)) |
| /* Expr is an array or struct subobject proxy, handle |
| bit-fields properly. */ |
| return unlowered_expr_type (expr); |
| else |
| /* Expr is a reference variable for the tuple case. */ |
| return lookup_decomp_type (expr); |
| } |
| |
| switch (TREE_CODE (expr)) |
| { |
| case FIELD_DECL: |
| if (DECL_BIT_FIELD_TYPE (expr)) |
| { |
| type = DECL_BIT_FIELD_TYPE (expr); |
| break; |
| } |
| /* Fall through for fields that aren't bitfields. */ |
| gcc_fallthrough (); |
| |
| case FUNCTION_DECL: |
| case VAR_DECL: |
| case CONST_DECL: |
| case PARM_DECL: |
| case RESULT_DECL: |
| case TEMPLATE_PARM_INDEX: |
| expr = mark_type_use (expr); |
| type = TREE_TYPE (expr); |
| break; |
| |
| case ERROR_MARK: |
| type = error_mark_node; |
| break; |
| |
| case COMPONENT_REF: |
| case COMPOUND_EXPR: |
| mark_type_use (expr); |
| type = is_bitfield_expr_with_lowered_type (expr); |
| if (!type) |
| type = TREE_TYPE (TREE_OPERAND (expr, 1)); |
| break; |
| |
| case BIT_FIELD_REF: |
| gcc_unreachable (); |
| |
| case INTEGER_CST: |
| case PTRMEM_CST: |
| /* We can get here when the id-expression refers to an |
| enumerator or non-type template parameter. */ |
| type = TREE_TYPE (expr); |
| break; |
| |
| default: |
| /* Handle instantiated template non-type arguments. */ |
| type = TREE_TYPE (expr); |
| break; |
| } |
| } |
| else |
| { |
| /* Within a lambda-expression: |
| |
| Every occurrence of decltype((x)) where x is a possibly |
| parenthesized id-expression that names an entity of |
| automatic storage duration is treated as if x were |
| transformed into an access to a corresponding data member |
| of the closure type that would have been declared if x |
| were a use of the denoted entity. */ |
| if (outer_automatic_var_p (expr) |
| && current_function_decl |
| && LAMBDA_FUNCTION_P (current_function_decl)) |
| type = capture_decltype (expr); |
| else if (error_operand_p (expr)) |
| type = error_mark_node; |
| else if (expr == current_class_ptr) |
| /* If the expression is just "this", we want the |
| cv-unqualified pointer for the "this" type. */ |
| type = TYPE_MAIN_VARIANT (TREE_TYPE (expr)); |
| else |
| { |
| /* Otherwise, where T is the type of e, if e is an lvalue, |
| decltype(e) is defined as T&; if an xvalue, T&&; otherwise, T. */ |
| cp_lvalue_kind clk = lvalue_kind (expr); |
| type = unlowered_expr_type (expr); |
| gcc_assert (TREE_CODE (type) != REFERENCE_TYPE); |
| |
| /* For vector types, pick a non-opaque variant. */ |
| if (VECTOR_TYPE_P (type)) |
| type = strip_typedefs (type); |
| |
| if (clk != clk_none && !(clk & clk_class)) |
| type = cp_build_reference_type (type, (clk & clk_rvalueref)); |
| } |
| } |
| |
| return type; |
| } |
| |
| /* Called from trait_expr_value to evaluate either __has_nothrow_assign or |
| __has_nothrow_copy, depending on assign_p. Returns true iff all |
| the copy {ctor,assign} fns are nothrow. */ |
| |
| static bool |
| classtype_has_nothrow_assign_or_copy_p (tree type, bool assign_p) |
| { |
| tree fns = NULL_TREE; |
| |
| if (assign_p || TYPE_HAS_COPY_CTOR (type)) |
| fns = get_class_binding (type, assign_p ? assign_op_identifier |
| : ctor_identifier); |
| |
| bool saw_copy = false; |
| for (ovl_iterator iter (fns); iter; ++iter) |
| { |
| tree fn = *iter; |
| |
| if (copy_fn_p (fn) > 0) |
| { |
| saw_copy = true; |
| maybe_instantiate_noexcept (fn); |
| if (!TYPE_NOTHROW_P (TREE_TYPE (fn))) |
| return false; |
| } |
| } |
| |
| return saw_copy; |
| } |
| |
| /* Actually evaluates the trait. */ |
| |
| static bool |
| trait_expr_value (cp_trait_kind kind, tree type1, tree type2) |
| { |
| enum tree_code type_code1; |
| tree t; |
| |
| type_code1 = TREE_CODE (type1); |
| |
| switch (kind) |
| { |
| case CPTK_HAS_NOTHROW_ASSIGN: |
| type1 = strip_array_types (type1); |
| return (!CP_TYPE_CONST_P (type1) && type_code1 != REFERENCE_TYPE |
| && (trait_expr_value (CPTK_HAS_TRIVIAL_ASSIGN, type1, type2) |
| || (CLASS_TYPE_P (type1) |
| && classtype_has_nothrow_assign_or_copy_p (type1, |
| true)))); |
| |
| case CPTK_HAS_TRIVIAL_ASSIGN: |
| /* ??? The standard seems to be missing the "or array of such a class |
| type" wording for this trait. */ |
| type1 = strip_array_types (type1); |
| return (!CP_TYPE_CONST_P (type1) && type_code1 != REFERENCE_TYPE |
| && (trivial_type_p (type1) |
| || (CLASS_TYPE_P (type1) |
| && TYPE_HAS_TRIVIAL_COPY_ASSIGN (type1)))); |
| |
| case CPTK_HAS_NOTHROW_CONSTRUCTOR: |
| type1 = strip_array_types (type1); |
| return (trait_expr_value (CPTK_HAS_TRIVIAL_CONSTRUCTOR, type1, type2) |
| || (CLASS_TYPE_P (type1) |
| && (t = locate_ctor (type1)) |
| && (maybe_instantiate_noexcept (t), |
| TYPE_NOTHROW_P (TREE_TYPE (t))))); |
| |
| case CPTK_HAS_TRIVIAL_CONSTRUCTOR: |
| type1 = strip_array_types (type1); |
| return (trivial_type_p (type1) |
| || (CLASS_TYPE_P (type1) && TYPE_HAS_TRIVIAL_DFLT (type1))); |
| |
| case CPTK_HAS_NOTHROW_COPY: |
| type1 = strip_array_types (type1); |
| return (trait_expr_value (CPTK_HAS_TRIVIAL_COPY, type1, type2) |
| || (CLASS_TYPE_P (type1) |
| && classtype_has_nothrow_assign_or_copy_p (type1, false))); |
| |
| case CPTK_HAS_TRIVIAL_COPY: |
| /* ??? The standard seems to be missing the "or array of such a class |
| type" wording for this trait. */ |
| type1 = strip_array_types (type1); |
| return (trivial_type_p (type1) || type_code1 == REFERENCE_TYPE |
| || (CLASS_TYPE_P (type1) && TYPE_HAS_TRIVIAL_COPY_CTOR (type1))); |
| |
| case CPTK_HAS_TRIVIAL_DESTRUCTOR: |
| type1 = strip_array_types (type1); |
| return (trivial_type_p (type1) || type_code1 == REFERENCE_TYPE |
| || (CLASS_TYPE_P (type1) |
| && TYPE_HAS_TRIVIAL_DESTRUCTOR (type1))); |
| |
| case CPTK_HAS_VIRTUAL_DESTRUCTOR: |
| return type_has_virtual_destructor (type1); |
| |
| case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS: |
| return type_has_unique_obj_representations (type1); |
| |
| case CPTK_IS_ABSTRACT: |
| return ABSTRACT_CLASS_TYPE_P (type1); |
| |
| case CPTK_IS_AGGREGATE: |
| return CP_AGGREGATE_TYPE_P (type1); |
| |
| case CPTK_IS_BASE_OF: |
| return (NON_UNION_CLASS_TYPE_P (type1) && NON_UNION_CLASS_TYPE_P (type2) |
| && (same_type_ignoring_top_level_qualifiers_p (type1, type2) |
| || DERIVED_FROM_P (type1, type2))); |
| |
| case CPTK_IS_CLASS: |
| return NON_UNION_CLASS_TYPE_P (type1); |
| |
| case CPTK_IS_EMPTY: |
| return NON_UNION_CLASS_TYPE_P (type1) && CLASSTYPE_EMPTY_P (type1); |
| |
| case CPTK_IS_ENUM: |
| return type_code1 == ENUMERAL_TYPE; |
| |
| case CPTK_IS_FINAL: |
| return CLASS_TYPE_P (type1) && CLASSTYPE_FINAL (type1); |
| |
| case CPTK_IS_LITERAL_TYPE: |
| return literal_type_p (type1); |
| |
| case CPTK_IS_POD: |
| return pod_type_p (type1); |
| |
| case CPTK_IS_POLYMORPHIC: |
| return CLASS_TYPE_P (type1) && TYPE_POLYMORPHIC_P (type1); |
| |
| case CPTK_IS_SAME_AS: |
| return same_type_p (type1, type2); |
| |
| case CPTK_IS_STD_LAYOUT: |
| return std_layout_type_p (type1); |
| |
| case CPTK_IS_TRIVIAL: |
| return trivial_type_p (type1); |
| |
| case CPTK_IS_TRIVIALLY_ASSIGNABLE: |
| return is_trivially_xible (MODIFY_EXPR, type1, type2); |
| |
| case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE: |
| return is_trivially_xible (INIT_EXPR, type1, type2); |
| |
| case CPTK_IS_TRIVIALLY_COPYABLE: |
| return trivially_copyable_p (type1); |
| |
| case CPTK_IS_UNION: |
| return type_code1 == UNION_TYPE; |
| |
| case CPTK_IS_ASSIGNABLE: |
| return is_xible (MODIFY_EXPR, type1, type2); |
| |
| case CPTK_IS_CONSTRUCTIBLE: |
| return is_xible (INIT_EXPR, type1, type2); |
| |
| default: |
| gcc_unreachable (); |
| return false; |
| } |
| } |
| |
| /* If TYPE is an array of unknown bound, or (possibly cv-qualified) |
| void, or a complete type, returns true, otherwise false. */ |
| |
| static bool |
| check_trait_type (tree type) |
| { |
| if (type == NULL_TREE) |
| return true; |
| |
| if (TREE_CODE (type) == TREE_LIST) |
| return (check_trait_type (TREE_VALUE (type)) |
| && check_trait_type (TREE_CHAIN (type))); |
| |
| if (TREE_CODE (type) == ARRAY_TYPE && !TYPE_DOMAIN (type) |
| && COMPLETE_TYPE_P (TREE_TYPE (type))) |
| return true; |
| |
| if (VOID_TYPE_P (type)) |
| return true; |
| |
| return !!complete_type_or_else (strip_array_types (type), NULL_TREE); |
| } |
| |
| /* Process a trait expression. */ |
| |
| tree |
| finish_trait_expr (cp_trait_kind kind, tree type1, tree type2) |
| { |
| if (type1 == error_mark_node |
| || type2 == error_mark_node) |
| return error_mark_node; |
| |
| if (processing_template_decl) |
| { |
| tree trait_expr = make_node (TRAIT_EXPR); |
| TREE_TYPE (trait_expr) = boolean_type_node; |
| TRAIT_EXPR_TYPE1 (trait_expr) = type1; |
| TRAIT_EXPR_TYPE2 (trait_expr) = type2; |
| TRAIT_EXPR_KIND (trait_expr) = kind; |
| return trait_expr; |
| } |
| |
| switch (kind) |
| { |
| case CPTK_HAS_NOTHROW_ASSIGN: |
| case CPTK_HAS_TRIVIAL_ASSIGN: |
| case CPTK_HAS_NOTHROW_CONSTRUCTOR: |
| case CPTK_HAS_TRIVIAL_CONSTRUCTOR: |
| case CPTK_HAS_NOTHROW_COPY: |
| case CPTK_HAS_TRIVIAL_COPY: |
| case CPTK_HAS_TRIVIAL_DESTRUCTOR: |
| case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS: |
| case CPTK_HAS_VIRTUAL_DESTRUCTOR: |
| case CPTK_IS_ABSTRACT: |
| case CPTK_IS_AGGREGATE: |
| case CPTK_IS_EMPTY: |
| case CPTK_IS_FINAL: |
| case CPTK_IS_LITERAL_TYPE: |
| case CPTK_IS_POD: |
| case CPTK_IS_POLYMORPHIC: |
| case CPTK_IS_STD_LAYOUT: |
| case CPTK_IS_TRIVIAL: |
| case CPTK_IS_TRIVIALLY_COPYABLE: |
| if (!check_trait_type (type1)) |
| return error_mark_node; |
| break; |
| |
| case CPTK_IS_ASSIGNABLE: |
| case CPTK_IS_CONSTRUCTIBLE: |
| break; |
| |
| case CPTK_IS_TRIVIALLY_ASSIGNABLE: |
| case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE: |
| if (!check_trait_type (type1) |
| || !check_trait_type (type2)) |
| return error_mark_node; |
| break; |
| |
| case CPTK_IS_BASE_OF: |
| if (NON_UNION_CLASS_TYPE_P (type1) && NON_UNION_CLASS_TYPE_P (type2) |
| && !same_type_ignoring_top_level_qualifiers_p (type1, type2) |
| && !complete_type_or_else (type2, NULL_TREE)) |
| /* We already issued an error. */ |
| return error_mark_node; |
| break; |
| |
| case CPTK_IS_CLASS: |
| case CPTK_IS_ENUM: |
| case CPTK_IS_UNION: |
| case CPTK_IS_SAME_AS: |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| return (trait_expr_value (kind, type1, type2) |
| ? boolean_true_node : boolean_false_node); |
| } |
| |
| /* Do-nothing variants of functions to handle pragma FLOAT_CONST_DECIMAL64, |
| which is ignored for C++. */ |
| |
| void |
| set_float_const_decimal64 (void) |
| { |
| } |
| |
| void |
| clear_float_const_decimal64 (void) |
| { |
| } |
| |
| bool |
| float_const_decimal64_p (void) |
| { |
| return 0; |
| } |
| |
| |
| /* Return true if T designates the implied `this' parameter. */ |
| |
| bool |
| is_this_parameter (tree t) |
| { |
| if (!DECL_P (t) || DECL_NAME (t) != this_identifier) |
| return false; |
| gcc_assert (TREE_CODE (t) == PARM_DECL || is_capture_proxy (t) |
| || (cp_binding_oracle && TREE_CODE (t) == VAR_DECL)); |
| return true; |
| } |
| |
| /* Insert the deduced return type for an auto function. */ |
| |
| void |
| apply_deduced_return_type (tree fco, tree return_type) |
| { |
| tree result; |
| |
| if (return_type == error_mark_node) |
| return; |
| |
| if (DECL_CONV_FN_P (fco)) |
| DECL_NAME (fco) = make_conv_op_name (return_type); |
| |
| TREE_TYPE (fco) = change_return_type (return_type, TREE_TYPE (fco)); |
| |
| result = DECL_RESULT (fco); |
| if (result == NULL_TREE) |
| return; |
| if (TREE_TYPE (result) == return_type) |
| return; |
| |
| if (!processing_template_decl && !VOID_TYPE_P (return_type) |
| && !complete_type_or_else (return_type, NULL_TREE)) |
| return; |
| |
| /* We already have a DECL_RESULT from start_preparsed_function. |
| Now we need to redo the work it and allocate_struct_function |
| did to reflect the new type. */ |
| gcc_assert (current_function_decl == fco); |
| result = build_decl (input_location, RESULT_DECL, NULL_TREE, |
| TYPE_MAIN_VARIANT (return_type)); |
| DECL_ARTIFICIAL (result) = 1; |
| DECL_IGNORED_P (result) = 1; |
| cp_apply_type_quals_to_decl (cp_type_quals (return_type), |
| result); |
| |
| DECL_RESULT (fco) = result; |
| |
| if (!processing_template_decl) |
| { |
| bool aggr = aggregate_value_p (result, fco); |
| #ifdef PCC_STATIC_STRUCT_RETURN |
| cfun->returns_pcc_struct = aggr; |
| #endif |
| cfun->returns_struct = aggr; |
| } |
| |
| } |
| |
| /* DECL is a local variable or parameter from the surrounding scope of a |
| lambda-expression. Returns the decltype for a use of the capture field |
| for DECL even if it hasn't been captured yet. */ |
| |
| static tree |
| capture_decltype (tree decl) |
| { |
| tree lam = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (current_function_decl)); |
| /* FIXME do lookup instead of list walk? */ |
| tree cap = value_member (decl, LAMBDA_EXPR_CAPTURE_LIST (lam)); |
| tree type; |
| |
| if (cap) |
| type = TREE_TYPE (TREE_PURPOSE (cap)); |
| else |
| switch (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lam)) |
| { |
| case CPLD_NONE: |
| error ("%qD is not captured", decl); |
| return error_mark_node; |
| |
| case CPLD_COPY: |
| type = TREE_TYPE (decl); |
| if (TREE_CODE (type) == REFERENCE_TYPE |
| && TREE_CODE (TREE_TYPE (type)) != FUNCTION_TYPE) |
| type = TREE_TYPE (type); |
| break; |
| |
| case CPLD_REFERENCE: |
| type = TREE_TYPE (decl); |
| if (TREE_CODE (type) != REFERENCE_TYPE) |
| type = build_reference_type (TREE_TYPE (decl)); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (TREE_CODE (type) != REFERENCE_TYPE) |
| { |
| if (!LAMBDA_EXPR_MUTABLE_P (lam)) |
| type = cp_build_qualified_type (type, (cp_type_quals (type) |
| |TYPE_QUAL_CONST)); |
| type = build_reference_type (type); |
| } |
| return type; |
| } |
| |
| /* Build a unary fold expression of EXPR over OP. If IS_RIGHT is true, |
| this is a right unary fold. Otherwise it is a left unary fold. */ |
| |
| static tree |
| finish_unary_fold_expr (tree expr, int op, tree_code dir) |
| { |
| // Build a pack expansion (assuming expr has pack type). |
| if (!uses_parameter_packs (expr)) |
| { |
| error_at (location_of (expr), "operand of fold expression has no " |
| "unexpanded parameter packs"); |
| return error_mark_node; |
| } |
| tree pack = make_pack_expansion (expr); |
| |
| // Build the fold expression. |
| tree code = build_int_cstu (integer_type_node, abs (op)); |
| tree fold = build_min_nt_loc (UNKNOWN_LOCATION, dir, code, pack); |
| FOLD_EXPR_MODIFY_P (fold) = (op < 0); |
| return fold; |
| } |
| |
| tree |
| finish_left_unary_fold_expr (tree expr, int op) |
| { |
| return finish_unary_fold_expr (expr, op, UNARY_LEFT_FOLD_EXPR); |
| } |
| |
| tree |
| finish_right_unary_fold_expr (tree expr, int op) |
| { |
| return finish_unary_fold_expr (expr, op, UNARY_RIGHT_FOLD_EXPR); |
| } |
| |
| /* Build a binary fold expression over EXPR1 and EXPR2. The |
| associativity of the fold is determined by EXPR1 and EXPR2 (whichever |
| has an unexpanded parameter pack). */ |
| |
| tree |
| finish_binary_fold_expr (tree pack, tree init, int op, tree_code dir) |
| { |
| pack = make_pack_expansion (pack); |
| tree code = build_int_cstu (integer_type_node, abs (op)); |
| tree fold = build_min_nt_loc (UNKNOWN_LOCATION, dir, code, pack, init); |
| FOLD_EXPR_MODIFY_P (fold) = (op < 0); |
| return fold; |
| } |
| |
| tree |
| finish_binary_fold_expr (tree expr1, tree expr2, int op) |
| { |
| // Determine which expr has an unexpanded parameter pack and |
| // set the pack and initial term. |
| bool pack1 = uses_parameter_packs (expr1); |
| bool pack2 = uses_parameter_packs (expr2); |
| if (pack1 && !pack2) |
| return finish_binary_fold_expr (expr1, expr2, op, BINARY_RIGHT_FOLD_EXPR); |
| else if (pack2 && !pack1) |
| return finish_binary_fold_expr (expr2, expr1, op, BINARY_LEFT_FOLD_EXPR); |
| else |
| { |
| if (pack1) |
| error ("both arguments in binary fold have unexpanded parameter packs"); |
| else |
| error ("no unexpanded parameter packs in binary fold"); |
| } |
| return error_mark_node; |
| } |
| |
| /* Finish __builtin_launder (arg). */ |
| |
| tree |
| finish_builtin_launder (location_t loc, tree arg, tsubst_flags_t complain) |
| { |
| tree orig_arg = arg; |
| if (!type_dependent_expression_p (arg)) |
| arg = decay_conversion (arg, complain); |
| if (error_operand_p (arg)) |
| return error_mark_node; |
| if (!type_dependent_expression_p (arg) |
| && TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE) |
| { |
| error_at (loc, "non-pointer argument to %<__builtin_launder%>"); |
| return error_mark_node; |
| } |
| if (processing_template_decl) |
| arg = orig_arg; |
| return build_call_expr_internal_loc (loc, IFN_LAUNDER, |
| TREE_TYPE (arg), 1, arg); |
| } |
| |
| #include "gt-cp-semantics.h" |