| /* 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-2021 Free Software Foundation, Inc. |
| Written by Mark Mitchell (mmitchell@usa.net) based on code found |
| formerly in parse.y and pt.c. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it |
| under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GCC is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "target.h" |
| #include "bitmap.h" |
| #include "cp-tree.h" |
| #include "stringpool.h" |
| #include "cgraph.h" |
| #include "stmt.h" |
| #include "varasm.h" |
| #include "stor-layout.h" |
| #include "c-family/c-objc.h" |
| #include "tree-inline.h" |
| #include "intl.h" |
| #include "tree-iterator.h" |
| #include "omp-general.h" |
| #include "convert.h" |
| #include "stringpool.h" |
| #include "attribs.h" |
| #include "gomp-constants.h" |
| #include "predict.h" |
| #include "memmodel.h" |
| |
| /* There routines provide a modular interface to perform many parsing |
| operations. They may therefore be used during actual parsing, or |
| during template instantiation, which may be regarded as a |
| degenerate form of parsing. */ |
| |
| static tree maybe_convert_cond (tree); |
| static tree finalize_nrv_r (tree *, int *, void *); |
| static tree capture_decltype (tree); |
| |
| /* Used for OpenMP non-static data member privatization. */ |
| |
| static hash_map<tree, tree> *omp_private_member_map; |
| static vec<tree> omp_private_member_vec; |
| static bool omp_private_member_ignore_next; |
| |
| |
| /* Deferred Access Checking Overview |
| --------------------------------- |
| |
| Most C++ expressions and declarations require access checking |
| to be performed during parsing. However, in several cases, |
| this has to be treated differently. |
| |
| For member declarations, access checking has to be deferred |
| until more information about the declaration is known. For |
| example: |
| |
| class A { |
| typedef int X; |
| public: |
| X f(); |
| }; |
| |
| A::X A::f(); |
| A::X g(); |
| |
| When we are parsing the function return type `A::X', we don't |
| really know if this is allowed until we parse the function name. |
| |
| Furthermore, some contexts require that access checking is |
| never performed at all. These include class heads, and template |
| instantiations. |
| |
| Typical use of access checking functions is described here: |
| |
| 1. When we enter a context that requires certain access checking |
| mode, the function `push_deferring_access_checks' is called with |
| DEFERRING argument specifying the desired mode. Access checking |
| may be performed immediately (dk_no_deferred), deferred |
| (dk_deferred), or not performed (dk_no_check). |
| |
| 2. When a declaration such as a type, or a variable, is encountered, |
| the function `perform_or_defer_access_check' is called. It |
| maintains a vector of all deferred checks. |
| |
| 3. The global `current_class_type' or `current_function_decl' is then |
| setup by the parser. `enforce_access' relies on these information |
| to check access. |
| |
| 4. Upon exiting the context mentioned in step 1, |
| `perform_deferred_access_checks' is called to check all declaration |
| stored in the vector. `pop_deferring_access_checks' is then |
| called to restore the previous access checking mode. |
| |
| In case of parsing error, we simply call `pop_deferring_access_checks' |
| without `perform_deferred_access_checks'. */ |
| |
| struct GTY(()) deferred_access { |
| /* A vector representing name-lookups for which we have deferred |
| checking access controls. We cannot check the accessibility of |
| names used in a decl-specifier-seq until we know what is being |
| declared because code like: |
| |
| class A { |
| class B {}; |
| B* f(); |
| } |
| |
| A::B* A::f() { return 0; } |
| |
| is valid, even though `A::B' is not generally accessible. */ |
| vec<deferred_access_check, va_gc> *deferred_access_checks; |
| |
| /* The current mode of access checks. */ |
| enum deferring_kind deferring_access_checks_kind; |
| }; |
| |
| /* Data for deferred access checking. */ |
| static GTY(()) vec<deferred_access, va_gc> *deferred_access_stack; |
| static GTY(()) unsigned deferred_access_no_check; |
| |
| /* Save the current deferred access states and start deferred |
| access checking iff DEFER_P is true. */ |
| |
| void |
| push_deferring_access_checks (deferring_kind deferring) |
| { |
| /* For context like template instantiation, access checking |
| disabling applies to all nested context. */ |
| if (deferred_access_no_check || deferring == dk_no_check) |
| deferred_access_no_check++; |
| else |
| { |
| deferred_access e = {NULL, deferring}; |
| vec_safe_push (deferred_access_stack, e); |
| } |
| } |
| |
| /* Save the current deferred access states and start deferred access |
| checking, continuing the set of deferred checks in CHECKS. */ |
| |
| void |
| reopen_deferring_access_checks (vec<deferred_access_check, va_gc> * checks) |
| { |
| push_deferring_access_checks (dk_deferred); |
| if (!deferred_access_no_check) |
| deferred_access_stack->last().deferred_access_checks = checks; |
| } |
| |
| /* Resume deferring access checks again after we stopped doing |
| this previously. */ |
| |
| void |
| resume_deferring_access_checks (void) |
| { |
| if (!deferred_access_no_check) |
| deferred_access_stack->last().deferring_access_checks_kind = dk_deferred; |
| } |
| |
| /* Stop deferring access checks. */ |
| |
| void |
| stop_deferring_access_checks (void) |
| { |
| if (!deferred_access_no_check) |
| deferred_access_stack->last().deferring_access_checks_kind = dk_no_deferred; |
| } |
| |
| /* Discard the current deferred access checks and restore the |
| previous states. */ |
| |
| void |
| pop_deferring_access_checks (void) |
| { |
| if (deferred_access_no_check) |
| deferred_access_no_check--; |
| else |
| deferred_access_stack->pop (); |
| } |
| |
| /* Returns a TREE_LIST representing the deferred checks. |
| The TREE_PURPOSE of each node is the type through which the |
| access occurred; the TREE_VALUE is the declaration named. |
| */ |
| |
| vec<deferred_access_check, va_gc> * |
| get_deferred_access_checks (void) |
| { |
| if (deferred_access_no_check) |
| return NULL; |
| else |
| return (deferred_access_stack->last().deferred_access_checks); |
| } |
| |
| /* Take current deferred checks and combine with the |
| previous states if we also defer checks previously. |
| Otherwise perform checks now. */ |
| |
| void |
| pop_to_parent_deferring_access_checks (void) |
| { |
| if (deferred_access_no_check) |
| deferred_access_no_check--; |
| else |
| { |
| vec<deferred_access_check, va_gc> *checks; |
| deferred_access *ptr; |
| |
| checks = (deferred_access_stack->last ().deferred_access_checks); |
| |
| deferred_access_stack->pop (); |
| ptr = &deferred_access_stack->last (); |
| if (ptr->deferring_access_checks_kind == dk_no_deferred) |
| { |
| /* Check access. */ |
| perform_access_checks (checks, tf_warning_or_error); |
| } |
| else |
| { |
| /* Merge with parent. */ |
| int i, j; |
| deferred_access_check *chk, *probe; |
| |
| FOR_EACH_VEC_SAFE_ELT (checks, i, chk) |
| { |
| FOR_EACH_VEC_SAFE_ELT (ptr->deferred_access_checks, j, probe) |
| { |
| if (probe->binfo == chk->binfo && |
| probe->decl == chk->decl && |
| probe->diag_decl == chk->diag_decl) |
| goto found; |
| } |
| /* Insert into parent's checks. */ |
| vec_safe_push (ptr->deferred_access_checks, *chk); |
| found:; |
| } |
| } |
| } |
| } |
| |
| /* Called from enforce_access. A class has attempted (but failed) to access |
| DECL. It is already established that a baseclass of that class, |
| PARENT_BINFO, has private access to DECL. Examine certain special cases |
| to find a decl that accurately describes the source of the problem. If |
| none of the special cases apply, simply return DECL as the source of the |
| problem. */ |
| |
| static tree |
| get_class_access_diagnostic_decl (tree parent_binfo, tree decl) |
| { |
| /* When a class is denied access to a decl in a baseclass, most of the |
| time it is because the decl itself was declared as private at the point |
| of declaration. |
| |
| However, in C++, there are (at least) two situations in which a decl |
| can be private even though it was not originally defined as such. |
| These two situations only apply if a baseclass had private access to |
| DECL (this function is only called if that is the case). */ |
| |
| /* We should first check whether the reason the parent had private access |
| to DECL was simply because DECL was created and declared as private in |
| the parent. If it was, then DECL is definitively the source of the |
| problem. */ |
| if (SAME_BINFO_TYPE_P (context_for_name_lookup (decl), |
| BINFO_TYPE (parent_binfo))) |
| return decl; |
| |
| /* 1. If the "using" keyword is used to inherit DECL within the parent, |
| this may cause DECL to be private, so we should return the using |
| statement as the source of the problem. |
| |
| Scan the fields of PARENT_BINFO and see if there are any using decls. If |
| there are, see if they inherit DECL. If they do, that's where DECL must |
| have been declared private. */ |
| |
| for (tree parent_field = TYPE_FIELDS (BINFO_TYPE (parent_binfo)); |
| parent_field; |
| parent_field = DECL_CHAIN (parent_field)) |
| /* Not necessary, but also check TREE_PRIVATE for the sake of |
| eliminating obviously non-relevant using decls. */ |
| if (TREE_CODE (parent_field) == USING_DECL |
| && TREE_PRIVATE (parent_field)) |
| { |
| tree decl_stripped = strip_using_decl (parent_field); |
| |
| /* The using statement might be overloaded. If so, we need to |
| check all of the overloads. */ |
| for (ovl_iterator iter (decl_stripped); iter; ++iter) |
| /* If equal, the using statement inherits DECL, and so is the |
| source of the access failure, so return it. */ |
| if (*iter == decl) |
| return parent_field; |
| } |
| |
| /* 2. If DECL was privately inherited by the parent class, then DECL will |
| be inaccessible, even though it may originally have been accessible to |
| deriving classes. In that case, the fault lies with the parent, since it |
| used a private inheritance, so we return the parent as the source of the |
| problem. |
| |
| Since this is the last check, we just assume it's true. At worst, it |
| will simply point to the class that failed to give access, which is |
| technically true. */ |
| return TYPE_NAME (BINFO_TYPE (parent_binfo)); |
| } |
| |
| /* If the current scope isn't allowed to access DECL along |
| BASETYPE_PATH, give an error, or if we're parsing a function or class |
| template, defer the access check to be performed at instantiation time. |
| The most derived class in BASETYPE_PATH is the one used to qualify DECL. |
| DIAG_DECL is the declaration to use in the error diagnostic. */ |
| |
| static bool |
| enforce_access (tree basetype_path, tree decl, tree diag_decl, |
| tsubst_flags_t complain, access_failure_info *afi = NULL) |
| { |
| gcc_assert (TREE_CODE (basetype_path) == TREE_BINFO); |
| |
| if (flag_new_inheriting_ctors |
| && DECL_INHERITED_CTOR (decl)) |
| { |
| /* 7.3.3/18: The additional constructors are accessible if they would be |
| accessible when used to construct an object of the corresponding base |
| class. */ |
| decl = strip_inheriting_ctors (decl); |
| basetype_path = lookup_base (basetype_path, DECL_CONTEXT (decl), |
| ba_any, NULL, complain); |
| } |
| |
| tree cs = current_scope (); |
| if (processing_template_decl |
| && (CLASS_TYPE_P (cs) || TREE_CODE (cs) == FUNCTION_DECL)) |
| if (tree template_info = get_template_info (cs)) |
| { |
| /* When parsing a function or class template, we in general need to |
| defer access checks until template instantiation time, since a friend |
| declaration may grant access only to a particular specialization of |
| the template. */ |
| |
| if (accessible_p (basetype_path, decl, /*consider_local_p=*/true)) |
| /* But if the member is deemed accessible at parse time, then we can |
| assume it'll be accessible at instantiation time. */ |
| return true; |
| |
| /* Access of a dependent decl should be rechecked after tsubst'ing |
| into the user of the decl, rather than explicitly deferring the |
| check here. */ |
| gcc_assert (!uses_template_parms (decl)); |
| if (TREE_CODE (decl) == FIELD_DECL) |
| gcc_assert (!uses_template_parms (DECL_CONTEXT (decl))); |
| |
| /* Defer this access check until instantiation time. */ |
| deferred_access_check access_check; |
| access_check.binfo = basetype_path; |
| access_check.decl = decl; |
| access_check.diag_decl = diag_decl; |
| access_check.loc = input_location; |
| vec_safe_push (TI_DEFERRED_ACCESS_CHECKS (template_info), access_check); |
| return true; |
| } |
| |
| if (!accessible_p (basetype_path, decl, /*consider_local_p=*/true)) |
| { |
| if (flag_new_inheriting_ctors) |
| diag_decl = strip_inheriting_ctors (diag_decl); |
| if (complain & tf_error) |
| { |
| access_kind access_failure_reason = ak_none; |
| |
| /* By default, using the decl as the source of the problem will |
| usually give correct results. */ |
| tree diag_location = diag_decl; |
| |
| /* However, if a parent of BASETYPE_PATH had private access to decl, |
| then it actually might be the case that the source of the problem |
| is not DECL. */ |
| tree parent_binfo = get_parent_with_private_access (decl, |
| basetype_path); |
| |
| /* So if a parent did have private access, then we need to do |
| special checks to obtain the best diagnostic location decl. */ |
| if (parent_binfo != NULL_TREE) |
| { |
| diag_location = get_class_access_diagnostic_decl (parent_binfo, |
| diag_decl); |
| |
| /* We also at this point know that the reason access failed was |
| because decl was private. */ |
| access_failure_reason = ak_private; |
| } |
| |
| /* Finally, generate an error message. */ |
| complain_about_access (decl, diag_decl, diag_location, true, |
| access_failure_reason); |
| } |
| if (afi) |
| afi->record_access_failure (basetype_path, decl, diag_decl); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* Perform the access checks in CHECKS. The TREE_PURPOSE of each node |
| is the BINFO indicating the qualifying scope used to access the |
| DECL node stored in the TREE_VALUE of the node. If CHECKS is empty |
| or we aren't in SFINAE context or all the checks succeed return TRUE, |
| otherwise FALSE. */ |
| |
| bool |
| perform_access_checks (vec<deferred_access_check, va_gc> *checks, |
| tsubst_flags_t complain) |
| { |
| int i; |
| deferred_access_check *chk; |
| location_t loc = input_location; |
| bool ok = true; |
| |
| if (!checks) |
| return true; |
| |
| FOR_EACH_VEC_SAFE_ELT (checks, i, chk) |
| { |
| input_location = chk->loc; |
| ok &= enforce_access (chk->binfo, chk->decl, chk->diag_decl, complain); |
| } |
| |
| input_location = loc; |
| return (complain & tf_error) ? true : ok; |
| } |
| |
| /* Perform the deferred access checks. |
| |
| After performing the checks, we still have to keep the list |
| `deferred_access_stack->deferred_access_checks' since we may want |
| to check access for them again later in a different context. |
| For example: |
| |
| class A { |
| typedef int X; |
| static X a; |
| }; |
| A::X A::a, x; // No error for `A::a', error for `x' |
| |
| We have to perform deferred access of `A::X', first with `A::a', |
| next with `x'. Return value like perform_access_checks above. */ |
| |
| bool |
| perform_deferred_access_checks (tsubst_flags_t complain) |
| { |
| return perform_access_checks (get_deferred_access_checks (), complain); |
| } |
| |
| /* Defer checking the accessibility of DECL, when looked up in |
| BINFO. DIAG_DECL is the declaration to use to print diagnostics. |
| Return value like perform_access_checks above. |
| If non-NULL, report failures to AFI. */ |
| |
| bool |
| perform_or_defer_access_check (tree binfo, tree decl, tree diag_decl, |
| tsubst_flags_t complain, |
| access_failure_info *afi) |
| { |
| int i; |
| deferred_access *ptr; |
| deferred_access_check *chk; |
| |
| /* Exit if we are in a context that no access checking is performed. */ |
| if (deferred_access_no_check) |
| return true; |
| |
| gcc_assert (TREE_CODE (binfo) == TREE_BINFO); |
| |
| ptr = &deferred_access_stack->last (); |
| |
| /* If we are not supposed to defer access checks, just check now. */ |
| if (ptr->deferring_access_checks_kind == dk_no_deferred) |
| { |
| bool ok = enforce_access (binfo, decl, diag_decl, complain, afi); |
| return (complain & tf_error) ? true : ok; |
| } |
| |
| /* See if we are already going to perform this check. */ |
| FOR_EACH_VEC_SAFE_ELT (ptr->deferred_access_checks, i, chk) |
| { |
| if (chk->decl == decl && chk->binfo == binfo && |
| chk->diag_decl == diag_decl) |
| { |
| return true; |
| } |
| } |
| /* If not, record the check. */ |
| deferred_access_check new_access = {binfo, decl, diag_decl, input_location}; |
| vec_safe_push (ptr->deferred_access_checks, new_access); |
| |
| return true; |
| } |
| |
| /* Returns nonzero if the current statement is a full expression, |
| i.e. temporaries created during that statement should be destroyed |
| at the end of the statement. */ |
| |
| int |
| stmts_are_full_exprs_p (void) |
| { |
| return current_stmt_tree ()->stmts_are_full_exprs_p; |
| } |
| |
| /* T is a statement. Add it to the statement-tree. This is the C++ |
| version. The C/ObjC frontends have a slightly different version of |
| this function. */ |
| |
| tree |
| add_stmt (tree t) |
| { |
| enum tree_code code = TREE_CODE (t); |
| |
| if (EXPR_P (t) && code != LABEL_EXPR) |
| { |
| if (!EXPR_HAS_LOCATION (t)) |
| SET_EXPR_LOCATION (t, input_location); |
| |
| /* When we expand a statement-tree, we must know whether or not the |
| statements are full-expressions. We record that fact here. */ |
| if (STATEMENT_CODE_P (TREE_CODE (t))) |
| STMT_IS_FULL_EXPR_P (t) = stmts_are_full_exprs_p (); |
| } |
| |
| if (code == LABEL_EXPR || code == CASE_LABEL_EXPR) |
| STATEMENT_LIST_HAS_LABEL (cur_stmt_list) = 1; |
| |
| /* Add T to the statement-tree. Non-side-effect statements need to be |
| recorded during statement expressions. */ |
| gcc_checking_assert (!stmt_list_stack->is_empty ()); |
| append_to_statement_list_force (t, &cur_stmt_list); |
| |
| return t; |
| } |
| |
| /* Returns the stmt_tree to which statements are currently being added. */ |
| |
| stmt_tree |
| current_stmt_tree (void) |
| { |
| return (cfun |
| ? &cfun->language->base.x_stmt_tree |
| : &scope_chain->x_stmt_tree); |
| } |
| |
| /* If statements are full expressions, wrap STMT in a CLEANUP_POINT_EXPR. */ |
| |
| static tree |
| maybe_cleanup_point_expr (tree expr) |
| { |
| if (!processing_template_decl && stmts_are_full_exprs_p ()) |
| expr = fold_build_cleanup_point_expr (TREE_TYPE (expr), expr); |
| return expr; |
| } |
| |
| /* Like maybe_cleanup_point_expr except have the type of the new expression be |
| void so we don't need to create a temporary variable to hold the inner |
| expression. The reason why we do this is because the original type might be |
| an aggregate and we cannot create a temporary variable for that type. */ |
| |
| tree |
| maybe_cleanup_point_expr_void (tree expr) |
| { |
| if (!processing_template_decl && stmts_are_full_exprs_p ()) |
| expr = fold_build_cleanup_point_expr (void_type_node, expr); |
| return expr; |
| } |
| |
| |
| |
| /* Create a declaration statement for the declaration given by the DECL. */ |
| |
| void |
| add_decl_expr (tree decl) |
| { |
| tree r = build_stmt (DECL_SOURCE_LOCATION (decl), DECL_EXPR, decl); |
| if (DECL_INITIAL (decl) |
| || (DECL_SIZE (decl) && TREE_SIDE_EFFECTS (DECL_SIZE (decl)))) |
| r = maybe_cleanup_point_expr_void (r); |
| add_stmt (r); |
| } |
| |
| /* Set EXPR_LOCATION of the cleanups of any CLEANUP_STMT in STMTS to LOC. */ |
| |
| static void |
| set_cleanup_locs (tree stmts, location_t loc) |
| { |
| if (TREE_CODE (stmts) == CLEANUP_STMT) |
| { |
| protected_set_expr_location (CLEANUP_EXPR (stmts), loc); |
| set_cleanup_locs (CLEANUP_BODY (stmts), loc); |
| } |
| else if (TREE_CODE (stmts) == STATEMENT_LIST) |
| for (tree stmt : tsi_range (stmts)) |
| set_cleanup_locs (stmt, loc); |
| } |
| |
| /* 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); |
| |
| /* input_location is the last token of the scope, usually a }. */ |
| set_cleanup_locs (stmt_list, input_location); |
| |
| if (!processing_template_decl) |
| { |
| stmt_list = c_build_bind_expr (input_location, block, stmt_list); |
| /* ??? See c_end_compound_stmt re statement expressions. */ |
| } |
| |
| return stmt_list; |
| } |
| |
| /* Begin a new scope. */ |
| |
| static tree |
| do_pushlevel (scope_kind sk) |
| { |
| tree ret = push_stmt_list (); |
| if (stmts_are_full_exprs_p ()) |
| begin_scope (sk, NULL); |
| return ret; |
| } |
| |
| /* Queue a cleanup. CLEANUP is an expression/statement to be executed |
| when the current scope is exited. EH_ONLY is true when this is not |
| meant to apply to normal control flow transfer. */ |
| |
| void |
| push_cleanup (tree decl, tree cleanup, bool eh_only) |
| { |
| tree stmt = build_stmt (input_location, CLEANUP_STMT, NULL, cleanup, decl); |
| CLEANUP_EH_ONLY (stmt) = eh_only; |
| add_stmt (stmt); |
| CLEANUP_BODY (stmt) = push_stmt_list (); |
| } |
| |
| /* Simple infinite loop tracking for -Wreturn-type. We keep a stack of all |
| the current loops, represented by 'NULL_TREE' if we've seen a possible |
| exit, and 'error_mark_node' if not. This is currently used only to |
| suppress the warning about a function with no return statements, and |
| therefore we don't bother noting returns as possible exits. We also |
| don't bother with gotos. */ |
| |
| static void |
| begin_maybe_infinite_loop (tree cond) |
| { |
| /* Only track this while parsing a function, not during instantiation. */ |
| if (!cfun || (DECL_TEMPLATE_INSTANTIATION (current_function_decl) |
| && !processing_template_decl)) |
| return; |
| bool maybe_infinite = true; |
| if (cond) |
| { |
| cond = fold_non_dependent_expr (cond); |
| maybe_infinite = integer_nonzerop (cond); |
| } |
| vec_safe_push (cp_function_chain->infinite_loops, |
| maybe_infinite ? error_mark_node : NULL_TREE); |
| |
| } |
| |
| /* A break is a possible exit for the current loop. */ |
| |
| void |
| break_maybe_infinite_loop (void) |
| { |
| if (!cfun) |
| return; |
| cp_function_chain->infinite_loops->last() = NULL_TREE; |
| } |
| |
| /* If we reach the end of the loop without seeing a possible exit, we have |
| an infinite loop. */ |
| |
| static void |
| end_maybe_infinite_loop (tree cond) |
| { |
| if (!cfun || (DECL_TEMPLATE_INSTANTIATION (current_function_decl) |
| && !processing_template_decl)) |
| return; |
| tree current = cp_function_chain->infinite_loops->pop(); |
| if (current != NULL_TREE) |
| { |
| cond = fold_non_dependent_expr (cond); |
| if (integer_nonzerop (cond)) |
| current_function_infinite_loop = 1; |
| } |
| } |
| |
| |
| /* Begin a conditional that might contain a declaration. When generating |
| normal code, we want the declaration to appear before the statement |
| containing the conditional. When generating template code, we want the |
| conditional to be rendered as the raw DECL_EXPR. */ |
| |
| static void |
| begin_cond (tree *cond_p) |
| { |
| if (processing_template_decl) |
| *cond_p = push_stmt_list (); |
| } |
| |
| /* Finish such a conditional. */ |
| |
| static void |
| finish_cond (tree *cond_p, tree expr) |
| { |
| if (processing_template_decl) |
| { |
| tree cond = pop_stmt_list (*cond_p); |
| |
| if (expr == NULL_TREE) |
| /* Empty condition in 'for'. */ |
| gcc_assert (empty_expr_stmt_p (cond)); |
| else if (check_for_bare_parameter_packs (expr)) |
| expr = error_mark_node; |
| else if (!empty_expr_stmt_p (cond)) |
| expr = build2 (COMPOUND_EXPR, TREE_TYPE (expr), cond, expr); |
| } |
| *cond_p = expr; |
| } |
| |
| /* If *COND_P specifies a conditional with a declaration, transform the |
| loop such that |
| while (A x = 42) { } |
| for (; A x = 42;) { } |
| becomes |
| while (true) { A x = 42; if (!x) break; } |
| for (;;) { A x = 42; if (!x) break; } |
| The statement list for BODY will be empty if the conditional did |
| not declare anything. */ |
| |
| static void |
| simplify_loop_decl_cond (tree *cond_p, tree body) |
| { |
| tree cond, if_stmt; |
| |
| if (!TREE_SIDE_EFFECTS (body)) |
| return; |
| |
| cond = *cond_p; |
| *cond_p = boolean_true_node; |
| |
| if_stmt = begin_if_stmt (); |
| cond = cp_build_unary_op (TRUTH_NOT_EXPR, cond, false, tf_warning_or_error); |
| finish_if_stmt_cond (cond, if_stmt); |
| finish_break_stmt (); |
| finish_then_clause (if_stmt); |
| finish_if_stmt (if_stmt); |
| } |
| |
| /* Finish a goto-statement. */ |
| |
| tree |
| finish_goto_stmt (tree destination) |
| { |
| if (identifier_p (destination)) |
| destination = lookup_label (destination); |
| |
| /* We warn about unused labels with -Wunused. That means we have to |
| mark the used labels as used. */ |
| if (TREE_CODE (destination) == LABEL_DECL) |
| TREE_USED (destination) = 1; |
| else |
| { |
| destination = mark_rvalue_use (destination); |
| if (!processing_template_decl) |
| { |
| destination = cp_convert (ptr_type_node, destination, |
| tf_warning_or_error); |
| if (error_operand_p (destination)) |
| return NULL_TREE; |
| destination |
| = fold_build_cleanup_point_expr (TREE_TYPE (destination), |
| destination); |
| } |
| } |
| |
| check_goto (destination); |
| |
| add_stmt (build_predict_expr (PRED_GOTO, NOT_TAKEN)); |
| return add_stmt (build_stmt (input_location, GOTO_EXPR, destination)); |
| } |
| |
| /* COND is the condition-expression for an if, while, etc., |
| statement. Convert it to a boolean value, if appropriate. |
| In addition, verify sequence points if -Wsequence-point is enabled. */ |
| |
| static tree |
| maybe_convert_cond (tree cond) |
| { |
| /* Empty conditions remain empty. */ |
| if (!cond) |
| return NULL_TREE; |
| |
| /* Wait until we instantiate templates before doing conversion. */ |
| if (type_dependent_expression_p (cond)) |
| return cond; |
| |
| if (warn_sequence_point && !processing_template_decl) |
| verify_sequence_points (cond); |
| |
| /* Do the conversion. */ |
| cond = convert_from_reference (cond); |
| |
| if (TREE_CODE (cond) == MODIFY_EXPR |
| && warn_parentheses |
| && !warning_suppressed_p (cond, OPT_Wparentheses) |
| && warning_at (cp_expr_loc_or_input_loc (cond), |
| OPT_Wparentheses, "suggest parentheses around " |
| "assignment used as truth value")) |
| suppress_warning (cond, OPT_Wparentheses); |
| |
| return condition_conversion (cond); |
| } |
| |
| /* Finish an expression-statement, whose EXPRESSION is as indicated. */ |
| |
| tree |
| finish_expr_stmt (tree expr) |
| { |
| tree r = NULL_TREE; |
| location_t loc = EXPR_LOCATION (expr); |
| |
| if (expr != NULL_TREE) |
| { |
| /* If we ran into a problem, make sure we complained. */ |
| gcc_assert (expr != error_mark_node || seen_error ()); |
| |
| if (!processing_template_decl) |
| { |
| if (warn_sequence_point) |
| verify_sequence_points (expr); |
| expr = convert_to_void (expr, ICV_STATEMENT, tf_warning_or_error); |
| } |
| else if (!type_dependent_expression_p (expr)) |
| convert_to_void (build_non_dependent_expr (expr), ICV_STATEMENT, |
| tf_warning_or_error); |
| |
| if (check_for_bare_parameter_packs (expr)) |
| expr = error_mark_node; |
| |
| /* Simplification of inner statement expressions, compound exprs, |
| etc can result in us already having an EXPR_STMT. */ |
| if (TREE_CODE (expr) != CLEANUP_POINT_EXPR) |
| { |
| if (TREE_CODE (expr) != EXPR_STMT) |
| expr = build_stmt (loc, EXPR_STMT, expr); |
| expr = maybe_cleanup_point_expr_void (expr); |
| } |
| |
| r = add_stmt (expr); |
| } |
| |
| return r; |
| } |
| |
| |
| /* Begin an if-statement. Returns a newly created IF_STMT if |
| appropriate. */ |
| |
| tree |
| begin_if_stmt (void) |
| { |
| tree r, scope; |
| scope = do_pushlevel (sk_cond); |
| r = build_stmt (input_location, IF_STMT, NULL_TREE, |
| NULL_TREE, NULL_TREE, scope); |
| current_binding_level->this_entity = r; |
| begin_cond (&IF_COND (r)); |
| return r; |
| } |
| |
| /* Returns true if FN, a CALL_EXPR, is a call to |
| std::is_constant_evaluated or __builtin_is_constant_evaluated. */ |
| |
| static bool |
| is_std_constant_evaluated_p (tree fn) |
| { |
| /* std::is_constant_evaluated takes no arguments. */ |
| if (call_expr_nargs (fn) != 0) |
| return false; |
| |
| tree fndecl = cp_get_callee_fndecl_nofold (fn); |
| if (fndecl == NULL_TREE) |
| return false; |
| |
| if (fndecl_built_in_p (fndecl, CP_BUILT_IN_IS_CONSTANT_EVALUATED, |
| BUILT_IN_FRONTEND)) |
| return true; |
| |
| if (!decl_in_std_namespace_p (fndecl)) |
| return false; |
| |
| tree name = DECL_NAME (fndecl); |
| return name && id_equal (name, "is_constant_evaluated"); |
| } |
| |
| /* Callback function for maybe_warn_for_constant_evaluated that looks |
| for calls to std::is_constant_evaluated in TP. */ |
| |
| static tree |
| find_std_constant_evaluated_r (tree *tp, int *walk_subtrees, void *) |
| { |
| tree t = *tp; |
| |
| if (TYPE_P (t) || TREE_CONSTANT (t)) |
| { |
| *walk_subtrees = false; |
| return NULL_TREE; |
| } |
| |
| switch (TREE_CODE (t)) |
| { |
| case CALL_EXPR: |
| if (is_std_constant_evaluated_p (t)) |
| return t; |
| break; |
| case EXPR_STMT: |
| /* Don't warn in statement expressions. */ |
| *walk_subtrees = false; |
| return NULL_TREE; |
| default: |
| break; |
| } |
| |
| return NULL_TREE; |
| } |
| |
| /* In certain contexts, std::is_constant_evaluated() is always true (for |
| instance, in a consteval function or in a constexpr if), or always false |
| (e.g., in a non-constexpr non-consteval function) so give the user a clue. */ |
| |
| static void |
| maybe_warn_for_constant_evaluated (tree cond, bool constexpr_if) |
| { |
| if (!warn_tautological_compare) |
| return; |
| |
| /* Suppress warning for std::is_constant_evaluated if the conditional |
| comes from a macro. */ |
| if (from_macro_expansion_at (EXPR_LOCATION (cond))) |
| return; |
| |
| cond = cp_walk_tree_without_duplicates (&cond, find_std_constant_evaluated_r, |
| NULL); |
| if (cond) |
| { |
| if (constexpr_if) |
| warning_at (EXPR_LOCATION (cond), OPT_Wtautological_compare, |
| "%<std::is_constant_evaluated%> always evaluates to " |
| "true in %<if constexpr%>"); |
| else if (!maybe_constexpr_fn (current_function_decl)) |
| warning_at (EXPR_LOCATION (cond), OPT_Wtautological_compare, |
| "%<std::is_constant_evaluated%> always evaluates to " |
| "false in a non-%<constexpr%> function"); |
| else if (DECL_IMMEDIATE_FUNCTION_P (current_function_decl)) |
| warning_at (EXPR_LOCATION (cond), OPT_Wtautological_compare, |
| "%<std::is_constant_evaluated%> always evaluates to " |
| "true in a %<consteval%> function"); |
| } |
| } |
| |
| /* Process the COND of an if-statement, which may be given by |
| IF_STMT. */ |
| |
| tree |
| finish_if_stmt_cond (tree cond, tree if_stmt) |
| { |
| cond = maybe_convert_cond (cond); |
| if (IF_STMT_CONSTEXPR_P (if_stmt) |
| && !type_dependent_expression_p (cond) |
| && require_constant_expression (cond) |
| && !instantiation_dependent_expression_p (cond) |
| /* Wait until instantiation time, since only then COND has been |
| converted to bool. */ |
| && TYPE_MAIN_VARIANT (TREE_TYPE (cond)) == boolean_type_node) |
| { |
| maybe_warn_for_constant_evaluated (cond, /*constexpr_if=*/true); |
| cond = instantiate_non_dependent_expr (cond); |
| cond = cxx_constant_value (cond, NULL_TREE); |
| } |
| else |
| maybe_warn_for_constant_evaluated (cond, /*constexpr_if=*/false); |
| finish_cond (&IF_COND (if_stmt), cond); |
| add_stmt (if_stmt); |
| THEN_CLAUSE (if_stmt) = push_stmt_list (); |
| return cond; |
| } |
| |
| /* Finish the then-clause of an if-statement, which may be given by |
| IF_STMT. */ |
| |
| tree |
| finish_then_clause (tree if_stmt) |
| { |
| THEN_CLAUSE (if_stmt) = pop_stmt_list (THEN_CLAUSE (if_stmt)); |
| return if_stmt; |
| } |
| |
| /* Begin the else-clause of an if-statement. */ |
| |
| void |
| begin_else_clause (tree if_stmt) |
| { |
| ELSE_CLAUSE (if_stmt) = push_stmt_list (); |
| } |
| |
| /* Finish the else-clause of an if-statement, which may be given by |
| IF_STMT. */ |
| |
| void |
| finish_else_clause (tree if_stmt) |
| { |
| ELSE_CLAUSE (if_stmt) = pop_stmt_list (ELSE_CLAUSE (if_stmt)); |
| } |
| |
| /* Callback for cp_walk_tree to mark all {VAR,PARM}_DECLs in a tree as |
| read. */ |
| |
| static tree |
| maybe_mark_exp_read_r (tree *tp, int *, void *) |
| { |
| tree t = *tp; |
| if (VAR_P (t) || TREE_CODE (t) == PARM_DECL) |
| mark_exp_read (t); |
| return NULL_TREE; |
| } |
| |
| /* Finish an if-statement. */ |
| |
| void |
| finish_if_stmt (tree if_stmt) |
| { |
| tree scope = IF_SCOPE (if_stmt); |
| IF_SCOPE (if_stmt) = NULL; |
| if (IF_STMT_CONSTEXPR_P (if_stmt)) |
| { |
| /* Prevent various -Wunused warnings. We might not instantiate |
| either of these branches, so we would not mark the variables |
| used in that branch as read. */ |
| cp_walk_tree_without_duplicates (&THEN_CLAUSE (if_stmt), |
| maybe_mark_exp_read_r, NULL); |
| cp_walk_tree_without_duplicates (&ELSE_CLAUSE (if_stmt), |
| maybe_mark_exp_read_r, NULL); |
| } |
| add_stmt (do_poplevel (scope)); |
| } |
| |
| /* Begin a while-statement. Returns a newly created WHILE_STMT if |
| appropriate. */ |
| |
| tree |
| begin_while_stmt (void) |
| { |
| tree r; |
| r = build_stmt (input_location, WHILE_STMT, NULL_TREE, NULL_TREE); |
| add_stmt (r); |
| WHILE_BODY (r) = do_pushlevel (sk_block); |
| begin_cond (&WHILE_COND (r)); |
| return r; |
| } |
| |
| /* Process the COND of a while-statement, which may be given by |
| WHILE_STMT. */ |
| |
| void |
| finish_while_stmt_cond (tree cond, tree while_stmt, bool ivdep, |
| unsigned short unroll) |
| { |
| cond = maybe_convert_cond (cond); |
| finish_cond (&WHILE_COND (while_stmt), cond); |
| begin_maybe_infinite_loop (cond); |
| if (ivdep && cond != error_mark_node) |
| WHILE_COND (while_stmt) = build3 (ANNOTATE_EXPR, |
| TREE_TYPE (WHILE_COND (while_stmt)), |
| WHILE_COND (while_stmt), |
| build_int_cst (integer_type_node, |
| annot_expr_ivdep_kind), |
| integer_zero_node); |
| if (unroll && cond != error_mark_node) |
| WHILE_COND (while_stmt) = build3 (ANNOTATE_EXPR, |
| TREE_TYPE (WHILE_COND (while_stmt)), |
| WHILE_COND (while_stmt), |
| build_int_cst (integer_type_node, |
| annot_expr_unroll_kind), |
| build_int_cst (integer_type_node, |
| unroll)); |
| simplify_loop_decl_cond (&WHILE_COND (while_stmt), WHILE_BODY (while_stmt)); |
| } |
| |
| /* Finish a while-statement, which may be given by WHILE_STMT. */ |
| |
| void |
| finish_while_stmt (tree while_stmt) |
| { |
| end_maybe_infinite_loop (boolean_true_node); |
| WHILE_BODY (while_stmt) = do_poplevel (WHILE_BODY (while_stmt)); |
| } |
| |
| /* Begin a do-statement. Returns a newly created DO_STMT if |
| appropriate. */ |
| |
| tree |
| begin_do_stmt (void) |
| { |
| tree r = build_stmt (input_location, DO_STMT, NULL_TREE, NULL_TREE); |
| begin_maybe_infinite_loop (boolean_true_node); |
| add_stmt (r); |
| DO_BODY (r) = push_stmt_list (); |
| return r; |
| } |
| |
| /* Finish the body of a do-statement, which may be given by DO_STMT. */ |
| |
| void |
| finish_do_body (tree do_stmt) |
| { |
| tree body = DO_BODY (do_stmt) = pop_stmt_list (DO_BODY (do_stmt)); |
| |
| if (TREE_CODE (body) == STATEMENT_LIST && STATEMENT_LIST_TAIL (body)) |
| body = STATEMENT_LIST_TAIL (body)->stmt; |
| |
| if (IS_EMPTY_STMT (body)) |
| warning (OPT_Wempty_body, |
| "suggest explicit braces around empty body in %<do%> statement"); |
| } |
| |
| /* Finish a do-statement, which may be given by DO_STMT, and whose |
| COND is as indicated. */ |
| |
| void |
| finish_do_stmt (tree cond, tree do_stmt, bool ivdep, unsigned short unroll) |
| { |
| cond = maybe_convert_cond (cond); |
| end_maybe_infinite_loop (cond); |
| /* Unlike other iteration statements, the condition may not contain |
| a declaration, so we don't call finish_cond which checks for |
| unexpanded parameter packs. */ |
| if (check_for_bare_parameter_packs (cond)) |
| cond = error_mark_node; |
| if (ivdep && cond != error_mark_node) |
| cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond, |
| build_int_cst (integer_type_node, annot_expr_ivdep_kind), |
| integer_zero_node); |
| if (unroll && cond != error_mark_node) |
| cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond, |
| build_int_cst (integer_type_node, annot_expr_unroll_kind), |
| build_int_cst (integer_type_node, unroll)); |
| DO_COND (do_stmt) = cond; |
| } |
| |
| /* Finish a return-statement. The EXPRESSION returned, if any, is as |
| indicated. */ |
| |
| tree |
| finish_return_stmt (tree expr) |
| { |
| tree r; |
| bool no_warning; |
| |
| expr = check_return_expr (expr, &no_warning); |
| |
| if (error_operand_p (expr) |
| || (flag_openmp && !check_omp_return ())) |
| { |
| /* Suppress -Wreturn-type for this function. */ |
| if (warn_return_type) |
| suppress_warning (current_function_decl, OPT_Wreturn_type); |
| 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); |
| if (no_warning) |
| suppress_warning (r, OPT_Wreturn_type); |
| r = maybe_cleanup_point_expr_void (r); |
| r = add_stmt (r); |
| |
| return r; |
| } |
| |
| /* Begin the scope of a for-statement or a range-for-statement. |
| Both the returned trees are to be used in a call to |
| begin_for_stmt or begin_range_for_stmt. */ |
| |
| tree |
| begin_for_scope (tree *init) |
| { |
| tree scope = 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); |
| scope = begin_for_scope (&init); |
| } |
| |
| FOR_INIT_STMT (r) = init; |
| FOR_SCOPE (r) = scope; |
| |
| return r; |
| } |
| |
| /* Finish the init-statement of a for-statement, which may be |
| given by FOR_STMT. */ |
| |
| void |
| finish_init_stmt (tree for_stmt) |
| { |
| if (processing_template_decl) |
| FOR_INIT_STMT (for_stmt) = pop_stmt_list (FOR_INIT_STMT (for_stmt)); |
| add_stmt (for_stmt); |
| FOR_BODY (for_stmt) = do_pushlevel (sk_block); |
| begin_cond (&FOR_COND (for_stmt)); |
| } |
| |
| /* Finish the COND of a for-statement, which may be given by |
| FOR_STMT. */ |
| |
| void |
| finish_for_cond (tree cond, tree for_stmt, bool ivdep, unsigned short unroll) |
| { |
| cond = maybe_convert_cond (cond); |
| finish_cond (&FOR_COND (for_stmt), cond); |
| begin_maybe_infinite_loop (cond); |
| if (ivdep && cond != error_mark_node) |
| FOR_COND (for_stmt) = build3 (ANNOTATE_EXPR, |
| TREE_TYPE (FOR_COND (for_stmt)), |
| FOR_COND (for_stmt), |
| build_int_cst (integer_type_node, |
| annot_expr_ivdep_kind), |
| integer_zero_node); |
| if (unroll && cond != error_mark_node) |
| FOR_COND (for_stmt) = build3 (ANNOTATE_EXPR, |
| TREE_TYPE (FOR_COND (for_stmt)), |
| FOR_COND (for_stmt), |
| build_int_cst (integer_type_node, |
| annot_expr_unroll_kind), |
| build_int_cst (integer_type_node, |
| unroll)); |
| simplify_loop_decl_cond (&FOR_COND (for_stmt), FOR_BODY (for_stmt)); |
| } |
| |
| /* Finish the increment-EXPRESSION in a for-statement, which may be |
| given by FOR_STMT. */ |
| |
| void |
| finish_for_expr (tree expr, tree for_stmt) |
| { |
| if (!expr) |
| return; |
| /* If EXPR is an overloaded function, issue an error; there is no |
| context available to use to perform overload resolution. */ |
| if (type_unknown_p (expr)) |
| { |
| cxx_incomplete_type_error (expr, TREE_TYPE (expr)); |
| expr = error_mark_node; |
| } |
| if (!processing_template_decl) |
| { |
| if (warn_sequence_point) |
| verify_sequence_points (expr); |
| expr = convert_to_void (expr, ICV_THIRD_IN_FOR, |
| tf_warning_or_error); |
| } |
| else if (!type_dependent_expression_p (expr)) |
| convert_to_void (build_non_dependent_expr (expr), ICV_THIRD_IN_FOR, |
| tf_warning_or_error); |
| expr = maybe_cleanup_point_expr_void (expr); |
| if (check_for_bare_parameter_packs (expr)) |
| expr = error_mark_node; |
| FOR_EXPR (for_stmt) = expr; |
| } |
| |
| /* Finish the body of a for-statement, which may be given by |
| FOR_STMT. The increment-EXPR for the loop must be |
| provided. |
| It can also finish RANGE_FOR_STMT. */ |
| |
| void |
| finish_for_stmt (tree for_stmt) |
| { |
| end_maybe_infinite_loop (boolean_true_node); |
| |
| if (TREE_CODE (for_stmt) == RANGE_FOR_STMT) |
| RANGE_FOR_BODY (for_stmt) = do_poplevel (RANGE_FOR_BODY (for_stmt)); |
| else |
| FOR_BODY (for_stmt) = do_poplevel (FOR_BODY (for_stmt)); |
| |
| /* Pop the scope for the body of the loop. */ |
| tree *scope_ptr = (TREE_CODE (for_stmt) == RANGE_FOR_STMT |
| ? &RANGE_FOR_SCOPE (for_stmt) |
| : &FOR_SCOPE (for_stmt)); |
| tree scope = *scope_ptr; |
| *scope_ptr = NULL; |
| |
| /* During parsing of the body, range for uses "__for_{range,begin,end} " |
| decl names to make those unaccessible by code in the body. |
| Change it to ones with underscore instead of space, so that it can |
| be inspected in the debugger. */ |
| tree range_for_decl[3] = { NULL_TREE, NULL_TREE, NULL_TREE }; |
| gcc_assert (CPTI_FOR_BEGIN__IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 1 |
| && CPTI_FOR_END__IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 2 |
| && CPTI_FOR_RANGE_IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 3 |
| && CPTI_FOR_BEGIN_IDENTIFIER == CPTI_FOR_BEGIN__IDENTIFIER + 3 |
| && CPTI_FOR_END_IDENTIFIER == CPTI_FOR_END__IDENTIFIER + 3); |
| for (int i = 0; i < 3; i++) |
| { |
| tree id = cp_global_trees[CPTI_FOR_RANGE__IDENTIFIER + i]; |
| if (IDENTIFIER_BINDING (id) |
| && IDENTIFIER_BINDING (id)->scope == current_binding_level) |
| { |
| range_for_decl[i] = IDENTIFIER_BINDING (id)->value; |
| gcc_assert (VAR_P (range_for_decl[i]) |
| && DECL_ARTIFICIAL (range_for_decl[i])); |
| } |
| } |
| |
| add_stmt (do_poplevel (scope)); |
| |
| for (int i = 0; i < 3; i++) |
| if (range_for_decl[i]) |
| DECL_NAME (range_for_decl[i]) |
| = cp_global_trees[CPTI_FOR_RANGE_IDENTIFIER + i]; |
| } |
| |
| /* 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) |
| { |
| begin_maybe_infinite_loop (boolean_false_node); |
| |
| tree r = build_stmt (input_location, RANGE_FOR_STMT, NULL_TREE, NULL_TREE, |
| NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE); |
| |
| if (scope == NULL_TREE) |
| { |
| gcc_assert (!init); |
| scope = begin_for_scope (&init); |
| } |
| |
| /* Since C++20, RANGE_FOR_STMTs can use the init tree, so save it. */ |
| RANGE_FOR_INIT_STMT (r) = 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) |
| { |
| if (processing_template_decl) |
| RANGE_FOR_INIT_STMT (range_for_stmt) |
| = pop_stmt_list (RANGE_FOR_INIT_STMT (range_for_stmt)); |
| RANGE_FOR_DECL (range_for_stmt) = decl; |
| RANGE_FOR_EXPR (range_for_stmt) = expr; |
| add_stmt (range_for_stmt); |
| RANGE_FOR_BODY (range_for_stmt) = do_pushlevel (sk_block); |
| } |
| |
| /* Finish a break-statement. */ |
| |
| tree |
| finish_break_stmt (void) |
| { |
| /* In switch statements break is sometimes stylistically used after |
| a return statement. This can lead to spurious warnings about |
| control reaching the end of a non-void function when it is |
| inlined. Note that we are calling block_may_fallthru with |
| language specific tree nodes; this works because |
| block_may_fallthru returns true when given something it does not |
| understand. */ |
| if (!block_may_fallthru (cur_stmt_list)) |
| return void_node; |
| note_break_stmt (); |
| 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. */ |
| tree orig_cond = cond; |
| cond = build_expr_type_conversion (WANT_INT | WANT_ENUM, cond, true); |
| if (cond == NULL_TREE) |
| { |
| error_at (cp_expr_loc_or_input_loc (orig_cond), |
| "switch quantity not an integer"); |
| cond = error_mark_node; |
| } |
| /* We want unlowered type here to handle enum bit-fields. */ |
| orig_type = unlowered_expr_type (cond); |
| if (TREE_CODE (orig_type) != ENUMERAL_TYPE) |
| orig_type = TREE_TYPE (cond); |
| if (cond != error_mark_node) |
| { |
| /* [stmt.switch] |
| |
| Integral promotions are performed. */ |
| cond = perform_integral_promotions (cond); |
| cond = maybe_cleanup_point_expr (cond); |
| } |
| } |
| if (check_for_bare_parameter_packs (cond)) |
| cond = error_mark_node; |
| else if (!processing_template_decl && warn_sequence_point) |
| verify_sequence_points (cond); |
| |
| finish_cond (&SWITCH_STMT_COND (switch_stmt), cond); |
| SWITCH_STMT_TYPE (switch_stmt) = orig_type; |
| add_stmt (switch_stmt); |
| push_switch (switch_stmt); |
| SWITCH_STMT_BODY (switch_stmt) = push_stmt_list (); |
| } |
| |
| /* Finish the body of a switch-statement, which may be given by |
| SWITCH_STMT. The COND to switch on is indicated. */ |
| |
| void |
| finish_switch_stmt (tree switch_stmt) |
| { |
| tree scope; |
| |
| SWITCH_STMT_BODY (switch_stmt) = |
| pop_stmt_list (SWITCH_STMT_BODY (switch_stmt)); |
| pop_switch (); |
| |
| scope = SWITCH_STMT_SCOPE (switch_stmt); |
| SWITCH_STMT_SCOPE (switch_stmt) = NULL; |
| add_stmt (do_poplevel (scope)); |
| } |
| |
| /* Begin a try-block. Returns a newly-created TRY_BLOCK if |
| appropriate. */ |
| |
| tree |
| begin_try_block (void) |
| { |
| tree r = build_stmt (input_location, TRY_BLOCK, NULL_TREE, NULL_TREE); |
| add_stmt (r); |
| TRY_STMTS (r) = push_stmt_list (); |
| return r; |
| } |
| |
| /* Likewise, for a function-try-block. The block returned in |
| *COMPOUND_STMT is an artificial outer scope, containing the |
| function-try-block. */ |
| |
| tree |
| begin_function_try_block (tree *compound_stmt) |
| { |
| tree r; |
| /* This outer scope does not exist in the C++ standard, but we need |
| a place to put __FUNCTION__ and similar variables. */ |
| *compound_stmt = begin_compound_stmt (0); |
| r = begin_try_block (); |
| FN_TRY_BLOCK_P (r) = 1; |
| return r; |
| } |
| |
| /* Finish a try-block, which may be given by TRY_BLOCK. */ |
| |
| void |
| finish_try_block (tree try_block) |
| { |
| TRY_STMTS (try_block) = pop_stmt_list (TRY_STMTS (try_block)); |
| TRY_HANDLERS (try_block) = push_stmt_list (); |
| } |
| |
| /* Finish the body of a cleanup try-block, which may be given by |
| TRY_BLOCK. */ |
| |
| void |
| finish_cleanup_try_block (tree try_block) |
| { |
| TRY_STMTS (try_block) = pop_stmt_list (TRY_STMTS (try_block)); |
| } |
| |
| /* Finish an implicitly generated try-block, with a cleanup is given |
| by CLEANUP. */ |
| |
| void |
| finish_cleanup (tree cleanup, tree try_block) |
| { |
| TRY_HANDLERS (try_block) = cleanup; |
| CLEANUP_P (try_block) = 1; |
| } |
| |
| /* Likewise, for a function-try-block. */ |
| |
| void |
| finish_function_try_block (tree try_block) |
| { |
| finish_try_block (try_block); |
| /* FIXME : something queer about CTOR_INITIALIZER somehow following |
| the try block, but moving it inside. */ |
| in_function_try_handler = 1; |
| } |
| |
| /* Finish a handler-sequence for a try-block, which may be given by |
| TRY_BLOCK. */ |
| |
| void |
| finish_handler_sequence (tree try_block) |
| { |
| TRY_HANDLERS (try_block) = pop_stmt_list (TRY_HANDLERS (try_block)); |
| check_handlers (TRY_HANDLERS (try_block)); |
| } |
| |
| /* Finish the handler-seq for a function-try-block, given by |
| TRY_BLOCK. COMPOUND_STMT is the outer block created by |
| begin_function_try_block. */ |
| |
| void |
| finish_function_handler_sequence (tree try_block, tree compound_stmt) |
| { |
| in_function_try_handler = 0; |
| finish_handler_sequence (try_block); |
| finish_compound_stmt (compound_stmt); |
| } |
| |
| /* Begin a handler. Returns a HANDLER if appropriate. */ |
| |
| tree |
| begin_handler (void) |
| { |
| tree r; |
| |
| r = build_stmt (input_location, HANDLER, NULL_TREE, NULL_TREE); |
| add_stmt (r); |
| |
| /* Create a binding level for the eh_info and the exception object |
| cleanup. */ |
| HANDLER_BODY (r) = do_pushlevel (sk_catch); |
| |
| return r; |
| } |
| |
| /* Finish the handler-parameters for a handler, which may be given by |
| HANDLER. DECL is the declaration for the catch parameter, or NULL |
| if this is a `catch (...)' clause. */ |
| |
| void |
| finish_handler_parms (tree decl, tree handler) |
| { |
| tree type = NULL_TREE; |
| if (processing_template_decl) |
| { |
| if (decl) |
| { |
| decl = pushdecl (decl); |
| decl = push_template_decl (decl); |
| HANDLER_PARMS (handler) = decl; |
| type = TREE_TYPE (decl); |
| } |
| } |
| else |
| { |
| type = expand_start_catch_block (decl); |
| if (warn_catch_value |
| && type != NULL_TREE |
| && type != error_mark_node |
| && !TYPE_REF_P (TREE_TYPE (decl))) |
| { |
| tree orig_type = TREE_TYPE (decl); |
| if (CLASS_TYPE_P (orig_type)) |
| { |
| if (TYPE_POLYMORPHIC_P (orig_type)) |
| warning_at (DECL_SOURCE_LOCATION (decl), |
| OPT_Wcatch_value_, |
| "catching polymorphic type %q#T by value", |
| orig_type); |
| else if (warn_catch_value > 1) |
| warning_at (DECL_SOURCE_LOCATION (decl), |
| OPT_Wcatch_value_, |
| "catching type %q#T by value", orig_type); |
| } |
| else if (warn_catch_value > 2) |
| warning_at (DECL_SOURCE_LOCATION (decl), |
| OPT_Wcatch_value_, |
| "catching non-reference type %q#T", orig_type); |
| } |
| } |
| HANDLER_TYPE (handler) = type; |
| } |
| |
| /* Finish a handler, which may be given by HANDLER. The BLOCKs are |
| the return value from the matching call to finish_handler_parms. */ |
| |
| void |
| finish_handler (tree handler) |
| { |
| if (!processing_template_decl) |
| expand_end_catch_block (); |
| HANDLER_BODY (handler) = do_poplevel (HANDLER_BODY (handler)); |
| } |
| |
| /* Begin a compound statement. FLAGS contains some bits that control the |
| behavior and context. If BCS_NO_SCOPE is set, the compound statement |
| does not define a scope. If BCS_FN_BODY is set, this is the outermost |
| block of a function. If BCS_TRY_BLOCK is set, this is the block |
| created on behalf of a TRY statement. Returns a token to be passed to |
| finish_compound_stmt. */ |
| |
| tree |
| begin_compound_stmt (unsigned int flags) |
| { |
| tree r; |
| |
| if (flags & BCS_NO_SCOPE) |
| { |
| r = push_stmt_list (); |
| STATEMENT_LIST_NO_SCOPE (r) = 1; |
| |
| /* Normally, we try hard to keep the BLOCK for a statement-expression. |
| But, if it's a statement-expression with a scopeless block, there's |
| nothing to keep, and we don't want to accidentally keep a block |
| *inside* the scopeless block. */ |
| keep_next_level (false); |
| } |
| else |
| { |
| scope_kind sk = sk_block; |
| if (flags & BCS_TRY_BLOCK) |
| sk = sk_try; |
| else if (flags & BCS_TRANSACTION) |
| sk = sk_transaction; |
| r = do_pushlevel (sk); |
| } |
| |
| /* When processing a template, we need to remember where the braces were, |
| so that we can set up identical scopes when instantiating the template |
| later. BIND_EXPR is a handy candidate for this. |
| Note that do_poplevel won't create a BIND_EXPR itself here (and thus |
| result in nested BIND_EXPRs), since we don't build BLOCK nodes when |
| processing templates. */ |
| if (processing_template_decl) |
| { |
| r = build3 (BIND_EXPR, NULL, NULL, r, NULL); |
| BIND_EXPR_TRY_BLOCK (r) = (flags & BCS_TRY_BLOCK) != 0; |
| BIND_EXPR_BODY_BLOCK (r) = (flags & BCS_FN_BODY) != 0; |
| TREE_SIDE_EFFECTS (r) = 1; |
| } |
| |
| return r; |
| } |
| |
| /* Finish a compound-statement, which is given by STMT. */ |
| |
| void |
| finish_compound_stmt (tree stmt) |
| { |
| if (TREE_CODE (stmt) == BIND_EXPR) |
| { |
| tree body = do_poplevel (BIND_EXPR_BODY (stmt)); |
| /* If the STATEMENT_LIST is empty and this BIND_EXPR isn't special, |
| discard the BIND_EXPR so it can be merged with the containing |
| STATEMENT_LIST. */ |
| if (TREE_CODE (body) == STATEMENT_LIST |
| && STATEMENT_LIST_HEAD (body) == NULL |
| && !BIND_EXPR_BODY_BLOCK (stmt) |
| && !BIND_EXPR_TRY_BLOCK (stmt)) |
| stmt = body; |
| else |
| BIND_EXPR_BODY (stmt) = body; |
| } |
| else if (STATEMENT_LIST_NO_SCOPE (stmt)) |
| stmt = pop_stmt_list (stmt); |
| else |
| { |
| /* Destroy any ObjC "super" receivers that may have been |
| created. */ |
| objc_clear_super_receiver (); |
| |
| stmt = do_poplevel (stmt); |
| } |
| |
| /* ??? See c_end_compound_stmt wrt statement expressions. */ |
| add_stmt (stmt); |
| } |
| |
| /* Finish an asm-statement, whose components are a STRING, some |
| OUTPUT_OPERANDS, some INPUT_OPERANDS, some CLOBBERS and some |
| LABELS. Also note whether the asm-statement should be |
| considered volatile, and whether it is asm inline. */ |
| |
| tree |
| finish_asm_stmt (location_t loc, int volatile_p, tree string, |
| tree output_operands, tree input_operands, tree clobbers, |
| tree labels, bool inline_p) |
| { |
| 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. */ |
| || FUNC_OR_METHOD_TYPE_P (TREE_TYPE (operand)) |
| /* 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 (loc, operand, lv_asm); |
| |
| tree *op = &operand; |
| while (TREE_CODE (*op) == COMPOUND_EXPR) |
| op = &TREE_OPERAND (*op, 1); |
| switch (TREE_CODE (*op)) |
| { |
| case PREINCREMENT_EXPR: |
| case PREDECREMENT_EXPR: |
| case MODIFY_EXPR: |
| *op = genericize_compound_lvalue (*op); |
| op = &TREE_OPERAND (*op, 1); |
| break; |
| default: |
| break; |
| } |
| |
| 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 (*op)) |
| 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_at (loc, |
| "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); |
| |
| tree *op = &operand; |
| while (TREE_CODE (*op) == COMPOUND_EXPR) |
| op = &TREE_OPERAND (*op, 1); |
| switch (TREE_CODE (*op)) |
| { |
| case PREINCREMENT_EXPR: |
| case PREDECREMENT_EXPR: |
| case MODIFY_EXPR: |
| *op = genericize_compound_lvalue (*op); |
| op = &TREE_OPERAND (*op, 1); |
| break; |
| default: |
| break; |
| } |
| |
| if (!cxx_mark_addressable (*op)) |
| 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 (loc, ASM_EXPR, string, |
| output_operands, input_operands, |
| clobbers, labels); |
| ASM_VOLATILE_P (r) = volatile_p || noutputs == 0; |
| ASM_INLINE_P (r) = inline_p; |
| r = maybe_cleanup_point_expr_void (r); |
| return add_stmt (r); |
| } |
| |
| /* Finish a label with the indicated NAME. Returns the new label. */ |
| |
| tree |
| finish_label_stmt (tree name) |
| { |
| tree decl = define_label (input_location, name); |
| |
| if (decl == error_mark_node) |
| return error_mark_node; |
| |
| add_stmt (build_stmt (input_location, LABEL_EXPR, decl)); |
| |
| return decl; |
| } |
| |
| /* Finish a series of declarations for local labels. G++ allows users |
| to declare "local" labels, i.e., labels with scope. This extension |
| is useful when writing code involving statement-expressions. */ |
| |
| void |
| finish_label_decl (tree name) |
| { |
| if (!at_function_scope_p ()) |
| { |
| error ("%<__label__%> declarations are only allowed in function scopes"); |
| return; |
| } |
| |
| add_decl_expr (declare_local_label (name)); |
| } |
| |
| /* When DECL goes out of scope, make sure that CLEANUP is executed. */ |
| |
| void |
| finish_decl_cleanup (tree decl, tree cleanup) |
| { |
| push_cleanup (decl, cleanup, false); |
| } |
| |
| /* If the current scope exits with an exception, run CLEANUP. */ |
| |
| void |
| finish_eh_cleanup (tree cleanup) |
| { |
| push_cleanup (NULL, cleanup, true); |
| } |
| |
| /* The MEM_INITS is a list of mem-initializers, in reverse of the |
| order they were written by the user. Each node is as for |
| emit_mem_initializers. */ |
| |
| void |
| finish_mem_initializers (tree mem_inits) |
| { |
| /* Reorder the MEM_INITS so that they are in the order they appeared |
| in the source program. */ |
| mem_inits = nreverse (mem_inits); |
| |
| if (processing_template_decl) |
| { |
| tree mem; |
| |
| for (mem = mem_inits; mem; mem = TREE_CHAIN (mem)) |
| { |
| /* If the TREE_PURPOSE is a TYPE_PACK_EXPANSION, skip the |
| check for bare parameter packs in the TREE_VALUE, because |
| any parameter packs in the TREE_VALUE have already been |
| bound as part of the TREE_PURPOSE. See |
| make_pack_expansion for more information. */ |
| if (TREE_CODE (TREE_PURPOSE (mem)) != TYPE_PACK_EXPANSION |
| && check_for_bare_parameter_packs (TREE_VALUE (mem))) |
| TREE_VALUE (mem) = error_mark_node; |
| } |
| |
| add_stmt (build_min_nt_loc (UNKNOWN_LOCATION, |
| CTOR_INITIALIZER, mem_inits)); |
| } |
| else |
| emit_mem_initializers (mem_inits); |
| } |
| |
| /* Obfuscate EXPR if it looks like an id-expression or member access so |
| that the call to finish_decltype in do_auto_deduction will give the |
| right result. If EVEN_UNEVAL, do this even in unevaluated context. */ |
| |
| tree |
| force_paren_expr (tree expr, bool even_uneval) |
| { |
| /* This is only needed for decltype(auto) in C++14. */ |
| if (cxx_dialect < cxx14) |
| return expr; |
| |
| /* If we're in unevaluated context, we can't be deducing a |
| return/initializer type, so we don't need to mess with this. */ |
| if (cp_unevaluated_operand && !even_uneval) |
| return expr; |
| |
| if (!DECL_P (tree_strip_any_location_wrapper (expr)) |
| && TREE_CODE (expr) != COMPONENT_REF |
| && TREE_CODE (expr) != SCOPE_REF) |
| return expr; |
| |
| location_t loc = cp_expr_location (expr); |
| |
| if (TREE_CODE (expr) == COMPONENT_REF |
| || TREE_CODE (expr) == SCOPE_REF) |
| REF_PARENTHESIZED_P (expr) = true; |
| else if (processing_template_decl) |
| expr = build1_loc (loc, PAREN_EXPR, TREE_TYPE (expr), expr); |
| else |
| { |
| expr = build1_loc (loc, VIEW_CONVERT_EXPR, TREE_TYPE (expr), expr); |
| REF_PARENTHESIZED_P (expr) = true; |
| } |
| |
| return expr; |
| } |
| |
| /* If T is an id-expression obfuscated by force_paren_expr, undo the |
| obfuscation and return the underlying id-expression. Otherwise |
| return T. */ |
| |
| tree |
| maybe_undo_parenthesized_ref (tree t) |
| { |
| if (cxx_dialect < cxx14) |
| return t; |
| |
| if (INDIRECT_REF_P (t) && REF_PARENTHESIZED_P (t)) |
| { |
| t = TREE_OPERAND (t, 0); |
| while (TREE_CODE (t) == NON_LVALUE_EXPR |
| || TREE_CODE (t) == NOP_EXPR) |
| t = TREE_OPERAND (t, 0); |
| |
| gcc_assert (TREE_CODE (t) == ADDR_EXPR |
| || TREE_CODE (t) == STATIC_CAST_EXPR); |
| t = TREE_OPERAND (t, 0); |
| } |
| else if (TREE_CODE (t) == PAREN_EXPR) |
| t = TREE_OPERAND (t, 0); |
| else if (TREE_CODE (t) == VIEW_CONVERT_EXPR |
| && REF_PARENTHESIZED_P (t)) |
| t = TREE_OPERAND (t, 0); |
| |
| return t; |
| } |
| |
| /* Finish a parenthesized expression EXPR. */ |
| |
| cp_expr |
| finish_parenthesized_expr (cp_expr expr) |
| { |
| if (EXPR_P (expr)) |
| /* This inhibits warnings in c_common_truthvalue_conversion. */ |
| suppress_warning (expr, OPT_Wparentheses); |
| |
| 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; |
| |
| tree stripped_expr = tree_strip_any_location_wrapper (expr); |
| if (TREE_CODE (stripped_expr) == STRING_CST) |
| PAREN_STRING_LITERAL_P (stripped_expr) = 1; |
| |
| expr = cp_expr (force_paren_expr (expr), expr.get_location ()); |
| |
| return expr; |
| } |
| |
| /* Finish a reference to a non-static data member (DECL) that is not |
| preceded by `.' or `->'. */ |
| |
| tree |
| finish_non_static_data_member (tree decl, tree object, tree qualifying_scope) |
| { |
| gcc_assert (TREE_CODE (decl) == FIELD_DECL); |
| bool try_omp_private = !object && omp_private_member_map; |
| tree ret; |
| |
| if (!object) |
| { |
| tree scope = qualifying_scope; |
| if (scope == NULL_TREE) |
| { |
| scope = context_for_name_lookup (decl); |
| if (!TYPE_P (scope)) |
| { |
| /* Can happen during error recovery (c++/85014). */ |
| gcc_assert (seen_error ()); |
| return error_mark_node; |
| } |
| } |
| object = maybe_dummy_object (scope, NULL); |
| } |
| |
| object = maybe_resolve_dummy (object, true); |
| if (object == error_mark_node) |
| return error_mark_node; |
| |
| /* DR 613/850: Can use non-static data members without an associated |
| object in sizeof/decltype/alignof. */ |
| if (is_dummy_object (object) && cp_unevaluated_operand == 0 |
| && (!processing_template_decl || !current_class_ref)) |
| { |
| if (current_function_decl |
| && DECL_STATIC_FUNCTION_P (current_function_decl)) |
| error ("invalid use of member %qD in static member function", decl); |
| else |
| error ("invalid use of non-static data member %qD", decl); |
| inform (DECL_SOURCE_LOCATION (decl), "declared here"); |
| |
| return error_mark_node; |
| } |
| |
| if (current_class_ptr) |
| TREE_USED (current_class_ptr) = 1; |
| if (processing_template_decl) |
| { |
| tree type = TREE_TYPE (decl); |
| |
| if (TYPE_REF_P (type)) |
| /* Quals on the object don't matter. */; |
| else if (PACK_EXPANSION_P (type)) |
| /* Don't bother trying to represent this. */ |
| type = NULL_TREE; |
| else |
| { |
| /* Set the cv qualifiers. */ |
| int quals = cp_type_quals (TREE_TYPE (object)); |
| |
| if (DECL_MUTABLE_P (decl)) |
| quals &= ~TYPE_QUAL_CONST; |
| |
| quals |= cp_type_quals (TREE_TYPE (decl)); |
| type = cp_build_qualified_type (type, quals); |
| } |
| |
| if (qualifying_scope) |
| /* Wrap this in a SCOPE_REF for now. */ |
| ret = build_qualified_name (type, qualifying_scope, decl, |
| /*template_p=*/false); |
| else |
| ret = (convert_from_reference |
| (build_min (COMPONENT_REF, type, object, decl, NULL_TREE))); |
| } |
| /* If PROCESSING_TEMPLATE_DECL is nonzero here, then |
| QUALIFYING_SCOPE is also non-null. */ |
| else |
| { |
| tree access_type = TREE_TYPE (object); |
| |
| perform_or_defer_access_check (TYPE_BINFO (access_type), decl, |
| decl, tf_warning_or_error); |
| |
| /* If the data member was named `C::M', convert `*this' to `C' |
| first. */ |
| if (qualifying_scope) |
| { |
| tree binfo = NULL_TREE; |
| object = build_scoped_ref (object, qualifying_scope, |
| &binfo); |
| } |
| |
| ret = build_class_member_access_expr (object, decl, |
| /*access_path=*/NULL_TREE, |
| /*preserve_reference=*/false, |
| tf_warning_or_error); |
| } |
| if (try_omp_private) |
| { |
| tree *v = omp_private_member_map->get (decl); |
| if (v) |
| ret = convert_from_reference (*v); |
| } |
| return ret; |
| } |
| |
| /* 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'. Return value is like |
| perform_access_checks above. */ |
| |
| bool |
| check_accessibility_of_qualified_id (tree decl, |
| tree object_type, |
| tree nested_name_specifier, |
| tsubst_flags_t complain) |
| { |
| /* If we're not checking, return immediately. */ |
| if (deferred_access_no_check) |
| return true; |
| |
| /* Determine the SCOPE of DECL. */ |
| tree scope = context_for_name_lookup (decl); |
| /* If the SCOPE is not a type, then DECL is not a member. */ |
| if (!TYPE_P (scope) |
| /* If SCOPE is dependent then we can't perform this access check now, |
| and since we'll perform this access check again after substitution |
| there's no need to explicitly defer it. */ |
| || dependent_type_p (scope)) |
| return true; |
| |
| tree qualifying_type = NULL_TREE; |
| /* 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) |
| if (tree current = current_nonlambda_class_type ()) |
| { |
| if (dependent_type_p (current)) |
| /* In general we can't know whether this access goes through |
| `this' until instantiation time. Punt now, or else we might |
| create a deferred access check that's not relative to `this' |
| when it ought to be. We'll check this access again after |
| substitution, e.g. from tsubst_qualified_id. */ |
| return true; |
| |
| if (DERIVED_FROM_P (scope, current)) |
| qualifying_type = current; |
| } |
| /* Otherwise, use the type indicated by the |
| nested-name-specifier. */ |
| if (!qualifying_type) |
| 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)) |
| return perform_or_defer_access_check (TYPE_BINFO (qualifying_type), decl, |
| decl, complain); |
| |
| return true; |
| } |
| |
| /* EXPR is the result of a qualified-id. The QUALIFYING_CLASS was the |
| class named to the left of the "::" operator. DONE is true if this |
| expression is a complete postfix-expression; it is false if this |
| expression is followed by '->', '[', '(', etc. ADDRESS_P is true |
| iff this expression is the operand of '&'. TEMPLATE_P is true iff |
| the qualified-id was of the form "A::template B". TEMPLATE_ARG_P |
| is true iff this qualified name appears as a template argument. */ |
| |
| tree |
| finish_qualified_id_expr (tree qualifying_class, |
| tree expr, |
| bool done, |
| bool address_p, |
| bool template_p, |
| bool template_arg_p, |
| tsubst_flags_t complain) |
| { |
| gcc_assert (TYPE_P (qualifying_class)); |
| |
| if (error_operand_p (expr)) |
| return error_mark_node; |
| |
| if ((DECL_P (expr) || BASELINK_P (expr)) |
| && !mark_used (expr, complain)) |
| return error_mark_node; |
| |
| if (template_p) |
| { |
| if (TREE_CODE (expr) == UNBOUND_CLASS_TEMPLATE) |
| { |
| /* cp_parser_lookup_name thought we were looking for a type, |
| but we're actually looking for a declaration. */ |
| qualifying_class = TYPE_CONTEXT (expr); |
| expr = TYPE_IDENTIFIER (expr); |
| } |
| else |
| check_template_keyword (expr); |
| } |
| |
| /* If EXPR occurs as the operand of '&', use special handling that |
| permits a pointer-to-member. */ |
| if (address_p && done) |
| { |
| if (TREE_CODE (expr) == SCOPE_REF) |
| expr = TREE_OPERAND (expr, 1); |
| expr = build_offset_ref (qualifying_class, expr, |
| /*address_p=*/true, complain); |
| return expr; |
| } |
| |
| /* No need to check access within an enum. */ |
| if (TREE_CODE (qualifying_class) == ENUMERAL_TYPE |
| && TREE_CODE (expr) != IDENTIFIER_NODE) |
| return expr; |
| |
| /* Within the scope of a class, turn references to non-static |
| members into expression of the form "this->...". */ |
| if (template_arg_p) |
| /* But, within a template argument, we do not want make the |
| transformation, as there is no "this" pointer. */ |
| ; |
| else if (TREE_CODE (expr) == FIELD_DECL) |
| { |
| push_deferring_access_checks (dk_no_check); |
| expr = finish_non_static_data_member (expr, NULL_TREE, |
| qualifying_class); |
| pop_deferring_access_checks (); |
| } |
| else if (BASELINK_P (expr)) |
| { |
| /* See if any of the functions are non-static members. */ |
| /* If so, the expression may be relative to 'this'. */ |
| if (!shared_member_p (expr) |
| && current_class_ptr |
| && DERIVED_FROM_P (qualifying_class, |
| current_nonlambda_class_type ())) |
| expr = (build_class_member_access_expr |
| (maybe_dummy_object (qualifying_class, NULL), |
| expr, |
| BASELINK_ACCESS_BINFO (expr), |
| /*preserve_reference=*/false, |
| complain)); |
| else if (done) |
| /* The expression is a qualified name whose address is not |
| being taken. */ |
| expr = build_offset_ref (qualifying_class, expr, /*address_p=*/false, |
| complain); |
| } |
| else if (!template_p |
| && TREE_CODE (expr) == TEMPLATE_DECL |
| && !DECL_FUNCTION_TEMPLATE_P (expr)) |
| { |
| if (complain & tf_error) |
| error ("%qE missing template arguments", expr); |
| return error_mark_node; |
| } |
| else |
| { |
| /* In a template, return a SCOPE_REF for most qualified-ids |
| so that we can check access at instantiation time. But if |
| we're looking at a member of the current instantiation, we |
| know we have access and building up the SCOPE_REF confuses |
| non-type template argument handling. */ |
| if (processing_template_decl |
| && (!currently_open_class (qualifying_class) |
| || TREE_CODE (expr) == IDENTIFIER_NODE |
| || TREE_CODE (expr) == TEMPLATE_ID_EXPR |
| || TREE_CODE (expr) == BIT_NOT_EXPR)) |
| expr = build_qualified_name (TREE_TYPE (expr), |
| qualifying_class, expr, |
| template_p); |
| else if (tree wrap = maybe_get_tls_wrapper_call (expr)) |
| expr = wrap; |
| |
| 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 (type && type_unknown_p (type)) |
| { |
| error ("a statement expression is an insufficient context" |
| " for overload resolution"); |
| TREE_TYPE (stmt_expr) = error_mark_node; |
| return error_mark_node; |
| } |
| else if (processing_template_decl) |
| { |
| expr = build_stmt (input_location, EXPR_STMT, expr); |
| expr = add_stmt (expr); |
| /* Mark the last statement so that we can recognize it as such at |
| template-instantiation time. */ |
| EXPR_STMT_STMT_EXPR_RESULT (expr) = 1; |
| } |
| else if (VOID_TYPE_P (type)) |
| { |
| /* Just treat this like an ordinary statement. */ |
| expr = finish_expr_stmt (expr); |
| } |
| else |
| { |
| /* It actually has a value we need to deal with. First, force it |
| to be an rvalue so that we won't need to build up a copy |
| constructor call later when we try to assign it to something. */ |
| expr = force_rvalue (expr, tf_warning_or_error); |
| if (error_operand_p (expr)) |
| return error_mark_node; |
| |
| /* Update for array-to-pointer decay. */ |
| type = TREE_TYPE (expr); |
| |
| /* Wrap it in a CLEANUP_POINT_EXPR and add it to the list like a |
| normal statement, but don't convert to void or actually add |
| the EXPR_STMT. */ |
| if (TREE_CODE (expr) != CLEANUP_POINT_EXPR) |
| expr = maybe_cleanup_point_expr (expr); |
| add_stmt (expr); |
| } |
| |
| /* The type of the statement-expression is the type of the last |
| expression. */ |
| TREE_TYPE (stmt_expr) = type; |
| } |
| |
| return stmt_expr; |
| } |
| |
| /* Finish a statement-expression. EXPR should be the value returned |
| by the previous begin_stmt_expr. Returns an expression |
| representing the statement-expression. */ |
| |
| tree |
| finish_stmt_expr (tree stmt_expr, bool has_no_scope) |
| { |
| tree type; |
| tree result; |
| |
| if (error_operand_p (stmt_expr)) |
| { |
| pop_stmt_list (stmt_expr); |
| return error_mark_node; |
| } |
| |
| gcc_assert (TREE_CODE (stmt_expr) == STATEMENT_LIST); |
| |
| type = TREE_TYPE (stmt_expr); |
| result = pop_stmt_list (stmt_expr); |
| TREE_TYPE (result) = type; |
| |
| if (processing_template_decl) |
| { |
| result = build_min (STMT_EXPR, type, result); |
| TREE_SIDE_EFFECTS (result) = 1; |
| STMT_EXPR_NO_SCOPE (result) = has_no_scope; |
| } |
| else if (CLASS_TYPE_P (type)) |
| { |
| /* Wrap the statement-expression in a TARGET_EXPR so that the |
| temporary object created by the final expression is destroyed at |
| the end of the full-expression containing the |
| statement-expression. */ |
| result = force_target_expr (type, result, tf_warning_or_error); |
| } |
| |
| return result; |
| } |
| |
| /* Returns the expression which provides the value of STMT_EXPR. */ |
| |
| tree |
| stmt_expr_value_expr (tree stmt_expr) |
| { |
| tree t = STMT_EXPR_STMT (stmt_expr); |
| |
| if (TREE_CODE (t) == BIND_EXPR) |
| t = BIND_EXPR_BODY (t); |
| |
| if (TREE_CODE (t) == STATEMENT_LIST && STATEMENT_LIST_TAIL (t)) |
| t = STATEMENT_LIST_TAIL (t)->stmt; |
| |
| if (TREE_CODE (t) == EXPR_STMT) |
| t = EXPR_STMT_EXPR (t); |
| |
| return t; |
| } |
| |
| /* Return TRUE iff EXPR_STMT is an empty list of |
| expression statements. */ |
| |
| bool |
| empty_expr_stmt_p (tree expr_stmt) |
| { |
| tree body = NULL_TREE; |
| |
| if (expr_stmt == void_node) |
| return true; |
| |
| if (expr_stmt) |
| { |
| if (TREE_CODE (expr_stmt) == EXPR_STMT) |
| body = EXPR_STMT_EXPR (expr_stmt); |
| else if (TREE_CODE (expr_stmt) == STATEMENT_LIST) |
| body = expr_stmt; |
| } |
| |
| if (body) |
| { |
| if (TREE_CODE (body) == STATEMENT_LIST) |
| return tsi_end_p (tsi_start (body)); |
| else |
| return empty_expr_stmt_p (body); |
| } |
| return false; |
| } |
| |
| /* Perform Koenig lookup. FN_EXPR is the postfix-expression representing |
| the function (or functions) to call; ARGS are the arguments to the |
| call. Returns the functions to be considered by overload resolution. */ |
| |
| cp_expr |
| perform_koenig_lookup (cp_expr fn_expr, vec<tree, va_gc> *args, |
| tsubst_flags_t complain) |
| { |
| tree identifier = NULL_TREE; |
| tree functions = NULL_TREE; |
| tree tmpl_args = NULL_TREE; |
| bool template_id = false; |
| location_t loc = fn_expr.get_location (); |
| tree fn = fn_expr.get_value (); |
| |
| STRIP_ANY_LOCATION_WRAPPER (fn); |
| |
| if (TREE_CODE (fn) == TEMPLATE_ID_EXPR) |
| { |
| /* Use a separate flag to handle null args. */ |
| template_id = true; |
| tmpl_args = TREE_OPERAND (fn, 1); |
| fn = TREE_OPERAND (fn, 0); |
| } |
| |
| /* Find the name of the overloaded function. */ |
| if (identifier_p (fn)) |
| identifier = fn; |
| else |
| { |
| functions = fn; |
| identifier = OVL_NAME (functions); |
| } |
| |
| /* A call to a namespace-scope function using an unqualified name. |
| |
| Do Koenig lookup -- unless any of the arguments are |
| type-dependent. */ |
| if (!any_type_dependent_arguments_p (args) |
| && !any_dependent_template_arguments_p (tmpl_args)) |
| { |
| fn = lookup_arg_dependent (identifier, functions, args); |
| if (!fn) |
| { |
| /* The unqualified name could not be resolved. */ |
| if (complain & tf_error) |
| fn = unqualified_fn_lookup_error (cp_expr (identifier, loc)); |
| else |
| fn = identifier; |
| } |
| } |
| |
| if (fn && template_id && fn != error_mark_node) |
| fn = build2 (TEMPLATE_ID_EXPR, unknown_type_node, fn, tmpl_args); |
| |
| return cp_expr (fn, loc); |
| } |
| |
| /* 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 = *args; |
| |
| if (fn == error_mark_node) |
| return error_mark_node; |
| |
| gcc_assert (!TYPE_P (fn)); |
| |
| /* If FN may be a FUNCTION_DECL obfuscated by force_paren_expr, undo |
| it so that we can tell this is a call to a known function. */ |
| fn = maybe_undo_parenthesized_ref (fn); |
| |
| STRIP_ANY_LOCATION_WRAPPER (fn); |
| |
| orig_fn = fn; |
| |
| if (processing_template_decl) |
| { |
| /* If FN is a local extern declaration or set thereof, look them up |
| again at instantiation time. */ |
| if (is_overloaded_fn (fn)) |
| { |
| tree ifn = get_first_fn (fn); |
| if (TREE_CODE (ifn) == FUNCTION_DECL |
| && DECL_LOCAL_DECL_P (ifn)) |
| orig_fn = DECL_NAME (ifn); |
| } |
| |
| /* If the call expression is dependent, build a CALL_EXPR node |
| with no type; type_dependent_expression_p recognizes |
| expressions with no type as being dependent. */ |
| if (type_dependent_expression_p (fn) |
| || any_type_dependent_arguments_p (*args)) |
| { |
| result = build_min_nt_call_vec (orig_fn, *args); |
| SET_EXPR_LOCATION (result, cp_expr_loc_or_input_loc (fn)); |
| KOENIG_LOOKUP_P (result) = koenig_p; |
| if (is_overloaded_fn (fn)) |
| fn = get_fns (fn); |
| |
| if (cfun) |
| { |
| bool abnormal = true; |
| for (lkp_iterator iter (fn); abnormal && iter; ++iter) |
| { |
| tree fndecl = STRIP_TEMPLATE (*iter); |
| if (TREE_CODE (fndecl) != FUNCTION_DECL |
| || !TREE_THIS_VOLATILE (fndecl)) |
| abnormal = false; |
| } |
| /* FIXME: Stop warning about falling off end of non-void |
| function. But this is wrong. Even if we only see |
| no-return fns at this point, we could select a |
| future-defined return fn during instantiation. Or |
| vice-versa. */ |
| if (abnormal) |
| current_function_returns_abnormally = 1; |
| } |
| return result; |
| } |
| orig_args = make_tree_vector_copy (*args); |
| if (!BASELINK_P (fn) |
| && TREE_CODE (fn) != PSEUDO_DTOR_EXPR |
| && TREE_TYPE (fn) != unknown_type_node) |
| fn = build_non_dependent_expr (fn); |
| make_args_non_dependent (*args); |
| } |
| |
| if (TREE_CODE (fn) == COMPONENT_REF) |
| { |
| tree member = TREE_OPERAND (fn, 1); |
| if (BASELINK_P (member)) |
| { |
| tree object = TREE_OPERAND (fn, 0); |
| return build_new_method_call (object, member, |
| args, NULL_TREE, |
| (disallow_virtual |
| ? LOOKUP_NORMAL | LOOKUP_NONVIRTUAL |
| : LOOKUP_NORMAL), |
| /*fn_p=*/NULL, |
| complain); |
| } |
| } |
| |
| /* Per 13.3.1.1, '(&f)(...)' is the same as '(f)(...)'. */ |
| if (TREE_CODE (fn) == ADDR_EXPR |
| && TREE_CODE (TREE_OPERAND (fn, 0)) == OVERLOAD) |
| fn = TREE_OPERAND (fn, 0); |
| |
| if (is_overloaded_fn (fn)) |
| fn = baselink_for_fns (fn); |
| |
| result = NULL_TREE; |
| if (BASELINK_P (fn)) |
| { |
| tree object; |
| |
| /* A call to a member function. From [over.call.func]: |
| |
| If the keyword this is in scope and refers to the class of |
| that member function, or a derived class thereof, then the |
| function call is transformed into a qualified function call |
| using (*this) as the postfix-expression to the left of the |
| . operator.... [Otherwise] a contrived object of type T |
| becomes the implied object argument. |
| |
| In this situation: |
| |
| struct A { void f(); }; |
| struct B : public A {}; |
| struct C : public A { void g() { B::f(); }}; |
| |
| "the class of that member function" refers to `A'. But 11.2 |
| [class.access.base] says that we need to convert 'this' to B* as |
| part of the access, so we pass 'B' to maybe_dummy_object. */ |
| |
| if (DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (get_first_fn (fn))) |
| { |
| /* A constructor call always uses a dummy object. (This constructor |
| call which has the form A::A () is actually invalid and we are |
| going to reject it later in build_new_method_call.) */ |
| object = build_dummy_object (BINFO_TYPE (BASELINK_ACCESS_BINFO (fn))); |
| } |
| else |
| object = maybe_dummy_object (BINFO_TYPE (BASELINK_ACCESS_BINFO (fn)), |
| NULL); |
| |
| result = build_new_method_call (object, fn, args, NULL_TREE, |
| (disallow_virtual |
| ? LOOKUP_NORMAL|LOOKUP_NONVIRTUAL |
| : LOOKUP_NORMAL), |
| /*fn_p=*/NULL, |
| complain); |
| } |
| else if (concept_check_p (fn)) |
| { |
| /* FN is actually a template-id referring to a concept definition. */ |
| tree id = unpack_concept_check (fn); |
| tree tmpl = TREE_OPERAND (id, 0); |
| tree args = TREE_OPERAND (id, 1); |
| |
| if (!function_concept_p (tmpl)) |
| { |
| error_at (EXPR_LOC_OR_LOC (fn, input_location), |
| "cannot call a concept as a function"); |
| return error_mark_node; |
| } |
| |
| /* Ensure the result is wrapped as a call expression. */ |
| result = build_concept_check (tmpl, args, tf_warning_or_error); |
| } |
| else if (is_overloaded_fn (fn)) |
| { |
| /* If the function is an overloaded builtin, resolve it. */ |
| if (TREE_CODE (fn) == FUNCTION_DECL |
| && (DECL_BUILT_IN_CLASS (fn) == BUILT_IN_NORMAL |
| || DECL_BUILT_IN_CLASS (fn) == BUILT_IN_MD)) |
| result = resolve_overloaded_builtin (input_location, fn, *args); |
| |
| if (!result) |
| { |
| if (warn_sizeof_pointer_memaccess |
| && (complain & tf_warning) |
| && !vec_safe_is_empty (*args) |
| && !processing_template_decl) |
| { |
| location_t sizeof_arg_loc[3]; |
| tree sizeof_arg[3]; |
| unsigned int i; |
| for (i = 0; i < 3; i++) |
| { |
| tree t; |
| |
| sizeof_arg_loc[i] = UNKNOWN_LOCATION; |
| sizeof_arg[i] = NULL_TREE; |
| if (i >= (*args)->length ()) |
| continue; |
| t = (**args)[i]; |
| if (TREE_CODE (t) != SIZEOF_EXPR) |
| continue; |
| if (SIZEOF_EXPR_TYPE_P (t)) |
| sizeof_arg[i] = TREE_TYPE (TREE_OPERAND (t, 0)); |
| else |
| sizeof_arg[i] = TREE_OPERAND (t, 0); |
| sizeof_arg_loc[i] = EXPR_LOCATION (t); |
| } |
| sizeof_pointer_memaccess_warning |
| (sizeof_arg_loc, fn, *args, |
| sizeof_arg, same_type_ignoring_top_level_qualifiers_p); |
| } |
| |
| if ((complain & tf_warning) |
| && TREE_CODE (fn) == FUNCTION_DECL |
| && fndecl_built_in_p (fn, BUILT_IN_MEMSET) |
| && vec_safe_length (*args) == 3 |
| && !any_type_dependent_arguments_p (*args)) |
| { |
| tree arg0 = (*orig_args)[0]; |
| tree arg1 = (*orig_args)[1]; |
| tree arg2 = (*orig_args)[2]; |
| int literal_mask = ((literal_integer_zerop (arg1) << 1) |
| | (literal_integer_zerop (arg2) << 2)); |
| warn_for_memset (input_location, arg0, arg2, literal_mask); |
| } |
| |
| /* A call to a namespace-scope function. */ |
| result = build_new_function_call (fn, args, complain); |
| } |
| } |
| else if (TREE_CODE (fn) == PSEUDO_DTOR_EXPR) |
| { |
| if (!vec_safe_is_empty (*args)) |
| error ("arguments to destructor are not allowed"); |
| /* C++20/DR: If the postfix-expression names a pseudo-destructor (in |
| which case the postfix-expression is a possibly-parenthesized class |
| member access), the function call destroys the object of scalar type |
| denoted by the object expression of the class member access. */ |
| tree ob = TREE_OPERAND (fn, 0); |
| if (obvalue_p (ob)) |
| result = build_trivial_dtor_call (ob, true); |
| else |
| /* No location to clobber. */ |
| result = convert_to_void (ob, ICV_STATEMENT, complain); |
| } |
| else if (CLASS_TYPE_P (TREE_TYPE (fn))) |
| /* If the "function" is really an object of class type, it might |
| have an overloaded `operator ()'. */ |
| result = build_op_call (fn, args, complain); |
| |
| if (!result) |
| /* A call where the function is unknown. */ |
| result = cp_build_function_call_vec (fn, args, complain); |
| |
| if (processing_template_decl && result != error_mark_node) |
| { |
| if (INDIRECT_REF_P (result)) |
| result = TREE_OPERAND (result, 0); |
| result = build_call_vec (TREE_TYPE (result), orig_fn, orig_args); |
| SET_EXPR_LOCATION (result, input_location); |
| KOENIG_LOOKUP_P (result) = koenig_p; |
| release_tree_vector (orig_args); |
| result = convert_from_reference (result); |
| } |
| |
| return result; |
| } |
| |
| /* Finish a call to a postfix increment or decrement or EXPR. (Which |
| is indicated by CODE, which should be POSTINCREMENT_EXPR or |
| POSTDECREMENT_EXPR.) */ |
| |
| cp_expr |
| finish_increment_expr (cp_expr expr, enum tree_code code) |
| { |
| /* input_location holds the location of the trailing operator token. |
| Build a location of the form: |
| expr++ |
| ~~~~^~ |
| with the caret at the operator token, ranging from the start |
| of EXPR to the end of the operator token. */ |
| location_t combined_loc = make_location (input_location, |
| expr.get_start (), |
| get_finish (input_location)); |
| cp_expr result = build_x_unary_op (combined_loc, code, expr, |
| tf_warning_or_error); |
| /* TODO: build_x_unary_op doesn't honor the location, so set it here. */ |
| result.set_location (combined_loc); |
| return result; |
| } |
| |
| /* Finish a use of `this'. Returns an expression for `this'. */ |
| |
| tree |
| finish_this_expr (void) |
| { |
| tree result = NULL_TREE; |
| |
| if (current_class_ptr) |
| { |
| tree type = TREE_TYPE (current_class_ref); |
| |
| /* In a lambda expression, 'this' refers to the captured 'this'. */ |
| if (LAMBDA_TYPE_P (type)) |
| result = lambda_expr_this_capture (CLASSTYPE_LAMBDA_EXPR (type), true); |
| else |
| result = current_class_ptr; |
| } |
| |
| if (result) |
| /* The keyword 'this' is a prvalue expression. */ |
| return rvalue (result); |
| |
| tree fn = current_nonlambda_function (); |
| if (fn && DECL_STATIC_FUNCTION_P (fn)) |
| error ("%<this%> is unavailable for static member functions"); |
| else if (fn) |
| error ("invalid use of %<this%> in non-member function"); |
| else |
| error ("invalid use of %<this%> at top level"); |
| return error_mark_node; |
| } |
| |
| /* Finish a pseudo-destructor expression. If SCOPE is NULL, the |
| expression was of the form `OBJECT.~DESTRUCTOR' where DESTRUCTOR is |
| the TYPE for the type given. If SCOPE is non-NULL, the expression |
| was of the form `OBJECT.SCOPE::~DESTRUCTOR'. */ |
| |
| tree |
| finish_pseudo_destructor_expr (tree object, tree scope, tree destructor, |
| location_t loc) |
| { |
| if (object == error_mark_node || destructor == error_mark_node) |
| return error_mark_node; |
| |
| gcc_assert (TYPE_P (destructor)); |
| |
| if (!processing_template_decl) |
| { |
| if (scope == error_mark_node) |
| { |
| error_at (loc, "invalid qualifying scope in pseudo-destructor name"); |
| return error_mark_node; |
| } |
| if (is_auto (destructor)) |
| destructor = TREE_TYPE (object); |
| if (scope && TYPE_P (scope) && !check_dtor_name (scope, destructor)) |
| { |
| error_at (loc, |
| "qualified type %qT does not match destructor name ~%qT", |
| scope, destructor); |
| return error_mark_node; |
| } |
| |
| |
| /* [expr.pseudo] says both: |
| |
| The type designated by the pseudo-destructor-name shall be |
| the same as the object type. |
| |
| and: |
| |
| The cv-unqualified versions of the object type and of the |
| type designated by the pseudo-destructor-name shall be the |
| same type. |
| |
| We implement the more generous second sentence, since that is |
| what most other compilers do. */ |
| if (!same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (object), |
| destructor)) |
| { |
| error_at (loc, "%qE is not of type %qT", object, destructor); |
| return error_mark_node; |
| } |
| } |
| |
| tree type = (type_dependent_expression_p (object) |
| ? NULL_TREE : void_type_node); |
| |
| return build3_loc (loc, PSEUDO_DTOR_EXPR, type, object, |
| scope, destructor); |
| } |
| |
| /* Finish an expression of the form CODE EXPR. */ |
| |
| cp_expr |
| finish_unary_op_expr (location_t op_loc, enum tree_code code, cp_expr expr, |
| tsubst_flags_t complain) |
| { |
| /* Build a location of the form: |
| ++expr |
| ^~~~~~ |
| with the caret at the operator token, ranging from the start |
| of the operator token to the end of EXPR. */ |
| location_t combined_loc = make_location (op_loc, |
| op_loc, expr.get_finish ()); |
| cp_expr result = build_x_unary_op (combined_loc, code, expr, complain); |
| /* TODO: build_x_unary_op doesn't always honor the location. */ |
| result.set_location (combined_loc); |
| |
| if (result == error_mark_node) |
| return result; |
| |
| if (!(complain & tf_warning)) |
| return result; |
| |
| tree result_ovl = result; |
| tree expr_ovl = expr; |
| |
| if (!processing_template_decl) |
| expr_ovl = cp_fully_fold (expr_ovl); |
| |
| if (!CONSTANT_CLASS_P (expr_ovl) |
| || TREE_OVERFLOW_P (expr_ovl)) |
| return result; |
| |
| if (!processing_template_decl) |
| result_ovl = cp_fully_fold (result_ovl); |
| |
| if (CONSTANT_CLASS_P (result_ovl) && TREE_OVERFLOW_P (result_ovl)) |
| overflow_warning (combined_loc, result_ovl); |
| |
| return result; |
| } |
| |
| /* Finish a compound-literal expression or C++11 functional cast with aggregate |
| initializer. TYPE is the type to which the CONSTRUCTOR in COMPOUND_LITERAL |
| is being cast. */ |
| |
| tree |
| finish_compound_literal (tree type, tree compound_literal, |
| tsubst_flags_t complain, |
| fcl_t fcl_context) |
| { |
| if (type == error_mark_node) |
| return error_mark_node; |
| |
| if (TYPE_REF_P (type)) |
| { |
| compound_literal |
| = finish_compound_literal (TREE_TYPE (type), compound_literal, |
| complain, fcl_context); |
| /* The prvalue is then used to direct-initialize the reference. */ |
| tree r = (perform_implicit_conversion_flags |
| (type, compound_literal, complain, LOOKUP_NORMAL)); |
| return convert_from_reference (r); |
| } |
| |
| if (!TYPE_OBJ_P (type)) |
| { |
| if (complain & tf_error) |
| error ("compound literal of non-object type %qT", type); |
| return error_mark_node; |
| } |
| |
| if (template_placeholder_p (type)) |
| { |
| type = do_auto_deduction (type, compound_literal, type, complain, |
| adc_variable_type); |
| if (type == error_mark_node) |
| return error_mark_node; |
| } |
| |
| /* Used to hold a copy of the compound literal in a template. */ |
| tree orig_cl = NULL_TREE; |
| |
| if (processing_template_decl) |
| { |
| const bool dependent_p |
| = (instantiation_dependent_expression_p (compound_literal) |
| || dependent_type_p (type)); |
| if (dependent_p) |
| /* We're about to return, no need to copy. */ |
| orig_cl = compound_literal; |
| else |
| /* We're going to need a copy. */ |
| orig_cl = unshare_constructor (compound_literal); |
| TREE_TYPE (orig_cl) = type; |
| /* Mark the expression as a compound literal. */ |
| TREE_HAS_CONSTRUCTOR (orig_cl) = 1; |
| /* And as instantiation-dependent. */ |
| CONSTRUCTOR_IS_DEPENDENT (orig_cl) = dependent_p; |
| if (fcl_context == fcl_c99) |
| CONSTRUCTOR_C99_COMPOUND_LITERAL (orig_cl) = 1; |
| /* If the compound literal is dependent, we're done for now. */ |
| if (dependent_p) |
| return orig_cl; |
| /* Otherwise, do go on to e.g. check narrowing. */ |
| } |
| |
| 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 (input_location, 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)) |
| { |
| tree t = instantiate_non_dependent_expr_sfinae (compound_literal, |
| complain); |
| if (!check_narrowing (type, t, complain)) |
| return error_mark_node; |
| } |
| if (TREE_CODE (type) == ARRAY_TYPE |
| && TYPE_DOMAIN (type) == NULL_TREE) |
| { |
| cp_complete_array_type_or_error (&type, compound_literal, |
| false, complain); |
| if (type == error_mark_node) |
| return error_mark_node; |
| } |
| compound_literal = digest_init_flags (type, compound_literal, |
| LOOKUP_NORMAL | LOOKUP_NO_NARROWING, |
| complain); |
| if (compound_literal == error_mark_node) |
| return error_mark_node; |
| |
| /* If we're in a template, return the original compound literal. */ |
| if (orig_cl) |
| return orig_cl; |
| |
| if (TREE_CODE (compound_literal) == CONSTRUCTOR) |
| { |
| TREE_HAS_CONSTRUCTOR (compound_literal) = true; |
| if (fcl_context == fcl_c99) |
| CONSTRUCTOR_C99_COMPOUND_LITERAL (compound_literal) = 1; |
| } |
| |
| /* Put static/constant array temporaries in static variables. */ |
| /* FIXME all C99 compound literals should be variables rather than C++ |
| temporaries, unless they are used as an aggregate initializer. */ |
| if ((!at_function_scope_p () || CP_TYPE_CONST_P (type)) |
| && fcl_context == fcl_c99 |
| && TREE_CODE (type) == ARRAY_TYPE |
| && !TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type) |
| && initializer_constant_valid_p (compound_literal, type)) |
| { |
| tree decl = create_temporary_var (type); |
| DECL_CONTEXT (decl) = NULL_TREE; |
| DECL_INITIAL (decl) = compound_literal; |
| TREE_STATIC (decl) = 1; |
| if (literal_type_p (type) && CP_TYPE_CONST_NON_VOLATILE_P (type)) |
| { |
| /* 5.19 says that a constant expression can include an |
| lvalue-rvalue conversion applied to "a glvalue of literal type |
| that refers to a non-volatile temporary object initialized |
| with a constant expression". Rather than try to communicate |
| that this VAR_DECL is a temporary, just mark it constexpr. */ |
| DECL_DECLARED_CONSTEXPR_P (decl) = true; |
| DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = true; |
| TREE_CONSTANT (decl) = true; |
| } |
| cp_apply_type_quals_to_decl (cp_type_quals (type), decl); |
| decl = pushdecl_top_level (decl); |
| DECL_NAME (decl) = make_anon_name (); |
| SET_DECL_ASSEMBLER_NAME (decl, DECL_NAME (decl)); |
| /* Make sure the destructor is callable. */ |
| tree clean = cxx_maybe_build_cleanup (decl, complain); |
| if (clean == error_mark_node) |
| return error_mark_node; |
| return decl; |
| } |
| |
| /* Represent other compound literals with TARGET_EXPR so we produce |
| a prvalue, and can elide copies. */ |
| if (!VECTOR_TYPE_P (type)) |
| { |
| /* The CONSTRUCTOR is now an initializer, not a compound literal. */ |
| TREE_HAS_CONSTRUCTOR (compound_literal) = false; |
| compound_literal = get_target_expr_sfinae (compound_literal, complain); |
| } |
| |
| return compound_literal; |
| } |
| |
| /* Return the declaration for the function-name variable indicated by |
| ID. */ |
| |
| tree |
| finish_fname (tree id) |
| { |
| tree decl; |
| |
| decl = fname_decl (input_location, C_RID_CODE (id), id); |
| if (processing_template_decl && current_function_decl |
| && decl != error_mark_node) |
| decl = DECL_NAME (decl); |
| return decl; |
| } |
| |
| /* Finish a translation unit. */ |
| |
| void |
| finish_translation_unit (void) |
| { |
| /* In case there were missing closebraces, |
| get us back to the global binding level. */ |
| pop_everything (); |
| while (current_namespace != global_namespace) |
| pop_namespace (); |
| |
| /* Do file scope __FUNCTION__ et al. */ |
| finish_fname_decls (); |
| |
| if (vec_safe_length (scope_chain->omp_declare_target_attribute)) |
| { |
| if (!errorcount) |
| error ("%<#pragma omp declare target%> without corresponding " |
| "%<#pragma omp end declare target%>"); |
| vec_safe_truncate (scope_chain->omp_declare_target_attribute, 0); |
| } |
| } |
| |
| /* Finish a template type parameter, specified as AGGR IDENTIFIER. |
| Returns the parameter. */ |
| |
| tree |
| finish_template_type_parm (tree aggr, tree identifier) |
| { |
| if (aggr != class_type_node) |
| { |
| permerror (input_location, "template type parameters must use the keyword %<class%> or %<typename%>"); |
| aggr = class_type_node; |
| } |
| |
| return build_tree_list (aggr, identifier); |
| } |
| |
| /* Finish a template template parameter, specified as AGGR IDENTIFIER. |
| Returns the parameter. */ |
| |
| tree |
| finish_template_template_parm (tree aggr, tree identifier) |
| { |
| tree decl = build_decl (input_location, |
| TYPE_DECL, identifier, NULL_TREE); |
| |
| tree tmpl = build_lang_decl (TEMPLATE_DECL, identifier, NULL_TREE); |
| DECL_TEMPLATE_PARMS (tmpl) = current_template_parms; |
| DECL_TEMPLATE_RESULT (tmpl) = decl; |
| DECL_ARTIFICIAL (decl) = 1; |
| |
| /* Associate the constraints with the underlying declaration, |
| not the template. */ |
| tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms); |
| tree constr = build_constraints (reqs, NULL_TREE); |
| set_constraints (decl, constr); |
| |
| end_template_decl (); |
| |
| gcc_assert (DECL_TEMPLATE_PARMS (tmpl)); |
| |
| check_default_tmpl_args (decl, DECL_TEMPLATE_PARMS (tmpl), |
| /*is_primary=*/true, /*is_partial=*/false, |
| /*is_friend=*/0); |
| |
| return finish_template_type_parm (aggr, tmpl); |
| } |
| |
| /* ARGUMENT is the default-argument value for a template template |
| parameter. If ARGUMENT is invalid, issue error messages and return |
| the ERROR_MARK_NODE. Otherwise, ARGUMENT itself is returned. */ |
| |
| tree |
| check_template_template_default_arg (tree argument) |
| { |
| if (TREE_CODE (argument) != TEMPLATE_DECL |
| && TREE_CODE (argument) != TEMPLATE_TEMPLATE_PARM |
| && TREE_CODE (argument) != UNBOUND_CLASS_TEMPLATE) |
| { |
| if (TREE_CODE (argument) == TYPE_DECL) |
| error ("invalid use of type %qT as a default value for a template " |
| "template-parameter", TREE_TYPE (argument)); |
| else |
| error ("invalid default argument for a template template parameter"); |
| return error_mark_node; |
| } |
| |
| return argument; |
| } |
| |
| /* Begin a class definition, as indicated by T. */ |
| |
| tree |
| begin_class_definition (tree t) |
| { |
| if (error_operand_p (t) || error_operand_p (TYPE_MAIN_DECL (t))) |
| return error_mark_node; |
| |
| if (processing_template_parmlist && !LAMBDA_TYPE_P (t)) |
| { |
| error ("definition of %q#T inside template parameter list", t); |
| return error_mark_node; |
| } |
| |
| /* According to the C++ ABI, decimal classes defined in ISO/IEC TR 24733 |
| are passed the same as decimal scalar types. */ |
| if (TREE_CODE (t) == RECORD_TYPE |
| && !processing_template_decl) |
| { |
| tree ns = TYPE_CONTEXT (t); |
| if (ns && TREE_CODE (ns) == NAMESPACE_DECL |
| && DECL_CONTEXT (ns) == std_node |
| && DECL_NAME (ns) |
| && id_equal (DECL_NAME (ns), "decimal")) |
| { |
| const char *n = TYPE_NAME_STRING (t); |
| if ((strcmp (n, "decimal32") == 0) |
| || (strcmp (n, "decimal64") == 0) |
| || (strcmp (n, "decimal128") == 0)) |
| TYPE_TRANSPARENT_AGGR (t) = 1; |
| } |
| } |
|