| /* 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-2013 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 "tm.h" |
| #include "tree.h" |
| #include "cp-tree.h" |
| #include "c-family/c-common.h" |
| #include "c-family/c-objc.h" |
| #include "tree-inline.h" |
| #include "intl.h" |
| #include "toplev.h" |
| #include "flags.h" |
| #include "timevar.h" |
| #include "diagnostic.h" |
| #include "cgraph.h" |
| #include "tree-iterator.h" |
| #include "vec.h" |
| #include "target.h" |
| #include "gimple.h" |
| #include "bitmap.h" |
| #include "hash-table.h" |
| |
| static bool verify_constant (tree, bool, bool *, bool *); |
| #define VERIFY_CONSTANT(X) \ |
| do { \ |
| if (verify_constant ((X), allow_non_constant, non_constant_p, overflow_p)) \ |
| return t; \ |
| } while (0) |
| |
| /* 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); |
| |
| |
| /* 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'. */ |
| |
| typedef 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; |
| |
| } deferred_access; |
| |
| /* 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. */ |
| |
| bool |
| perform_or_defer_access_check (tree binfo, tree decl, tree diag_decl, |
| tsubst_flags_t complain) |
| { |
| 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); |
| 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 (); |
| } |
| |
| /* 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 (input_location, 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 (); |
| } |
| |
| /* 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, 0, 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 (TREE_CODE (destination) == IDENTIFIER_NODE) |
| 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 |
| { |
| 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); |
| |
| 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 (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; |
| |
| if (expr != NULL_TREE) |
| { |
| 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 (input_location, EXPR_STMT, expr); |
| expr = maybe_cleanup_point_expr_void (expr); |
| } |
| |
| r = add_stmt (expr); |
| } |
| |
| finish_stmt (); |
| |
| 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); |
| begin_cond (&IF_COND (r)); |
| return r; |
| } |
| |
| /* Process the COND of an if-statement, which may be given by |
| IF_STMT. */ |
| |
| void |
| finish_if_stmt_cond (tree cond, tree if_stmt) |
| { |
| finish_cond (&IF_COND (if_stmt), maybe_convert_cond (cond)); |
| add_stmt (if_stmt); |
| THEN_CLAUSE (if_stmt) = push_stmt_list (); |
| } |
| |
| /* 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)); |
| finish_stmt (); |
| } |
| |
| /* 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) |
| { |
| finish_cond (&WHILE_COND (while_stmt), maybe_convert_cond (cond)); |
| 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) |
| { |
| WHILE_BODY (while_stmt) = do_poplevel (WHILE_BODY (while_stmt)); |
| finish_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); |
| 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) |
| { |
| cond = maybe_convert_cond (cond); |
| DO_COND (do_stmt) = cond; |
| finish_stmt (); |
| } |
| |
| /* 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 (flag_openmp && !check_omp_return ()) |
| 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); |
| finish_stmt (); |
| |
| 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 for-init-statement of a for-statement, which may be |
| given by FOR_STMT. */ |
| |
| void |
| finish_for_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) |
| { |
| finish_cond (&FOR_COND (for_stmt), maybe_convert_cond (cond)); |
| 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) |
| { |
| 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)); |
| } |
| |
| finish_stmt (); |
| } |
| |
| /* 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; |
| |
| 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_zero_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 (!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; |
| } |
| 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 (); |
| finish_stmt (); |
| |
| 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); |
| HANDLER_TYPE (handler) = type; |
| if (!processing_template_decl && type) |
| mark_used (eh_type_info (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 |
| r = do_pushlevel (flags & BCS_TRY_BLOCK ? sk_try : sk_block); |
| |
| /* 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_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); |
| } |
| |
| /* Finish a parenthesized expression EXPR. */ |
| |
| tree |
| finish_parenthesized_expr (tree 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; |
| |
| 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); |
| |
| 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); |
| if (object == error_mark_node) |
| return error_mark_node; |
| |
| /* DR 613: 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 %q+D in static member function", decl); |
| else |
| error ("invalid use of non-static data member %q+D", decl); |
| error ("from this location"); |
| |
| 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 |
| { |
| /* 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); |
| } |
| |
| return (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) |
| return 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); |
| } |
| |
| return build_class_member_access_expr (object, decl, |
| /*access_path=*/NULL_TREE, |
| /*preserve_reference=*/false, |
| tf_warning_or_error); |
| } |
| } |
| |
| /* 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'. */ |
| if (DECL_NONSTATIC_MEMBER_P (decl) |
| && current_class_ptr |
| && DERIVED_FROM_P (scope, current_class_type)) |
| qualifying_type = current_class_type; |
| /* 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); |
| |
| if (template_p) |
| 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) && !processing_template_decl) |
| { |
| /* 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 if (BASELINK_P (expr)) |
| ; |
| 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)) |
| 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_zero_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; if INCLUDE_STD then the `std' namespace is automatically |
| considered an associated namespace (used in range-based for loops). |
| Returns the functions to be considered by overload resolution. */ |
| |
| tree |
| perform_koenig_lookup (tree fn, vec<tree, va_gc> *args, bool include_std, |
| tsubst_flags_t complain) |
| { |
| tree identifier = NULL_TREE; |
| tree functions = NULL_TREE; |
| tree tmpl_args = NULL_TREE; |
| bool template_id = false; |
| |
| 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 (TREE_CODE (fn) == IDENTIFIER_NODE) |
| identifier = fn; |
| else if (is_overloaded_fn (fn)) |
| { |
| functions = fn; |
| identifier = DECL_NAME (get_first_fn (functions)); |
| } |
| else if (DECL_P (fn)) |
| { |
| functions = fn; |
| identifier = DECL_NAME (fn); |
| } |
| |
| /* 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, include_std); |
| if (!fn) |
| { |
| /* The unqualified name could not be resolved. */ |
| if (complain) |
| fn = unqualified_fn_lookup_error (identifier); |
| else |
| fn = identifier; |
| } |
| } |
| |
| if (fn && template_id) |
| 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)); |
| |
| orig_fn = fn; |
| |
| if (processing_template_decl) |
| { |
| /* 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) |
| /* For a non-static member function that doesn't have an |
| explicit object argument, we need to specifically |
| test the type dependency of the "this" pointer because it |
| is not included in *ARGS even though it is considered to |
| be part of the list of arguments. Note that this is |
| related to CWG issues 515 and 1005. */ |
| || (TREE_CODE (fn) != COMPONENT_REF |
| && non_static_member_function_p (fn) |
| && current_class_ref |
| && type_dependent_expression_p (current_class_ref))) |
| { |
| result = build_nt_call_vec (fn, *args); |
| SET_EXPR_LOCATION (result, EXPR_LOC_OR_HERE (fn)); |
| KOENIG_LOOKUP_P (result) = koenig_p; |
| if (cfun) |
| { |
| do |
| { |
| tree fndecl = OVL_CURRENT (fn); |
| if (TREE_CODE (fndecl) != FUNCTION_DECL |
| || !TREE_THIS_VOLATILE (fndecl)) |
| break; |
| fn = OVL_NEXT (fn); |
| } |
| while (fn); |
| if (!fn) |
| 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); |
| } |
| } |
| |
| 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. */ |
| |
| object = maybe_dummy_object (BINFO_TYPE (BASELINK_ACCESS_BINFO (fn)), |
| NULL); |
| |
| if (processing_template_decl) |
| { |
| if (type_dependent_expression_p (object)) |
| { |
| tree ret = build_nt_call_vec (orig_fn, orig_args); |
| release_tree_vector (orig_args); |
| return ret; |
| } |
| object = build_non_dependent_expr (object); |
| } |
| |
| 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 |
| && !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, koenig_p, 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 (TREE_CODE (result) == INDIRECT_REF) |
| 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); |
| } |
| |
| if (koenig_p) |
| { |
| /* Free garbage OVERLOADs from arg-dependent lookup. */ |
| tree next = NULL_TREE; |
| for (fn = orig_fn; |
| fn && TREE_CODE (fn) == OVERLOAD && OVL_ARG_DEPENDENT (fn); |
| fn = next) |
| { |
| if (processing_template_decl) |
| /* In a template, we'll re-use them at instantiation time. */ |
| OVL_ARG_DEPENDENT (fn) = false; |
| else |
| { |
| next = OVL_CHAIN (fn); |
| ggc_free (fn); |
| } |
| } |
| } |
| |
| 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.) */ |
| |
| tree |
| finish_increment_expr (tree expr, enum tree_code code) |
| { |
| return build_x_unary_op (input_location, code, expr, tf_warning_or_error); |
| } |
| |
| /* Finish a use of `this'. Returns an expression for `this'. */ |
| |
| tree |
| finish_this_expr (void) |
| { |
| tree result; |
| |
| 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)); |
| else |
| result = current_class_ptr; |
| |
| } |
| else if (current_function_decl |
| && DECL_STATIC_FUNCTION_P (current_function_decl)) |
| { |
| error ("%<this%> is unavailable for static member functions"); |
| result = error_mark_node; |
| } |
| else |
| { |
| if (current_function_decl) |
| error ("invalid use of %<this%> in non-member function"); |
| else |
| error ("invalid use of %<this%> at top level"); |
| result = error_mark_node; |
| } |
| |
| return result; |
| } |
| |
| /* 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) |
| { |
| 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 ("invalid qualifying scope in pseudo-destructor name"); |
| return error_mark_node; |
| } |
| if (scope && TYPE_P (scope) && !check_dtor_name (scope, destructor)) |
| { |
| error ("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 ("%qE is not of type %qT", object, destructor); |
| return error_mark_node; |
| } |
| } |
| |
| return build3 (PSEUDO_DTOR_EXPR, void_type_node, object, scope, destructor); |
| } |
| |
| /* Finish an expression of the form CODE EXPR. */ |
| |
| tree |
| finish_unary_op_expr (location_t loc, enum tree_code code, tree expr, |
| tsubst_flags_t complain) |
| { |
| tree result = build_x_unary_op (loc, code, expr, complain); |
| if ((complain & tf_warning) |
| && TREE_OVERFLOW_P (result) && !TREE_OVERFLOW_P (expr)) |
| overflow_warning (input_location, result); |
| |
| return result; |
| } |
| |
| /* Finish a compound-literal expression. 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) |
| { |
| 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); |
| 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 (processing_template_decl) |
| { |
| TREE_TYPE (compound_literal) = type; |
| /* Mark the expression as a compound literal. */ |
| TREE_HAS_CONSTRUCTOR (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) |
| && (complain & tf_warning_or_error)) |
| check_narrowing (type, compound_literal); |
| 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 (type, compound_literal, complain); |
| if (TREE_CODE (compound_literal) == CONSTRUCTOR) |
| TREE_HAS_CONSTRUCTOR (compound_literal) = true; |
| /* Put static/constant array temporaries in static variables, but always |
| represent class temporaries with TARGET_EXPR so we elide copies. */ |
| if ((!at_function_scope_p () || CP_TYPE_CONST_P (type)) |
| && 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)); |
| return decl; |
| } |
| else |
| return get_target_expr_sfinae (compound_literal, complain); |
| } |
| |
| /* 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; |
| 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) |
| && !strcmp (IDENTIFIER_POINTER (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; |
| |
| 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_ANONYMOUS_P (t)) |
| { |
| struct c_fileinfo *finfo = get_fileinfo (input_filename); |
| 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); |
| |
| /* 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; |
| |
| /* Check for bare parameter packs in the member variable 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) && DECL_LANGUAGE (decl) == lang_c) |
| SET_DECL_LANGUAGE (decl, lang_cplusplus); |
| |
| /* Put functions on the TYPE_METHODS list and everything else on the |
| TYPE_FIELDS list. Note that these are built up in reverse order. |
| We reverse them (to obtain declaration order) in finish_struct. */ |
| if (TREE_CODE (decl) == FUNCTION_DECL |
| || DECL_FUNCTION_TEMPLATE_P (decl)) |
| { |
| /* We also need to add this function to the |
| CLASSTYPE_METHOD_VEC. */ |
| if (add_method (current_class_type, decl, NULL_TREE)) |
| { |
| DECL_CHAIN (decl) = TYPE_METHODS (current_class_type); |
| TYPE_METHODS (current_class_type) = decl; |
| |
| maybe_add_class_template_decl_list (current_class_type, decl, |
| /*friend_p=*/0); |
| } |
| } |
| /* 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)) |
| { |
| 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; |
| } |
| |
| /* All TYPE_DECLs go at the end of TYPE_FIELDS. Ordinary fields |
| go at the beginning. The reason is that lookup_field_1 |
| 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 { }; int E } s; |
| s.E = 3; |
| |
| is valid. In addition, the FIELD_DECLs must be maintained in |
| declaration order so that class layout works as expected. |
| However, we don't need that order until class layout, so we |
| save a little time by putting FIELD_DECLs on in reverse order |
| here, and then reversing them in finish_struct_1. (We could |
| also keep a pointer to the correct insertion points in the |
| list.) */ |
| |
| 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); |
| } |
| |
| if (pch_file) |
| note_decl_for_pch (decl); |
| } |
| |
| /* DECL has been declared while we are building a PCH file. Perform |
| actions that we might normally undertake lazily, but which can be |
| performed now so that they do not have to be performed in |
| translation units which include the PCH file. */ |
| |
| void |
| note_decl_for_pch (tree decl) |
| { |
| gcc_assert (pch_file); |
| |
| /* There's a good chance that we'll have to mangle names at some |
| point, even if only for emission in debugging information. */ |
| if ((TREE_CODE (decl) == VAR_DECL |
| || TREE_CODE (decl) == FUNCTION_DECL) |
| && !processing_template_decl) |
| mangle_decl (decl); |
| } |
| |
| /* 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 (); |
| } |
| |
| /* 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 (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 ((TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == PARM_DECL) |
| && DECL_FUNCTION_SCOPE_P (decl) |
| && DECL_CONTEXT (decl) != current_function_decl); |
| } |
| |
| /* As above, but also checks that DECL is automatic. */ |
| |
| static bool |
| outer_automatic_var_p (tree decl) |
| { |
| return (outer_var_p (decl) |
| && !TREE_STATIC (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. */ |
| tree |
| 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) |
| && !(TREE_CODE (id_expression) == IDENTIFIER_NODE |
| && IDENTIFIER_TYPENAME_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 |
| && TREE_CODE (id_expression) == IDENTIFIER_NODE) |
| maybe_note_name_used_in_class (id_expression, decl); |
| |
| /* Disallow uses of local variables from containing functions, except |
| within lambda-expressions. */ |
| if (!outer_var_p (decl) |
| /* It's not a use (3.2) if we're in an unevaluated context. */ |
| || cp_unevaluated_operand) |
| /* OK. */; |
| else if (TREE_STATIC (decl)) |
| { |
| if (processing_template_decl) |
| /* For a use of an outer static var, return the identifier so |
| that we'll look it up again in the instantiation. */ |
| return id_expression; |
| } |
| else |
| { |
| 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. */ |
| mark_used (decl); |
| |
| /* Core issue 696: "[At the July 2009 meeting] the CWG expressed |
| support for an approach in which a reference to a local |
| [constant] automatic variable in a nested class or lambda body |
| would enter the expression as an rvalue, which would reduce |
| the complexity of the problem" |
| |
| FIXME update for final resolution of core issue 696. */ |
| if (decl_constant_var_p (decl)) |
| { |
| if (processing_template_decl) |
| /* In a template, the constant value may not be in a usable |
| form, so look it up again at instantiation time. */ |
| return id_expression; |
| else |
| return integral_constant_value (decl); |
| } |
| |
| /* 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 |
| && LAMBDA_FUNCTION_P (containing_function)) |
| { |
| lambda_expr = CLASSTYPE_LAMBDA_EXPR |
| (DECL_CONTEXT (containing_function)); |
| |
| 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); |
| } |
| |
| if (context == containing_function) |
| { |
| decl = add_default_capture (lambda_stack, |
| /*id=*/DECL_NAME (decl), |
| initializer); |
| } |
| else if (lambda_expr) |
| { |
| error ("%qD is not captured", decl); |
| return error_mark_node; |
| } |
| else |
| { |
| error (TREE_CODE (decl) == VAR_DECL |
| ? G_("use of local variable with automatic storage from containing function") |
| : G_("use of parameter from containing function")); |
| error (" %q+#D declared here", decl); |
| 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 = "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 = "missing template arguments"; |
| return error_mark_node; |
| } |
| else if (TREE_CODE (decl) == TYPE_DECL |
| || TREE_CODE (decl) == NAMESPACE_DECL) |
| { |
| *error_msg = "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; |
| |
| /* 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 |
| : CP_ID_KIND_UNQUALIFIED)); |
| |
| |
| /* [temp.dep.expr] |
| |
| An id-expression is type-dependent if it contains an |
| identifier that was declared with a dependent type. |
| |
| The standard is not very specific about an id-expression that |
| names a set of overloaded functions. What if some of them |
| have dependent types and some of them do not? Presumably, |
| such a name should be treated as a dependent name. */ |
| /* Assume the name is not dependent. */ |
| dependent_p = false; |
| if (!processing_template_decl) |
| /* No names are dependent outside a template. */ |
| ; |
| else if (TREE_CODE (decl) == CONST_DECL) |
| /* We don't want to treat enumerators as dependent. */ |
| ; |
| /* A template-id where the name of the template was not resolved |
| is definitely dependent. */ |
| else if (TREE_CODE (decl) == TEMPLATE_ID_EXPR |
| && (TREE_CODE (TREE_OPERAND (decl, 0)) |
| == IDENTIFIER_NODE)) |
| dependent_p = true; |
| /* For anything except an overloaded function, just check its |
| type. */ |
| else if (!is_overloaded_fn (decl)) |
| dependent_p |
| = dependent_type_p (TREE_TYPE (decl)); |
| /* For a set of overloaded functions, check each of the |
| functions. */ |
| else |
| { |
| tree fns = decl; |
| |
| if (BASELINK_P (fns)) |
| fns = BASELINK_FUNCTIONS (fns); |
| |
| /* For a template-id, check to see if the template |
| arguments are dependent. */ |
| if (TREE_CODE (fns) == TEMPLATE_ID_EXPR) |
| { |
| tree args = TREE_OPERAND (fns, 1); |
| dependent_p = any_dependent_template_arguments_p (args); |
| /* The functions are those referred to by the |
| template-id. */ |
| fns = TREE_OPERAND (fns, 0); |
| } |
| |
| /* If there are no dependent template arguments, go through |
| the overloaded functions. */ |
| while (fns && !dependent_p) |
| { |
| tree fn = OVL_CURRENT (fns); |
| |
| /* Member functions of dependent classes are |
| dependent. */ |
| if (TREE_CODE (fn) == FUNCTION_DECL |
| && type_dependent_expression_p (fn)) |
| dependent_p = true; |
| else if (TREE_CODE (fn) == TEMPLATE_DECL |
| && dependent_template_p (fn)) |
| dependent_p = true; |
| |
| fns = OVL_NEXT (fns); |
| } |
| } |
| |
| /* If the name was dependent on a template parameter, we will |
| resolve the name at instantiation time. */ |
| if (dependent_p) |
| { |
| /* Create a SCOPE_REF for qualified names, if the scope is |
| dependent. */ |
| if (scope) |
| { |
| if (TYPE_P (scope)) |
| { |
| if (address_p && done) |
| decl = finish_qualified_id_expr (scope, decl, |
| done, address_p, |
| template_p, |
| template_arg_p, |
| tf_warning_or_error); |
| else |
| { |
| tree type = NULL_TREE; |
| if (DECL_P (decl) && !dependent_scope_p (scope)) |
| type = TREE_TYPE (decl); |
| decl = build_qualified_name (type, |
| scope, |
| id_expression, |
| template_p); |
| } |
| } |
| if (TREE_TYPE (decl)) |
| decl = convert_from_reference (decl); |
| return decl; |
| } |
| /* A TEMPLATE_ID already contains all the information we |
| need. */ |
| if (TREE_CODE (id_expression) == TEMPLATE_ID_EXPR) |
| return id_expression; |
| *idk = CP_ID_KIND_UNQUALIFIED_DEPENDENT; |
| /* If we found a variable, then name lookup during the |
| instantiation will always resolve to the same VAR_DECL |
| (or an instantiation thereof). */ |
| if (TREE_CODE (decl) == VAR_DECL |
| || TREE_CODE (decl) == PARM_DECL) |
| { |
| mark_used (decl); |
| return convert_from_reference (decl); |
| } |
| /* The same is true for FIELD_DECL, but we also need to |
| make sure that the syntax is correct. */ |
| 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 (); |
| return decl; |
| } |
| 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 ((TREE_CODE (decl) == VAR_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) |
| && 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 (TREE_CODE (decl) == VAR_DECL |
| && !cp_unevaluated_operand |
| && !processing_template_decl |
| && 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 (scope) |
| { |
| 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; |
| |
| first_fn = get_first_fn (decl); |
| if (TREE_CODE (first_fn) == TEMPLATE_DECL) |
| first_fn = DECL_TEMPLATE_RESULT (first_fn); |
| |
| if (!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); |
| } |
| } |
| |
| if (TREE_DEPRECATED (decl)) |
| warn_deprecated_use (decl, NULL_TREE); |
| |
| return decl; |
| } |
| |
| /* 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; |
| } |
| |
| complete_type (type); |
| |
| 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 */ |
| 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); |
| |
| /* 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]; |
| } |
| 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 expr) |
| { |
| /* 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 = build1 (OFFSETOF_EXPR, size_type_node, expr); |
| 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 (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 (TREE_CODE (expr) == COMPONENT_REF) |
| { |
| tree object = TREE_OPERAND (expr, 0); |
| if (!complete_type_or_else (TREE_TYPE (object), object)) |
| return error_mark_node; |
| } |
| 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); |
| |
| 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)); |
| } |
| } |
| } |
| |
| /* Returns true iff FUN is an instantiation of a constexpr function |
| template. */ |
| |
| static inline bool |
| is_instantiation_of_constexpr (tree fun) |
| { |
| return (DECL_TEMPLOID_INSTANTIATION (fun) |
| && DECL_DECLARED_CONSTEXPR_P (DECL_TEMPLATE_RESULT |
| (DECL_TI_TEMPLATE (fun)))); |
| } |
| |
| /* 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)); |
| |
| /* 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 an instantiation of a constexpr function, keep |
| DECL_SAVED_TREE for explain_invalid_constexpr_fn. */ |
| if (!is_instantiation_of_constexpr (fn)) |
| DECL_SAVED_TREE (fn) = NULL_TREE; |
| return false; |
| } |
| |
| /* 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) |
| { |
| DECL_EXTERNAL (fn) = 1; |
| DECL_NOT_REALLY_EXTERN (fn) = 1; |
| note_vague_linkage_fn (fn); |
| /* A non-template inline function with external linkage will |
| always be COMDAT. As we must eventually determine the |
| linkage of all functions, and as that causes writes to |
| the data mapped in from the PCH file, it's advantageous |
| to mark the functions at this point. */ |
| if (!DECL_IMPLICIT_INSTANTIATION (fn) || DECL_DEFAULTED_FN (fn)) |
| { |
| /* This function must have external linkage, as |
| otherwise DECL_INTERFACE_KNOWN would have been |
| set. */ |
| gcc_assert (TREE_PUBLIC (fn)); |
| comdat_linkage (fn); |
| DECL_INTERFACE_KNOWN (fn) = 1; |
| } |
| } |
| 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 ((flag_keep_inline_functions |
| && DECL_DECLARED_INLINE_P (fn) |
| && !DECL_REALLY_EXTERN (fn)) |
| || (flag_keep_inline_dllexport |
| && lookup_attribute ("dllexport", DECL_ATTRIBUTES (fn)))) |
| { |
| mark_needed (fn); |
| DECL_EXTERNAL (fn) = 0; |
| } |
| } |
| |
| /* 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_finalize_function (fn, function_depth > 1); |
| emit_associated_thunks (fn); |
| |
| function_depth--; |
| } |
| } |
| |
| struct nrv_data |
| { |
| tree var; |
| tree result; |
| hash_table <pointer_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; |
| data.visited.create (37); |
| cp_walk_tree (tp, finalize_nrv_r, &data, 0); |
| data.visited.dispose (); |
| } |
| |
| /* 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) |
| { |
| 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_default_ctor || need_copy_ctor) |
| && 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; |
| } |
| |
| /* 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) |
| { |
| bitmap_head generic_head, firstprivate_head, lastprivate_head; |
| tree c, t, *pc = &clauses; |
| const char *name; |
| |
| 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); |
| |
| for (pc = &clauses, c = clauses; c ; c = *pc) |
| { |
| bool remove = false; |
| |
| switch (OMP_CLAUSE_CODE (c)) |
| { |
| case OMP_CLAUSE_SHARED: |
| name = "shared"; |
| goto check_dup_generic; |
| case OMP_CLAUSE_PRIVATE: |
| name = "private"; |
| goto check_dup_generic; |
| case OMP_CLAUSE_REDUCTION: |
| name = "reduction"; |
| goto check_dup_generic; |
| case OMP_CLAUSE_COPYPRIVATE: |
| name = "copyprivate"; |
| goto check_dup_generic; |
| case OMP_CLAUSE_COPYIN: |
| name = "copyin"; |
| goto check_dup_generic; |
| check_dup_generic: |
| t = OMP_CLAUSE_DECL (c); |
| if (TREE_CODE (t) != VAR_DECL && TREE_CODE (t) != PARM_DECL) |
| { |
| if (processing_template_decl) |
| break; |
| if (DECL_P (t)) |
| error ("%qD is not a variable in clause %qs", t, name); |
| else |
| error ("%qE is not a variable in clause %qs", t, name); |
| remove = true; |
| } |
| 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 |
| bitmap_set_bit (&generic_head, DECL_UID (t)); |
| break; |
| |
| case OMP_CLAUSE_FIRSTPRIVATE: |
| t = OMP_CLAUSE_DECL (c); |
| if (TREE_CODE (t) != VAR_DECL && TREE_CODE (t) != PARM_DECL) |
| { |
| if (processing_template_decl) |
| 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 |
| bitmap_set_bit (&firstprivate_head, DECL_UID (t)); |
| break; |
| |
| case OMP_CLAUSE_LASTPRIVATE: |
| t = OMP_CLAUSE_DECL (c); |
| if (TREE_CODE (t) != VAR_DECL && TREE_CODE (t) != PARM_DECL) |
| { |
| if (processing_template_decl) |
| 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)); |
| break; |
| |
| 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_NUM_THREADS: |
| t = OMP_CLAUSE_NUM_THREADS_EXPR (c); |
| if (t == error_mark_node) |
| remove = true; |
| else if (!type_dependent_expression_p (t) |
| && !INTEGRAL_TYPE_P (TREE_TYPE (t))) |
| { |
| error ("num_threads 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_NUM_THREADS_EXPR (c) = t; |
| } |
| break; |
| |
| case OMP_CLAUSE_SCHEDULE: |
| 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) |
| && !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) |
| t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); |
| OMP_CLAUSE_SCHEDULE_CHUNK_EXPR (c) = t; |
| } |
| break; |
| |
| case OMP_CLAUSE_NOWAIT: |
| case OMP_CLAUSE_ORDERED: |
| case OMP_CLAUSE_DEFAULT: |
| case OMP_CLAUSE_UNTIED: |
| case OMP_CLAUSE_COLLAPSE: |
| case OMP_CLAUSE_MERGEABLE: |
| 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_non_reference = false; |
| bool need_default_ctor = false; |
| bool need_copy_ctor = false; |
| bool need_copy_assignment = false; |
| bool need_implicitly_determined = false; |
| tree type, inner_type; |
| |
| switch (c_kind) |
| { |
| case OMP_CLAUSE_SHARED: |
| name = "shared"; |
| need_implicitly_determined = true; |
| break; |
| case OMP_CLAUSE_PRIVATE: |
| name = "private"; |
| need_complete_non_reference = true; |
| need_default_ctor = true; |
| need_implicitly_determined = true; |
| break; |
| case OMP_CLAUSE_FIRSTPRIVATE: |
| name = "firstprivate"; |
| need_complete_non_reference = true; |
| need_copy_ctor = true; |
| need_implicitly_determined = true; |
| break; |
| case OMP_CLAUSE_LASTPRIVATE: |
| name = "lastprivate"; |
| need_complete_non_reference = true; |
| need_copy_assignment = true; |
| need_implicitly_determined = true; |
| break; |
| case OMP_CLAUSE_REDUCTION: |
| name = "reduction"; |
| need_implicitly_determined = true; |
| break; |
| case OMP_CLAUSE_COPYPRIVATE: |
| name = "copyprivate"; |
| need_copy_assignment = true; |
| break; |
| case OMP_CLAUSE_COPYIN: |
| name = "copyin"; |
| need_copy_assignment = true; |
| break; |
| default: |
| pc = &OMP_CLAUSE_CHAIN (c); |
| continue; |
| } |
| |
| t = OMP_CLAUSE_DECL (c); |
| if (processing_template_decl |
| && TREE_CODE (t) != VAR_DECL && 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; |
| break; |
| |
| case OMP_CLAUSE_REDUCTION: |
| if (AGGREGATE_TYPE_P (TREE_TYPE (t)) |
| || POINTER_TYPE_P (TREE_TYPE (t))) |
| { |
| error ("%qE has invalid type for %<reduction%>", t); |
| remove = true; |
| } |
| else if (FLOAT_TYPE_P (TREE_TYPE (t)) |
| || TREE_CODE (TREE_TYPE (t)) == COMPLEX_TYPE) |
| { |
| enum tree_code r_code = OMP_CLAUSE_REDUCTION_CODE (c); |
| switch (r_code) |
| { |
| case PLUS_EXPR: |
| case MULT_EXPR: |
| case MINUS_EXPR: |
| break; |
| case MIN_EXPR: |
| case MAX_EXPR: |
| if (TREE_CODE (TREE_TYPE (t)) == COMPLEX_TYPE) |
| r_code = ERROR_MARK; |
| break; |
| case BIT_AND_EXPR: |
| case BIT_XOR_EXPR: |
| case BIT_IOR_EXPR: |
| default: |
| r_code = ERROR_MARK; |
| break; |
| case TRUTH_ANDIF_EXPR: |
| case TRUTH_ORIF_EXPR: |
| if (FLOAT_TYPE_P (TREE_TYPE (t))) |
| r_code = ERROR_MARK; |
| break; |
| } |
| if (r_code == ERROR_MARK) |
| { |
| error ("%qE has invalid type for %<reduction(%s)%>", |
| t, operator_name_info[r_code].name); |
| remove = true; |
| } |
| } |
| break; |
| |
| case OMP_CLAUSE_COPYIN: |
| if (TREE_CODE (t) != VAR_DECL || !DECL_THREAD_LOCAL_P (t)) |
| { |
| error ("%qE must be %<threadprivate%> for %<copyin%>", t); |
| remove = true; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| if (need_complete_non_reference || need_copy_assignment) |
| { |
| t = require_complete_type (t); |
| if (t == error_mark_node) |
| remove = true; |
| else if (TREE_CODE (TREE_TYPE (t)) == REFERENCE_TYPE |
| && need_complete_non_reference) |
| { |
| error ("%qE has reference type for %qs", t, name); |
| remove = true; |
| } |
| } |
| if (need_implicitly_determined) |
| { |
| const char *share_name = NULL; |
| |
| if (TREE_CODE (t) == VAR_DECL && 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", |
| t, share_name, name); |
| remove = true; |
| } |
| } |
| |
| /* We're interested in the base element, not arrays. */ |
| inner_type = type = TREE_TYPE (t); |
| 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) |
| && !type_dependent_expression_p (t) |
| && cxx_omp_create_clause_info (c, inner_type, need_default_ctor, |
| need_copy_ctor, need_copy_assignment)) |
| remove = true; |
| |
| if (remove) |
| *pc = OMP_CLAUSE_CHAIN (c); |
| else |
| pc = &OMP_CLAUSE_CHAIN (c); |
| } |
| |
| bitmap_obstack_release (NULL); |
| return clauses; |
| } |
| |
| /* 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 (TREE_CODE (v) != VAR_DECL) |
| 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 (! DECL_THREAD_LOCAL_P (v)) |
| { |
| 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 (); |
| } |
| |
| 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, tree declv, tree initv, |
| tree condv, tree incrv, tree *body, |
| tree *pre_body, tree clauses) |
| { |
| 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); |
| |
| switch (TREE_CODE (cond)) |
| { |
| case GT_EXPR: |
| case GE_EXPR: |
| case LT_EXPR: |
| case LE_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); |
| 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; |
| } |
| |
| 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_if_not_in_template (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); |
| for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE |
| && OMP_CLAUSE_DECL (c) == iter) |
| break; |
| |
| 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) |
| { |
| incr_var = create_temporary_var (TREE_TYPE (diff)); |
| pushdecl (incr_var); |
| add_decl_expr (incr_var); |
| } |
| gcc_assert (stmts_are_full_exprs_p ()); |
| |
| 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) |
| { |
| 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); |
| } |
| finish_expr_stmt (build_x_modify_expr (elocus, |
| last, NOP_EXPR, init, |
| tf_warning_or_error)); |
| *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); |
| 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 (); |
| finish_expr_stmt (iter_incr); |
| 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; |
| |
| 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, tree declv, tree initv, tree condv, |
| tree incrv, tree body, tree pre_body, tree clauses) |
| { |
| tree omp_for = NULL, orig_incr = NULL; |
| tree decl, init, cond, incr; |
| location_t elocus; |
| int i; |
| |
| 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)); |
| 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 (dependent_omp_for_p (declv, initv, condv, incrv)) |
| { |
| tree stmt; |
| |
| stmt = make_node (OMP_FOR); |
| |
| 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 (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 (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 (handle_omp_for_class_iterator (i, locus, declv, initv, condv, |
| incrv, &body, &pre_body, clauses)) |
| return NULL; |
| continue; |
| } |
| |
| if (!INTEGRAL_TYPE_P (TREE_TYPE (decl)) |
| && TREE_CODE (TREE_TYPE (decl)) != POINTER_TYPE) |
| { |
| 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 (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; |
| |
| omp_for = c_finish_omp_for (locus, declv, initv, condv, incrv, |
| body, pre_body); |
| |
| if (omp_for == NULL) |
| return NULL; |
| |
| 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); |
| } |
| if (omp_for != NULL) |
| 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) |
| { |
| 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) |
| { |
| stmt = c_finish_omp_atomic (input_location, code, opcode, lhs, rhs, |
| v, lhs1, rhs1); |
| 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); |
| 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); |
| stmt = build2 (MODIFY_EXPR, void_type_node, orig_v, stmt); |
| } |
| } |
| stmt = build2 (OMP_ATOMIC, void_type_node, integer_zero_node, stmt); |
| } |
| 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); |
| } |
| |
| /* 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); |
| 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); |
| finish_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); |
| 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 = fold_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)) |
| /* Report the error. */ |
| error ("static assertion failed: %s", TREE_STRING_POINTER (message)); |
| else if (condition && condition != error_mark_node) |
| { |
| error ("non-constant condition for static assertion"); |
| 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_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); |
| |
| if (type_unknown_p (expr)) |
| { |
| if (complain & tf_error) |
| error ("decltype cannot resolve address of overloaded function"); |
| return error_mark_node; |
| } |
| |
| if (invalid_nonstatic_memfn_p (expr, complain)) |
| 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 (TREE_CODE (expr) == VAR_DECL |
| && 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 (TREE_CODE (expr) == IDENTIFIER_NODE) |
| expr = lookup_name (expr); |
| |
| if (TREE_CODE (expr) == INDIRECT_REF) |
| /* 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); |
| |
| 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. */ |
| |
| 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 (TREE_CODE (type) == VECTOR_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. */ |
| |
| static bool |
| classtype_has_nothrow_assign_or_copy_p (tree type, bool assign_p) |
| { |
| tree fns; |
| |
| if (assign_p) |
| { |
| int ix; |
| ix = lookup_fnfields_1 (type, ansi_assopname (NOP_EXPR)); |
| if (ix < 0) |
| return false; |
| fns = (*CLASSTYPE_METHOD_VEC (type))[ix]; |
| } |
| else if (TYPE_HAS_COPY_CTOR (type)) |
| { |
| /* If construction of the copy constructor was postponed, create |
| it now. */ |
| if (CLASSTYPE_LAZY_COPY_CTOR (type)) |
| lazily_declare_fn (sfk_copy_constructor, type); |
| if (CLASSTYPE_LAZY_MOVE_CTOR (type)) |
| lazily_declare_fn (sfk_move_constructor, type); |
| fns = CLASSTYPE_CONSTRUCTORS (type); |
| } |
| else |
| return false; |
| |
| for (; fns; fns = OVL_NEXT (fns)) |
| { |
| tree fn = OVL_CURRENT (fns); |
| |
| if (assign_p) |
| { |
| if (copy_fn_p (fn) == 0) |
| continue; |
| } |
| else if (copy_fn_p (fn) <= 0) |
| continue; |
| |
| maybe_instantiate_noexcept (fn); |
| if (!TYPE_NOTHROW_P (TREE_TYPE (fn))) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* 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_IS_ABSTRACT: |
| return (ABSTRACT_CLASS_TYPE_P (type1)); |
| |
| case CPTK_IS_BASE_OF: |
| return (NON_UNION_CLASS_TYPE_P (type1) && NON_UNION_CLASS_TYPE_P (type2) |
| && DERIVED_FROM_P (type1, type2)); |
| |
| case CPTK_IS_CLASS: |
| return (NON_UNION_CLASS_TYPE_P (type1)); |
| |
| case CPTK_IS_CONVERTIBLE_TO: |
| /* TODO */ |
| return false; |
| |
| 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_STD_LAYOUT: |
| return (std_layout_type_p (type1)); |
| |
| case CPTK_IS_TRIVIAL: |
| return (trivial_type_p (type1)); |
| |
| case CPTK_IS_UNION: |
| return (type_code1 == UNION_TYPE); |
| |
| default: |
| gcc_unreachable (); |
| return false; |
| } |
| } |
| |
| /* If TYPE is an array of unknown bound, or (possibly cv-qualified) |
| void, or a complete type, returns it, otherwise NULL_TREE. */ |
| |
| static tree |
| check_trait_type (tree type) |
| { |
| if (TREE_CODE (type) == ARRAY_TYPE && !TYPE_DOMAIN (type) |
| && COMPLETE_TYPE_P (TREE_TYPE (type))) |
| return type; |
| |
| if (VOID_TYPE_P (type)) |
| return type; |
| |
| 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) |
| { |
| gcc_assert (kind == CPTK_HAS_NOTHROW_ASSIGN |
| || kind == CPTK_HAS_NOTHROW_CONSTRUCTOR |
| || kind == CPTK_HAS_NOTHROW_COPY |
| || kind == CPTK_HAS_TRIVIAL_ASSIGN |
| || kind == CPTK_HAS_TRIVIAL_CONSTRUCTOR |
| || kind == CPTK_HAS_TRIVIAL_COPY |
| || kind == CPTK_HAS_TRIVIAL_DESTRUCTOR |
| || kind == CPTK_HAS_VIRTUAL_DESTRUCTOR |
| || kind == CPTK_IS_ABSTRACT |
| || kind == CPTK_IS_BASE_OF |
| || kind == CPTK_IS_CLASS |
| || kind == CPTK_IS_CONVERTIBLE_TO |
| || kind == CPTK_IS_EMPTY |
| || kind == CPTK_IS_ENUM |
| || kind == CPTK_IS_FINAL |
| || kind == CPTK_IS_LITERAL_TYPE |
| || kind == CPTK_IS_POD |
| || kind == CPTK_IS_POLYMORPHIC |
| || kind == CPTK_IS_STD_LAYOUT |
| || kind == CPTK_IS_TRIVIAL |
| || kind == CPTK_IS_UNION); |
| |
| if (kind == CPTK_IS_CONVERTIBLE_TO) |
| { |
| sorry ("__is_convertible_to"); |
| return error_mark_node; |
| } |
| |
| if (type1 == error_mark_node |
| || ((kind == CPTK_IS_BASE_OF || kind == CPTK_IS_CONVERTIBLE_TO) |
| && 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_VIRTUAL_DESTRUCTOR: |
| case CPTK_IS_ABSTRACT: |
| 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: |
| if (!check_trait_type (type1)) |
| 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: |
| break; |
| |
| case CPTK_IS_CONVERTIBLE_TO: |
| 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 is a literal type. */ |
| |
| bool |
| literal_type_p (tree t) |
| { |
| if (SCALAR_TYPE_P (t) |
| || TREE_CODE (t) == VECTOR_TYPE |
| || TREE_CODE (t) == REFERENCE_TYPE) |
| return true; |
| if (CLASS_TYPE_P (t)) |
| { |
| t = complete_type (t); |
| gcc_assert (COMPLETE_TYPE_P (t) || errorcount); |
| return CLASSTYPE_LITERAL_P (t); |
| } |
| if (TREE_CODE (t) == ARRAY_TYPE) |
| return literal_type_p (strip_array_types (t)); |
| return false; |
| } |
| |
| /* If DECL is a variable declared `constexpr', require its type |
| be literal. Return the DECL if OK, otherwise NULL. */ |
| |
| tree |
| ensure_literal_type_for_constexpr_object (tree decl) |
| { |
| tree type = TREE_TYPE (decl); |
| if (TREE_CODE (decl) == VAR_DECL && DECL_DECLARED_CONSTEXPR_P (decl) |
| && !processing_template_decl) |
| { |
| if (CLASS_TYPE_P (type) && !COMPLETE_TYPE_P (complete_type (type))) |
| /* Don't complain here, we'll complain about incompleteness |
| when we try to initialize the variable. */; |
| else if (!literal_type_p (type)) |
| { |
| error ("the type %qT of constexpr variable %qD is not literal", |
| type, decl); |
| explain_non_literal_class (type); |
| return NULL; |
| } |
| } |
| return decl; |
| } |
| |
| /* Representation of entries in the constexpr function definition table. */ |
| |
| typedef struct GTY(()) constexpr_fundef { |
| tree decl; |
| tree body; |
| } constexpr_fundef; |
| |
| /* This table holds all constexpr function definitions seen in |
| the current translation unit. */ |
| |
| static GTY ((param_is (constexpr_fundef))) htab_t constexpr_fundef_table; |
| |
| /* Utility function used for managing the constexpr function table. |
| Return true if the entries pointed to by P and Q are for the |
| same constexpr function. */ |
| |
| static inline int |
| constexpr_fundef_equal (const void *p, const void *q) |
| { |
| const constexpr_fundef *lhs = (const constexpr_fundef *) p; |
| const constexpr_fundef *rhs = (const constexpr_fundef *) q; |
| return lhs->decl == rhs->decl; |
| } |
| |
| /* Utility function used for managing the constexpr function table. |
| Return a hash value for the entry pointed to by Q. */ |
| |
| static inline hashval_t |
| constexpr_fundef_hash (const void *p) |
| { |
| const constexpr_fundef *fundef = (const constexpr_fundef *) p; |
| return DECL_UID (fundef->decl); |
| } |
| |
| /* Return a previously saved definition of function FUN. */ |
| |
| static constexpr_fundef * |
| retrieve_constexpr_fundef (tree fun) |
| { |
| constexpr_fundef fundef = { NULL, NULL }; |
| if (constexpr_fundef_table == NULL) |
| return NULL; |
| |
| fundef.decl = fun; |
| return (constexpr_fundef *) htab_find (constexpr_fundef_table, &fundef); |
| } |
| |
| /* Check whether the parameter and return types of FUN are valid for a |
| constexpr function, and complain if COMPLAIN. */ |
| |
| static bool |
| is_valid_constexpr_fn (tree fun, bool complain) |
| { |
| tree parm = FUNCTION_FIRST_USER_PARM (fun); |
| bool ret = true; |
| for (; parm != NULL; parm = TREE_CHAIN (parm)) |
| if (!literal_type_p (TREE_TYPE (parm))) |
| { |
| ret = false; |
| if (complain) |
| { |
| error ("invalid type for parameter %d of constexpr " |
| "function %q+#D", DECL_PARM_INDEX (parm), fun); |
| explain_non_literal_class (TREE_TYPE (parm)); |
| } |
| } |
| |
| if (!DECL_CONSTRUCTOR_P (fun)) |
| { |
| tree rettype = TREE_TYPE (TREE_TYPE (fun)); |
| if (!literal_type_p (rettype)) |
| { |
| ret = false; |
| if (complain) |
| { |
| error ("invalid return type %qT of constexpr function %q+D", |
| rettype, fun); |
| explain_non_literal_class (rettype); |
| } |
| } |
| |
| if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fun) |
| && !CLASSTYPE_LITERAL_P (DECL_CONTEXT (fun))) |
| { |
| ret = false; |
| if (complain) |
| { |
| error ("enclosing class of constexpr non-static member " |
| "function %q+#D is not a literal type", fun); |
| explain_non_literal_class (DECL_CONTEXT (fun)); |
| } |
| } |
| } |
| else if (CLASSTYPE_VBASECLASSES (DECL_CONTEXT (fun))) |
| { |
| ret = false; |
| if (complain) |
| error ("%q#T has virtual base classes", DECL_CONTEXT (fun)); |
| } |
| |
| return ret; |
| } |
| |
| /* Subroutine of build_data_member_initialization. MEMBER is a COMPONENT_REF |
| for a member of an anonymous aggregate, INIT is the initializer for that |
| member, and VEC_OUTER is the vector of constructor elements for the class |
| whose constructor we are processing. Add the initializer to the vector |
| and return true to indicate success. */ |
| |
| static bool |
| build_anon_member_initialization (tree member, tree init, |
| vec<constructor_elt, va_gc> **vec_outer) |
| { |
| /* MEMBER presents the relevant fields from the inside out, but we need |
| to build up the initializer from the outside in so that we can reuse |
| previously built CONSTRUCTORs if this is, say, the second field in an |
| anonymous struct. So we use a vec as a stack. */ |
| vec<tree> fields; |
| fields.create (2); |
| do |
| { |
| fields.safe_push (TREE_OPERAND (member, 1)); |
| member = TREE_OPERAND (member, 0); |
| } |
| while (ANON_AGGR_TYPE_P (TREE_TYPE (member))); |
| |
| /* VEC has the constructor elements vector for the context of FIELD. |
| If FIELD is an anonymous aggregate, we will push inside it. */ |
| vec<constructor_elt, va_gc> **vec = vec_outer; |
| tree field; |
| while (field = fields.pop(), |
| ANON_AGGR_TYPE_P (TREE_TYPE (field))) |
| { |
| tree ctor; |
| /* If there is already an outer constructor entry for the anonymous |
| aggregate FIELD, use it; otherwise, insert one. */ |
| if (vec_safe_is_empty (*vec) |
| || (*vec)->last().index != field) |
| { |
| ctor = build_constructor (TREE_TYPE (field), NULL); |
| CONSTRUCTOR_APPEND_ELT (*vec, field, ctor); |
| } |
| else |
| ctor = (*vec)->last().value; |
| vec = &CONSTRUCTOR_ELTS (ctor); |
| } |
| |
| /* Now we're at the innermost field, the one that isn't an anonymous |
| aggregate. Add its initializer to the CONSTRUCTOR and we're done. */ |
| gcc_assert (fields.is_empty()); |
| fields.release (); |
| CONSTRUCTOR_APPEND_ELT (*vec, field, init); |
| |
| return true; |
| } |
| |
| /* Subroutine of build_constexpr_constructor_member_initializers. |
| The expression tree T represents a data member initialization |
| in a (constexpr) constructor definition. Build a pairing of |
| the data member with its initializer, and prepend that pair |
| to the existing initialization pair INITS. */ |
| |
| static bool |
| build_data_member_initialization (tree t, vec<constructor_elt, va_gc> **vec) |
| { |
| tree member, init; |
| if (TREE_CODE (t) == CLEANUP_POINT_EXPR) |
| t = TREE_OPERAND (t, 0); |
| if (TREE_CODE (t) == EXPR_STMT) |
| t = TREE_OPERAND (t, 0); |
| if (t == error_mark_node) |
| return false; |
| if (TREE_CODE (t) == STATEMENT_LIST) |
| { |
| tree_stmt_iterator i; |
| for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i)) |
| { |
| if (! build_data_member_initialization (tsi_stmt (i), vec)) |
| return false; |
| } |
| return true; |
| } |
| if (TREE_CODE (t) == CLEANUP_STMT) |
| { |
| /* We can't see a CLEANUP_STMT in a constructor for a literal class, |
| but we can in a constexpr constructor for a non-literal class. Just |
| ignore it; either all the initialization will be constant, in which |
| case the cleanup can't run, or it can't be constexpr. |
| Still recurse into CLEANUP_BODY. */ |
| return build_data_member_initialization (CLEANUP_BODY (t), vec); |
| } |
| if (TREE_CODE (t) == CONVERT_EXPR) |
| t = TREE_OPERAND (t, 0); |
| if (TREE_CODE (t) == INIT_EXPR |
| || TREE_CODE (t) == MODIFY_EXPR) |
| { |
| member = TREE_OPERAND (t, 0); |
| init = break_out_target_exprs (TREE_OPERAND (t, 1)); |
| } |
| else if (TREE_CODE (t) == CALL_EXPR) |
| { |
| member = CALL_EXPR_ARG (t, 0); |
| /* We don't use build_cplus_new here because it complains about |
| abstract bases. Leaving the call unwrapped means that it has the |
| wrong type, but cxx_eval_constant_expression doesn't care. */ |
| init = break_out_target_exprs (t); |
| } |
| else if (TREE_CODE (t) == DECL_EXPR) |
| /* Declaring a temporary, don't add it to the CONSTRUCTOR. */ |
| return true; |
| else |
| gcc_unreachable (); |
| if (TREE_CODE (member) == INDIRECT_REF) |
| member = TREE_OPERAND (member, 0); |
| if (TREE_CODE (member) == NOP_EXPR) |
| { |
| tree op = member; |
| STRIP_NOPS (op); |
| if (TREE_CODE (op) == ADDR_EXPR) |
| { |
| gcc_assert (same_type_ignoring_top_level_qualifiers_p |
| (TREE_TYPE (TREE_TYPE (op)), |
| TREE_TYPE (TREE_TYPE (member)))); |
| /* Initializing a cv-qualified member; we need to look through |
| the const_cast. */ |
| member = op; |
| } |
| else if (op == current_class_ptr |
| && (same_type_ignoring_top_level_qualifiers_p |
| (TREE_TYPE (TREE_TYPE (member)), |
| current_class_type))) |
| /* Delegating constructor. */ |
| member = op; |
| else |
| { |
| /* This is an initializer for an empty base; keep it for now so |
| we can check it in cxx_eval_bare_aggregate. */ |
| gcc_assert (is_empty_class (TREE_TYPE (TREE_TYPE (member)))); |
| } |
| } |
| if (TREE_CODE (member) == ADDR_EXPR) |
| member = TREE_OPERAND (member, 0); |
| if (TREE_CODE (member) == COMPONENT_REF) |
| { |
| tree aggr = TREE_OPERAND (member, 0); |
| if (TREE_CODE (aggr) != COMPONENT_REF) |
| /* Normal member initialization. */ |
| member = TREE_OPERAND (member, 1); |
| else if (ANON_AGGR_TYPE_P (TREE_TYPE (aggr))) |
| /* Initializing a member of an anonymous union. */ |
| return build_anon_member_initialization (member, init, vec); |
| else |
| /* We're initializing a vtable pointer in a base. Leave it as |
| COMPONENT_REF so we remember the path to get to the vfield. */ |
| gcc_assert (TREE_TYPE (member) == vtbl_ptr_type_node); |
| } |
| |
| CONSTRUCTOR_APPEND_ELT (*vec, member, init); |
| return true; |
| } |
| |
| /* Make sure that there are no statements after LAST in the constructor |
| body represented by LIST. */ |
| |
| bool |
| check_constexpr_ctor_body (tree last, tree list) |
| { |
| bool ok = true; |
| if (TREE_CODE (list) == STATEMENT_LIST) |
| { |
| tree_stmt_iterator i = tsi_last (list); |
| for (; !tsi_end_p (i); tsi_prev (&i)) |
| { |
| tree t = tsi_stmt (i); |
| if (t == last) |
| break; |
| if (TREE_CODE (t) == BIND_EXPR) |
| { |
| if (!check_constexpr_ctor_body (last, BIND_EXPR_BODY (t))) |
| return false; |
| else |
| continue; |
| } |
| /* We currently allow typedefs and static_assert. |
| FIXME allow them in the standard, too. */ |
| if (TREE_CODE (t) != STATIC_ASSERT) |
| { |
| ok = false; |
| break; |
| } |
| } |
| } |
| else if (list != last |
| && TREE_CODE (list) != STATIC_ASSERT) |
| ok = false; |
| if (!ok) |
| { |
| error ("constexpr constructor does not have empty body"); |
| DECL_DECLARED_CONSTEXPR_P (current_function_decl) = false; |
| } |
| return ok; |
| } |
| |
| /* V is a vector of constructor elements built up for the base and member |
| initializers of a constructor for TYPE. They need to be in increasing |
| offset order, which they might not be yet if TYPE has a primary base |
| which is not first in the base-clause or a vptr and at least one base |
| all of which are non-primary. */ |
| |
| static vec<constructor_elt, va_gc> * |
| sort_constexpr_mem_initializers (tree type, vec<constructor_elt, va_gc> *v) |
| { |
| tree pri = CLASSTYPE_PRIMARY_BINFO (type); |
| tree field_type; |
| constructor_elt elt; |
| int i; |
| |
| if (pri) |
| field_type = BINFO_TYPE (pri); |
| else if (TYPE_CONTAINS_VPTR_P (type)) |
| field_type = vtbl_ptr_type_node; |
| else |
| return v; |
| |
| /* Find the element for the primary base or vptr and move it to the |
| beginning of the vec. */ |
| vec<constructor_elt, va_gc> &vref = *v; |
| for (i = 0; ; ++i) |
| if (TREE_TYPE (vref[i].index) == field_type) |
| break; |
| |
| if (i > 0) |
| { |
| elt = vref[i]; |
| for (; i > 0; --i) |
| vref[i] = vref[i-1]; |
| vref[0] = elt; |
| } |
| |
| return v; |
| } |
| |
| /* Build compile-time evalable representations of member-initializer list |
| for a constexpr constructor. */ |
| |
| static tree |
| build_constexpr_constructor_member_initializers (tree type, tree body) |
| { |
| vec<constructor_elt, va_gc> *vec = NULL; |
| bool ok = true; |
| if (TREE_CODE (body) == MUST_NOT_THROW_EXPR |
| || TREE_CODE (body) == EH_SPEC_BLOCK) |
| body = TREE_OPERAND (body, 0); |
| if (TREE_CODE (body) == STATEMENT_LIST) |
| body = STATEMENT_LIST_HEAD (body)->stmt; |
| body = BIND_EXPR_BODY (body); |
| if (TREE_CODE (body) == CLEANUP_POINT_EXPR) |
| { |
| body = TREE_OPERAND (body, 0); |
| if (TREE_CODE (body) == EXPR_STMT) |
| body = TREE_OPERAND (body, 0); |
| if (TREE_CODE (body) == INIT_EXPR |
| && (same_type_ignoring_top_level_qualifiers_p |
| (TREE_TYPE (TREE_OPERAND (body, 0)), |
| current_class_type))) |
| { |
| /* Trivial copy. */ |
| return TREE_OPERAND (body, 1); |
| } |
| ok = build_data_member_initialization (body, &vec); |
| } |
| else if (TREE_CODE (body) == STATEMENT_LIST) |
| { |
| tree_stmt_iterator i; |
| for (i = tsi_start (body); !tsi_end_p (i); tsi_next (&i)) |
| { |
| ok = build_data_member_initialization (tsi_stmt (i), &vec); |
| if (!ok) |
| break; |
| } |
| } |
| else if (TREE_CODE (body) == TRY_BLOCK) |
| { |
| error ("body of %<constexpr%> constructor cannot be " |
| "a function-try-block"); |
| return error_mark_node; |
| } |
| else if (EXPR_P (body)) |
| ok = build_data_member_initialization (body, &vec); |
| else |
| gcc_assert (errorcount > 0); |
| if (ok) |
| { |
| if (vec_safe_length (vec) > 0) |
| { |
| /* In a delegating constructor, return the target. */ |
| constructor_elt *ce = &(*vec)[0]; |
| if (ce->index == current_class_ptr) |
| { |
| body = ce->value; |
| vec_free (vec); |
| return body; |
| } |
| } |
| vec = sort_constexpr_mem_initializers (type, vec); |
| return build_constructor (type, vec); |
| } |
| else |
| return error_mark_node; |
| } |
| |
| /* Subroutine of register_constexpr_fundef. BODY is the body of a function |
| declared to be constexpr, or a sub-statement thereof. Returns the |
| return value if suitable, error_mark_node for a statement not allowed in |
| a constexpr function, or NULL_TREE if no return value was found. */ |
| |
| static tree |
| constexpr_fn_retval (tree body) |
| { |
| switch (TREE_CODE (body)) |
| { |
| case STATEMENT_LIST: |
| { |
| tree_stmt_iterator i; |
| tree expr = NULL_TREE; |
| for (i = tsi_start (body); !tsi_end_p (i); tsi_next (&i)) |
| { |
| tree s = constexpr_fn_retval (tsi_stmt (i)); |
| if (s == error_mark_node) |
| return error_mark_node; |
| else if (s == NULL_TREE) |
| /* Keep iterating. */; |
| else if (expr) |
| /* Multiple return statements. */ |
| return error_mark_node; |
| else |
| expr = s; |
| } |
| return expr; |
| } |
| |
| case RETURN_EXPR: |
| return break_out_target_exprs (TREE_OPERAND (body, 0)); |
| |
| case DECL_EXPR: |
| if (TREE_CODE (DECL_EXPR_DECL (body)) == USING_DECL) |
| return NULL_TREE; |
| return error_mark_node; |
| |
| case CLEANUP_POINT_EXPR: |
| return constexpr_fn_retval (TREE_OPERAND (body, 0)); |
| |
| case USING_STMT: |
| return NULL_TREE; |
| |
| default: |
| return error_mark_node; |
| } |
| } |
| |
| /* Subroutine of register_constexpr_fundef. BODY is the DECL_SAVED_TREE of |
| FUN; do the necessary transformations to turn it into a single expression |
| that we can store in the hash table. */ |
| |
| static tree |
| massage_constexpr_body (tree fun, tree body) |
| { |
| if (DECL_CONSTRUCTOR_P (fun)) |
| body = build_constexpr_constructor_member_initializers |
| (DECL_CONTEXT (fun), body); |
| else |
| { |
| if (TREE_CODE (body) == EH_SPEC_BLOCK) |
| body = EH_SPEC_STMTS (body); |
| if (TREE_CODE (body) == MUST_NOT_THROW_EXPR) |
| body = TREE_OPERAND (body, 0); |
| if (TREE_CODE (body) == BIND_EXPR) |
| body = BIND_EXPR_BODY (body); |
| body = constexpr_fn_retval (body); |
| } |
| return body; |
| } |
| |
| /* FUN is a constexpr constructor with massaged body BODY. Return true |
| if some bases/fields are uninitialized, and complain if COMPLAIN. */ |
| |
| static bool |
| cx_check_missing_mem_inits (tree fun, tree body, bool complain) |
| { |
| bool bad; |
| tree field; |
| unsigned i, nelts; |
| tree ctype; |
| |
| if (TREE_CODE (body) != CONSTRUCTOR) |
| return false; |
| |
| nelts = CONSTRUCTOR_NELTS (body); |
| ctype = DECL_CONTEXT (fun); |
| field = TYPE_FIELDS (ctype); |
| |
| if (TREE_CODE (ctype) == UNION_TYPE) |
| { |
| if (nelts == 0 && next_initializable_field (field)) |
| { |
| if (complain) |
| error ("%<constexpr%> constructor for union %qT must " |
| "initialize exactly one non-static data member", ctype); |
| return true; |
| } |
| return false; |
| } |
| |
| bad = false; |
| for (i = 0; i <= nelts; ++i) |
| { |
| tree index; |
| if (i == nelts) |
| index = NULL_TREE; |
| else |
| { |
| index = CONSTRUCTOR_ELT (body, i)->index; |
| /* Skip base and vtable inits. */ |
| if (TREE_CODE (index) != FIELD_DECL |
| || DECL_ARTIFICIAL (index)) |
| continue; |
| } |
| for (; field != index; field = DECL_CHAIN (field)) |
| { |
| tree ftype; |
| if (TREE_CODE (field) != FIELD_DECL |
| || (DECL_C_BIT_FIELD (field) && !DECL_NAME (field)) |
| || DECL_ARTIFICIAL (field)) |
| continue; |
| ftype = strip_array_types (TREE_TYPE (field)); |
| if (type_has_constexpr_default_constructor (ftype)) |
| { |
| /* It's OK to skip a member with a trivial constexpr ctor. |
| A constexpr ctor that isn't trivial should have been |
| added in by now. */ |
| gcc_checking_assert (!TYPE_HAS_COMPLEX_DFLT (ftype) |
| || errorcount != 0); |
| continue; |
| } |
| if (!complain) |
| return true; |
| error ("uninitialized member %qD in %<constexpr%> constructor", |
| field); |
| bad = true; |
| } |
| if (field == NULL_TREE) |
| break; |
| field = DECL_CHAIN (field); |
| } |
| |
| return bad; |
| } |
| |
| /* We are processing the definition of the constexpr function FUN. |
| Check that its BODY fulfills the propriate requirements and |
| enter it in the constexpr function definition table. |
| For constructor BODY is actually the TREE_LIST of the |
| member-initializer list. */ |
| |
| tree |
| register_constexpr_fundef (tree fun, tree body) |
| { |
| constexpr_fundef entry; |
| constexpr_fundef **slot; |
| |
| if (!is_valid_constexpr_fn (fun, !DECL_GENERATED_P (fun))) |
| return NULL; |
| |
| body = massage_constexpr_body (fun, body); |
| if (body == NULL_TREE || body == error_mark_node) |
| { |
| if (!DECL_CONSTRUCTOR_P (fun)) |
| error ("body of constexpr function %qD not a return-statement", fun); |
| return NULL; |
| } |
| |
| if (!potential_rvalue_constant_expression (body)) |
| { |
| if (!DECL_GENERATED_P (fun)) |
| require_potential_rvalue_constant_expression (body); |
| return NULL; |
| } |
| |
| if (DECL_CONSTRUCTOR_P (fun) |
| && cx_check_missing_mem_inits (fun, body, !DECL_GENERATED_P (fun))) |
| return NULL; |
| |
| /* Create the constexpr function table if necessary. */ |
| if (constexpr_fundef_table == NULL) |
| constexpr_fundef_table = htab_create_ggc (101, |
| constexpr_fundef_hash, |
| constexpr_fundef_equal, |
| ggc_free); |
| entry.decl = fun; |
| entry.body = body; |
| slot = (constexpr_fundef **) |
| htab_find_slot (constexpr_fundef_table, &entry, INSERT); |
| |
| gcc_assert (*slot == NULL); |
| *slot = ggc_alloc_constexpr_fundef (); |
| **slot = entry; |
| |
| return fun; |
| } |
| |
| /* FUN is a non-constexpr function called in a context that requires a |
| constant expression. If it comes from a constexpr template, explain why |
| the instantiation isn't constexpr. */ |
| |
| void |
| explain_invalid_constexpr_fn (tree fun) |
| { |
| static struct pointer_set_t *diagnosed; |
| tree body; |
| location_t save_loc; |
| /* Only diagnose defaulted functions or instantiations. */ |
| if (!DECL_DEFAULTED_FN (fun) |
| && !is_instantiation_of_constexpr (fun)) |
| return; |
| if (diagnosed == NULL) |
| diagnosed = pointer_set_create (); |
| if (pointer_set_insert (diagnosed, fun) != 0) |
| /* Already explained. */ |
| return; |
| |
| save_loc = input_location; |
| input_location = DECL_SOURCE_LOCATION (fun); |
| inform (0, "%q+D is not usable as a constexpr function because:", fun); |
| /* First check the declaration. */ |
| if (is_valid_constexpr_fn (fun, true)) |
| { |
| /* Then if it's OK, the body. */ |
| if (DECL_DEFAULTED_FN (fun)) |
| explain_implicit_non_constexpr (fun); |
| else |
| { |
| body = massage_constexpr_body (fun, DECL_SAVED_TREE (fun)); |
| require_potential_rvalue_constant_expression (body); |
| if (DECL_CONSTRUCTOR_P (fun)) |
| cx_check_missing_mem_inits (fun, body, true); |
| } |
| } |
| input_location = save_loc; |
| } |
| |
| /* Objects of this type represent calls to constexpr functions |
| along with the bindings of parameters to their arguments, for |
| the purpose of compile time evaluation. */ |
| |
| typedef struct GTY(()) constexpr_call { |
| /* Description of the constexpr function definition. */ |
| constexpr_fundef *fundef; |
| /* Parameter bindings environment. A TREE_LIST where each TREE_PURPOSE |
| is a parameter _DECL and the TREE_VALUE is the value of the parameter. |
| Note: This arrangement is made to accomodate the use of |
| iterative_hash_template_arg (see pt.c). If you change this |
| representation, also change the hash calculation in |
| cxx_eval_call_expression. */ |
| tree bindings; |
| /* Result of the call. |
| NULL means the call is being evaluated. |
| error_mark_node means that the evaluation was erroneous; |
| otherwise, the actuall value of the call. */ |
| tree result; |
| /* The hash of this call; we remember it here to avoid having to |
| recalculate it when expanding the hash table. */ |
| hashval_t hash; |
| } constexpr_call; |
| |
| /* A table of all constexpr calls that have been evaluated by the |
| compiler in this translation unit. */ |
| |
| static GTY ((param_is (constexpr_call))) htab_t constexpr_call_table; |
| |
| static tree cxx_eval_constant_expression (const constexpr_call *, tree, |
| bool, bool, bool *, bool *); |
| |
| /* Compute a hash value for a constexpr call representation. */ |
| |
| static hashval_t |
| constexpr_call_hash (const void *p) |
| { |
| const constexpr_call *info = (const constexpr_call *) p; |
| return info->hash; |
| } |
| |
| /* Return 1 if the objects pointed to by P and Q represent calls |
| to the same constexpr function with the same arguments. |
| Otherwise, return 0. */ |
| |
| static int |
| constexpr_call_equal (const void *p, const void *q) |
| { |
| const constexpr_call *lhs = (const constexpr_call *) p; |
| const constexpr_call *rhs = (const constexpr_call *) q; |
| tree lhs_bindings; |
| tree rhs_bindings; |
| if (lhs == rhs) |
| return 1; |
| if (!constexpr_fundef_equal (lhs->fundef, rhs->fundef)) |
| return 0; |
| lhs_bindings = lhs->bindings; |
| rhs_bindings = rhs->bindings; |
| while (lhs_bindings != NULL && rhs_bindings != NULL) |
| { |
| tree lhs_arg = TREE_VALUE (lhs_bindings); |
| tree rhs_arg = TREE_VALUE (rhs_bindings); |
| gcc_assert (TREE_TYPE (lhs_arg) == TREE_TYPE (rhs_arg)); |
| if (!cp_tree_equal (lhs_arg, rhs_arg)) |
| return 0; |
| lhs_bindings = TREE_CHAIN (lhs_bindings); |
| rhs_bindings = TREE_CHAIN (rhs_bindings); |
| } |
| return lhs_bindings == rhs_bindings; |
| } |
| |
| /* Initialize the constexpr call table, if needed. */ |
| |
| static void |
| maybe_initialize_constexpr_call_table (void) |
| { |
| if (constexpr_call_table == NULL) |
| constexpr_call_table = htab_create_ggc (101, |
| constexpr_call_hash, |
| constexpr_call_equal, |
| ggc_free); |
| } |
| |
| /* Return true if T designates the implied `this' parameter. */ |
| |
| static inline bool |
| is_this_parameter (tree t) |
| { |
| return t == current_class_ptr; |
| } |
| |
| /* We have an expression tree T that represents a call, either CALL_EXPR |
| or AGGR_INIT_EXPR. If the call is lexically to a named function, |
| retrun the _DECL for that function. */ |
| |
| static tree |
| get_function_named_in_call (tree t) |
| { |
| tree fun = NULL; |
| switch (TREE_CODE (t)) |
| { |
| case CALL_EXPR: |
| fun = CALL_EXPR_FN (t); |
| break; |
| |
| case AGGR_INIT_EXPR: |
| fun = AGGR_INIT_EXPR_FN (t); |
| break; |
| |
| default: |
| gcc_unreachable(); |
| break; |
| } |
| if (TREE_CODE (fun) == ADDR_EXPR |
| && TREE_CODE (TREE_OPERAND (fun, 0)) == FUNCTION_DECL) |
| fun = TREE_OPERAND (fun, 0); |
| return fun; |
| } |
| |
| /* We have an expression tree T that represents a call, either CALL_EXPR |
| or AGGR_INIT_EXPR. Return the Nth argument. */ |
| |
| static inline tree |
| get_nth_callarg (tree t, int n) |
| { |
| switch (TREE_CODE (t)) |
| { |
| case CALL_EXPR: |
| return CALL_EXPR_ARG (t, n); |
| |
| case AGGR_INIT_EXPR: |
| return AGGR_INIT_EXPR_ARG (t, n); |
| |
| default: |
| gcc_unreachable (); |
| return NULL; |
| } |
| } |
| |
| /* Look up the binding of the function parameter T in a constexpr |
| function call context CALL. */ |
| |
| static tree |
| lookup_parameter_binding (const constexpr_call *call, tree t) |
| { |
| tree b = purpose_member (t, call->bindings); |
| return TREE_VALUE (b); |
| } |
| |
| /* Attempt to evaluate T which represents a call to a builtin function. |
| We assume here that all builtin functions evaluate to scalar types |
| represented by _CST nodes. */ |
| |
| static tree |
| cxx_eval_builtin_function_call (const constexpr_call *call, tree t, |
| bool allow_non_constant, bool addr, |
| bool *non_constant_p, bool *overflow_p) |
| { |
| const int nargs = call_expr_nargs (t); |
| tree *args = (tree *) alloca (nargs * sizeof (tree)); |
| tree new_call; |
| int i; |
| for (i = 0; i < nargs; ++i) |
| { |
| args[i] = cxx_eval_constant_expression (call, CALL_EXPR_ARG (t, i), |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| if (allow_non_constant && *non_constant_p) |
| return t; |
| } |
| if (*non_constant_p) |
| return t; |
| new_call = build_call_array_loc (EXPR_LOCATION (t), TREE_TYPE (t), |
| CALL_EXPR_FN (t), nargs, args); |
| new_call = fold (new_call); |
| VERIFY_CONSTANT (new_call); |
| return new_call; |
| } |
| |
| /* TEMP is the constant value of a temporary object of type TYPE. Adjust |
| the type of the value to match. */ |
| |
| static tree |
| adjust_temp_type (tree type, tree temp) |
| { |
| if (TREE_TYPE (temp) == type) |
| return temp; |
| /* Avoid wrapping an aggregate value in a NOP_EXPR. */ |
| if (TREE_CODE (temp) == CONSTRUCTOR) |
| return build_constructor (type, CONSTRUCTOR_ELTS (temp)); |
| gcc_assert (scalarish_type_p (type)); |
| return cp_fold_convert (type, temp); |
| } |
| |
| /* Subroutine of cxx_eval_call_expression. |
| We are processing a call expression (either CALL_EXPR or |
| AGGR_INIT_EXPR) in the call context of OLD_CALL. Evaluate |
| all arguments and bind their values to correspondings |
| parameters, making up the NEW_CALL context. */ |
| |
| static void |
| cxx_bind_parameters_in_call (const constexpr_call *old_call, tree t, |
| constexpr_call *new_call, |
| bool allow_non_constant, |
| bool *non_constant_p, bool *overflow_p) |
| { |
| const int nargs = call_expr_nargs (t); |
| tree fun = new_call->fundef->decl; |
| tree parms = DECL_ARGUMENTS (fun); |
| int i; |
| for (i = 0; i < nargs; ++i) |
| { |
| tree x, arg; |
| tree type = parms ? TREE_TYPE (parms) : void_type_node; |
| /* For member function, the first argument is a pointer to the implied |
| object. And for an object contruction, don't bind `this' before |
| it is fully constructed. */ |
| if (i == 0 && DECL_CONSTRUCTOR_P (fun)) |
| goto next; |
| x = get_nth_callarg (t, i); |
| if (parms && DECL_BY_REFERENCE (parms)) |
| { |
| /* cp_genericize made this a reference for argument passing, but |
| we don't want to treat it like one for constexpr evaluation. */ |
| gcc_assert (TREE_CODE (type) == REFERENCE_TYPE); |
| gcc_assert (TREE_CODE (TREE_TYPE (x)) == REFERENCE_TYPE); |
| type = TREE_TYPE (type); |
| x = convert_from_reference (x); |
| } |
| arg = cxx_eval_constant_expression (old_call, x, allow_non_constant, |
| TREE_CODE (type) == REFERENCE_TYPE, |
| non_constant_p, overflow_p); |
| /* Don't VERIFY_CONSTANT here. */ |
| if (*non_constant_p && allow_non_constant) |
| return; |
| /* Just discard ellipsis args after checking their constantitude. */ |
| if (!parms) |
| continue; |
| if (*non_constant_p) |
| /* Don't try to adjust the type of non-constant args. */ |
| goto next; |
| |
| /* Make sure the binding has the same type as the parm. */ |
| if (TREE_CODE (type) != REFERENCE_TYPE) |
| arg = adjust_temp_type (type, arg); |
| new_call->bindings = tree_cons (parms, arg, new_call->bindings); |
| next: |
| parms = TREE_CHAIN (parms); |
| } |
| } |
| |
| /* Variables and functions to manage constexpr call expansion context. |
| These do not need to be marked for PCH or GC. */ |
| |
| /* FIXME remember and print actual constant arguments. */ |
| static vec<tree> call_stack = vNULL; |
| static int call_stack_tick; |
| static int last_cx_error_tick; |
| |
| static bool |
| push_cx_call_context (tree call) |
| { |
| ++call_stack_tick; |
| if (!EXPR_HAS_LOCATION (call)) |
| SET_EXPR_LOCATION (call, input_location); |
| call_stack.safe_push (call); |
| if (call_stack.length () > (unsigned) max_constexpr_depth) |
| return false; |
| return true; |
| } |
| |
| static void |
| pop_cx_call_context (void) |
| { |
| ++call_stack_tick; |
| call_stack.pop (); |
| } |
| |
| vec<tree> |
| cx_error_context (void) |
| { |
| vec<tree> r = vNULL; |
| if (call_stack_tick != last_cx_error_tick |
| && !call_stack.is_empty ()) |
| r = call_stack; |
| last_cx_error_tick = call_stack_tick; |
| return r; |
| } |
| |
| /* Subroutine of cxx_eval_constant_expression. |
| Evaluate the call expression tree T in the context of OLD_CALL expression |
| evaluation. */ |
| |
| static tree |
| cxx_eval_call_expression (const constexpr_call *old_call, tree t, |
| bool allow_non_constant, bool addr, |
| bool *non_constant_p, bool *overflow_p) |
| { |
| location_t loc = EXPR_LOC_OR_HERE (t); |
| tree fun = get_function_named_in_call (t); |
| tree result; |
| constexpr_call new_call = { NULL, NULL, NULL, 0 }; |
| constexpr_call **slot; |
| constexpr_call *entry; |
| bool depth_ok; |
| |
| if (TREE_CODE (fun) != FUNCTION_DECL) |
| { |
| /* Might be a constexpr function pointer. */ |
| fun = cxx_eval_constant_expression (old_call, fun, allow_non_constant, |
| /*addr*/false, non_constant_p, overflow_p); |
| if (TREE_CODE (fun) == ADDR_EXPR) |
| fun = TREE_OPERAND (fun, 0); |
| } |
| if (TREE_CODE (fun) != FUNCTION_DECL) |
| { |
| if (!allow_non_constant && !*non_constant_p) |
| error_at (loc, "expression %qE does not designate a constexpr " |
| "function", fun); |
| *non_constant_p = true; |
| return t; |
| } |
| if (DECL_CLONED_FUNCTION_P (fun)) |
| fun = DECL_CLONED_FUNCTION (fun); |
| if (is_builtin_fn (fun)) |
| return cxx_eval_builtin_function_call (old_call, t, allow_non_constant, |
| addr, non_constant_p, overflow_p); |
| if (!DECL_DECLARED_CONSTEXPR_P (fun)) |
| { |
| if (!allow_non_constant) |
| { |
| error_at (loc, "call to non-constexpr function %qD", fun); |
| explain_invalid_constexpr_fn (fun); |
| } |
| *non_constant_p = true; |
| return t; |
| } |
| |
| /* Shortcut trivial copy constructor/op=. */ |
| if (call_expr_nargs (t) == 2 && trivial_fn_p (fun)) |
| { |
| tree arg = convert_from_reference (get_nth_callarg (t, 1)); |
| return cxx_eval_constant_expression (old_call, arg, allow_non_constant, |
| addr, non_constant_p, overflow_p); |
| } |
| |
| /* If in direct recursive call, optimize definition search. */ |
| if (old_call != NULL && old_call->fundef->decl == fun) |
| new_call.fundef = old_call->fundef; |
| else |
| { |
| new_call.fundef = retrieve_constexpr_fundef (fun); |
| if (new_call.fundef == NULL || new_call.fundef->body == NULL) |
| { |
| if (!allow_non_constant) |
| { |
| if (DECL_INITIAL (fun)) |
| { |
| /* The definition of fun was somehow unsuitable. */ |
| error_at (loc, "%qD called in a constant expression", fun); |
| explain_invalid_constexpr_fn (fun); |
| } |
| else |
| error_at (loc, "%qD used before its definition", fun); |
| } |
| *non_constant_p = true; |
| return t; |
| } |
| } |
| cxx_bind_parameters_in_call (old_call, t, &new_call, |
| allow_non_constant, non_constant_p, overflow_p); |
| if (*non_constant_p) |
| return t; |
| |
| depth_ok = push_cx_call_context (t); |
| |
| new_call.hash |
| = iterative_hash_template_arg (new_call.bindings, |
| constexpr_fundef_hash (new_call.fundef)); |
| |
| /* If we have seen this call before, we are done. */ |
| maybe_initialize_constexpr_call_table (); |
| slot = (constexpr_call **) |
| htab_find_slot (constexpr_call_table, &new_call, INSERT); |
| entry = *slot; |
| if (entry == NULL) |
| { |
| /* We need to keep a pointer to the entry, not just the slot, as the |
| slot can move in the call to cxx_eval_builtin_function_call. */ |
| *slot = entry = ggc_alloc_constexpr_call (); |
| *entry = new_call; |
| } |
| /* Calls which are in progress have their result set to NULL |
| so that we can detect circular dependencies. */ |
| else if (entry->result == NULL) |
| { |
| if (!allow_non_constant) |
| error ("call has circular dependency"); |
| *non_constant_p = true; |
| entry->result = result = error_mark_node; |
| } |
| |
| if (!depth_ok) |
| { |
| if (!allow_non_constant) |
| error ("constexpr evaluation depth exceeds maximum of %d (use " |
| "-fconstexpr-depth= to increase the maximum)", |
| max_constexpr_depth); |
| *non_constant_p = true; |
| entry->result = result = error_mark_node; |
| } |
| else |
| { |
| result = entry->result; |
| if (!result || result == error_mark_node) |
| result = (cxx_eval_constant_expression |
| (&new_call, new_call.fundef->body, |
| allow_non_constant, addr, |
| non_constant_p, overflow_p)); |
| if (result == error_mark_node) |
| *non_constant_p = true; |
| if (*non_constant_p) |
| entry->result = result = error_mark_node; |
| else |
| { |
| /* If this was a call to initialize an object, set the type of |
| the CONSTRUCTOR to the type of that object. */ |
| if (DECL_CONSTRUCTOR_P (fun)) |
| { |
| tree ob_arg = get_nth_callarg (t, 0); |
| STRIP_NOPS (ob_arg); |
| gcc_assert (TREE_CODE (TREE_TYPE (ob_arg)) == POINTER_TYPE |
| && CLASS_TYPE_P (TREE_TYPE (TREE_TYPE (ob_arg)))); |
| result = adjust_temp_type (TREE_TYPE (TREE_TYPE (ob_arg)), |
| result); |
| } |
| entry->result = result; |
| } |
| } |
| |
| pop_cx_call_context (); |
| return unshare_expr (result); |
| } |
| |
| /* FIXME speed this up, it's taking 16% of compile time on sieve testcase. */ |
| |
| bool |
| reduced_constant_expression_p (tree t) |
| { |
| /* FIXME are we calling this too much? */ |
| return initializer_constant_valid_p (t, TREE_TYPE (t)) != NULL_TREE; |
| } |
| |
| /* Some expressions may have constant operands but are not constant |
| themselves, such as 1/0. Call this function (or rather, the macro |
| following it) to check for that condition. |
| |
| We only call this in places that require an arithmetic constant, not in |
| places where we might have a non-constant expression that can be a |
| component of a constant expression, such as the address of a constexpr |
| variable that might be dereferenced later. */ |
| |
| static bool |
| verify_constant (tree t, bool allow_non_constant, bool *non_constant_p, |
| bool *overflow_p) |
| { |
| if (!*non_constant_p && !reduced_constant_expression_p (t)) |
| { |
| if (!allow_non_constant) |
| error ("%q+E is not a constant expression", t); |
| *non_constant_p = true; |
| } |
| if (TREE_OVERFLOW_P (t)) |
| { |
| if (!allow_non_constant) |
| { |
| permerror (input_location, "overflow in constant expression"); |
| /* If we're being permissive (and are in an enforcing |
| context), ignore the overflow. */ |
| if (flag_permissive) |
| return *non_constant_p; |
| } |
| *overflow_p = true; |
| } |
| return *non_constant_p; |
| } |
| |
| /* Subroutine of cxx_eval_constant_expression. |
| Attempt to reduce the unary expression tree T to a compile time value. |
| If successful, return the value. Otherwise issue a diagnostic |
| and return error_mark_node. */ |
| |
| static tree |
| cxx_eval_unary_expression (const constexpr_call *call, tree t, |
| bool allow_non_constant, bool addr, |
| bool *non_constant_p, bool *overflow_p) |
| { |
| tree r; |
| tree orig_arg = TREE_OPERAND (t, 0); |
| tree arg = cxx_eval_constant_expression (call, orig_arg, allow_non_constant, |
| addr, non_constant_p, overflow_p); |
| VERIFY_CONSTANT (arg); |
| if (arg == orig_arg) |
| return t; |
| r = fold_build1 (TREE_CODE (t), TREE_TYPE (t), arg); |
| VERIFY_CONSTANT (r); |
| return r; |
| } |
| |
| /* Subroutine of cxx_eval_constant_expression. |
| Like cxx_eval_unary_expression, except for binary expressions. */ |
| |
| static tree |
| cxx_eval_binary_expression (const constexpr_call *call, tree t, |
| bool allow_non_constant, bool addr, |
| bool *non_constant_p, bool *overflow_p) |
| { |
| tree r; |
| tree orig_lhs = TREE_OPERAND (t, 0); |
| tree orig_rhs = TREE_OPERAND (t, 1); |
| tree lhs, rhs; |
| lhs = cxx_eval_constant_expression (call, orig_lhs, |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| VERIFY_CONSTANT (lhs); |
| rhs = cxx_eval_constant_expression (call, orig_rhs, |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| VERIFY_CONSTANT (rhs); |
| if (lhs == orig_lhs && rhs == orig_rhs) |
| return t; |
| r = fold_build2 (TREE_CODE (t), TREE_TYPE (t), lhs, rhs); |
| VERIFY_CONSTANT (r); |
| return r; |
| } |
| |
| /* Subroutine of cxx_eval_constant_expression. |
| Attempt to evaluate condition expressions. Dead branches are not |
| looked into. */ |
| |
| static tree |
| cxx_eval_conditional_expression (const constexpr_call *call, tree t, |
| bool allow_non_constant, bool addr, |
| bool *non_constant_p, bool *overflow_p) |
| { |
| tree val = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0), |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| VERIFY_CONSTANT (val); |
| /* Don't VERIFY_CONSTANT the other operands. */ |
| if (integer_zerop (val)) |
| return cxx_eval_constant_expression (call, TREE_OPERAND (t, 2), |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| return cxx_eval_constant_expression (call, TREE_OPERAND (t, 1), |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| } |
| |
| /* Subroutine of cxx_eval_constant_expression. |
| Attempt to reduce a reference to an array slot. */ |
| |
| static tree |
| cxx_eval_array_reference (const constexpr_call *call, tree t, |
| bool allow_non_constant, bool addr, |
| bool *non_constant_p, bool *overflow_p) |
| { |
| tree oldary = TREE_OPERAND (t, 0); |
| tree ary = cxx_eval_constant_expression (call, oldary, |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| tree index, oldidx; |
| HOST_WIDE_INT i; |
| tree elem_type; |
| unsigned len, elem_nchars = 1; |
| if (*non_constant_p) |
| return t; |
| oldidx = TREE_OPERAND (t, 1); |
| index = cxx_eval_constant_expression (call, oldidx, |
| allow_non_constant, false, |
| non_constant_p, overflow_p); |
| VERIFY_CONSTANT (index); |
| if (addr && ary == oldary && index == oldidx) |
| return t; |
| else if (addr) |
| return build4 (ARRAY_REF, TREE_TYPE (t), ary, index, NULL, NULL); |
| elem_type = TREE_TYPE (TREE_TYPE (ary)); |
| if (TREE_CODE (ary) == CONSTRUCTOR) |
| len = CONSTRUCTOR_NELTS (ary); |
| else if (TREE_CODE (ary) == STRING_CST) |
| { |
| elem_nchars = (TYPE_PRECISION (elem_type) |
| / TYPE_PRECISION (char_type_node)); |
| len = (unsigned) TREE_STRING_LENGTH (ary) / elem_nchars; |
| } |
| else |
| { |
| /* We can't do anything with other tree codes, so use |
| VERIFY_CONSTANT to complain and fail. */ |
| VERIFY_CONSTANT (ary); |
| gcc_unreachable (); |
| } |
| if (compare_tree_int (index, len) >= 0) |
| { |
| if (tree_int_cst_lt (index, array_type_nelts_top (TREE_TYPE (ary)))) |
| { |
| /* If it's within the array bounds but doesn't have an explicit |
| initializer, it's value-initialized. */ |
| tree val = build_value_init (elem_type, tf_warning_or_error); |
| return cxx_eval_constant_expression (call, val, |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| } |
| |
| if (!allow_non_constant) |
| error ("array subscript out of bound"); |
| *non_constant_p = true; |
| return t; |
| } |
| else if (tree_int_cst_lt (index, integer_zero_node)) |
| { |
| if (!allow_non_constant) |
| error ("negative array subscript"); |
| *non_constant_p = true; |
| return t; |
| } |
| i = tree_low_cst (index, 0); |
| if (TREE_CODE (ary) == CONSTRUCTOR) |
| return (*CONSTRUCTOR_ELTS (ary))[i].value; |
| else if (elem_nchars == 1) |
| return build_int_cst (cv_unqualified (TREE_TYPE (TREE_TYPE (ary))), |
| TREE_STRING_POINTER (ary)[i]); |
| else |
| { |
| tree type = cv_unqualified (TREE_TYPE (TREE_TYPE (ary))); |
| return native_interpret_expr (type, (const unsigned char *) |
| TREE_STRING_POINTER (ary) |
| + i * elem_nchars, elem_nchars); |
| } |
| /* Don't VERIFY_CONSTANT here. */ |
| } |
| |
| /* Subroutine of cxx_eval_constant_expression. |
| Attempt to reduce a field access of a value of class type. */ |
| |
| static tree |
| cxx_eval_component_reference (const constexpr_call *call, tree t, |
| bool allow_non_constant, bool addr, |
| bool *non_constant_p, bool *overflow_p) |
| { |
| unsigned HOST_WIDE_INT i; |
| tree field; |
| tree value; |
| tree part = TREE_OPERAND (t, 1); |
| tree orig_whole = TREE_OPERAND (t, 0); |
| tree whole = cxx_eval_constant_expression (call, orig_whole, |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| if (whole == orig_whole) |
| return t; |
| if (addr) |
| return fold_build3 (COMPONENT_REF, TREE_TYPE (t), |
| whole, part, NULL_TREE); |
| /* Don't VERIFY_CONSTANT here; we only want to check that we got a |
| CONSTRUCTOR. */ |
| if (!*non_constant_p && TREE_CODE (whole) != CONSTRUCTOR) |
| { |
| if (!allow_non_constant) |
| error ("%qE is not a constant expression", orig_whole); |
| *non_constant_p = true; |
| } |
| if (DECL_MUTABLE_P (part)) |
| { |
| if (!allow_non_constant) |
| error ("mutable %qD is not usable in a constant expression", part); |
| *non_constant_p = true; |
| } |
| if (*non_constant_p) |
| return t; |
| FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (whole), i, field, value) |
| { |
| if (field == part) |
| return value; |
| } |
| if (TREE_CODE (TREE_TYPE (whole)) == UNION_TYPE |
| && CONSTRUCTOR_NELTS (whole) > 0) |
| { |
| /* DR 1188 says we don't have to deal with this. */ |
| if (!allow_non_constant) |
| error ("accessing %qD member instead of initialized %qD member in " |
| "constant expression", part, CONSTRUCTOR_ELT (whole, 0)->index); |
| *non_constant_p = true; |
| return t; |
| } |
| |
| /* If there's no explicit init for this field, it's value-initialized. */ |
| value = build_value_init (TREE_TYPE (t), tf_warning_or_error); |
| return cxx_eval_constant_expression (call, value, |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| } |
| |
| /* Subroutine of cxx_eval_constant_expression. |
| Attempt to reduce a field access of a value of class type that is |
| expressed as a BIT_FIELD_REF. */ |
| |
| static tree |
| cxx_eval_bit_field_ref (const constexpr_call *call, tree t, |
| bool allow_non_constant, bool addr, |
| bool *non_constant_p, bool *overflow_p) |
| { |
| tree orig_whole = TREE_OPERAND (t, 0); |
| tree retval, fldval, utype, mask; |
| bool fld_seen = false; |
| HOST_WIDE_INT istart, isize; |
| tree whole = cxx_eval_constant_expression (call, orig_whole, |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| tree start, field, value; |
| unsigned HOST_WIDE_INT i; |
| |
| if (whole == orig_whole) |
| return t; |
| /* Don't VERIFY_CONSTANT here; we only want to check that we got a |
| CONSTRUCTOR. */ |
| if (!*non_constant_p && TREE_CODE (whole) != CONSTRUCTOR) |
| { |
| if (!allow_non_constant) |
| error ("%qE is not a constant expression", orig_whole); |
| *non_constant_p = true; |
| } |
| if (*non_constant_p) |
| return t; |
| |
| start = TREE_OPERAND (t, 2); |
| istart = tree_low_cst (start, 0); |
| isize = tree_low_cst (TREE_OPERAND (t, 1), 0); |
| utype = TREE_TYPE (t); |
| if (!TYPE_UNSIGNED (utype)) |
| utype = build_nonstandard_integer_type (TYPE_PRECISION (utype), 1); |
| retval = build_int_cst (utype, 0); |
| FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (whole), i, field, value) |
| { |
| tree bitpos = bit_position (field); |
| if (bitpos == start && DECL_SIZE (field) == TREE_OPERAND (t, 1)) |
| return value; |
| if (TREE_CODE (TREE_TYPE (field)) == INTEGER_TYPE |
| && TREE_CODE (value) == INTEGER_CST |
| && host_integerp (bitpos, 0) |
| && host_integerp (DECL_SIZE (field), 0)) |
| { |
| HOST_WIDE_INT bit = tree_low_cst (bitpos, 0); |
| HOST_WIDE_INT sz = tree_low_cst (DECL_SIZE (field), 0); |
| HOST_WIDE_INT shift; |
| if (bit >= istart && bit + sz <= istart + isize) |
| { |
| fldval = fold_convert (utype, value); |
| mask = build_int_cst_type (utype, -1); |
| mask = fold_build2 (LSHIFT_EXPR, utype, mask, |
| size_int (TYPE_PRECISION (utype) - sz)); |
| mask = fold_build2 (RSHIFT_EXPR, utype, mask, |
| size_int (TYPE_PRECISION (utype) - sz)); |
| fldval = fold_build2 (BIT_AND_EXPR, utype, fldval, mask); |
| shift = bit - istart; |
| if (BYTES_BIG_ENDIAN) |
| shift = TYPE_PRECISION (utype) - shift - sz; |
| fldval = fold_build2 (LSHIFT_EXPR, utype, fldval, |
| size_int (shift)); |
| retval = fold_build2 (BIT_IOR_EXPR, utype, retval, fldval); |
| fld_seen = true; |
| } |
| } |
| } |
| if (fld_seen) |
| return fold_convert (TREE_TYPE (t), retval); |
| gcc_unreachable (); |
| return error_mark_node; |
| } |
| |
| /* Subroutine of cxx_eval_constant_expression. |
| Evaluate a short-circuited logical expression T in the context |
| of a given constexpr CALL. BAILOUT_VALUE is the value for |
| early return. CONTINUE_VALUE is used here purely for |
| sanity check purposes. */ |
| |
| static tree |
| cxx_eval_logical_expression (const constexpr_call *call, tree t, |
| tree bailout_value, tree continue_value, |
| bool allow_non_constant, bool addr, |
| bool *non_constant_p, bool *overflow_p) |
| { |
| tree r; |
| tree lhs = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0), |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| VERIFY_CONSTANT (lhs); |
| if (tree_int_cst_equal (lhs, bailout_value)) |
| return lhs; |
| gcc_assert (tree_int_cst_equal (lhs, continue_value)); |
| r = cxx_eval_constant_expression (call, TREE_OPERAND (t, 1), |
| allow_non_constant, addr, non_constant_p, overflow_p); |
| VERIFY_CONSTANT (r); |
| return r; |
| } |
| |
| /* REF is a COMPONENT_REF designating a particular field. V is a vector of |
| CONSTRUCTOR elements to initialize (part of) an object containing that |
| field. Return a pointer to the constructor_elt corresponding to the |
| initialization of the field. */ |
| |
| static constructor_elt * |
| base_field_constructor_elt (vec<constructor_elt, va_gc> *v, tree ref) |
| { |
| tree aggr = TREE_OPERAND (ref, 0); |
| tree field = TREE_OPERAND (ref, 1); |
| HOST_WIDE_INT i; |
| constructor_elt *ce; |
| |
| gcc_assert (TREE_CODE (ref) == COMPONENT_REF); |
| |
| if (TREE_CODE (aggr) == COMPONENT_REF) |
| { |
| constructor_elt *base_ce |
| = base_field_constructor_elt (v, aggr); |
| v = CONSTRUCTOR_ELTS (base_ce->value); |
| } |
| |
| for (i = 0; vec_safe_iterate (v, i, &ce); ++i) |
| if (ce->index == field) |
| return ce; |
| |
| gcc_unreachable (); |
| return NULL; |
| } |
| |
| /* Subroutine of cxx_eval_constant_expression. |
| The expression tree T denotes a C-style array or a C-style |
| aggregate. Reduce it to a constant expression. */ |
| |
| static tree |
| cxx_eval_bare_aggregate (const constexpr_call *call, tree t, |
| bool allow_non_constant, bool addr, |
| bool *non_constant_p, bool *overflow_p) |
| { |
| vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (t); |
| vec<constructor_elt, va_gc> *n; |
| vec_alloc (n, vec_safe_length (v)); |
| constructor_elt *ce; |
| HOST_WIDE_INT i; |
| bool changed = false; |
| gcc_assert (!BRACE_ENCLOSED_INITIALIZER_P (t)); |
| for (i = 0; vec_safe_iterate (v, i, &ce); ++i) |
| { |
| tree elt = cxx_eval_constant_expression (call, ce->value, |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| /* Don't VERIFY_CONSTANT here. */ |
| if (allow_non_constant && *non_constant_p) |
| goto fail; |
| if (elt != ce->value) |
| changed = true; |
| if (ce->index && TREE_CODE (ce->index) == COMPONENT_REF) |
| { |
| /* This is an initialization of a vfield inside a base |
| subaggregate that we already initialized; push this |
| initialization into the previous initialization. */ |
| constructor_elt *inner = base_field_constructor_elt (n, ce->index); |
| inner->value = elt; |
| } |
| else if (ce->index |
| && (TREE_CODE (ce->index) == NOP_EXPR |
| || TREE_CODE (ce->index) == POINTER_PLUS_EXPR)) |
| { |
| /* This is an initializer for an empty base; now that we've |
| checked that it's constant, we can ignore it. */ |
| gcc_assert (is_empty_class (TREE_TYPE (TREE_TYPE (ce->index)))); |
| } |
| else |
| CONSTRUCTOR_APPEND_ELT (n, ce->index, elt); |
| } |
| if (*non_constant_p || !changed) |
| { |
| fail: |
| vec_free (n); |
| return t; |
| } |
| t = build_constructor (TREE_TYPE (t), n); |
| TREE_CONSTANT (t) = true; |
| if (TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE) |
| t = fold (t); |
| return t; |
| } |
| |
| /* Subroutine of cxx_eval_constant_expression. |
| The expression tree T is a VEC_INIT_EXPR which denotes the desired |
| initialization of a non-static data member of array type. Reduce it to a |
| CONSTRUCTOR. |
| |
| Note that apart from value-initialization (when VALUE_INIT is true), |
| this is only intended to support value-initialization and the |
| initializations done by defaulted constructors for classes with |
| non-static data members of array type. In this case, VEC_INIT_EXPR_INIT |
| will either be NULL_TREE for the default constructor, or a COMPONENT_REF |
| for the copy/move constructor. */ |
| |
| static tree |
| cxx_eval_vec_init_1 (const constexpr_call *call, tree atype, tree init, |
| bool value_init, bool allow_non_constant, bool addr, |
| bool *non_constant_p, bool *overflow_p) |
| { |
| tree elttype = TREE_TYPE (atype); |
| int max = tree_low_cst (array_type_nelts (atype), 0); |
| vec<constructor_elt, va_gc> *n; |
| vec_alloc (n, max + 1); |
| bool pre_init = false; |
| int i; |
| |
| /* For the default constructor, build up a call to the default |
| constructor of the element type. We only need to handle class types |
| here, as for a constructor to be constexpr, all members must be |
| initialized, which for a defaulted default constructor means they must |
| be of a class type with a constexpr default constructor. */ |
| if (TREE_CODE (elttype) == ARRAY_TYPE) |
| /* We only do this at the lowest level. */; |
| else if (value_init) |
| { |
| init = build_value_init (elttype, tf_warning_or_error); |
| init = cxx_eval_constant_expression |
| (call, init, allow_non_constant, addr, non_constant_p, overflow_p); |
| pre_init = true; |
| } |
| else if (!init) |
| { |
| vec<tree, va_gc> *argvec = make_tree_vector (); |
| init = build_special_member_call (NULL_TREE, complete_ctor_identifier, |
| &argvec, elttype, LOOKUP_NORMAL, |
| tf_warning_or_error); |
| release_tree_vector (argvec); |
| init = cxx_eval_constant_expression (call, init, allow_non_constant, |
| addr, non_constant_p, overflow_p); |
| pre_init = true; |
| } |
| |
| if (*non_constant_p && !allow_non_constant) |
| goto fail; |
| |
| for (i = 0; i <= max; ++i) |
| { |
| tree idx = build_int_cst (size_type_node, i); |
| tree eltinit; |
| if (TREE_CODE (elttype) == ARRAY_TYPE) |
| { |
| /* A multidimensional array; recurse. */ |
| if (value_init || init == NULL_TREE) |
| eltinit = NULL_TREE; |
| else |
| eltinit = cp_build_array_ref (input_location, init, idx, |
| tf_warning_or_error); |
| eltinit = cxx_eval_vec_init_1 (call, elttype, eltinit, value_init, |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| } |
| else if (pre_init) |
| { |
| /* Initializing an element using value or default initialization |
| we just pre-built above. */ |
| if (i == 0) |
| eltinit = init; |
| else |
| eltinit = unshare_expr (init); |
| } |
| else |
| { |
| /* Copying an element. */ |
| vec<tree, va_gc> *argvec; |
| gcc_assert (same_type_ignoring_top_level_qualifiers_p |
| (atype, TREE_TYPE (init))); |
| eltinit = cp_build_array_ref (input_location, init, idx, |
| tf_warning_or_error); |
| if (!real_lvalue_p (init)) |
| eltinit = move (eltinit); |
| argvec = make_tree_vector (); |
| argvec->quick_push (eltinit); |
| eltinit = (build_special_member_call |
| (NULL_TREE, complete_ctor_identifier, &argvec, |
| elttype, LOOKUP_NORMAL, tf_warning_or_error)); |
| release_tree_vector (argvec); |
| eltinit = cxx_eval_constant_expression |
| (call, eltinit, allow_non_constant, addr, non_constant_p, overflow_p); |
| } |
| if (*non_constant_p && !allow_non_constant) |
| goto fail; |
| CONSTRUCTOR_APPEND_ELT (n, idx, eltinit); |
| } |
| |
| if (!*non_constant_p) |
| { |
| init = build_constructor (atype, n); |
| TREE_CONSTANT (init) = true; |
| return init; |
| } |
| |
| fail: |
| vec_free (n); |
| return init; |
| } |
| |
| static tree |
| cxx_eval_vec_init (const constexpr_call *call, tree t, |
| bool allow_non_constant, bool addr, |
| bool *non_constant_p, bool *overflow_p) |
| { |
| tree atype = TREE_TYPE (t); |
| tree init = VEC_INIT_EXPR_INIT (t); |
| tree r = cxx_eval_vec_init_1 (call, atype, init, |
| VEC_INIT_EXPR_VALUE_INIT (t), |
| allow_non_constant, addr, non_constant_p, overflow_p); |
| if (*non_constant_p) |
| return t; |
| else |
| return r; |
| } |
| |
| /* A less strict version of fold_indirect_ref_1, which requires cv-quals to |
| match. We want to be less strict for simple *& folding; if we have a |
| non-const temporary that we access through a const pointer, that should |
| work. We handle this here rather than change fold_indirect_ref_1 |
| because we're dealing with things like ADDR_EXPR of INTEGER_CST which |
| don't really make sense outside of constant expression evaluation. Also |
| we want to allow folding to COMPONENT_REF, which could cause trouble |
| with TBAA in fold_indirect_ref_1. |
| |
| Try to keep this function synced with fold_indirect_ref_1. */ |
| |
| static tree |
| cxx_fold_indirect_ref (location_t loc, tree type, tree op0, bool *empty_base) |
| { |
| tree sub, subtype; |
| |
| sub = op0; |
| STRIP_NOPS (sub); |
| subtype = TREE_TYPE (sub); |
| if (!POINTER_TYPE_P (subtype)) |
| return NULL_TREE; |
| |
| if (TREE_CODE (sub) == ADDR_EXPR) |
| { |
| tree op = TREE_OPERAND (sub, 0); |
| tree optype = TREE_TYPE (op); |
| |
| /* *&CONST_DECL -> to the value of the const decl. */ |
| if (TREE_CODE (op) == CONST_DECL) |
| return DECL_INITIAL (op); |
| /* *&p => p; make sure to handle *&"str"[cst] here. */ |
| if (same_type_ignoring_top_level_qualifiers_p (optype, type)) |
| { |
| tree fop = fold_read_from_constant_string (op); |
| if (fop) |
| return fop; |
| else |
| return op; |
| } |
| /* *(foo *)&fooarray => fooarray[0] */ |
| else if (TREE_CODE (optype) == ARRAY_TYPE |
| && (same_type_ignoring_top_level_qualifiers_p |
| (type, TREE_TYPE (optype)))) |
| { |
| tree type_domain = TYPE_DOMAIN (optype); |
| tree min_val = size_zero_node; |
| if (type_domain && TYPE_MIN_VALUE (type_domain)) |
| min_val = TYPE_MIN_VALUE (type_domain); |
| return build4_loc (loc, ARRAY_REF, type, op, min_val, |
| NULL_TREE, NULL_TREE); |
| } |
| /* *(foo *)&complexfoo => __real__ complexfoo */ |
| else if (TREE_CODE (optype) == COMPLEX_TYPE |
| && (same_type_ignoring_top_level_qualifiers_p |
| (type, TREE_TYPE (optype)))) |
| return fold_build1_loc (loc, REALPART_EXPR, type, op); |
| /* *(foo *)&vectorfoo => BIT_FIELD_REF<vectorfoo,...> */ |
| else if (TREE_CODE (optype) == VECTOR_TYPE |
| && (same_type_ignoring_top_level_qualifiers_p |
| (type, TREE_TYPE (optype)))) |
| { |
| tree part_width = TYPE_SIZE (type); |
| tree index = bitsize_int (0); |
| return fold_build3_loc (loc, BIT_FIELD_REF, type, op, part_width, index); |
| } |
| /* Also handle conversion to an empty base class, which |
| is represented with a NOP_EXPR. */ |
| else if (is_empty_class (type) |
| && CLASS_TYPE_P (optype) |
| && DERIVED_FROM_P (type, optype)) |
| { |
| *empty_base = true; |
| return op; |
| } |
| /* *(foo *)&struct_with_foo_field => COMPONENT_REF */ |
| else if (RECORD_OR_UNION_TYPE_P (optype)) |
| { |
| tree field = TYPE_FIELDS (optype); |
| for (; field; field = DECL_CHAIN (field)) |
| if (TREE_CODE (field) == FIELD_DECL |
| && integer_zerop (byte_position (field)) |
| && (same_type_ignoring_top_level_qualifiers_p |
| (TREE_TYPE (field), type))) |
| { |
| return fold_build3 (COMPONENT_REF, type, op, field, NULL_TREE); |
| break; |
| } |
| } |
| } |
| else if (TREE_CODE (sub) == POINTER_PLUS_EXPR |
| && TREE_CODE (TREE_OPERAND (sub, 1)) == INTEGER_CST) |
| { |
| tree op00 = TREE_OPERAND (sub, 0); |
| tree op01 = TREE_OPERAND (sub, 1); |
| |
| STRIP_NOPS (op00); |
| if (TREE_CODE (op00) == ADDR_EXPR) |
| { |
| tree op00type; |
| op00 = TREE_OPERAND (op00, 0); |
| op00type = TREE_TYPE (op00); |
| |
| /* ((foo*)&vectorfoo)[1] => BIT_FIELD_REF<vectorfoo,...> */ |
| if (TREE_CODE (op00type) == VECTOR_TYPE |
| && (same_type_ignoring_top_level_qualifiers_p |
| (type, TREE_TYPE (op00type)))) |
| { |
| HOST_WIDE_INT offset = tree_low_cst (op01, 0); |
| tree part_width = TYPE_SIZE (type); |
| unsigned HOST_WIDE_INT part_widthi = tree_low_cst (part_width, 0)/BITS_PER_UNIT; |
| unsigned HOST_WIDE_INT indexi = offset * BITS_PER_UNIT; |
| tree index = bitsize_int (indexi); |
| |
| if (offset / part_widthi < TYPE_VECTOR_SUBPARTS (op00type)) |
| return fold_build3_loc (loc, |
| BIT_FIELD_REF, type, op00, |
| part_width, index); |
| |
| } |
| /* ((foo*)&complexfoo)[1] => __imag__ complexfoo */ |
| else if (TREE_CODE (op00type) == COMPLEX_TYPE |
| && (same_type_ignoring_top_level_qualifiers_p |
| (type, TREE_TYPE (op00type)))) |
| { |
| tree size = TYPE_SIZE_UNIT (type); |
| if (tree_int_cst_equal (size, op01)) |
| return fold_build1_loc (loc, IMAGPART_EXPR, type, op00); |
| } |
| /* ((foo *)&fooarray)[1] => fooarray[1] */ |
| else if (TREE_CODE (op00type) == ARRAY_TYPE |
| && (same_type_ignoring_top_level_qualifiers_p |
| (type, TREE_TYPE (op00type)))) |
| { |
| tree type_domain = TYPE_DOMAIN (op00type); |
| tree min_val = size_zero_node; |
| if (type_domain && TYPE_MIN_VALUE (type_domain)) |
| min_val = TYPE_MIN_VALUE (type_domain); |
| op01 = size_binop_loc (loc, EXACT_DIV_EXPR, op01, |
| TYPE_SIZE_UNIT (type)); |
| op01 = size_binop_loc (loc, PLUS_EXPR, op01, min_val); |
| return build4_loc (loc, ARRAY_REF, type, op00, op01, |
| NULL_TREE, NULL_TREE); |
| } |
| /* Also handle conversion to an empty base class, which |
| is represented with a NOP_EXPR. */ |
| else if (is_empty_class (type) |
| && CLASS_TYPE_P (op00type) |
| && DERIVED_FROM_P (type, op00type)) |
| { |
| *empty_base = true; |
| return op00; |
| } |
| /* ((foo *)&struct_with_foo_field)[1] => COMPONENT_REF */ |
| else if (RECORD_OR_UNION_TYPE_P (op00type)) |
| { |
| tree field = TYPE_FIELDS (op00type); |
| for (; field; field = DECL_CHAIN (field)) |
| if (TREE_CODE (field) == FIELD_DECL |
| && tree_int_cst_equal (byte_position (field), op01) |
| && (same_type_ignoring_top_level_qualifiers_p |
| (TREE_TYPE (field), type))) |
| { |
| return fold_build3 (COMPONENT_REF, type, op00, |
| field, NULL_TREE); |
| break; |
| } |
| } |
| } |
| } |
| /* *(foo *)fooarrptr => (*fooarrptr)[0] */ |
| else if (TREE_CODE (TREE_TYPE (subtype)) == ARRAY_TYPE |
| && (same_type_ignoring_top_level_qualifiers_p |
| (type, TREE_TYPE (TREE_TYPE (subtype))))) |
| { |
| tree type_domain; |
| tree min_val = size_zero_node; |
| tree newsub = cxx_fold_indirect_ref (loc, TREE_TYPE (subtype), sub, NULL); |
| if (newsub) |
| sub = newsub; |
| else |
| sub = build1_loc (loc, INDIRECT_REF, TREE_TYPE (subtype), sub); |
| type_domain = TYPE_DOMAIN (TREE_TYPE (sub)); |
| if (type_domain && TYPE_MIN_VALUE (type_domain)) |
| min_val = TYPE_MIN_VALUE (type_domain); |
| return build4_loc (loc, ARRAY_REF, type, sub, min_val, NULL_TREE, |
| NULL_TREE); |
| } |
| |
| return NULL_TREE; |
| } |
| |
| static tree |
| cxx_eval_indirect_ref (const constexpr_call *call, tree t, |
| bool allow_non_constant, bool addr, |
| bool *non_constant_p, bool *overflow_p) |
| { |
| tree orig_op0 = TREE_OPERAND (t, 0); |
| tree op0 = cxx_eval_constant_expression (call, orig_op0, allow_non_constant, |
| /*addr*/false, non_constant_p, overflow_p); |
| bool empty_base = false; |
| tree r; |
| |
| /* Don't VERIFY_CONSTANT here. */ |
| if (*non_constant_p) |
| return t; |
| |
| r = cxx_fold_indirect_ref (EXPR_LOCATION (t), TREE_TYPE (t), op0, |
| &empty_base); |
| |
| if (r) |
| r = cxx_eval_constant_expression (call, r, allow_non_constant, |
| addr, non_constant_p, overflow_p); |
| else |
| { |
| tree sub = op0; |
| STRIP_NOPS (sub); |
| if (TREE_CODE (sub) == ADDR_EXPR) |
| { |
| /* We couldn't fold to a constant value. Make sure it's not |
| something we should have been able to fold. */ |
| gcc_assert (!same_type_ignoring_top_level_qualifiers_p |
| (TREE_TYPE (TREE_TYPE (sub)), TREE_TYPE (t))); |
| /* DR 1188 says we don't have to deal with this. */ |
| if (!allow_non_constant) |
| error ("accessing value of %qE through a %qT glvalue in a " |
| "constant expression", build_fold_indirect_ref (sub), |
| TREE_TYPE (t)); |
| *non_constant_p = true; |
| return t; |
| } |
| } |
| |
| /* If we're pulling out the value of an empty base, make sure |
| that the whole object is constant and then return an empty |
| CONSTRUCTOR. */ |
| if (empty_base) |
| { |
| VERIFY_CONSTANT (r); |
| r = build_constructor (TREE_TYPE (t), NULL); |
| TREE_CONSTANT (r) = true; |
| } |
| |
| if (r == NULL_TREE) |
| { |
| if (addr && op0 != orig_op0) |
| return build1 (INDIRECT_REF, TREE_TYPE (t), op0); |
| if (!addr) |
| VERIFY_CONSTANT (t); |
| return t; |
| } |
| return r; |
| } |
| |
| /* Complain about R, a VAR_DECL, not being usable in a constant expression. |
| Shared between potential_constant_expression and |
| cxx_eval_constant_expression. */ |
| |
| static void |
| non_const_var_error (tree r) |
| { |
| tree type = TREE_TYPE (r); |
| error ("the value of %qD is not usable in a constant " |
| "expression", r); |
| /* Avoid error cascade. */ |
| if (DECL_INITIAL (r) == error_mark_node) |
| return; |
| if (DECL_DECLARED_CONSTEXPR_P (r)) |
| inform (DECL_SOURCE_LOCATION (r), |
| "%qD used in its own initializer", r); |
| else if (INTEGRAL_OR_ENUMERATION_TYPE_P (type)) |
| { |
| if (!CP_TYPE_CONST_P (type)) |
| inform (DECL_SOURCE_LOCATION (r), |
| "%q#D is not const", r); |
| else if (CP_TYPE_VOLATILE_P (type)) |
| inform (DECL_SOURCE_LOCATION (r), |
| "%q#D is volatile", r); |
| else if (!DECL_INITIAL (r) |
| || !TREE_CONSTANT (DECL_INITIAL (r))) |
| inform (DECL_SOURCE_LOCATION (r), |
| "%qD was not initialized with a constant " |
| "expression", r); |
| else |
| gcc_unreachable (); |
| } |
| else |
| { |
| if (cxx_dialect >= cxx0x && !DECL_DECLARED_CONSTEXPR_P (r)) |
| inform (DECL_SOURCE_LOCATION (r), |
| "%qD was not declared %<constexpr%>", r); |
| else |
| inform (DECL_SOURCE_LOCATION (r), |
| "%qD does not have integral or enumeration type", |
| r); |
| } |
| } |
| |
| /* Evaluate VEC_PERM_EXPR (v1, v2, mask). */ |
| static tree |
| cxx_eval_vec_perm_expr (const constexpr_call *call, tree t, |
| bool allow_non_constant, bool addr, |
| bool *non_constant_p, bool *overflow_p) |
| { |
| int i; |
| tree args[3]; |
| tree val; |
| tree elttype = TREE_TYPE (t); |
| |
| for (i = 0; i < 3; i++) |
| { |
| args[i] = cxx_eval_constant_expression (call, TREE_OPERAND (t, i), |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| if (*non_constant_p) |
| goto fail; |
| } |
| |
| gcc_assert (TREE_CODE (TREE_TYPE (args[0])) == VECTOR_TYPE); |
| gcc_assert (TREE_CODE (TREE_TYPE (args[1])) == VECTOR_TYPE); |
| gcc_assert (TREE_CODE (TREE_TYPE (args[2])) == VECTOR_TYPE); |
| |
| val = fold_ternary_loc (EXPR_LOCATION (t), VEC_PERM_EXPR, elttype, |
| args[0], args[1], args[2]); |
| if (val != NULL_TREE) |
| return val; |
| |
| fail: |
| return t; |
| } |
| |
| /* Attempt to reduce the expression T to a constant value. |
| On failure, issue diagnostic and return error_mark_node. */ |
| /* FIXME unify with c_fully_fold */ |
| |
| static tree |
| cxx_eval_constant_expression (const constexpr_call *call, tree t, |
| bool allow_non_constant, bool addr, |
| bool *non_constant_p, bool *overflow_p) |
| { |
| tree r = t; |
| |
| if (t == error_mark_node) |
| { |
| *non_constant_p = true; |
| return t; |
| } |
| if (CONSTANT_CLASS_P (t)) |
| { |
| if (TREE_CODE (t) == PTRMEM_CST) |
| t = cplus_expand_constant (t); |
| else if (TREE_OVERFLOW (t) && (!flag_permissive || allow_non_constant)) |
| *overflow_p = true; |
| return t; |
| } |
| if (TREE_CODE (t) != NOP_EXPR |
| && reduced_constant_expression_p (t)) |
| return fold (t); |
| |
| switch (TREE_CODE (t)) |
| { |
| case VAR_DECL: |
| if (addr) |
| return t; |
| /* else fall through. */ |
| case CONST_DECL: |
| r = integral_constant_value (t); |
| if (TREE_CODE (r) == TARGET_EXPR |
| && TREE_CODE (TARGET_EXPR_INITIAL (r)) == CONSTRUCTOR) |
| r = TARGET_EXPR_INITIAL (r); |
| if (DECL_P (r)) |
| { |
| if (!allow_non_constant) |
| non_const_var_error (r); |
| *non_constant_p = true; |
| } |
| break; |
| |
| case FUNCTION_DECL: |
| case TEMPLATE_DECL: |
| case LABEL_DECL: |
| return t; |
| |
| case PARM_DECL: |
| if (call && DECL_CONTEXT (t) == call->fundef->decl) |
| { |
| if (DECL_ARTIFICIAL (t) && DECL_CONSTRUCTOR_P (DECL_CONTEXT (t))) |
| { |
| if (!allow_non_constant) |
| sorry ("use of the value of the object being constructed " |
| "in a constant expression"); |
| *non_constant_p = true; |
| } |
| else |
| r = lookup_parameter_binding (call, t); |
| } |
| else if (addr) |
| /* Defer in case this is only used for its type. */; |
| else |
| { |
| if (!allow_non_constant) |
| error ("%qE is not a constant expression", t); |
| *non_constant_p = true; |
| } |
| break; |
| |
| case CALL_EXPR: |
| case AGGR_INIT_EXPR: |
| r = cxx_eval_call_expression (call, t, allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| break; |
| |
| case TARGET_EXPR: |
| if (!literal_type_p (TREE_TYPE (t))) |
| { |
| if (!allow_non_constant) |
| { |
| error ("temporary of non-literal type %qT in a " |
| "constant expression", TREE_TYPE (t)); |
| explain_non_literal_class (TREE_TYPE (t)); |
| } |
| *non_constant_p = true; |
| break; |
| } |
| /* else fall through. */ |
| case INIT_EXPR: |
| /* Pass false for 'addr' because these codes indicate |
| initialization of a temporary. */ |
| r = cxx_eval_constant_expression (call, TREE_OPERAND (t, 1), |
| allow_non_constant, false, |
| non_constant_p, overflow_p); |
| if (!*non_constant_p) |
| /* Adjust the type of the result to the type of the temporary. */ |
| r = adjust_temp_type (TREE_TYPE (t), r); |
| break; |
| |
| case SCOPE_REF: |
| r = cxx_eval_constant_expression (call, TREE_OPERAND (t, 1), |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| break; |
| |
| case RETURN_EXPR: |
| case NON_LVALUE_EXPR: |
| case TRY_CATCH_EXPR: |
| case CLEANUP_POINT_EXPR: |
| case MUST_NOT_THROW_EXPR: |
| case SAVE_EXPR: |
| r = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0), |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| break; |
| |
| /* These differ from cxx_eval_unary_expression in that this doesn't |
| check for a constant operand or result; an address can be |
| constant without its operand being, and vice versa. */ |
| case INDIRECT_REF: |
| r = cxx_eval_indirect_ref (call, t, allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| break; |
| |
| case ADDR_EXPR: |
| { |
| tree oldop = TREE_OPERAND (t, 0); |
| tree op = cxx_eval_constant_expression (call, oldop, |
| allow_non_constant, |
| /*addr*/true, |
| non_constant_p, overflow_p); |
| /* Don't VERIFY_CONSTANT here. */ |
| if (*non_constant_p) |
| return t; |
| /* This function does more aggressive folding than fold itself. */ |
| r = build_fold_addr_expr_with_type (op, TREE_TYPE (t)); |
| if (TREE_CODE (r) == ADDR_EXPR && TREE_OPERAND (r, 0) == oldop) |
| return t; |
| break; |
| } |
| |
| case REALPART_EXPR: |
| case IMAGPART_EXPR: |
| case CONJ_EXPR: |
| case FIX_TRUNC_EXPR: |
| case FLOAT_EXPR: |
| case NEGATE_EXPR: |
| case ABS_EXPR: |
| case BIT_NOT_EXPR: |
| case TRUTH_NOT_EXPR: |
| case FIXED_CONVERT_EXPR: |
| r = cxx_eval_unary_expression (call, t, allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| break; |
| |
| case SIZEOF_EXPR: |
| if (SIZEOF_EXPR_TYPE_P (t)) |
| r = cxx_sizeof_or_alignof_type (TREE_TYPE (TREE_OPERAND (t, 0)), |
| SIZEOF_EXPR, false); |
| else if (TYPE_P (TREE_OPERAND (t, 0))) |
| r = cxx_sizeof_or_alignof_type (TREE_OPERAND (t, 0), SIZEOF_EXPR, |
| false); |
| else |
| r = cxx_sizeof_or_alignof_expr (TREE_OPERAND (t, 0), SIZEOF_EXPR, |
| false); |
| if (r == error_mark_node) |
| r = size_one_node; |
| VERIFY_CONSTANT (r); |
| break; |
| |
| case COMPOUND_EXPR: |
| { |
| /* check_return_expr sometimes wraps a TARGET_EXPR in a |
| COMPOUND_EXPR; don't get confused. Also handle EMPTY_CLASS_EXPR |
| introduced by build_call_a. */ |
| tree op0 = TREE_OPERAND (t, 0); |
| tree op1 = TREE_OPERAND (t, 1); |
| STRIP_NOPS (op1); |
| if ((TREE_CODE (op0) == TARGET_EXPR && op1 == TARGET_EXPR_SLOT (op0)) |
| || TREE_CODE (op1) == EMPTY_CLASS_EXPR) |
| r = cxx_eval_constant_expression (call, op0, allow_non_constant, |
| addr, non_constant_p, overflow_p); |
| else |
| { |
| /* Check that the LHS is constant and then discard it. */ |
| cxx_eval_constant_expression (call, op0, allow_non_constant, |
| false, non_constant_p, overflow_p); |
| op1 = TREE_OPERAND (t, 1); |
| r = cxx_eval_constant_expression (call, op1, allow_non_constant, |
| addr, non_constant_p, overflow_p); |
| } |
| } |
| break; |
| |
| case POINTER_PLUS_EXPR: |
| case PLUS_EXPR: |
| case MINUS_EXPR: |
| case MULT_EXPR: |
| case TRUNC_DIV_EXPR: |
| case CEIL_DIV_EXPR: |
| case FLOOR_DIV_EXPR: |
| case ROUND_DIV_EXPR: |
| case TRUNC_MOD_EXPR: |
| case CEIL_MOD_EXPR: |
| case ROUND_MOD_EXPR: |
| case RDIV_EXPR: |
| case EXACT_DIV_EXPR: |
| case MIN_EXPR: |
| case MAX_EXPR: |
| case LSHIFT_EXPR: |
| case RSHIFT_EXPR: |
| case LROTATE_EXPR: |
| case RROTATE_EXPR: |
| case BIT_IOR_EXPR: |
| case BIT_XOR_EXPR: |
| case BIT_AND_EXPR: |
| case TRUTH_XOR_EXPR: |
| case LT_EXPR: |
| case LE_EXPR: |
| case GT_EXPR: |
| case GE_EXPR: |
| case EQ_EXPR: |
| case NE_EXPR: |
| case UNORDERED_EXPR: |
| case ORDERED_EXPR: |
| case UNLT_EXPR: |
| case UNLE_EXPR: |
| case UNGT_EXPR: |
| case UNGE_EXPR: |
| case UNEQ_EXPR: |
| case LTGT_EXPR: |
| case RANGE_EXPR: |
| case COMPLEX_EXPR: |
| r = cxx_eval_binary_expression (call, t, allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| break; |
| |
| /* fold can introduce non-IF versions of these; still treat them as |
| short-circuiting. */ |
| case TRUTH_AND_EXPR: |
| case TRUTH_ANDIF_EXPR: |
| r = cxx_eval_logical_expression (call, t, boolean_false_node, |
| boolean_true_node, |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| break; |
| |
| case TRUTH_OR_EXPR: |
| case TRUTH_ORIF_EXPR: |
| r = cxx_eval_logical_expression (call, t, boolean_true_node, |
| boolean_false_node, |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| break; |
| |
| case ARRAY_REF: |
| r = cxx_eval_array_reference (call, t, allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| break; |
| |
| case COMPONENT_REF: |
| r = cxx_eval_component_reference (call, t, allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| break; |
| |
| case BIT_FIELD_REF: |
| r = cxx_eval_bit_field_ref (call, t, allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| break; |
| |
| case COND_EXPR: |
| case VEC_COND_EXPR: |
| r = cxx_eval_conditional_expression (call, t, allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| break; |
| |
| case CONSTRUCTOR: |
| r = cxx_eval_bare_aggregate (call, t, allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| break; |
| |
| case VEC_INIT_EXPR: |
| /* We can get this in a defaulted constructor for a class with a |
| non-static data member of array type. Either the initializer will |
| be NULL, meaning default-initialization, or it will be an lvalue |
| or xvalue of the same type, meaning direct-initialization from the |
| corresponding member. */ |
| r = cxx_eval_vec_init (call, t, allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| break; |
| |
| case VEC_PERM_EXPR: |
| r = cxx_eval_vec_perm_expr (call, t, allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| break; |
| |
| case CONVERT_EXPR: |
| case VIEW_CONVERT_EXPR: |
| case NOP_EXPR: |
| { |
| tree oldop = TREE_OPERAND (t, 0); |
| tree op = cxx_eval_constant_expression (call, oldop, |
| allow_non_constant, addr, |
| non_constant_p, overflow_p); |
| if (*non_constant_p) |
| return t; |
| if (op == oldop) |
| /* We didn't fold at the top so we could check for ptr-int |
| conversion. */ |
| return fold (t); |
| r = fold_build1 (TREE_CODE (t), TREE_TYPE (t), op); |
| /* Conversion of an out-of-range value has implementation-defined |
| behavior; the language considers it different from arithmetic |
| overflow, which is undefined. */ |
| if (TREE_OVERFLOW_P (r) && !TREE_OVERFLOW_P (op)) |
| TREE_OVERFLOW (r) = false; |
| } |
| break; |
| |
| case EMPTY_CLASS_EXPR: |
| /* This is good enough for a function argument that might not get |
| used, and they can't do anything with it, so just return it. */ |
| return t; |
| |
| case LAMBDA_EXPR: |
| case PREINCREMENT_EXPR: |
| case POSTINCREMENT_EXPR: |
| case PREDECREMENT_EXPR: |
| case POSTDECREMENT_EXPR: |
| case NEW_EXPR: |
| case VEC_NEW_EXPR: |
| case DELETE_EXPR: |
| case VEC_DELETE_EXPR: |
| case THROW_EXPR: |
| case MODIFY_EXPR: |
| case MODOP_EXPR: |
| /* GCC internal stuff. */ |
| case VA_ARG_EXPR: |
| case OBJ_TYPE_REF: |
| case WITH_CLEANUP_EXPR: |
| case STATEMENT_LIST: |
| case BIND_EXPR: |
| case NON_DEPENDENT_EXPR: |
| case BASELINK: |
| case EXPR_STMT: |
| case OFFSET_REF: |
| if (!allow_non_constant) |
| error_at (EXPR_LOC_OR_HERE (t), |
| "expression %qE is not a constant-expression", t); |
| *non_constant_p = true; |
| break; |
| |
| default: |
| internal_error ("unexpected expression %qE of kind %s", t, |
| tree_code_name[TREE_CODE (t)]); |
| *non_constant_p = true; |
| break; |
| } |
| |
| if (r == error_mark_node) |
| *non_constant_p = true; |
| |
| if (*non_constant_p) |
| return t; |
| else |
| return r; |
| } |
| |
| static tree |
| cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant) |
| { |
| bool non_constant_p = false; |
| bool overflow_p = false; |
| tree r = cxx_eval_constant_expression (NULL, t, allow_non_constant, |
| false, &non_constant_p, &overflow_p); |
| |
| verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p); |
| |
| if (TREE_CODE (t) != CONSTRUCTOR |
| && cp_has_mutable_p (TREE_TYPE (t))) |
| { |
| /* We allow a mutable type if the original expression was a |
| CONSTRUCTOR so that we can do aggregate initialization of |
| constexpr variables. */ |
| if (!allow_non_constant) |
| error ("%qT cannot be the type of a complete constant expression " |
| "because it has mutable sub-objects", TREE_TYPE (t)); |
| non_constant_p = true; |
| } |
| |
| /* Technically we should check this for all subexpressions, but that |
| runs into problems with our internal representation of pointer |
| subtraction and the 5.19 rules are still in flux. */ |
| if (CONVERT_EXPR_CODE_P (TREE_CODE (r)) |
| && ARITHMETIC_TYPE_P (TREE_TYPE (r)) |
| && TREE_CODE (TREE_OPERAND (r, 0)) == ADDR_EXPR) |
| { |
| if (!allow_non_constant) |
| error ("conversion from pointer type %qT " |
| "to arithmetic type %qT in a constant-expression", |
| TREE_TYPE (TREE_OPERAND (r, 0)), TREE_TYPE (r)); |
| non_constant_p = true; |
| } |
| |
| if (!non_constant_p && overflow_p) |
| non_constant_p = true; |
| |
| if (non_constant_p && !allow_non_constant) |
| return error_mark_node; |
| else if (non_constant_p && TREE_CONSTANT (r)) |
| { |
| /* This isn't actually constant, so unset TREE_CONSTANT. */ |
| if (EXPR_P (r)) |
| r = copy_node (r); |
| else if (TREE_CODE (r) == CONSTRUCTOR) |
| r = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (r), r); |
| else |
| r = build_nop (TREE_TYPE (r), r); |
| TREE_CONSTANT (r) = false; |
| } |
| else if (non_constant_p || r == t) |
| return t; |
| |
| if (TREE_CODE (r) == CONSTRUCTOR && CLASS_TYPE_P (TREE_TYPE (r))) |
| { |
| if (TREE_CODE (t) == TARGET_EXPR |
| && TARGET_EXPR_INITIAL (t) == r) |
| return t; |
| else |
| { |
| r = get_target_expr (r); |
| TREE_CONSTANT (r) = true; |
| return r; |
| } |
| } |
| else |
| return r; |
| } |
| |
| /* Returns true if T is a valid subexpression of a constant expression, |
| even if it isn't itself a constant expression. */ |
| |
| bool |
| is_sub_constant_expr (tree t) |
| { |
| bool non_constant_p = false; |
| bool overflow_p = false; |
| cxx_eval_constant_expression (NULL, t, true, false, &non_constant_p, |
| &overflow_p); |
| return !non_constant_p && !overflow_p; |
| } |
| |
| /* If T represents a constant expression returns its reduced value. |
| Otherwise return error_mark_node. If T is dependent, then |
| return NULL. */ |
| |
| tree |
| cxx_constant_value (tree t) |
| { |
| return cxx_eval_outermost_constant_expr (t, false); |
| } |
| |
| /* If T is a constant expression, returns its reduced value. |
| Otherwise, if T does not have TREE_CONSTANT set, returns T. |
| Otherwise, returns a version of T without TREE_CONSTANT. */ |
| |
| tree |
| maybe_constant_value (tree t) |
| { |
| tree r; |
| |
| if (type_dependent_expression_p (t) |
| || type_unknown_p (t) |
| || BRACE_ENCLOSED_INITIALIZER_P (t) |
| || !potential_constant_expression (t) |
| || value_dependent_expression_p (t)) |
| { |
| if (TREE_OVERFLOW_P (t)) |
| { |
| t = build_nop (TREE_TYPE (t), t); |
| TREE_CONSTANT (t) = false; |
| } |
| return t; |
| } |
| |
| r = cxx_eval_outermost_constant_expr (t, true); |
| #ifdef ENABLE_CHECKING |
| /* cp_tree_equal looks through NOPs, so allow them. */ |
| gcc_assert (r == t |
| || CONVERT_EXPR_P (t) |
| || (TREE_CONSTANT (t) && !TREE_CONSTANT (r)) |
| || !cp_tree_equal (r, t)); |
| #endif |
| return r; |
| } |
| |
| /* Like maybe_constant_value, but returns a CONSTRUCTOR directly, rather |
| than wrapped in a TARGET_EXPR. */ |
| |
| tree |
| maybe_constant_init (tree t) |
| { |
| t = maybe_constant_value (t); |
| if (TREE_CODE (t) == TARGET_EXPR) |
| { |
| tree init = TARGET_EXPR_INITIAL (t); |
| if (TREE_CODE (init) == CONSTRUCTOR) |
| t = init; |
| } |
| return t; |
| } |
| |
| #if 0 |
| /* FIXME see ADDR_EXPR section in potential_constant_expression_1. */ |
| /* Return true if the object referred to by REF has automatic or thread |
| local storage. */ |
| |
| enum { ck_ok, ck_bad, ck_unknown }; |
| static int |
| check_automatic_or_tls (tree ref) |
| { |
| enum machine_mode mode; |
| HOST_WIDE_INT bitsize, bitpos; |
| tree offset; |
| int volatilep = 0, unsignedp = 0; |
| tree decl = get_inner_reference (ref, &bitsize, &bitpos, &offset, |
| &mode, &unsignedp, &volatilep, false); |
| duration_kind dk; |
| |
| /* If there isn't a decl in the middle, we don't know the linkage here, |
| and this isn't a constant expression anyway. */ |
| if (!DECL_P (decl)) |
| return ck_unknown; |
| dk = decl_storage_duration (decl); |
| return (dk == dk_auto || dk == dk_thread) ? ck_bad : ck_ok; |
| } |
| #endif |
| |
| /* Return true if T denotes a potentially constant expression. Issue |
| diagnostic as appropriate under control of FLAGS. If WANT_RVAL is true, |
| an lvalue-rvalue conversion is implied. |
| |
| C++0x [expr.const] used to say |
| |
| 6 An expression is a potential constant expression if it is |
| a constant expression where all occurences of function |
| parameters are replaced by arbitrary constant expressions |
| of the appropriate type. |
| |
| 2 A conditional expression is a constant expression unless it |
| involves one of the following as a potentially evaluated |
| subexpression (3.2), but subexpressions of logical AND (5.14), |
| logical OR (5.15), and conditional (5.16) operations that are |
| not evaluated are not considered. */ |
| |
| static bool |
| potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags) |
| { |
| enum { any = false, rval = true }; |
| int i; |
| tree tmp; |
| |
| if (t == error_mark_node) |
| return false; |
| if (t == NULL_TREE) |
| return true; |
| if (TREE_THIS_VOLATILE (t)) |
| { |
| if (flags & tf_error) |
| error ("expression %qE has side-effects", t); |
| return false; |
| } |
| if (CONSTANT_CLASS_P (t)) |
| return true; |
| |
| switch (TREE_CODE (t)) |
| { |
| case FUNCTION_DECL: |
| case BASELINK: |
| case TEMPLATE_DECL: |
| case OVERLOAD: |
| case TEMPLATE_ID_EXPR: |
| case LABEL_DECL: |
| case CONST_DECL: |
| case SIZEOF_EXPR: |
| case ALIGNOF_EXPR: |
| case OFFSETOF_EXPR: |
| case NOEXCEPT_EXPR: |
| case TEMPLATE_PARM_INDEX: |
| case TRAIT_EXPR: |
| case IDENTIFIER_NODE: |
| case USERDEF_LITERAL: |
| /* We can see a FIELD_DECL in a pointer-to-member expression. */ |
| case FIELD_DECL: |
| case PARM_DECL: |
| case USING_DECL: |
| return true; |
| |
| case AGGR_INIT_EXPR: |
| case CALL_EXPR: |
| /* -- an invocation of a function other than a constexpr function |
| or a constexpr constructor. */ |
| { |
| tree fun = get_function_named_in_call (t); |
| const int nargs = call_expr_nargs (t); |
| i = 0; |
| |
| if (is_overloaded_fn (fun)) |
| { |
| if (TREE_CODE (fun) == FUNCTION_DECL) |
| { |
| if (builtin_valid_in_constant_expr_p (fun)) |
| return true; |
| if (!DECL_DECLARED_CONSTEXPR_P (fun) |
| /* Allow any built-in function; if the expansion |
| isn't constant, we'll deal with that then. */ |
| && !is_builtin_fn (fun)) |
| { |
| if (flags & tf_error) |
| { |
| error_at (EXPR_LOC_OR_HERE (t), |
| "call to non-constexpr function %qD", fun); |
| explain_invalid_constexpr_fn (fun); |
| } |
| return false; |
| } |
| /* A call to a non-static member function takes the address |
| of the object as the first argument. But in a constant |
| expression the address will be folded away, so look |
| through it now. */ |
| if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fun) |
| && !DECL_CONSTRUCTOR_P (fun)) |
| { |
| tree x = get_nth_callarg (t, 0); |
| if (is_this_parameter (x)) |
| { |
| if (DECL_CONSTRUCTOR_P (DECL_CONTEXT (x))) |
| { |
| if (flags & tf_error) |
| sorry ("calling a member function of the " |
| "object being constructed in a constant " |
| "expression"); |
| return false; |
| } |
| /* Otherwise OK. */; |
| } |
| else if (!potential_constant_expression_1 (x, rval, flags)) |
| return false; |
| i = 1; |
| } |
| } |
| else |
| { |
| if (!potential_constant_expression_1 (fun, true, flags)) |
| return false; |
| fun = get_first_fn (fun); |
| } |
| /* Skip initial arguments to base constructors. */ |
| if (DECL_BASE_CONSTRUCTOR_P (fun)) |
| i = num_artificial_parms_for (fun); |
| fun = DECL_ORIGIN (fun); |
| } |
| else |
| { |
| if (potential_constant_expression_1 (fun, rval, flags)) |
| /* Might end up being a constant function pointer. */; |
| else |
| return false; |
| } |
| for (; i < nargs; ++i) |
| { |
| tree x = get_nth_callarg (t, i); |
| if (!potential_constant_expression_1 (x, rval, flags)) |
| return false; |
| } |
| return true; |
| } |
| |
| case NON_LVALUE_EXPR: |
| /* -- an lvalue-to-rvalue conversion (4.1) unless it is applied to |
| -- an lvalue of integral type that refers to a non-volatile |
| const variable or static data member initialized with |
| constant expressions, or |
| |
| -- an lvalue of literal type that refers to non-volatile |
| object defined with constexpr, or that refers to a |
| sub-object of such an object; */ |
| return potential_constant_expression_1 (TREE_OPERAND (t, 0), rval, flags); |
| |
| case VAR_DECL: |
| if (want_rval && !decl_constant_var_p (t) |
| && !dependent_type_p (TREE_TYPE (t))) |
| { |
| if (flags & tf_error) |
| non_const_var_error (t); |
| return false; |
| } |
| return true; |
| |
| case NOP_EXPR: |
| case CONVERT_EXPR: |
| case VIEW_CONVERT_EXPR: |
| /* -- a reinterpret_cast. FIXME not implemented, and this rule |
| may change to something more specific to type-punning (DR 1312). */ |
| { |
| tree from = TREE_OPERAND (t, 0); |
| return (potential_constant_expression_1 |
| (from, TREE_CODE (t) != VIEW_CONVERT_EXPR, flags)); |
| } |
| |
| case ADDR_EXPR: |
| /* -- a unary operator & that is applied to an lvalue that |
| designates an object with thread or automatic storage |
| duration; */ |
| t = TREE_OPERAND (t, 0); |
| #if 0 |
| /* FIXME adjust when issue 1197 is fully resolved. For now don't do |
| any checking here, as we might dereference the pointer later. If |
| we remove this code, also remove check_automatic_or_tls. */ |
| i = check_automatic_or_tls (t); |
| if (i == ck_ok) |
| return true; |
| if (i == ck_bad) |
| { |
| if (flags & tf_error) |
| error ("address-of an object %qE with thread local or " |
| "automatic storage is not a constant expression", t); |
| return false; |
| } |
| #endif |
| return potential_constant_expression_1 (t, any, flags); |
| |
| case COMPONENT_REF: |
| case BIT_FIELD_REF: |
| case ARROW_EXPR: |
| case OFFSET_REF: |
| /* -- a class member access unless its postfix-expression is |
| of literal type or of pointer to literal type. */ |
| /* This test would be redundant, as it follows from the |
| postfix-expression being a potential constant expression. */ |
| return potential_constant_expression_1 (TREE_OPERAND (t, 0), |
| want_rval, flags); |
| |
| case EXPR_PACK_EXPANSION: |
| return potential_constant_expression_1 (PACK_EXPANSION_PATTERN (t), |
| want_rval, flags); |
| |
| case INDIRECT_REF: |
| { |
| tree x = TREE_OPERAND (t, 0); |
| STRIP_NOPS (x); |
| if (is_this_parameter (x)) |
| { |
| if (want_rval && DECL_CONTEXT (x) |
| && DECL_CONSTRUCTOR_P (DECL_CONTEXT (x))) |
| { |
| if (flags & tf_error) |
| sorry ("use of the value of the object being constructed " |
| "in a constant expression"); |
| return false; |
| } |
| return true; |
| } |
| return potential_constant_expression_1 (x, rval, flags); |
| } |
| |
| case LAMBDA_EXPR: |
| case DYNAMIC_CAST_EXPR: |
| case PSEUDO_DTOR_EXPR: |
| case PREINCREMENT_EXPR: |
| case POSTINCREMENT_EXPR: |
| case PREDECREMENT_EXPR: |
| case POSTDECREMENT_EXPR: |
| case NEW_EXPR: |
| case VEC_NEW_EXPR: |
| case DELETE_EXPR: |
| case VEC_DELETE_EXPR: |
| case THROW_EXPR: |
| case MODIFY_EXPR: |
| case MODOP_EXPR: |
| /* GCC internal stuff. */ |
| case VA_ARG_EXPR: |
| case OBJ_TYPE_REF: |
| case WITH_CLEANUP_EXPR: |
| case CLEANUP_POINT_EXPR: |
| case MUST_NOT_THROW_EXPR: |
| case TRY_CATCH_EXPR: |
| case STATEMENT_LIST: |
| /* Don't bother trying to define a subset of statement-expressions to |
| be constant-expressions, at least for now. */ |
| case STMT_EXPR: |
| case EXPR_STMT: |
| case BIND_EXPR: |
| case TRANSACTION_EXPR: |
| case IF_STMT: |
| case DO_STMT: |
| case FOR_STMT: |
| case WHILE_STMT: |
| if (flags & tf_error) |
| error ("expression %qE is not a constant-expression", t); |
| return false; |
| |
| case TYPEID_EXPR: |
| /* -- a typeid expression whose operand is of polymorphic |
| class type; */ |
| { |
| tree e = TREE_OPERAND (t, 0); |
| if (!TYPE_P (e) && !type_dependent_expression_p (e) |
| && TYPE_POLYMORPHIC_P (TREE_TYPE (e))) |
| { |
| if (flags & tf_error) |
| error ("typeid-expression is not a constant expression " |
| "because %qE is of polymorphic type", e); |
| return false; |
| } |
| return true; |
| } |
| |
| case MINUS_EXPR: |
| /* -- a subtraction where both operands are pointers. */ |
| if (TYPE_PTR_P (TREE_OPERAND (t, 0)) |
| && TYPE_PTR_P (TREE_OPERAND (t, 1))) |
| { |
| if (flags & tf_error) |
| error ("difference of two pointer expressions is not " |
| "a constant expression"); |
| return false; |
| } |
| want_rval = true; |
| goto binary; |
| |
| case LT_EXPR: |
| case LE_EXPR: |
| case GT_EXPR: |
| case GE_EXPR: |
| case EQ_EXPR: |
| case NE_EXPR: |
| /* -- a relational or equality operator where at least |
| one of the operands is a pointer. */ |
| if (TYPE_PTR_P (TREE_OPERAND (t, 0)) |
| || TYPE_PTR_P (TREE_OPERAND (t, 1))) |
| { |
| if (flags & tf_error) |
| error ("pointer comparison expression is not a " |
| "constant expression"); |
| return false; |
| } |
| want_rval = true; |
| goto binary; |
| |
| case BIT_NOT_EXPR: |
| /* A destructor. */ |
| if (TYPE_P (TREE_OPERAND (t, 0))) |
| return true; |
| /* else fall through. */ |
| |
| case REALPART_EXPR: |
| case IMAGPART_EXPR: |
| case CONJ_EXPR: |
| case SAVE_EXPR: |
| case FIX_TRUNC_EXPR: |
| case FLOAT_EXPR: |
| case NEGATE_EXPR: |
| case ABS_EXPR: |
| case TRUTH_NOT_EXPR: |
| case FIXED_CONVERT_EXPR: |
| case UNARY_PLUS_EXPR: |
| return potential_constant_expression_1 (TREE_OPERAND (t, 0), rval, |
| flags); |
| |
| case CAST_EXPR: |
| case CONST_CAST_EXPR: |
| case STATIC_CAST_EXPR: |
| case REINTERPRET_CAST_EXPR: |
| case IMPLICIT_CONV_EXPR: |
| if (cxx_dialect < cxx0x |
| && !dependent_type_p (TREE_TYPE (t)) |
| && !INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (t))) |
| /* In C++98, a conversion to non-integral type can't be part of a |
| constant expression. */ |
| { |
| if (flags & tf_error) |
| error ("cast to non-integral type %qT in a constant expression", |
| TREE_TYPE (t)); |
| return false; |
| } |
| |
| return (potential_constant_expression_1 |
| (TREE_OPERAND (t, 0), |
| TREE_CODE (TREE_TYPE (t)) != REFERENCE_TYPE, flags)); |
| |
| case PAREN_EXPR: |
| case NON_DEPENDENT_EXPR: |
| /* For convenience. */ |
| case RETURN_EXPR: |
| return potential_constant_expression_1 (TREE_OPERAND (t, 0), |
| want_rval, flags); |
| |
| case SCOPE_REF: |
| return potential_constant_expression_1 (TREE_OPERAND (t, 1), |
| want_rval, flags); |
| |
| case TARGET_EXPR: |
| if (!literal_type_p (TREE_TYPE (t))) |
| { |
| if (flags & tf_error) |
| { |
| error ("temporary of non-literal type %qT in a " |
| "constant expression", TREE_TYPE (t)); |
| explain_non_literal_class (TREE_TYPE (t)); |
| } |
| return false; |
| } |
| case INIT_EXPR: |
| return potential_constant_expression_1 (TREE_OPERAND (t, 1), |
| rval, flags); |
| |
| case CONSTRUCTOR: |
| { |
| vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (t); |
| constructor_elt *ce; |
| for (i = 0; vec_safe_iterate (v, i, &ce); ++i) |
| if (!potential_constant_expression_1 (ce->value, want_rval, flags)) |
| return false; |
| return true; |
| } |
| |
| case TREE_LIST: |
| { |
| gcc_assert (TREE_PURPOSE (t) == NULL_TREE |
| || DECL_P (TREE_PURPOSE (t))); |
| if (!potential_constant_expression_1 (TREE_VALUE (t), want_rval, |
| flags)) |
| return false; |
| if (TREE_CHAIN (t) == NULL_TREE) |
| return true; |
| return potential_constant_expression_1 (TREE_CHAIN (t), want_rval, |
| flags); |
| } |
| |
| case TRUNC_DIV_EXPR: |
| case CEIL_DIV_EXPR: |
| case FLOOR_DIV_EXPR: |
| case ROUND_DIV_EXPR: |
| case TRUNC_MOD_EXPR: |
| case CEIL_MOD_EXPR: |
| case ROUND_MOD_EXPR: |
| { |
| tree denom = TREE_OPERAND (t, 1); |
| if (!potential_constant_expression_1 (denom, rval, flags)) |
| return false; |
| /* We can't call cxx_eval_outermost_constant_expr on an expression |
| that hasn't been through fold_non_dependent_expr yet. */ |
| if (!processing_template_decl) |
| denom = cxx_eval_outermost_constant_expr (denom, true); |
| if (integer_zerop (denom)) |
| { |
| if (flags & tf_error) |
| error ("division by zero is not a constant-expression"); |
| return false; |
| } |
| else |
| { |
| want_rval = true; |
| return potential_constant_expression_1 (TREE_OPERAND (t, 0), |
| want_rval, flags); |
| } |
| } |
| |
| case COMPOUND_EXPR: |
| { |
| /* check_return_expr sometimes wraps a TARGET_EXPR in a |
| COMPOUND_EXPR; don't get confused. Also handle EMPTY_CLASS_EXPR |
| introduced by build_call_a. */ |
| tree op0 = TREE_OPERAND (t, 0); |
| tree op1 = TREE_OPERAND (t, 1); |
| STRIP_NOPS (op1); |
| if ((TREE_CODE (op0) == TARGET_EXPR && op1 == TARGET_EXPR_SLOT (op0)) |
| || TREE_CODE (op1) == EMPTY_CLASS_EXPR) |
| return potential_constant_expression_1 (op0, want_rval, flags); |
| else |
| goto binary; |
| } |
| |
| /* If the first operand is the non-short-circuit constant, look at |
| the second operand; otherwise we only care about the first one for |
| potentiality. */ |
| case TRUTH_AND_EXPR: |
| case TRUTH_ANDIF_EXPR: |
| tmp = boolean_true_node; |
| goto truth; |
| case TRUTH_OR_EXPR: |
| case TRUTH_ORIF_EXPR: |
| tmp = boolean_false_node; |
| truth: |
| { |
| tree op = TREE_OPERAND (t, 0); |
| if (!potential_constant_expression_1 (op, rval, flags)) |
| return false; |
| if (!processing_template_decl) |
| op = cxx_eval_outermost_constant_expr (op, true); |
| if (tree_int_cst_equal (op, tmp)) |
| return potential_constant_expression_1 (TREE_OPERAND (t, 1), rval, flags); |
| else |
| return true; |
| } |
| |
| case PLUS_EXPR: |
| case MULT_EXPR: |
| case POINTER_PLUS_EXPR: |
| case RDIV_EXPR: |
| case EXACT_DIV_EXPR: |
| case MIN_EXPR: |
| case MAX_EXPR: |
| case LSHIFT_EXPR: |
| case RSHIFT_EXPR: |
| case LROTATE_EXPR: |
| case RROTATE_EXPR: |
| case BIT_IOR_EXPR: |
| case BIT_XOR_EXPR: |
| case BIT_AND_EXPR: |
| case TRUTH_XOR_EXPR: |
| case UNORDERED_EXPR: |
| case ORDERED_EXPR: |
| case UNLT_EXPR: |
| case UNLE_EXPR: |
| case UNGT_EXPR: |
| case UNGE_EXPR: |
| case UNEQ_EXPR: |
| case LTGT_EXPR: |
| case RANGE_EXPR: |
| case COMPLEX_EXPR: |
| want_rval = true; |
| /* Fall through. */ |
| case ARRAY_REF: |
| case ARRAY_RANGE_REF: |
| case MEMBER_REF: |
| case DOTSTAR_EXPR: |
| binary: |
| for (i = 0; i < 2; ++i) |
| if (!potential_constant_expression_1 (TREE_OPERAND (t, i), |
| want_rval, flags)) |
| return false; |
| return true; |
| |
| case FMA_EXPR: |
| case VEC_PERM_EXPR: |
| for (i = 0; i < 3; ++i) |
| if (!potential_constant_expression_1 (TREE_OPERAND (t, i), |
| true, flags)) |
| return false; |
| return true; |
| |
| case COND_EXPR: |
| case VEC_COND_EXPR: |
| /* If the condition is a known constant, we know which of the legs we |
| care about; otherwise we only require that the condition and |
| either of the legs be potentially constant. */ |
| tmp = TREE_OPERAND (t, 0); |
| if (!potential_constant_expression_1 (tmp, rval, flags)) |
| return false; |
| if (!processing_template_decl) |
| tmp = cxx_eval_outermost_constant_expr (tmp, true); |
| if (integer_zerop (tmp)) |
| return potential_constant_expression_1 (TREE_OPERAND (t, 2), |
| want_rval, flags); |
| else if (TREE_CODE (tmp) == INTEGER_CST) |
| return potential_constant_expression_1 (TREE_OPERAND (t, 1), |
| want_rval, flags); |
| for (i = 1; i < 3; ++i) |
| if (potential_constant_expression_1 (TREE_OPERAND (t, i), |
| want_rval, tf_none)) |
| return true; |
| if (flags & tf_error) |
| error ("expression %qE is not a constant-expression", t); |
| return false; |
| |
| case VEC_INIT_EXPR: |
| if (VEC_INIT_EXPR_IS_CONSTEXPR (t)) |
| return true; |
| if (flags & tf_error) |
| { |
| error ("non-constant array initialization"); |
| diagnose_non_constexpr_vec_init (t); |
| } |
| return false; |
| |
| case OMP_ATOMIC: |
| case OMP_ATOMIC_READ: |
| case OMP_ATOMIC_CAPTURE_OLD: |
| case OMP_ATOMIC_CAPTURE_NEW: |
| return false; |
| |
| default: |
| if (objc_is_property_ref (t)) |
| return false; |
| |
| sorry ("unexpected AST of kind %s", tree_code_name[TREE_CODE (t)]); |
| gcc_unreachable(); |
| return false; |
| } |
| } |
| |
| /* The main entry point to the above. */ |
| |
| bool |
| potential_constant_expression (tree t) |
| { |
| return potential_constant_expression_1 (t, false, tf_none); |
| } |
| |
| /* As above, but require a constant rvalue. */ |
| |
| bool |
| potential_rvalue_constant_expression (tree t) |
| { |
| return potential_constant_expression_1 (t, true, tf_none); |
| } |
| |
| /* Like above, but complain about non-constant expressions. */ |
| |
| bool |
| require_potential_constant_expression (tree t) |
| { |
| return potential_constant_expression_1 (t, false, tf_warning_or_error); |
| } |
| |
| /* Cross product of the above. */ |
| |
| bool |
| require_potential_rvalue_constant_expression (tree t) |
| { |
| return potential_constant_expression_1 (t, true, tf_warning_or_error); |
| } |
| |
| /* Constructor for a lambda expression. */ |
| |
| tree |
| build_lambda_expr (void) |
| { |
| tree lambda = make_node (LAMBDA_EXPR); |
| LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda) = CPLD_NONE; |
| LAMBDA_EXPR_CAPTURE_LIST (lambda) = NULL_TREE; |
| LAMBDA_EXPR_THIS_CAPTURE (lambda) = NULL_TREE; |
| LAMBDA_EXPR_PENDING_PROXIES (lambda) = NULL; |
| LAMBDA_EXPR_RETURN_TYPE (lambda) = NULL_TREE; |
| LAMBDA_EXPR_MUTABLE_P (lambda) = false; |
| return lambda; |
| } |
| |
| /* Create the closure object for a LAMBDA_EXPR. */ |
| |
| tree |
| build_lambda_object (tree lambda_expr) |
| { |
| /* Build aggregate constructor call. |
| - cp_parser_braced_list |
| - cp_parser_functional_cast */ |
| vec<constructor_elt, va_gc> *elts = NULL; |
| tree node, expr, type; |
| location_t saved_loc; |
| |
| if (processing_template_decl) |
| return lambda_expr; |
| |
| /* Make sure any error messages refer to the lambda-introducer. */ |
| saved_loc = input_location; |
| input_location = LAMBDA_EXPR_LOCATION (lambda_expr); |
| |
| for (node = LAMBDA_EXPR_CAPTURE_LIST (lambda_expr); |
| node; |
| node = TREE_CHAIN (node)) |
| { |
| tree field = TREE_PURPOSE (node); |
| tree val = TREE_VALUE (node); |
| |
| if (field == error_mark_node) |
| { |
| expr = error_mark_node; |
| goto out; |
| } |
| |
| if (DECL_P (val)) |
| mark_used (val); |
| |
| /* Mere mortals can't copy arrays with aggregate initialization, so |
| do some magic to make it work here. */ |
| if (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE) |
| val = build_array_copy (val); |
| else if (DECL_NORMAL_CAPTURE_P (field) |
| && TREE_CODE (TREE_TYPE (field)) != REFERENCE_TYPE) |
| { |
| /* "the entities that are captured by copy are used to |
| direct-initialize each corresponding non-static data |
| member of the resulting closure object." |
| |
| There's normally no way to express direct-initialization |
| from an element of a CONSTRUCTOR, so we build up a special |
| TARGET_EXPR to bypass the usual copy-initialization. */ |
| val = force_rvalue (val, tf_warning_or_error); |
| if (TREE_CODE (val) == TARGET_EXPR) |
| TARGET_EXPR_DIRECT_INIT_P (val) = true; |
| } |
| |
| CONSTRUCTOR_APPEND_ELT (elts, DECL_NAME (field), val); |
| } |
| |
| expr = build_constructor (init_list_type_node, elts); |
| CONSTRUCTOR_IS_DIRECT_INIT (expr) = 1; |
| |
| /* N2927: "[The closure] class type is not an aggregate." |
| But we briefly treat it as an aggregate to make this simpler. */ |
| type = LAMBDA_EXPR_CLOSURE (lambda_expr); |
| CLASSTYPE_NON_AGGREGATE (type) = 0; |
| expr = finish_compound_literal (type, expr, tf_warning_or_error); |
| CLASSTYPE_NON_AGGREGATE (type) = 1; |
| |
| out: |
| input_location = saved_loc; |
| return expr; |
| } |
| |
| /* Return an initialized RECORD_TYPE for LAMBDA. |
| LAMBDA must have its explicit captures already. */ |
| |
| tree |
| begin_lambda_type (tree lambda) |
| { |
| tree type; |
| |
| { |
| /* Unique name. This is just like an unnamed class, but we cannot use |
| make_anon_name because of certain checks against TYPE_ANONYMOUS_P. */ |
| tree name; |
| name = make_lambda_name (); |
| |
| /* Create the new RECORD_TYPE for this lambda. */ |
| type = xref_tag (/*tag_code=*/record_type, |
| name, |
| /*scope=*/ts_lambda, |
| /*template_header_p=*/false); |
| if (type == error_mark_node) |
| return error_mark_node; |
| } |
| |
| /* Designate it as a struct so that we can use aggregate initialization. */ |
| CLASSTYPE_DECLARED_CLASS (type) = false; |
| |
| /* Cross-reference the expression and the type. */ |
| LAMBDA_EXPR_CLOSURE (lambda) = type; |
| CLASSTYPE_LAMBDA_EXPR (type) = lambda; |
| |
| /* Clear base types. */ |
| xref_basetypes (type, /*bases=*/NULL_TREE); |
| |
| /* Start the class. */ |
| type = begin_class_definition (type); |
| |
| return type; |
| } |
| |
| /* Returns the type to use for the return type of the operator() of a |
| closure class. */ |
| |
| tree |
| lambda_return_type (tree expr) |
| { |
| if (expr == NULL_TREE) |
| return void_type_node; |
| if (type_unknown_p (expr) |
| || BRACE_ENCLOSED_INITIALIZER_P (expr)) |
| { |
| cxx_incomplete_type_error (expr, TREE_TYPE (expr)); |
| return void_type_node; |
| } |
| gcc_checking_assert (!type_dependent_expression_p (expr)); |
| return cv_unqualified (type_decays_to (unlowered_expr_type (expr))); |
| } |
| |
| /* Given a LAMBDA_EXPR or closure type LAMBDA, return the op() of the |
| closure type. */ |
| |
| tree |
| lambda_function (tree lambda) |
| { |
| tree type; |
| if (TREE_CODE (lambda) == LAMBDA_EXPR) |
| type = LAMBDA_EXPR_CLOSURE (lambda); |
| else |
| type = lambda; |
| gcc_assert (LAMBDA_TYPE_P (type)); |
| /* Don't let debug_tree cause instantiation. */ |
| if (CLASSTYPE_TEMPLATE_INSTANTIATION (type) |
| && !COMPLETE_OR_OPEN_TYPE_P (type)) |
| return NULL_TREE; |
| lambda = lookup_member (type, ansi_opname (CALL_EXPR), |
| /*protect=*/0, /*want_type=*/false, |
| tf_warning_or_error); |
| if (lambda) |
| lambda = BASELINK_FUNCTIONS (lambda); |
| return lambda; |
| } |
| |
| /* Returns the type to use for the FIELD_DECL corresponding to the |
| capture of EXPR. |
| The caller should add REFERENCE_TYPE for capture by reference. */ |
| |
| tree |
| lambda_capture_field_type (tree expr) |
| { |
| tree type; |
| if (type_dependent_expression_p (expr) |
| && !(TREE_TYPE (expr) && TREE_CODE (TREE_TYPE (expr)) == POINTER_TYPE |
| && !type_uses_auto (TREE_TYPE (expr)))) |
| { |
| type = cxx_make_type (DECLTYPE_TYPE); |
| DECLTYPE_TYPE_EXPR (type) = expr; |
| DECLTYPE_FOR_LAMBDA_CAPTURE (type) = true; |
| SET_TYPE_STRUCTURAL_EQUALITY (type); |
| } |
| else |
| type = non_reference (unlowered_expr_type (expr)); |
| return type; |
| } |
| |
| /* 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 (LAMBDA_FUNCTION_P (fco)) |
| { |
| tree lambda = CLASSTYPE_LAMBDA_EXPR (current_class_type); |
| LAMBDA_EXPR_RETURN_TYPE (lambda) = return_type; |
| } |
| |
| if (DECL_CONV_FN_P (fco)) |
| DECL_NAME (fco) = mangle_conv_op_name_for_type (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; |
| |
| /* 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; |
| } |
| |
| /* Returns true iff DECL is a lambda capture proxy variable created by |
| build_capture_proxy. */ |
| |
| bool |
| is_capture_proxy (tree decl) |
| { |
| return (TREE_CODE (decl) == VAR_DECL |
| && DECL_HAS_VALUE_EXPR_P (decl) |
| && !DECL_ANON_UNION_VAR_P (decl) |
| && LAMBDA_FUNCTION_P (DECL_CONTEXT (decl))); |
| } |
| |
| /* Returns true iff DECL is a capture proxy for a normal capture |
| (i.e. without explicit initializer). */ |
| |
| bool |
| is_normal_capture_proxy (tree decl) |
| { |
| if (!is_capture_proxy (decl)) |
| /* It's not a capture proxy. */ |
| return false; |
| |
| /* It is a capture proxy, is it a normal capture? */ |
| tree val = DECL_VALUE_EXPR (decl); |
| if (val == error_mark_node) |
| return true; |
| |
| gcc_assert (TREE_CODE (val) == COMPONENT_REF); |
| val = TREE_OPERAND (val, 1); |
| return DECL_NORMAL_CAPTURE_P (val); |
| } |
| |
| /* VAR is a capture proxy created by build_capture_proxy; add it to the |
| current function, which is the operator() for the appropriate lambda. */ |
| |
| void |
| insert_capture_proxy (tree var) |
| { |
| cp_binding_level *b; |
| tree stmt_list; |
| |
| /* Put the capture proxy in the extra body block so that it won't clash |
| with a later local variable. */ |
| b = current_binding_level; |
| for (;;) |
| { |
| cp_binding_level *n = b->level_chain; |
| if (n->kind == sk_function_parms) |
| break; |
| b = n; |
| } |
| pushdecl_with_scope (var, b, false); |
| |
| /* And put a DECL_EXPR in the STATEMENT_LIST for the same block. */ |
| var = build_stmt (DECL_SOURCE_LOCATION (var), DECL_EXPR, var); |
| stmt_list = (*stmt_list_stack)[1]; |
| gcc_assert (stmt_list); |
| append_to_statement_list_force (var, &stmt_list); |
| } |
| |
| /* We've just finished processing a lambda; if the containing scope is also |
| a lambda, insert any capture proxies that were created while processing |
| the nested lambda. */ |
| |
| void |
| insert_pending_capture_proxies (void) |
| { |
| tree lam; |
| vec<tree, va_gc> *proxies; |
| unsigned i; |
| |
| if (!current_function_decl || !LAMBDA_FUNCTION_P (current_function_decl)) |
| return; |
| |
| lam = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (current_function_decl)); |
| proxies = LAMBDA_EXPR_PENDING_PROXIES (lam); |
| for (i = 0; i < vec_safe_length (proxies); ++i) |
| { |
| tree var = (*proxies)[i]; |
| insert_capture_proxy (var); |
| } |
| release_tree_vector (LAMBDA_EXPR_PENDING_PROXIES (lam)); |
| LAMBDA_EXPR_PENDING_PROXIES (lam) = NULL; |
| } |
| |
| /* Given REF, a COMPONENT_REF designating a field in the lambda closure, |
| return the type we want the proxy to have: the type of the field itself, |
| with added const-qualification if the lambda isn't mutable and the |
| capture is by value. */ |
| |
| tree |
| lambda_proxy_type (tree ref) |
| { |
| tree type; |
| if (REFERENCE_REF_P (ref)) |
| ref = TREE_OPERAND (ref, 0); |
| type = TREE_TYPE (ref); |
| if (!dependent_type_p (type) |
| || (type && TREE_CODE (type) == POINTER_TYPE)) |
| return type; |
| type = cxx_make_type (DECLTYPE_TYPE); |
| DECLTYPE_TYPE_EXPR (type) = ref; |
| DECLTYPE_FOR_LAMBDA_PROXY (type) = true; |
| SET_TYPE_STRUCTURAL_EQUALITY (type); |
| return type; |
| } |
| |
| /* MEMBER is a capture field in a lambda closure class. Now that we're |
| inside the operator(), build a placeholder var for future lookups and |
| debugging. */ |
| |
| tree |
| build_capture_proxy (tree member) |
| { |
| tree var, object, fn, closure, name, lam, type; |
| |
| closure = DECL_CONTEXT (member); |
| fn = lambda_function (closure); |
| lam = CLASSTYPE_LAMBDA_EXPR (closure); |
| |
| /* The proxy variable forwards to the capture field. */ |
| object = build_fold_indirect_ref (DECL_ARGUMENTS (fn)); |
| object = finish_non_static_data_member (member, object, NULL_TREE); |
| if (REFERENCE_REF_P (object)) |
| object = TREE_OPERAND (object, 0); |
| |
| /* Remove the __ inserted by add_capture. */ |
| name = get_identifier (IDENTIFIER_POINTER (DECL_NAME (member)) + 2); |
| |
| type = lambda_proxy_type (object); |
| var = build_decl (input_location, VAR_DECL, name, type); |
| SET_DECL_VALUE_EXPR (var, object); |
| DECL_HAS_VALUE_EXPR_P (var) = 1; |
| DECL_ARTIFICIAL (var) = 1; |
| TREE_USED (var) = 1; |
| DECL_CONTEXT (var) = fn; |
| |
| if (name == this_identifier) |
| { |
| gcc_assert (LAMBDA_EXPR_THIS_CAPTURE (lam) == member); |
| LAMBDA_EXPR_THIS_CAPTURE (lam) = var; |
| } |
| |
| if (fn == current_function_decl) |
| insert_capture_proxy (var); |
| else |
| vec_safe_push (LAMBDA_EXPR_PENDING_PROXIES (lam), var); |
| |
| return var; |
| } |
| |
| /* From an ID and INITIALIZER, create a capture (by reference if |
| BY_REFERENCE_P is true), add it to the capture-list for LAMBDA, |
| and return it. */ |
| |
| tree |
| add_capture (tree lambda, tree id, tree initializer, bool by_reference_p, |
| bool explicit_init_p) |
| { |
| char *buf; |
| tree type, member, name; |
| |
| type = lambda_capture_field_type (initializer); |
| if (by_reference_p) |
| { |
| type = build_reference_type (type); |
| if (!real_lvalue_p (initializer)) |
| error ("cannot capture %qE by reference", initializer); |
| } |
| else |
| /* Capture by copy requires a complete type. */ |
| type = complete_type (type); |
| |
| /* Add __ to the beginning of the field name so that user code |
| won't find the field with name lookup. We can't just leave the name |
| unset because template instantiation uses the name to find |
| instantiated fields. */ |
| buf = (char *) alloca (IDENTIFIER_LENGTH (id) + 3); |
| buf[1] = buf[0] = '_'; |
| memcpy (buf + 2, IDENTIFIER_POINTER (id), |
| IDENTIFIER_LENGTH (id) + 1); |
| name = get_identifier (buf); |
| |
| /* If TREE_TYPE isn't set, we're still in the introducer, so check |
| for duplicates. */ |
| if (!LAMBDA_EXPR_CLOSURE (lambda)) |
| { |
| if (IDENTIFIER_MARKED (name)) |
| { |
| pedwarn (input_location, 0, |
| "already captured %qD in lambda expression", id); |
| return NULL_TREE; |
| } |
| IDENTIFIER_MARKED (name) = true; |
| } |
| |
| /* Make member variable. */ |
| member = build_lang_decl (FIELD_DECL, name, type); |
| |
| if (!explicit_init_p) |
| /* Normal captures are invisible to name lookup but uses are replaced |
| with references to the capture field; we implement this by only |
| really making them invisible in unevaluated context; see |
| qualify_lookup. For now, let's make explicitly initialized captures |
| always visible. */ |
| DECL_NORMAL_CAPTURE_P (member) = true; |
| |
| if (id == this_identifier) |
| LAMBDA_EXPR_THIS_CAPTURE (lambda) = member; |
| |
| /* Add it to the appropriate closure class if we've started it. */ |
| if (current_class_type |
| && current_class_type == LAMBDA_EXPR_CLOSURE (lambda)) |
| finish_member_declaration (member); |
| |
| LAMBDA_EXPR_CAPTURE_LIST (lambda) |
| = tree_cons (member, initializer, LAMBDA_EXPR_CAPTURE_LIST (lambda)); |
| |
| if (LAMBDA_EXPR_CLOSURE (lambda)) |
| return build_capture_proxy (member); |
| /* For explicit captures we haven't started the function yet, so we wait |
| and build the proxy from cp_parser_lambda_body. */ |
| return NULL_TREE; |
| } |
| |
| /* Register all the capture members on the list CAPTURES, which is the |
| LAMBDA_EXPR_CAPTURE_LIST for the lambda after the introducer. */ |
| |
| void |
| register_capture_members (tree captures) |
| { |
| if (captures == NULL_TREE) |
| return; |
| |
| register_capture_members (TREE_CHAIN (captures)); |
| /* We set this in add_capture to avoid duplicates. */ |
| IDENTIFIER_MARKED (DECL_NAME (TREE_PURPOSE (captures))) = false; |
| finish_member_declaration (TREE_PURPOSE (captures)); |
| } |
| |
| /* Similar to add_capture, except this works on a stack of nested lambdas. |
| BY_REFERENCE_P in this case is derived from the default capture mode. |
| Returns the capture for the lambda at the bottom of the stack. */ |
| |
| tree |
| add_default_capture (tree lambda_stack, tree id, tree initializer) |
| { |
| bool this_capture_p = (id == this_identifier); |
| |
| tree var = NULL_TREE; |
| |
| tree saved_class_type = current_class_type; |
| |
| tree node; |
| |
| for (node = lambda_stack; |
| node; |
| node = TREE_CHAIN (node)) |
| { |
| tree lambda = TREE_VALUE (node); |
| |
| current_class_type = LAMBDA_EXPR_CLOSURE (lambda); |
| var = add_capture (lambda, |
| id, |
| initializer, |
| /*by_reference_p=*/ |
| (!this_capture_p |
| && (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda) |
| == CPLD_REFERENCE)), |
| /*explicit_init_p=*/false); |
| initializer = convert_from_reference (var); |
| } |
| |
| current_class_type = saved_class_type; |
| |
| return var; |
| } |
| |
| /* Return the capture pertaining to a use of 'this' in LAMBDA, in the form of an |
| INDIRECT_REF, possibly adding it through default capturing. */ |
| |
| tree |
| lambda_expr_this_capture (tree lambda) |
| { |
| tree result; |
| |
| tree this_capture = LAMBDA_EXPR_THIS_CAPTURE (lambda); |
| |
| /* In unevaluated context this isn't an odr-use, so just return the |
| nearest 'this'. */ |
| if (cp_unevaluated_operand) |
| { |
| /* In an NSDMI the fake 'this' pointer that we're using for |
| parsing is in scope_chain. */ |
| if (LAMBDA_EXPR_EXTRA_SCOPE (lambda) |
| && TREE_CODE (LAMBDA_EXPR_EXTRA_SCOPE (lambda)) == FIELD_DECL) |
| return scope_chain->x_current_class_ptr; |
| return lookup_name (this_identifier); |
| } |
| |
| /* Try to default capture 'this' if we can. */ |
| if (!this_capture |
| && LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda) != CPLD_NONE) |
| { |
| tree lambda_stack = NULL_TREE; |
| tree init = NULL_TREE; |
| |
| /* If we are in a lambda function, we can move out until we hit: |
| 1. a non-lambda function or NSDMI, |
| 2. a lambda function capturing 'this', or |
| 3. a non-default capturing lambda function. */ |
| for (tree tlambda = lambda; ;) |
| { |
| lambda_stack = tree_cons (NULL_TREE, |
| tlambda, |
| lambda_stack); |
| |
| if (LAMBDA_EXPR_EXTRA_SCOPE (tlambda) |
| && TREE_CODE (LAMBDA_EXPR_EXTRA_SCOPE (tlambda)) == FIELD_DECL) |
| { |
| /* In an NSDMI, we don't have a function to look up the decl in, |
| but the fake 'this' pointer that we're using for parsing is |
| in scope_chain. */ |
| init = scope_chain->x_current_class_ptr; |
| gcc_checking_assert |
| (init && (TREE_TYPE (TREE_TYPE (init)) |
| == current_nonlambda_class_type ())); |
| break; |
| } |
| |
| tree closure_decl = TYPE_NAME (LAMBDA_EXPR_CLOSURE (tlambda)); |
| tree containing_function = decl_function_context (closure_decl); |
| |
| if (containing_function == NULL_TREE) |
| /* We ran out of scopes; there's no 'this' to capture. */ |
| break; |
| |
| if (!LAMBDA_FUNCTION_P (containing_function)) |
| { |
| /* We found a non-lambda function. */ |
| if (DECL_NONSTATIC_MEMBER_FUNCTION_P (containing_function)) |
| /* First parameter is 'this'. */ |
| init = DECL_ARGUMENTS (containing_function); |
| break; |
| } |
| |
| tlambda |
| = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (containing_function)); |
| |
| if (LAMBDA_EXPR_THIS_CAPTURE (tlambda)) |
| { |
| /* An outer lambda has already captured 'this'. */ |
| init = LAMBDA_EXPR_THIS_CAPTURE (tlambda); |
| break; |
| } |
| |
| if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (tlambda) == CPLD_NONE) |
| /* An outer lambda won't let us capture 'this'. */ |
| break; |
| } |
| |
| if (init) |
| this_capture = add_default_capture (lambda_stack, |
| /*id=*/this_identifier, |
| init); |
| } |
| |
| if (!this_capture) |
| { |
| error ("%<this%> was not captured for this lambda function"); |
| result = error_mark_node; |
| } |
| else |
| { |
| /* To make sure that current_class_ref is for the lambda. */ |
| gcc_assert (TYPE_MAIN_VARIANT (TREE_TYPE (current_class_ref)) |
| == LAMBDA_EXPR_CLOSURE (lambda)); |
| |
| result = this_capture; |
| |
| /* If 'this' is captured, each use of 'this' is transformed into an |
| access to the corresponding unnamed data member of the closure |
| type cast (_expr.cast_ 5.4) to the type of 'this'. [ The cast |
| ensures that the transformed expression is an rvalue. ] */ |
| result = rvalue (result); |
| } |
| |
| return result; |
| } |
| |
| /* We don't want to capture 'this' until we know we need it, i.e. after |
| overload resolution has chosen a non-static member function. At that |
| point we call this function to turn a dummy object into a use of the |
| 'this' capture. */ |
| |
| tree |
| maybe_resolve_dummy (tree object) |
| { |
| if (!is_dummy_object (object)) |
| return object; |
| |
| tree type = TYPE_MAIN_VARIANT (TREE_TYPE (object)); |
| gcc_assert (TREE_CODE (type) != POINTER_TYPE); |
| |
| if (type != current_class_type |
| && current_class_type |
| && LAMBDA_TYPE_P (current_class_type) |
| && DERIVED_FROM_P (type, current_nonlambda_class_type ())) |
| { |
| /* In a lambda, need to go through 'this' capture. */ |
| tree lam = CLASSTYPE_LAMBDA_EXPR (current_class_type); |
| tree cap = lambda_expr_this_capture (lam); |
| if (cap && cap != error_mark_node) |
| object = build_x_indirect_ref (EXPR_LOCATION (object), cap, |
| RO_NULL, tf_warning_or_error); |
| } |
| |
| return object; |
| } |
| |
| /* Returns the method basetype of the innermost non-lambda function, or |
| NULL_TREE if none. */ |
| |
| tree |
| nonlambda_method_basetype (void) |
| { |
| tree fn, type; |
| if (!current_class_ref) |
| return NULL_TREE; |
| |
| type = current_class_type; |
| if (!LAMBDA_TYPE_P (type)) |
| return type; |
| |
| /* Find the nearest enclosing non-lambda function. */ |
| fn = TYPE_NAME (type); |
| do |
| fn = decl_function_context (fn); |
| while (fn && LAMBDA_FUNCTION_P (fn)); |
| |
| if (!fn || !DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)) |
| return NULL_TREE; |
| |
| return TYPE_METHOD_BASETYPE (TREE_TYPE (fn)); |
| } |
| |
| /* If the closure TYPE has a static op(), also add a conversion to function |
| pointer. */ |
| |
| void |
| maybe_add_lambda_conv_op (tree type) |
| { |
| bool nested = (current_function_decl != NULL_TREE); |
| tree callop = lambda_function (type); |
| tree rettype, name, fntype, fn, body, compound_stmt; |
| tree thistype, stattype, statfn, convfn, call, arg; |
| vec<tree, va_gc> *argvec; |
| |
| if (LAMBDA_EXPR_CAPTURE_LIST (CLASSTYPE_LAMBDA_EXPR (type)) != NULL_TREE) |
| return; |
| |
| if (processing_template_decl) |
| return; |
| |
| stattype = build_function_type (TREE_TYPE (TREE_TYPE (callop)), |
| FUNCTION_ARG_CHAIN (callop)); |
| |
| /* First build up the conversion op. */ |
| |
| rettype = build_pointer_type (stattype); |
| name = mangle_conv_op_name_for_type (rettype); |
| thistype = cp_build_qualified_type (type, TYPE_QUAL_CONST); |
| fntype = build_method_type_directly (thistype, rettype, void_list_node); |
| fn = convfn = build_lang_decl (FUNCTION_DECL, name, fntype); |
| DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (callop); |
| |
| if (TARGET_PTRMEMFUNC_VBIT_LOCATION == ptrmemfunc_vbit_in_pfn |
| && DECL_ALIGN (fn) < 2 * BITS_PER_UNIT) |
| DECL_ALIGN (fn) = 2 * BITS_PER_UNIT; |
| |
| SET_OVERLOADED_OPERATOR_CODE (fn, TYPE_EXPR); |
| grokclassfn (type, fn, NO_SPECIAL); |
| set_linkage_according_to_type (type, fn); |
| rest_of_decl_compilation (fn, toplevel_bindings_p (), at_eof); |
| DECL_IN_AGGR_P (fn) = 1; |
| DECL_ARTIFICIAL (fn) = 1; |
| DECL_NOT_REALLY_EXTERN (fn) = 1; |
| DECL_DECLARED_INLINE_P (fn) = 1; |
| DECL_ARGUMENTS (fn) = build_this_parm (fntype, TYPE_QUAL_CONST); |
| if (nested) |
| DECL_INTERFACE_KNOWN (fn) = 1; |
| |
| add_method (type, fn, NULL_TREE); |
| |
| /* Generic thunk code fails for varargs; we'll complain in mark_used if |
| the conversion op is used. */ |
| if (varargs_function_p (callop)) |
| { |
| DECL_DELETED_FN (fn) = 1; |
| return; |
| } |
| |
| /* Now build up the thunk to be returned. */ |
| |
| name = get_identifier ("_FUN"); |
| fn = statfn = build_lang_decl (FUNCTION_DECL, name, stattype); |
| DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (callop); |
| if (TARGET_PTRMEMFUNC_VBIT_LOCATION == ptrmemfunc_vbit_in_pfn |
| && DECL_ALIGN (fn) < 2 * BITS_PER_UNIT) |
| DECL_ALIGN (fn) = 2 * BITS_PER_UNIT; |
| grokclassfn (type, fn, NO_SPECIAL); |
| set_linkage_according_to_type (type, fn); |
| rest_of_decl_compilation (fn, toplevel_bindings_p (), at_eof); |
| DECL_IN_AGGR_P (fn) = 1; |
| DECL_ARTIFICIAL (fn) = 1; |
| DECL_NOT_REALLY_EXTERN (fn) = 1; |
| DECL_DECLARED_INLINE_P (fn) = 1; |
| DECL_STATIC_FUNCTION_P (fn) = 1; |
| DECL_ARGUMENTS (fn) = copy_list (DECL_CHAIN (DECL_ARGUMENTS (callop))); |
| for (arg = DECL_ARGUMENTS (fn); arg; arg = DECL_CHAIN (arg)) |
| DECL_CONTEXT (arg) = fn; |
| if (nested) |
| DECL_INTERFACE_KNOWN (fn) = 1; |
| |
| add_method (type, fn, NULL_TREE); |
| |
| if (nested) |
| push_function_context (); |
| else |
| /* Still increment function_depth so that we don't GC in the |
| middle of an expression. */ |
| ++function_depth; |
| |
| /* Generate the body of the thunk. */ |
| |
| start_preparsed_function (statfn, NULL_TREE, |
| SF_PRE_PARSED | SF_INCLASS_INLINE); |
| if (DECL_ONE_ONLY (statfn)) |
| { |
| /* Put the thunk in the same comdat group as the call op. */ |
| symtab_add_to_same_comdat_group |
| ((symtab_node) cgraph_get_create_node (statfn), |
| (symtab_node) cgraph_get_create_node (callop)); |
| } |
| body = begin_function_body (); |
| compound_stmt = begin_compound_stmt (0); |
| |
| arg = build1 (NOP_EXPR, TREE_TYPE (DECL_ARGUMENTS (callop)), |
| null_pointer_node); |
| argvec = make_tree_vector (); |
| argvec->quick_push (arg); |
| for (arg = DECL_ARGUMENTS (statfn); arg; arg = DECL_CHAIN (arg)) |
| { |
| mark_exp_read (arg); |
| vec_safe_push (argvec, arg); |
| } |
| call = build_call_a (callop, argvec->length (), argvec->address ()); |
| CALL_FROM_THUNK_P (call) = 1; |
| if (MAYBE_CLASS_TYPE_P (TREE_TYPE (call))) |
| call = build_cplus_new (TREE_TYPE (call), call, tf_warning_or_error); |
| call = convert_from_reference (call); |
| finish_return_stmt (call); |
| |
| finish_compound_stmt (compound_stmt); |
| finish_function_body (body); |
| |
| expand_or_defer_fn (finish_function (2)); |
| |
| /* Generate the body of the conversion op. */ |
| |
| start_preparsed_function (convfn, NULL_TREE, |
| SF_PRE_PARSED | SF_INCLASS_INLINE); |
| body = begin_function_body (); |
| compound_stmt = begin_compound_stmt (0); |
| |
| /* decl_needed_p needs to see that it's used. */ |
| TREE_USED (statfn) = 1; |
| finish_return_stmt (decay_conversion (statfn, tf_warning_or_error)); |
| |
| finish_compound_stmt (compound_stmt); |
| finish_function_body (body); |
| |
| expand_or_defer_fn (finish_function (2)); |
| |
| if (nested) |
| pop_function_context (); |
| else |
| --function_depth; |
| } |
| |
| /* Returns true iff VAL is a lambda-related declaration which should |
| be ignored by unqualified lookup. */ |
| |
| bool |
| is_lambda_ignored_entity (tree val) |
| { |
| /* In unevaluated context, look past normal capture proxies. */ |
| if (cp_unevaluated_operand && is_normal_capture_proxy (val)) |
| return true; |
| |
| /* Always ignore lambda fields, their names are only for debugging. */ |
| if (TREE_CODE (val) == FIELD_DECL |
| && CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (val))) |
| return true; |
| |
| /* None of the lookups that use qualify_lookup want the op() from the |
| lambda; they want the one from the enclosing class. */ |
| if (TREE_CODE (val) == FUNCTION_DECL && LAMBDA_FUNCTION_P (val)) |
| return true; |
| |
| return false; |
| } |
| |
| #include "gt-cp-semantics.h" |