| /* 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, 1999, 2000, 2001, 2002, |
| 2003, 2004 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 2, 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 COPYING. If not, write to the Free |
| Software Foundation, 59 Temple Place - Suite 330, Boston, MA |
| 02111-1307, USA. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "tree.h" |
| #include "cp-tree.h" |
| #include "tree-inline.h" |
| #include "except.h" |
| #include "lex.h" |
| #include "toplev.h" |
| #include "flags.h" |
| #include "rtl.h" |
| #include "expr.h" |
| #include "output.h" |
| #include "timevar.h" |
| #include "debug.h" |
| #include "cgraph.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. Since the current g++ parser is |
| lacking in several respects, and will be reimplemented, we are |
| attempting to move most code that is not directly related to |
| parsing into this file; that will make implementing the new parser |
| much easier since it will be able to make use of these routines. */ |
| |
| static tree maybe_convert_cond (tree); |
| static tree simplify_aggr_init_exprs_r (tree *, int *, void *); |
| static void emit_associated_thunks (tree); |
| static void genrtl_try_block (tree); |
| static void genrtl_eh_spec_block (tree); |
| static void genrtl_handler (tree); |
| static void cp_expand_stmt (tree); |
| |
| |
| /* Finish processing the COND, the SUBSTMT condition for STMT. */ |
| |
| #define FINISH_COND(COND, STMT, SUBSTMT) \ |
| do { \ |
| if (last_tree != (STMT)) \ |
| { \ |
| RECHAIN_STMTS (STMT, SUBSTMT); \ |
| if (!processing_template_decl) \ |
| { \ |
| (COND) = build_tree_list (SUBSTMT, COND); \ |
| (SUBSTMT) = (COND); \ |
| } \ |
| } \ |
| else \ |
| (SUBSTMT) = (COND); \ |
| } while (0) |
| |
| /* 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 TREE_LIST 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 TREE_LIST. `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'. */ |
| |
| /* Data for deferred access checking. */ |
| static GTY(()) deferred_access *deferred_access_stack; |
| static GTY(()) deferred_access *deferred_access_free_list; |
| |
| /* Save the current deferred access states and start deferred |
| access checking iff DEFER_P is true. */ |
| |
| void |
| push_deferring_access_checks (deferring_kind deferring) |
| { |
| deferred_access *d; |
| |
| /* For context like template instantiation, access checking |
| disabling applies to all nested context. */ |
| if (deferred_access_stack |
| && deferred_access_stack->deferring_access_checks_kind == dk_no_check) |
| deferring = dk_no_check; |
| |
| /* Recycle previously used free store if available. */ |
| if (deferred_access_free_list) |
| { |
| d = deferred_access_free_list; |
| deferred_access_free_list = d->next; |
| } |
| else |
| d = ggc_alloc (sizeof (deferred_access)); |
| |
| d->next = deferred_access_stack; |
| d->deferred_access_checks = NULL_TREE; |
| d->deferring_access_checks_kind = deferring; |
| deferred_access_stack = d; |
| } |
| |
| /* Resume deferring access checks again after we stopped doing |
| this previously. */ |
| |
| void |
| resume_deferring_access_checks (void) |
| { |
| if (deferred_access_stack->deferring_access_checks_kind == dk_no_deferred) |
| deferred_access_stack->deferring_access_checks_kind = dk_deferred; |
| } |
| |
| /* Stop deferring access checks. */ |
| |
| void |
| stop_deferring_access_checks (void) |
| { |
| if (deferred_access_stack->deferring_access_checks_kind == dk_deferred) |
| deferred_access_stack->deferring_access_checks_kind = dk_no_deferred; |
| } |
| |
| /* Discard the current deferred access checks and restore the |
| previous states. */ |
| |
| void |
| pop_deferring_access_checks (void) |
| { |
| deferred_access *d = deferred_access_stack; |
| deferred_access_stack = d->next; |
| |
| /* Remove references to access checks TREE_LIST. */ |
| d->deferred_access_checks = NULL_TREE; |
| |
| /* Store in free list for later use. */ |
| d->next = deferred_access_free_list; |
| deferred_access_free_list = d; |
| } |
| |
| /* 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. |
| */ |
| |
| tree |
| get_deferred_access_checks (void) |
| { |
| return deferred_access_stack->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) |
| { |
| tree deferred_check = get_deferred_access_checks (); |
| deferred_access *d1 = deferred_access_stack; |
| deferred_access *d2 = deferred_access_stack->next; |
| deferred_access *d3 = deferred_access_stack->next->next; |
| |
| /* Temporary swap the order of the top two states, just to make |
| sure the garbage collector will not reclaim the memory during |
| processing below. */ |
| deferred_access_stack = d2; |
| d2->next = d1; |
| d1->next = d3; |
| |
| for ( ; deferred_check; deferred_check = TREE_CHAIN (deferred_check)) |
| /* Perform deferred check if required. */ |
| perform_or_defer_access_check (TREE_PURPOSE (deferred_check), |
| TREE_VALUE (deferred_check)); |
| |
| deferred_access_stack = d1; |
| d1->next = d2; |
| d2->next = d3; |
| pop_deferring_access_checks (); |
| } |
| |
| /* 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'. */ |
| |
| void |
| perform_deferred_access_checks (void) |
| { |
| tree deferred_check; |
| for (deferred_check = deferred_access_stack->deferred_access_checks; |
| deferred_check; |
| deferred_check = TREE_CHAIN (deferred_check)) |
| /* Check access. */ |
| enforce_access (TREE_PURPOSE (deferred_check), |
| TREE_VALUE (deferred_check)); |
| } |
| |
| /* Defer checking the accessibility of DECL, when looked up in |
| BINFO. */ |
| |
| void |
| perform_or_defer_access_check (tree binfo, tree decl) |
| { |
| tree check; |
| |
| my_friendly_assert (TREE_CODE (binfo) == TREE_VEC, 20030623); |
| |
| /* If we are not supposed to defer access checks, just check now. */ |
| if (deferred_access_stack->deferring_access_checks_kind == dk_no_deferred) |
| { |
| enforce_access (binfo, decl); |
| return; |
| } |
| /* Exit if we are in a context that no access checking is performed. */ |
| else if (deferred_access_stack->deferring_access_checks_kind == dk_no_check) |
| return; |
| |
| /* See if we are already going to perform this check. */ |
| for (check = deferred_access_stack->deferred_access_checks; |
| check; |
| check = TREE_CHAIN (check)) |
| if (TREE_VALUE (check) == decl && TREE_PURPOSE (check) == binfo) |
| return; |
| /* If not, record the check. */ |
| deferred_access_stack->deferred_access_checks |
| = tree_cons (binfo, decl, |
| deferred_access_stack->deferred_access_checks); |
| } |
| |
| /* 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; |
| } |
| |
| /* Returns the stmt_tree (if any) to which statements are currently |
| being added. If there is no active statement-tree, NULL is |
| returned. */ |
| |
| stmt_tree |
| current_stmt_tree (void) |
| { |
| return (cfun |
| ? &cfun->language->base.x_stmt_tree |
| : &scope_chain->x_stmt_tree); |
| } |
| |
| /* Nonzero if TYPE is an anonymous union or struct type. We have to use a |
| flag for this because "A union for which objects or pointers are |
| declared is not an anonymous union" [class.union]. */ |
| |
| int |
| anon_aggr_type_p (tree node) |
| { |
| return ANON_AGGR_TYPE_P (node); |
| } |
| |
| /* Finish a scope. */ |
| |
| tree |
| do_poplevel (void) |
| { |
| tree block = NULL_TREE; |
| |
| if (stmts_are_full_exprs_p ()) |
| { |
| tree scope_stmts = NULL_TREE; |
| |
| block = poplevel (kept_level_p (), 1, 0); |
| if (!processing_template_decl) |
| { |
| /* This needs to come after the poplevel so that partial scopes |
| are properly nested. */ |
| scope_stmts = add_scope_stmt (/*begin_p=*/0, /*partial_p=*/0); |
| if (block) |
| { |
| SCOPE_STMT_BLOCK (TREE_PURPOSE (scope_stmts)) = block; |
| SCOPE_STMT_BLOCK (TREE_VALUE (scope_stmts)) = block; |
| } |
| } |
| } |
| |
| return block; |
| } |
| |
| /* Begin a new scope. */ |
| |
| void |
| do_pushlevel (scope_kind sk) |
| { |
| if (stmts_are_full_exprs_p ()) |
| { |
| if (!processing_template_decl) |
| add_scope_stmt (/*begin_p=*/1, /*partial_p=*/0); |
| begin_scope (sk, NULL); |
| } |
| } |
| |
| /* Finish a goto-statement. */ |
| |
| tree |
| finish_goto_stmt (tree destination) |
| { |
| if (TREE_CODE (destination) == IDENTIFIER_NODE) |
| destination = lookup_label (destination); |
| |
| /* We warn about unused labels with -Wunused. That means we have to |
| mark the used labels as used. */ |
| if (TREE_CODE (destination) == LABEL_DECL) |
| TREE_USED (destination) = 1; |
| else |
| { |
| /* The DESTINATION is being used as an rvalue. */ |
| if (!processing_template_decl) |
| destination = decay_conversion (destination); |
| /* We don't inline calls to functions with computed gotos. |
| Those functions are typically up to some funny business, |
| and may be depending on the labels being at particular |
| addresses, or some such. */ |
| DECL_UNINLINABLE (current_function_decl) = 1; |
| } |
| |
| check_goto (destination); |
| |
| return add_stmt (build_stmt (GOTO_STMT, destination)); |
| } |
| |
| /* COND is the condition-expression for an if, while, etc., |
| statement. Convert it to a boolean value, if appropriate. */ |
| |
| static tree |
| maybe_convert_cond (tree cond) |
| { |
| /* Empty conditions remain empty. */ |
| if (!cond) |
| return NULL_TREE; |
| |
| /* Wait until we instantiate templates before doing conversion. */ |
| if (processing_template_decl) |
| return cond; |
| |
| /* Do the conversion. */ |
| cond = convert_from_reference (cond); |
| return condition_conversion (cond); |
| } |
| |
| /* Finish an expression-statement, whose EXPRESSION is as indicated. */ |
| |
| tree |
| finish_expr_stmt (tree expr) |
| { |
| tree r = NULL_TREE; |
| |
| if (expr != NULL_TREE) |
| { |
| if (!processing_template_decl) |
| expr = convert_to_void (expr, "statement"); |
| else if (!type_dependent_expression_p (expr)) |
| convert_to_void (build_non_dependent_expr (expr), "statement"); |
| |
| r = add_stmt (build_stmt (EXPR_STMT, expr)); |
| } |
| |
| finish_stmt (); |
| |
| return r; |
| } |
| |
| |
| /* Begin an if-statement. Returns a newly created IF_STMT if |
| appropriate. */ |
| |
| tree |
| begin_if_stmt (void) |
| { |
| tree r; |
| do_pushlevel (sk_block); |
| r = build_stmt (IF_STMT, NULL_TREE, NULL_TREE, NULL_TREE); |
| add_stmt (r); |
| return r; |
| } |
| |
| /* Process the COND of an if-statement, which may be given by |
| IF_STMT. */ |
| |
| void |
| finish_if_stmt_cond (tree cond, tree if_stmt) |
| { |
| cond = maybe_convert_cond (cond); |
| FINISH_COND (cond, if_stmt, IF_COND (if_stmt)); |
| } |
| |
| /* Finish the then-clause of an if-statement, which may be given by |
| IF_STMT. */ |
| |
| tree |
| finish_then_clause (tree if_stmt) |
| { |
| RECHAIN_STMTS (if_stmt, THEN_CLAUSE (if_stmt)); |
| return if_stmt; |
| } |
| |
| /* Begin the else-clause of an if-statement. */ |
| |
| void |
| begin_else_clause (void) |
| { |
| } |
| |
| /* Finish the else-clause of an if-statement, which may be given by |
| IF_STMT. */ |
| |
| void |
| finish_else_clause (tree if_stmt) |
| { |
| RECHAIN_STMTS (if_stmt, ELSE_CLAUSE (if_stmt)); |
| } |
| |
| /* Finish an if-statement. */ |
| |
| void |
| finish_if_stmt (void) |
| { |
| finish_stmt (); |
| do_poplevel (); |
| } |
| |
| /* Begin a while-statement. Returns a newly created WHILE_STMT if |
| appropriate. */ |
| |
| tree |
| begin_while_stmt (void) |
| { |
| tree r; |
| r = build_stmt (WHILE_STMT, NULL_TREE, NULL_TREE); |
| add_stmt (r); |
| do_pushlevel (sk_block); |
| 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) |
| { |
| cond = maybe_convert_cond (cond); |
| if (processing_template_decl) |
| /* Don't mess with condition decls in a template. */ |
| FINISH_COND (cond, while_stmt, WHILE_COND (while_stmt)); |
| else if (getdecls () == NULL_TREE) |
| /* It was a simple condition; install it. */ |
| WHILE_COND (while_stmt) = cond; |
| else |
| { |
| /* If there was a declaration in the condition, we can't leave it |
| there; transform |
| while (A x = 42) { } |
| to |
| while (true) { A x = 42; if (!x) break; } */ |
| tree if_stmt; |
| WHILE_COND (while_stmt) = boolean_true_node; |
| |
| if_stmt = begin_if_stmt (); |
| cond = build_unary_op (TRUTH_NOT_EXPR, cond, 0); |
| finish_if_stmt_cond (cond, if_stmt); |
| finish_break_stmt (); |
| finish_then_clause (if_stmt); |
| finish_if_stmt (); |
| } |
| } |
| |
| /* Finish a while-statement, which may be given by WHILE_STMT. */ |
| |
| void |
| finish_while_stmt (tree while_stmt) |
| { |
| do_poplevel (); |
| RECHAIN_STMTS (while_stmt, WHILE_BODY (while_stmt)); |
| finish_stmt (); |
| } |
| |
| /* Begin a do-statement. Returns a newly created DO_STMT if |
| appropriate. */ |
| |
| tree |
| begin_do_stmt (void) |
| { |
| tree r = build_stmt (DO_STMT, NULL_TREE, NULL_TREE); |
| add_stmt (r); |
| return r; |
| } |
| |
| /* Finish the body of a do-statement, which may be given by DO_STMT. */ |
| |
| void |
| finish_do_body (tree do_stmt) |
| { |
| RECHAIN_STMTS (do_stmt, DO_BODY (do_stmt)); |
| } |
| |
| /* Finish a do-statement, which may be given by DO_STMT, and whose |
| COND is as indicated. */ |
| |
| void |
| finish_do_stmt (tree cond, tree do_stmt) |
| { |
| cond = maybe_convert_cond (cond); |
| DO_COND (do_stmt) = cond; |
| finish_stmt (); |
| } |
| |
| /* Finish a return-statement. The EXPRESSION returned, if any, is as |
| indicated. */ |
| |
| tree |
| finish_return_stmt (tree expr) |
| { |
| tree r; |
| |
| expr = check_return_expr (expr); |
| if (!processing_template_decl) |
| { |
| if (DECL_DESTRUCTOR_P (current_function_decl)) |
| { |
| /* 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 (dtor_label); |
| } |
| } |
| r = add_stmt (build_stmt (RETURN_STMT, expr)); |
| finish_stmt (); |
| |
| return r; |
| } |
| |
| /* Begin a for-statement. Returns a new FOR_STMT if appropriate. */ |
| |
| tree |
| begin_for_stmt (void) |
| { |
| tree r; |
| |
| r = build_stmt (FOR_STMT, NULL_TREE, NULL_TREE, |
| NULL_TREE, NULL_TREE); |
| NEW_FOR_SCOPE_P (r) = flag_new_for_scope > 0; |
| if (NEW_FOR_SCOPE_P (r)) |
| do_pushlevel (sk_for); |
| add_stmt (r); |
| |
| return r; |
| } |
| |
| /* Finish the for-init-statement of a for-statement, which may be |
| given by FOR_STMT. */ |
| |
| void |
| finish_for_init_stmt (tree for_stmt) |
| { |
| if (last_tree != for_stmt) |
| RECHAIN_STMTS (for_stmt, FOR_INIT_STMT (for_stmt)); |
| do_pushlevel (sk_block); |
| } |
| |
| /* Finish the COND of a for-statement, which may be given by |
| FOR_STMT. */ |
| |
| void |
| finish_for_cond (tree cond, tree for_stmt) |
| { |
| cond = maybe_convert_cond (cond); |
| if (processing_template_decl) |
| /* Don't mess with condition decls in a template. */ |
| FINISH_COND (cond, for_stmt, FOR_COND (for_stmt)); |
| else if (getdecls () == NULL_TREE) |
| /* It was a simple condition; install it. */ |
| FOR_COND (for_stmt) = cond; |
| else |
| { |
| /* If there was a declaration in the condition, we can't leave it |
| there; transform |
| for (; A x = 42;) { } |
| to |
| for (;;) { A x = 42; if (!x) break; } */ |
| tree if_stmt; |
| FOR_COND (for_stmt) = NULL_TREE; |
| |
| if_stmt = begin_if_stmt (); |
| cond = build_unary_op (TRUTH_NOT_EXPR, cond, 0); |
| finish_if_stmt_cond (cond, if_stmt); |
| finish_break_stmt (); |
| finish_then_clause (if_stmt); |
| finish_if_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 is an overloaded function, issue an error; there is no |
| context available to use to perform overload resolution. */ |
| if (expr && type_unknown_p (expr)) |
| { |
| cxx_incomplete_type_error (expr, TREE_TYPE (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. */ |
| |
| void |
| finish_for_stmt (tree for_stmt) |
| { |
| /* Pop the scope for the body of the loop. */ |
| do_poplevel (); |
| RECHAIN_STMTS (for_stmt, FOR_BODY (for_stmt)); |
| if (NEW_FOR_SCOPE_P (for_stmt)) |
| do_poplevel (); |
| finish_stmt (); |
| } |
| |
| /* Finish a break-statement. */ |
| |
| tree |
| finish_break_stmt (void) |
| { |
| return add_stmt (build_break_stmt ()); |
| } |
| |
| /* Finish a continue-statement. */ |
| |
| tree |
| finish_continue_stmt (void) |
| { |
| return add_stmt (build_continue_stmt ()); |
| } |
| |
| /* Begin a switch-statement. Returns a new SWITCH_STMT if |
| appropriate. */ |
| |
| tree |
| begin_switch_stmt (void) |
| { |
| tree r; |
| do_pushlevel (sk_block); |
| r = build_stmt (SWITCH_STMT, NULL_TREE, NULL_TREE, NULL_TREE); |
| add_stmt (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) |
| { |
| tree index; |
| |
| /* Convert the condition to an integer or enumeration type. */ |
| cond = build_expr_type_conversion (WANT_INT | WANT_ENUM, cond, true); |
| if (cond == NULL_TREE) |
| { |
| error ("switch quantity not an integer"); |
| cond = error_mark_node; |
| } |
| orig_type = TREE_TYPE (cond); |
| if (cond != error_mark_node) |
| { |
| /* [stmt.switch] |
| |
| Integral promotions are performed. */ |
| cond = perform_integral_promotions (cond); |
| cond = fold (build1 (CLEANUP_POINT_EXPR, TREE_TYPE (cond), cond)); |
| } |
| |
| if (cond != error_mark_node) |
| { |
| index = get_unwidened (cond, NULL_TREE); |
| /* We can't strip a conversion from a signed type to an unsigned, |
| because if we did, int_fits_type_p would do the wrong thing |
| when checking case values for being in range, |
| and it's too hard to do the right thing. */ |
| if (TREE_UNSIGNED (TREE_TYPE (cond)) |
| == TREE_UNSIGNED (TREE_TYPE (index))) |
| cond = index; |
| } |
| } |
| FINISH_COND (cond, switch_stmt, SWITCH_COND (switch_stmt)); |
| SWITCH_TYPE (switch_stmt) = orig_type; |
| push_switch (switch_stmt); |
| } |
| |
| /* 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) |
| { |
| RECHAIN_STMTS (switch_stmt, SWITCH_BODY (switch_stmt)); |
| pop_switch (); |
| finish_stmt (); |
| do_poplevel (); |
| } |
| |
| /* Generate the RTL for T, which is a TRY_BLOCK. */ |
| |
| static void |
| genrtl_try_block (tree t) |
| { |
| if (CLEANUP_P (t)) |
| { |
| expand_eh_region_start (); |
| expand_stmt (TRY_STMTS (t)); |
| expand_eh_region_end_cleanup (TRY_HANDLERS (t)); |
| } |
| else |
| { |
| if (!FN_TRY_BLOCK_P (t)) |
| emit_line_note (input_location); |
| |
| expand_eh_region_start (); |
| expand_stmt (TRY_STMTS (t)); |
| |
| if (FN_TRY_BLOCK_P (t)) |
| { |
| expand_start_all_catch (); |
| in_function_try_handler = 1; |
| expand_stmt (TRY_HANDLERS (t)); |
| in_function_try_handler = 0; |
| expand_end_all_catch (); |
| } |
| else |
| { |
| expand_start_all_catch (); |
| expand_stmt (TRY_HANDLERS (t)); |
| expand_end_all_catch (); |
| } |
| } |
| } |
| |
| /* Generate the RTL for T, which is an EH_SPEC_BLOCK. */ |
| |
| static void |
| genrtl_eh_spec_block (tree t) |
| { |
| expand_eh_region_start (); |
| expand_stmt (EH_SPEC_STMTS (t)); |
| expand_eh_region_end_allowed (EH_SPEC_RAISES (t), |
| build_call (call_unexpected_node, |
| tree_cons (NULL_TREE, |
| build_exc_ptr (), |
| NULL_TREE))); |
| } |
| |
| /* Begin a try-block. Returns a newly-created TRY_BLOCK if |
| appropriate. */ |
| |
| tree |
| begin_try_block (void) |
| { |
| tree r = build_stmt (TRY_BLOCK, NULL_TREE, NULL_TREE); |
| add_stmt (r); |
| return r; |
| } |
| |
| /* Likewise, for a function-try-block. */ |
| |
| tree |
| begin_function_try_block (void) |
| { |
| tree r = build_stmt (TRY_BLOCK, NULL_TREE, NULL_TREE); |
| FN_TRY_BLOCK_P (r) = 1; |
| add_stmt (r); |
| return r; |
| } |
| |
| /* Finish a try-block, which may be given by TRY_BLOCK. */ |
| |
| void |
| finish_try_block (tree try_block) |
| { |
| RECHAIN_STMTS (try_block, TRY_STMTS (try_block)); |
| } |
| |
| /* Finish the body of a cleanup try-block, which may be given by |
| TRY_BLOCK. */ |
| |
| void |
| finish_cleanup_try_block (tree try_block) |
| { |
| RECHAIN_STMTS (try_block, 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) |
| { |
| if (TREE_CHAIN (try_block) |
| && TREE_CODE (TREE_CHAIN (try_block)) == CTOR_INITIALIZER) |
| { |
| /* Chain the compound statement after the CTOR_INITIALIZER. */ |
| TREE_CHAIN (TREE_CHAIN (try_block)) = last_tree; |
| /* And make the CTOR_INITIALIZER the body of the try-block. */ |
| RECHAIN_STMTS (try_block, TRY_STMTS (try_block)); |
| } |
| else |
| RECHAIN_STMTS (try_block, TRY_STMTS (try_block)); |
| 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) |
| { |
| RECHAIN_STMTS (try_block, TRY_HANDLERS (try_block)); |
| check_handlers (TRY_HANDLERS (try_block)); |
| } |
| |
| /* Likewise, for a function-try-block. */ |
| |
| void |
| finish_function_handler_sequence (tree try_block) |
| { |
| in_function_try_handler = 0; |
| RECHAIN_STMTS (try_block, TRY_HANDLERS (try_block)); |
| check_handlers (TRY_HANDLERS (try_block)); |
| } |
| |
| /* Generate the RTL for T, which is a HANDLER. */ |
| |
| static void |
| genrtl_handler (tree t) |
| { |
| genrtl_do_pushlevel (); |
| if (!processing_template_decl) |
| expand_start_catch (HANDLER_TYPE (t)); |
| expand_stmt (HANDLER_BODY (t)); |
| if (!processing_template_decl) |
| expand_end_catch (); |
| } |
| |
| /* Begin a handler. Returns a HANDLER if appropriate. */ |
| |
| tree |
| begin_handler (void) |
| { |
| tree r; |
| r = build_stmt (HANDLER, NULL_TREE, NULL_TREE); |
| add_stmt (r); |
| /* Create a binding level for the eh_info and the exception object |
| cleanup. */ |
| 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); |
| add_decl_stmt (decl); |
| RECHAIN_STMTS (handler, HANDLER_PARMS (handler)); |
| type = TREE_TYPE (decl); |
| } |
| } |
| else |
| type = expand_start_catch_block (decl); |
| |
| HANDLER_TYPE (handler) = type; |
| if (!processing_template_decl && type) |
| mark_used (eh_type_info (type)); |
| } |
| |
| /* Finish a handler, which may be given by HANDLER. The BLOCKs are |
| the return value from the matching call to finish_handler_parms. */ |
| |
| void |
| finish_handler (tree handler) |
| { |
| if (!processing_template_decl) |
| expand_end_catch_block (); |
| do_poplevel (); |
| RECHAIN_STMTS (handler, HANDLER_BODY (handler)); |
| } |
| |
| /* Begin a compound-statement. If HAS_NO_SCOPE is true, the |
| compound-statement does not define a scope. Returns a new |
| COMPOUND_STMT. */ |
| |
| tree |
| begin_compound_stmt (bool has_no_scope) |
| { |
| tree r; |
| int is_try = 0; |
| |
| r = build_stmt (COMPOUND_STMT, NULL_TREE); |
| |
| if (last_tree && TREE_CODE (last_tree) == TRY_BLOCK) |
| is_try = 1; |
| |
| add_stmt (r); |
| if (has_no_scope) |
| COMPOUND_STMT_NO_SCOPE (r) = 1; |
| |
| last_expr_type = NULL_TREE; |
| |
| if (!has_no_scope) |
| do_pushlevel (is_try ? sk_try : sk_block); |
| else |
| /* 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); |
| |
| return r; |
| } |
| |
| /* Finish a compound-statement, which is given by COMPOUND_STMT. */ |
| |
| tree |
| finish_compound_stmt (tree compound_stmt) |
| { |
| tree r; |
| tree t; |
| |
| if (COMPOUND_STMT_NO_SCOPE (compound_stmt)) |
| r = NULL_TREE; |
| else |
| r = do_poplevel (); |
| |
| RECHAIN_STMTS (compound_stmt, COMPOUND_BODY (compound_stmt)); |
| |
| /* When we call finish_stmt we will lose LAST_EXPR_TYPE. But, since |
| the precise purpose of that variable is store the type of the |
| last expression statement within the last compound statement, we |
| preserve the value. */ |
| t = last_expr_type; |
| finish_stmt (); |
| last_expr_type = t; |
| |
| return r; |
| } |
| |
| /* Finish an asm-statement, whose components are a CV_QUALIFIER, a |
| STRING, some OUTPUT_OPERANDS, some INPUT_OPERANDS, and some |
| CLOBBERS. */ |
| |
| tree |
| finish_asm_stmt (tree cv_qualifier, |
| tree string, |
| tree output_operands, |
| tree input_operands, |
| tree clobbers) |
| { |
| tree r; |
| tree t; |
| |
| if (cv_qualifier != NULL_TREE |
| && cv_qualifier != ridpointers[(int) RID_VOLATILE]) |
| { |
| warning ("%s qualifier ignored on asm", |
| IDENTIFIER_POINTER (cv_qualifier)); |
| cv_qualifier = NULL_TREE; |
| } |
| |
| if (!processing_template_decl) |
| { |
| int i; |
| int ninputs; |
| int noutputs; |
| |
| for (t = input_operands; t; t = TREE_CHAIN (t)) |
| { |
| tree converted_operand |
| = decay_conversion (TREE_VALUE (t)); |
| |
| /* 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 (converted_operand) == unknown_type_node) |
| { |
| error ("type of asm operand `%E' could not be determined", |
| TREE_VALUE (t)); |
| converted_operand = error_mark_node; |
| } |
| TREE_VALUE (t) = converted_operand; |
| } |
| |
| ninputs = list_length (input_operands); |
| noutputs = list_length (output_operands); |
| |
| for (i = 0, t = output_operands; t; t = TREE_CHAIN (t), ++i) |
| { |
| bool allows_mem; |
| bool allows_reg; |
| bool is_inout; |
| const char *constraint; |
| tree operand; |
| |
| constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))); |
| operand = TREE_VALUE (t); |
| |
| if (!parse_output_constraint (&constraint, |
| i, ninputs, noutputs, |
| &allows_mem, |
| &allows_reg, |
| &is_inout)) |
| { |
| /* By marking this operand as erroneous, we will not try |
| to process this operand again in expand_asm_operands. */ |
| TREE_VALUE (t) = error_mark_node; |
| continue; |
| } |
| |
| /* If the operand is a DECL that is going to end up in |
| memory, assume it is addressable. This is a bit more |
| conservative than it would ideally be; the exact test is |
| buried deep in expand_asm_operands and depends on the |
| DECL_RTL for the OPERAND -- which we don't have at this |
| point. */ |
| if (!allows_reg && DECL_P (operand)) |
| cxx_mark_addressable (operand); |
| } |
| } |
| |
| r = build_stmt (ASM_STMT, cv_qualifier, string, |
| output_operands, input_operands, |
| clobbers); |
| return add_stmt (r); |
| } |
| |
| /* Finish a label with the indicated NAME. */ |
| |
| tree |
| finish_label_stmt (tree name) |
| { |
| tree decl = define_label (input_location, name); |
| return add_stmt (build_stmt (LABEL_STMT, 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) |
| { |
| tree decl = declare_local_label (name); |
| add_decl_stmt (decl); |
| } |
| |
| /* When DECL goes out of scope, make sure that CLEANUP is executed. */ |
| |
| void |
| finish_decl_cleanup (tree decl, tree cleanup) |
| { |
| add_stmt (build_stmt (CLEANUP_STMT, decl, cleanup)); |
| } |
| |
| /* If the current scope exits with an exception, run CLEANUP. */ |
| |
| void |
| finish_eh_cleanup (tree cleanup) |
| { |
| tree r = build_stmt (CLEANUP_STMT, NULL_TREE, cleanup); |
| CLEANUP_EH_ONLY (r) = 1; |
| add_stmt (r); |
| } |
| |
| /* 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) |
| add_stmt (build_min_nt (CTOR_INITIALIZER, mem_inits)); |
| else |
| emit_mem_initializers (mem_inits); |
| } |
| |
| /* Returns the stack of SCOPE_STMTs for the current function. */ |
| |
| tree * |
| current_scope_stmt_stack (void) |
| { |
| return &cfun->language->base.x_scope_stmt_stack; |
| } |
| |
| /* Finish a parenthesized expression EXPR. */ |
| |
| tree |
| finish_parenthesized_expr (tree expr) |
| { |
| if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (TREE_CODE (expr)))) |
| /* This inhibits warnings in c_common_truthvalue_conversion. */ |
| C_SET_EXP_ORIGINAL_CODE (expr, ERROR_MARK); |
| |
| if (TREE_CODE (expr) == OFFSET_REF) |
| /* [expr.unary.op]/3 The qualified id of a pointer-to-member must not be |
| enclosed in parentheses. */ |
| PTRMEM_OK_P (expr) = 0; |
| 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) |
| { |
| my_friendly_assert (TREE_CODE (decl) == FIELD_DECL, 20020909); |
| |
| if (!object) |
| { |
| if (current_function_decl |
| && DECL_STATIC_FUNCTION_P (current_function_decl)) |
| cp_error_at ("invalid use of member `%D' in static member function", |
| decl); |
| else |
| cp_error_at ("invalid use of non-static data member `%D'", decl); |
| error ("from this location"); |
| |
| return error_mark_node; |
| } |
| TREE_USED (current_class_ptr) = 1; |
| if (processing_template_decl && !qualifying_scope) |
| { |
| tree type = TREE_TYPE (decl); |
| |
| if (TREE_CODE (type) == REFERENCE_TYPE) |
| type = TREE_TYPE (type); |
| else |
| { |
| /* Set the cv qualifiers. */ |
| int quals = cp_type_quals (TREE_TYPE (current_class_ref)); |
| |
| if (DECL_MUTABLE_P (decl)) |
| quals &= ~TYPE_QUAL_CONST; |
| |
| quals |= cp_type_quals (TREE_TYPE (decl)); |
| type = cp_build_qualified_type (type, quals); |
| } |
| |
| return build_min (COMPONENT_REF, type, object, decl); |
| } |
| else |
| { |
| tree access_type = TREE_TYPE (object); |
| tree lookup_context = context_for_name_lookup (decl); |
| |
| while (!DERIVED_FROM_P (lookup_context, access_type)) |
| { |
| access_type = TYPE_CONTEXT (access_type); |
| while (access_type && DECL_P (access_type)) |
| access_type = DECL_CONTEXT (access_type); |
| |
| if (!access_type) |
| { |
| cp_error_at ("object missing in reference to `%D'", decl); |
| error ("from this location"); |
| return error_mark_node; |
| } |
| } |
| |
| /* If PROCESSING_TEMPLATE_DECL is nonzero here, then |
| QUALIFYING_SCOPE is also non-null. Wrap this in a SCOPE_REF |
| for now. */ |
| if (processing_template_decl) |
| return build_min (SCOPE_REF, TREE_TYPE (decl), |
| qualifying_scope, DECL_NAME (decl)); |
| |
| perform_or_defer_access_check (TYPE_BINFO (access_type), decl); |
| |
| /* If the data member was named `C::M', convert `*this' to `C' |
| first. */ |
| if (qualifying_scope) |
| { |
| tree binfo = NULL_TREE; |
| object = build_scoped_ref (object, qualifying_scope, |
| &binfo); |
| } |
| |
| return build_class_member_access_expr (object, decl, |
| /*access_path=*/NULL_TREE, |
| /*preserve_reference=*/false); |
| } |
| } |
| |
| /* DECL was the declaration to which a qualified-id resolved. Issue |
| an error message if it is not accessible. If OBJECT_TYPE is |
| non-NULL, we have just seen `x->' or `x.' and OBJECT_TYPE is the |
| type of `*x', or `x', respectively. If the DECL was named as |
| `A::B' then NESTED_NAME_SPECIFIER is `A'. */ |
| |
| void |
| check_accessibility_of_qualified_id (tree decl, |
| tree object_type, |
| tree nested_name_specifier) |
| { |
| tree scope; |
| tree qualifying_type = NULL_TREE; |
| |
| /* Determine the SCOPE of DECL. */ |
| scope = context_for_name_lookup (decl); |
| /* If the SCOPE is not a type, then DECL is not a member. */ |
| if (!TYPE_P (scope)) |
| return; |
| /* Compute the scope through which DECL is being accessed. */ |
| if (object_type |
| /* OBJECT_TYPE might not be a class type; consider: |
| |
| class A { typedef int I; }; |
| I *p; |
| p->A::I::~I(); |
| |
| In this case, we will have "A::I" as the DECL, but "I" as the |
| OBJECT_TYPE. */ |
| && CLASS_TYPE_P (object_type) |
| && DERIVED_FROM_P (scope, object_type)) |
| /* If we are processing a `->' or `.' expression, use the type of the |
| left-hand side. */ |
| qualifying_type = object_type; |
| else if (nested_name_specifier) |
| { |
| /* If the reference is to a non-static member of the |
| current class, treat it as if it were referenced through |
| `this'. */ |
| if (DECL_NONSTATIC_MEMBER_P (decl) |
| && current_class_ptr |
| && DERIVED_FROM_P (scope, current_class_type)) |
| qualifying_type = current_class_type; |
| /* Otherwise, use the type indicated by the |
| nested-name-specifier. */ |
| else |
| qualifying_type = nested_name_specifier; |
| } |
| else |
| /* Otherwise, the name must be from the current class or one of |
| its bases. */ |
| qualifying_type = currently_open_derived_class (scope); |
| |
| if (qualifying_type) |
| perform_or_defer_access_check (TYPE_BINFO (qualifying_type), decl); |
| } |
| |
| /* 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 '&'. */ |
| |
| tree |
| finish_qualified_id_expr (tree qualifying_class, tree expr, bool done, |
| bool address_p) |
| { |
| if (error_operand_p (expr)) |
| return error_mark_node; |
| |
| /* 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); |
| return expr; |
| } |
| |
| if (TREE_CODE (expr) == FIELD_DECL) |
| expr = finish_non_static_data_member (expr, current_class_ref, |
| qualifying_class); |
| else if (BASELINK_P (expr) && !processing_template_decl) |
| { |
| tree fns; |
| |
| /* See if any of the functions are non-static members. */ |
| fns = BASELINK_FUNCTIONS (expr); |
| if (TREE_CODE (fns) == TEMPLATE_ID_EXPR) |
| fns = TREE_OPERAND (fns, 0); |
| /* If so, the expression may be relative to the current |
| class. */ |
| if (!shared_member_p (fns) |
| && current_class_type |
| && DERIVED_FROM_P (qualifying_class, current_class_type)) |
| expr = (build_class_member_access_expr |
| (maybe_dummy_object (qualifying_class, NULL), |
| expr, |
| BASELINK_ACCESS_BINFO (expr), |
| /*preserve_reference=*/false)); |
| 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); |
| } |
| |
| return expr; |
| } |
| |
| /* Begin a statement-expression. The value returned must be passed to |
| finish_stmt_expr. */ |
| |
| tree |
| begin_stmt_expr (void) |
| { |
| /* If we're outside a function, we won't have a statement-tree to |
| work with. But, if we see a statement-expression we need to |
| create one. */ |
| if (! cfun && !last_tree) |
| begin_stmt_tree (&scope_chain->x_saved_tree); |
| |
| last_expr_type = NULL_TREE; |
| |
| keep_next_level (true); |
| |
| return last_tree; |
| } |
| |
| /* Process the final expression of a statement expression. EXPR can be |
| NULL, if the final expression is empty. Build up a TARGET_EXPR so |
| that the result value can be safely returned to the enclosing |
| expression. */ |
| |
| tree |
| finish_stmt_expr_expr (tree expr) |
| { |
| tree result = NULL_TREE; |
| tree type = void_type_node; |
| |
| if (expr) |
| { |
| type = TREE_TYPE (expr); |
| |
| if (!processing_template_decl && !VOID_TYPE_P (TREE_TYPE (expr))) |
| { |
| if (TREE_CODE (type) == ARRAY_TYPE |
| || TREE_CODE (type) == FUNCTION_TYPE) |
| expr = decay_conversion (expr); |
| |
| expr = convert_from_reference (expr); |
| expr = require_complete_type (expr); |
| |
| /* Build a TARGET_EXPR for this aggregate. finish_stmt_expr |
| will then pull it apart so the lifetime of the target is |
| within the scope of the expression containing this statement |
| expression. */ |
| if (TREE_CODE (expr) == TARGET_EXPR) |
| ; |
| else if (!IS_AGGR_TYPE (type) || TYPE_HAS_TRIVIAL_INIT_REF (type)) |
| expr = build_target_expr_with_type (expr, type); |
| else |
| { |
| /* Copy construct. */ |
| expr = build_special_member_call |
| (NULL_TREE, complete_ctor_identifier, |
| build_tree_list (NULL_TREE, expr), |
| TYPE_BINFO (type), LOOKUP_NORMAL); |
| expr = build_cplus_new (type, expr); |
| my_friendly_assert (TREE_CODE (expr) == TARGET_EXPR, 20030729); |
| } |
| } |
| |
| if (expr != error_mark_node) |
| { |
| result = build_stmt (EXPR_STMT, expr); |
| add_stmt (result); |
| } |
| } |
| |
| finish_stmt (); |
| |
| /* Remember the last expression so that finish_stmt_expr can pull it |
| apart. */ |
| last_expr_type = result ? result : void_type_node; |
| |
| return result; |
| } |
| |
| /* 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 rtl_expr, bool has_no_scope) |
| { |
| tree result; |
| tree result_stmt = last_expr_type; |
| tree type; |
| |
| if (!last_expr_type) |
| type = void_type_node; |
| else |
| { |
| if (result_stmt == void_type_node) |
| { |
| type = void_type_node; |
| result_stmt = NULL_TREE; |
| } |
| else |
| type = TREE_TYPE (EXPR_STMT_EXPR (result_stmt)); |
| } |
| |
| result = build_min (STMT_EXPR, type, last_tree); |
| TREE_SIDE_EFFECTS (result) = 1; |
| STMT_EXPR_NO_SCOPE (result) = has_no_scope; |
| |
| last_expr_type = NULL_TREE; |
| |
| /* Remove the compound statement from the tree structure; it is |
| now saved in the STMT_EXPR. */ |
| last_tree = rtl_expr; |
| TREE_CHAIN (last_tree) = NULL_TREE; |
| |
| /* If we created a statement-tree for this statement-expression, |
| remove it now. */ |
| if (! cfun |
| && TREE_CHAIN (scope_chain->x_saved_tree) == NULL_TREE) |
| finish_stmt_tree (&scope_chain->x_saved_tree); |
| |
| if (processing_template_decl) |
| return result; |
| |
| if (!VOID_TYPE_P (type)) |
| { |
| /* Pull out the TARGET_EXPR that is the final expression. Put |
| the target's init_expr as the final expression and then put |
| the statement expression itself as the target's init |
| expr. Finally, return the target expression. */ |
| tree last_expr = EXPR_STMT_EXPR (result_stmt); |
| |
| my_friendly_assert (TREE_CODE (last_expr) == TARGET_EXPR, 20030729); |
| EXPR_STMT_EXPR (result_stmt) = TREE_OPERAND (last_expr, 1); |
| TREE_OPERAND (last_expr, 1) = result; |
| result = last_expr; |
| } |
| return result; |
| } |
| |
| /* Perform Koenig lookup. FN is the postfix-expression representing |
| the function (or functions) to call; ARGS are the arguments to the |
| call. Returns the functions to be considered by overload |
| resolution. */ |
| |
| tree |
| perform_koenig_lookup (tree fn, tree args) |
| { |
| tree identifier = NULL_TREE; |
| tree functions = NULL_TREE; |
| |
| /* Find the name of the overloaded function. */ |
| if (TREE_CODE (fn) == IDENTIFIER_NODE) |
| identifier = fn; |
| else if (is_overloaded_fn (fn)) |
| { |
| functions = fn; |
| identifier = DECL_NAME (get_first_fn (functions)); |
| } |
| else if (DECL_P (fn)) |
| { |
| functions = fn; |
| identifier = DECL_NAME (fn); |
| } |
| |
| /* A call to a namespace-scope function using an unqualified name. |
| |
| Do Koenig lookup -- unless any of the arguments are |
| type-dependent. */ |
| if (!any_type_dependent_arguments_p (args)) |
| { |
| fn = lookup_arg_dependent (identifier, functions, args); |
| if (!fn) |
| /* The unqualified name could not be resolved. */ |
| fn = unqualified_fn_lookup_error (identifier); |
| } |
| else |
| fn = identifier; |
| |
| return fn; |
| } |
| |
| /* Generate an expression for `FN (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, tree args, bool disallow_virtual, bool koenig_p) |
| { |
| tree result; |
| tree orig_fn; |
| tree orig_args; |
| |
| if (fn == error_mark_node || args == error_mark_node) |
| return error_mark_node; |
| |
| /* ARGS should be a list of arguments. */ |
| my_friendly_assert (!args || TREE_CODE (args) == TREE_LIST, |
| 20020712); |
| |
| orig_fn = fn; |
| orig_args = args; |
| |
| if (processing_template_decl) |
| { |
| if (type_dependent_expression_p (fn) |
| || any_type_dependent_arguments_p (args)) |
| { |
| result = build_nt (CALL_EXPR, fn, args); |
| KOENIG_LOOKUP_P (result) = koenig_p; |
| return result; |
| } |
| if (!BASELINK_P (fn) |
| && TREE_CODE (fn) != PSEUDO_DTOR_EXPR |
| && TREE_TYPE (fn) != unknown_type_node) |
| fn = build_non_dependent_expr (fn); |
| args = build_non_dependent_args (orig_args); |
| } |
| |
| /* A reference to a member function will appear as an overloaded |
| function (rather than a BASELINK) if an unqualified name was used |
| to refer to it. */ |
| if (!BASELINK_P (fn) && is_overloaded_fn (fn)) |
| { |
| tree f = fn; |
| |
| if (TREE_CODE (f) == TEMPLATE_ID_EXPR) |
| f = TREE_OPERAND (f, 0); |
| f = get_first_fn (f); |
| if (DECL_FUNCTION_MEMBER_P (f)) |
| { |
| tree type = currently_open_derived_class (DECL_CONTEXT (f)); |
| if (!type) |
| type = DECL_CONTEXT (f); |
| fn = build_baselink (TYPE_BINFO (type), |
| TYPE_BINFO (type), |
| fn, /*optype=*/NULL_TREE); |
| } |
| } |
| |
| 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. |
| |
| This paragraph is unclear about this situation: |
| |
| struct A { void f(); }; |
| struct B : public A {}; |
| struct C : public A { void g() { B::f(); }}; |
| |
| In particular, for `B::f', this paragraph does not make clear |
| whether "the class of that member function" refers to `A' or |
| to `B'. We believe it refers to `B'. */ |
| if (current_class_type |
| && DERIVED_FROM_P (BINFO_TYPE (BASELINK_ACCESS_BINFO (fn)), |
| current_class_type) |
| && current_class_ref) |
| object = maybe_dummy_object (BINFO_TYPE (BASELINK_ACCESS_BINFO (fn)), |
| NULL); |
| else |
| { |
| tree representative_fn; |
| |
| representative_fn = BASELINK_FUNCTIONS (fn); |
| if (TREE_CODE (representative_fn) == TEMPLATE_ID_EXPR) |
| representative_fn = TREE_OPERAND (representative_fn, 0); |
| representative_fn = get_first_fn (representative_fn); |
| object = build_dummy_object (DECL_CONTEXT (representative_fn)); |
| } |
| |
| if (processing_template_decl) |
| { |
| if (type_dependent_expression_p (object)) |
| return build_nt (CALL_EXPR, orig_fn, orig_args); |
| object = build_non_dependent_expr (object); |
| } |
| |
| result = build_new_method_call (object, fn, args, NULL_TREE, |
| (disallow_virtual |
| ? LOOKUP_NONVIRTUAL : 0)); |
| } |
| else if (is_overloaded_fn (fn)) |
| /* A call to a namespace-scope function. */ |
| result = build_new_function_call (fn, args); |
| else if (TREE_CODE (fn) == PSEUDO_DTOR_EXPR) |
| { |
| if (args) |
| error ("arguments to destructor are not allowed"); |
| /* Mark the pseudo-destructor call as having side-effects so |
| that we do not issue warnings about its use. */ |
| result = build1 (NOP_EXPR, |
| void_type_node, |
| TREE_OPERAND (fn, 0)); |
| TREE_SIDE_EFFECTS (result) = 1; |
| } |
| else if (CLASS_TYPE_P (TREE_TYPE (fn))) |
| /* If the "function" is really an object of class type, it might |
| have an overloaded `operator ()'. */ |
| result = build_new_op (CALL_EXPR, LOOKUP_NORMAL, fn, args, NULL_TREE, |
| /*overloaded_p=*/NULL); |
| if (!result) |
| /* A call where the function is unknown. */ |
| result = build_function_call (fn, args); |
| |
| if (processing_template_decl) |
| { |
| result = build (CALL_EXPR, TREE_TYPE (result), orig_fn, orig_args); |
| KOENIG_LOOKUP_P (result) = koenig_p; |
| } |
| return result; |
| } |
| |
| /* Finish a call to a postfix increment or decrement or EXPR. (Which |
| is indicated by CODE, which should be POSTINCREMENT_EXPR or |
| POSTDECREMENT_EXPR.) */ |
| |
| tree |
| finish_increment_expr (tree expr, enum tree_code code) |
| { |
| return build_x_unary_op (code, expr); |
| } |
| |
| /* Finish a use of `this'. Returns an expression for `this'. */ |
| |
| tree |
| finish_this_expr (void) |
| { |
| tree result; |
| |
| if (current_class_ptr) |
| { |
| result = current_class_ptr; |
| } |
| else if (current_function_decl |
| && DECL_STATIC_FUNCTION_P (current_function_decl)) |
| { |
| error ("`this' is unavailable for static member functions"); |
| result = error_mark_node; |
| } |
| else |
| { |
| if (current_function_decl) |
| error ("invalid use of `this' in non-member function"); |
| else |
| error ("invalid use of `this' at top level"); |
| result = error_mark_node; |
| } |
| |
| return result; |
| } |
| |
| /* Finish a pseudo-destructor expression. If SCOPE is NULL, the |
| expression was of the form `OBJECT.~DESTRUCTOR' where DESTRUCTOR is |
| the TYPE for the type given. If SCOPE is non-NULL, the expression |
| was of the form `OBJECT.SCOPE::~DESTRUCTOR'. */ |
| |
| tree |
| finish_pseudo_destructor_expr (tree object, tree scope, tree destructor) |
| { |
| if (destructor == error_mark_node) |
| return error_mark_node; |
| |
| my_friendly_assert (TYPE_P (destructor), 20010905); |
| |
| if (!processing_template_decl) |
| { |
| if (scope == error_mark_node) |
| { |
| error ("invalid qualifying scope in pseudo-destructor name"); |
| 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 ("`%E' is not of type `%T'", object, destructor); |
| return error_mark_node; |
| } |
| } |
| |
| return build (PSEUDO_DTOR_EXPR, void_type_node, object, scope, destructor); |
| } |
| |
| /* Finish an expression of the form CODE EXPR. */ |
| |
| tree |
| finish_unary_op_expr (enum tree_code code, tree expr) |
| { |
| tree result = build_x_unary_op (code, expr); |
| /* Inside a template, build_x_unary_op does not fold the |
| expression. So check whether the result is folded before |
| setting TREE_NEGATED_INT. */ |
| if (code == NEGATE_EXPR && TREE_CODE (expr) == INTEGER_CST |
| && TREE_CODE (result) == INTEGER_CST |
| && !TREE_UNSIGNED (TREE_TYPE (result)) |
| && INT_CST_LT (result, integer_zero_node)) |
| TREE_NEGATED_INT (result) = 1; |
| overflow_warning (result); |
| return result; |
| } |
| |
| /* Finish a compound-literal expression. TYPE is the type to which |
| the INITIALIZER_LIST is being cast. */ |
| |
| tree |
| finish_compound_literal (tree type, tree initializer_list) |
| { |
| tree compound_literal; |
| |
| /* Build a CONSTRUCTOR for the INITIALIZER_LIST. */ |
| compound_literal = build_constructor (NULL_TREE, initializer_list); |
| /* Mark it as a compound-literal. */ |
| TREE_HAS_CONSTRUCTOR (compound_literal) = 1; |
| if (processing_template_decl) |
| TREE_TYPE (compound_literal) = type; |
| else |
| { |
| /* Check the initialization. */ |
| compound_literal = digest_init (type, compound_literal, NULL); |
| /* If the TYPE was an array type with an unknown bound, then we can |
| figure out the dimension now. For example, something like: |
| |
| `(int []) { 2, 3 }' |
| |
| implies that the array has two elements. */ |
| if (TREE_CODE (type) == ARRAY_TYPE && !COMPLETE_TYPE_P (type)) |
| complete_array_type (type, compound_literal, 1); |
| } |
| |
| return compound_literal; |
| } |
| |
| /* Return the declaration for the function-name variable indicated by |
| ID. */ |
| |
| tree |
| finish_fname (tree id) |
| { |
| tree decl; |
| |
| decl = fname_decl (C_RID_CODE (id), id); |
| if (processing_template_decl) |
| decl = DECL_NAME (decl); |
| return decl; |
| } |
| |
| /* Begin a function definition declared with DECL_SPECS, ATTRIBUTES, |
| and DECLARATOR. Returns nonzero if the function-declaration is |
| valid. */ |
| |
| int |
| begin_function_definition (tree decl_specs, tree attributes, tree declarator) |
| { |
| if (!start_function (decl_specs, declarator, attributes, SF_DEFAULT)) |
| return 0; |
| |
| /* The things we're about to see are not directly qualified by any |
| template headers we've seen thus far. */ |
| reset_specialization (); |
| |
| return 1; |
| } |
| |
| /* Finish a translation unit. */ |
| |
| void |
| finish_translation_unit (void) |
| { |
| /* In case there were missing closebraces, |
| get us back to the global binding level. */ |
| pop_everything (); |
| while (current_namespace != global_namespace) |
| pop_namespace (); |
| |
| /* Do file scope __FUNCTION__ et al. */ |
| finish_fname_decls (); |
| } |
| |
| /* Finish a template type parameter, specified as AGGR IDENTIFIER. |
| Returns the parameter. */ |
| |
| tree |
| finish_template_type_parm (tree aggr, tree identifier) |
| { |
| if (aggr != class_type_node) |
| { |
| pedwarn ("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 (TYPE_DECL, identifier, NULL_TREE); |
| tree tmpl = build_lang_decl (TEMPLATE_DECL, identifier, NULL_TREE); |
| DECL_TEMPLATE_PARMS (tmpl) = current_template_parms; |
| DECL_TEMPLATE_RESULT (tmpl) = decl; |
| DECL_ARTIFICIAL (decl) = 1; |
| end_template_decl (); |
| |
| my_friendly_assert (DECL_TEMPLATE_PARMS (tmpl), 20010110); |
| |
| 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) |
| { |
| tree t = TREE_TYPE (argument); |
| |
| /* Try to emit a slightly smarter error message if we detect |
| that the user is using a template instantiation. */ |
| if (CLASSTYPE_TEMPLATE_INFO (t) |
| && CLASSTYPE_TEMPLATE_INSTANTIATION (t)) |
| error ("invalid use of type `%T' as a default value for a " |
| "template template-parameter", t); |
| else |
| error ("invalid use of `%D' as a default value for a template " |
| "template-parameter", argument); |
| } |
| else |
| error ("invalid default argument for a template template parameter"); |
| return error_mark_node; |
| } |
| |
| return argument; |
| } |
| |
| /* Finish a parameter list, indicated by PARMS. If ELLIPSIS is |
| nonzero, the parameter list was terminated by a `...'. */ |
| |
| tree |
| finish_parmlist (tree parms, int ellipsis) |
| { |
| if (parms) |
| { |
| /* We mark the PARMS as a parmlist so that declarator processing can |
| disambiguate certain constructs. */ |
| TREE_PARMLIST (parms) = 1; |
| /* We do not append void_list_node here, but leave it to grokparms |
| to do that. */ |
| PARMLIST_ELLIPSIS_P (parms) = ellipsis; |
| } |
| return parms; |
| } |
| |
| /* Begin a class definition, as indicated by T. */ |
| |
| tree |
| begin_class_definition (tree t) |
| { |
| if (t == error_mark_node) |
| return error_mark_node; |
| |
| if (processing_template_parmlist) |
| { |
| error ("definition of `%#T' inside template parameter list", t); |
| return error_mark_node; |
| } |
| /* A non-implicit typename comes from code like: |
| |
| template <typename T> struct A { |
| template <typename U> struct A<T>::B ... |
| |
| This is erroneous. */ |
| else if (TREE_CODE (t) == TYPENAME_TYPE) |
| { |
| error ("invalid definition of qualified type `%T'", t); |
| t = error_mark_node; |
| } |
| |
| if (t == error_mark_node || ! IS_AGGR_TYPE (t)) |
| { |
| t = make_aggr_type (RECORD_TYPE); |
| pushtag (make_anon_name (), t, 0); |
| } |
| |
| /* If this type was already complete, and we see another definition, |
| that's an error. */ |
| if (COMPLETE_TYPE_P (t)) |
| { |
| error ("redefinition of `%#T'", t); |
| cp_error_at ("previous definition of `%#T'", t); |
| return error_mark_node; |
| } |
| |
| /* Update the location of the decl. */ |
| DECL_SOURCE_LOCATION (TYPE_NAME (t)) = input_location; |
| |
| if (TYPE_BEING_DEFINED (t)) |
| { |
| t = make_aggr_type (TREE_CODE (t)); |
| pushtag (TYPE_IDENTIFIER (t), t, 0); |
| } |
| maybe_process_partial_specialization (t); |
| pushclass (t); |
| TYPE_BEING_DEFINED (t) = 1; |
| if (flag_pack_struct) |
| { |
| tree v; |
| TYPE_PACKED (t) = 1; |
| /* Even though the type is being defined for the first time |
| here, there might have been a forward declaration, so there |
| might be cv-qualified variants of T. */ |
| for (v = TYPE_NEXT_VARIANT (t); v; v = TYPE_NEXT_VARIANT (v)) |
| TYPE_PACKED (v) = 1; |
| } |
| /* Reset the interface data, at the earliest possible |
| moment, as it might have been set via a class foo; |
| before. */ |
| if (! TYPE_ANONYMOUS_P (t)) |
| { |
| CLASSTYPE_INTERFACE_ONLY (t) = interface_only; |
| SET_CLASSTYPE_INTERFACE_UNKNOWN_X |
| (t, interface_unknown); |
| } |
| reset_specialization(); |
| |
| /* Make a declaration for this class in its own scope. */ |
| build_self_reference (); |
| |
| return t; |
| } |
| |
| /* Finish the member declaration given by DECL. */ |
| |
| void |
| finish_member_declaration (tree decl) |
| { |
| if (decl == error_mark_node || decl == NULL_TREE) |
| return; |
| |
| if (decl == void_type_node) |
| /* The COMPONENT was a friend, not a member, and so there's |
| nothing for us to do. */ |
| return; |
| |
| /* We should see only one DECL at a time. */ |
| my_friendly_assert (TREE_CHAIN (decl) == NULL_TREE, 0); |
| |
| /* Set up access control for DECL. */ |
| TREE_PRIVATE (decl) |
| = (current_access_specifier == access_private_node); |
| TREE_PROTECTED (decl) |
| = (current_access_specifier == access_protected_node); |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| { |
| TREE_PRIVATE (DECL_TEMPLATE_RESULT (decl)) = TREE_PRIVATE (decl); |
| TREE_PROTECTED (DECL_TEMPLATE_RESULT (decl)) = TREE_PROTECTED (decl); |
| } |
| |
| /* Mark the DECL as a member of the current class. */ |
| DECL_CONTEXT (decl) = current_class_type; |
| |
| /* [dcl.link] |
| |
| A C language linkage is ignored for the names of class members |
| and the member function type of class member functions. */ |
| if (DECL_LANG_SPECIFIC (decl) && DECL_LANGUAGE (decl) == lang_c) |
| SET_DECL_LANGUAGE (decl, lang_cplusplus); |
| |
| /* Put functions on the TYPE_METHODS list and everything else on the |
| TYPE_FIELDS list. Note that these are built up in reverse order. |
| We reverse them (to obtain declaration order) in finish_struct. */ |
| if (TREE_CODE (decl) == FUNCTION_DECL |
| || DECL_FUNCTION_TEMPLATE_P (decl)) |
| { |
| /* We also need to add this function to the |
| CLASSTYPE_METHOD_VEC. */ |
| add_method (current_class_type, decl, /*error_p=*/0); |
| |
| TREE_CHAIN (decl) = TYPE_METHODS (current_class_type); |
| TYPE_METHODS (current_class_type) = decl; |
| |
| maybe_add_class_template_decl_list (current_class_type, decl, |
| /*friend_p=*/0); |
| } |
| /* Enter the DECL into the scope of the class. */ |
| else if ((TREE_CODE (decl) == USING_DECL && TREE_TYPE (decl)) |
| || pushdecl_class_level (decl)) |
| { |
| /* All TYPE_DECLs go at the end of TYPE_FIELDS. Ordinary fields |
| go at the beginning. The reason is that lookup_field_1 |
| searches the list in order, and we want a field name to |
| override a type name so that the "struct stat hack" will |
| work. In particular: |
| |
| struct S { enum E { }; int E } s; |
| s.E = 3; |
| |
| is valid. In addition, the FIELD_DECLs must be maintained in |
| declaration order so that class layout works as expected. |
| However, we don't need that order until class layout, so we |
| save a little time by putting FIELD_DECLs on in reverse order |
| here, and then reversing them in finish_struct_1. (We could |
| also keep a pointer to the correct insertion points in the |
| list.) */ |
| |
| if (TREE_CODE (decl) == TYPE_DECL) |
| TYPE_FIELDS (current_class_type) |
| = chainon (TYPE_FIELDS (current_class_type), decl); |
| else |
| { |
| TREE_CHAIN (decl) = TYPE_FIELDS (current_class_type); |
| TYPE_FIELDS (current_class_type) = decl; |
| } |
| |
| maybe_add_class_template_decl_list (current_class_type, decl, |
| /*friend_p=*/0); |
| } |
| } |
| |
| /* Finish processing the declaration of a member class template |
| TYPES whose template parameters are given by PARMS. */ |
| |
| tree |
| finish_member_class_template (tree types) |
| { |
| tree t; |
| |
| /* If there are declared, but undefined, partial specializations |
| mixed in with the typespecs they will not yet have passed through |
| maybe_process_partial_specialization, so we do that here. */ |
| for (t = types; t != NULL_TREE; t = TREE_CHAIN (t)) |
| if (IS_AGGR_TYPE_CODE (TREE_CODE (TREE_VALUE (t)))) |
| maybe_process_partial_specialization (TREE_VALUE (t)); |
| |
| grok_x_components (types); |
| if (TYPE_CONTEXT (TREE_VALUE (types)) != current_class_type) |
| /* The component was in fact a friend declaration. We avoid |
| finish_member_template_decl performing certain checks by |
| unsetting TYPES. */ |
| types = NULL_TREE; |
| |
| finish_member_template_decl (types); |
| |
| /* As with other component type declarations, we do |
| not store the new DECL on the list of |
| component_decls. */ |
| return NULL_TREE; |
| } |
| |
| /* Finish processing a complete template declaration. The PARMS are |
| the template parameters. */ |
| |
| void |
| finish_template_decl (tree parms) |
| { |
| if (parms) |
| end_template_decl (); |
| else |
| end_specialization (); |
| } |
| |
| /* Finish processing a template-id (which names a type) of the form |
| NAME < ARGS >. Return the TYPE_DECL for the type named by the |
| template-id. If ENTERING_SCOPE is nonzero we are about to enter |
| the scope of template-id indicated. */ |
| |
| tree |
| finish_template_type (tree name, tree args, int entering_scope) |
| { |
| tree decl; |
| |
| decl = lookup_template_class (name, args, |
| NULL_TREE, NULL_TREE, entering_scope, |
| tf_error | tf_warning | tf_user); |
| if (decl != error_mark_node) |
| decl = TYPE_STUB_DECL (decl); |
| |
| return decl; |
| } |
| |
| /* Finish processing a BASE_CLASS with the indicated ACCESS_SPECIFIER. |
| Return a TREE_LIST containing the ACCESS_SPECIFIER and the |
| BASE_CLASS, or NULL_TREE if an error occurred. The |
| ACCESS_SPECIFIER is one of |
| access_{default,public,protected_private}[_virtual]_node.*/ |
| |
| tree |
| finish_base_specifier (tree base, tree access, bool virtual_p) |
| { |
| tree result; |
| |
| if (base == error_mark_node) |
| { |
| error ("invalid base-class specification"); |
| result = NULL_TREE; |
| } |
| else if (! is_aggr_type (base, 1)) |
| result = NULL_TREE; |
| else |
| { |
| if (cp_type_quals (base) != 0) |
| { |
| error ("base class `%T' has cv qualifiers", base); |
| base = TYPE_MAIN_VARIANT (base); |
| } |
| result = build_tree_list (access, base); |
| TREE_VIA_VIRTUAL (result) = virtual_p; |
| } |
| |
| return result; |
| } |
| |
| /* Called when multiple declarators are processed. If that is not |
| permitted in this context, an error is issued. */ |
| |
| void |
| check_multiple_declarators (void) |
| { |
| /* [temp] |
| |
| In a template-declaration, explicit specialization, or explicit |
| instantiation the init-declarator-list in the declaration shall |
| contain at most one declarator. |
| |
| We don't just use PROCESSING_TEMPLATE_DECL for the first |
| condition since that would disallow the perfectly valid code, |
| like `template <class T> struct S { int i, j; };'. */ |
| if (at_function_scope_p ()) |
| /* It's OK to write `template <class T> void f() { int i, j;}'. */ |
| return; |
| |
| if (PROCESSING_REAL_TEMPLATE_DECL_P () |
| || processing_explicit_instantiation |
| || processing_specialization) |
| error ("multiple declarators in template declaration"); |
| } |
| |
| /* Issue a diagnostic that NAME cannot be found in SCOPE. */ |
| |
| void |
| qualified_name_lookup_error (tree scope, tree name) |
| { |
| if (TYPE_P (scope)) |
| { |
| if (!COMPLETE_TYPE_P (scope)) |
| error ("incomplete type `%T' used in nested name specifier", scope); |
| else |
| error ("`%D' is not a member of `%T'", name, scope); |
| } |
| else if (scope != global_namespace) |
| error ("`%D' is not a member of `%D'", name, scope); |
| else |
| error ("`::%D' has not been declared", name); |
| } |
| |
| /* ID_EXPRESSION is a representation of parsed, but unprocessed, |
| id-expression. (See cp_parser_id_expression for details.) SCOPE, |
| if non-NULL, is the type or namespace used to explicitly qualify |
| ID_EXPRESSION. DECL is the entity to which that name has been |
| resolved. |
| |
| *CONSTANT_EXPRESSION_P is true if we are presently parsing a |
| constant-expression. In that case, *NON_CONSTANT_EXPRESSION_P will |
| be set to true if this expression isn't permitted in a |
| constant-expression, but it is otherwise not set by this function. |
| *ALLOW_NON_CONSTANT_EXPRESSION_P is true if we are parsing a |
| constant-expression, but a non-constant expression is also |
| permissible. |
| |
| If an error occurs, and it is the kind of error that might cause |
| the parser to abort a tentative parse, *ERROR_MSG is filled in. It |
| is the caller's responsibility to issue the message. *ERROR_MSG |
| will be a string with static storage duration, so the caller need |
| not "free" it. |
| |
| Return an expression for the entity, after issuing appropriate |
| diagnostics. This function is also responsible for transforming a |
| reference to a non-static member into a COMPONENT_REF that makes |
| the use of "this" explicit. |
| |
| Upon return, *IDK will be filled in appropriately. */ |
| |
| tree |
| finish_id_expression (tree id_expression, |
| tree decl, |
| tree scope, |
| cp_id_kind *idk, |
| tree *qualifying_class, |
| bool integral_constant_expression_p, |
| bool allow_non_integral_constant_expression_p, |
| bool *non_integral_constant_expression_p, |
| const char **error_msg) |
| { |
| /* Initialize the output parameters. */ |
| *idk = CP_ID_KIND_NONE; |
| *error_msg = NULL; |
| |
| if (id_expression == error_mark_node) |
| return error_mark_node; |
| /* If we have a template-id, then no further lookup is |
| required. If the template-id was for a template-class, we |
| will sometimes have a TYPE_DECL at this point. */ |
| else if (TREE_CODE (decl) == TEMPLATE_ID_EXPR |
| || TREE_CODE (decl) == TYPE_DECL) |
| ; |
| /* Look up the name. */ |
| else |
| { |
| if (decl == error_mark_node) |
| { |
| /* Name lookup failed. */ |
| if (scope |
| && (!TYPE_P (scope) |
| || (!dependent_type_p (scope) |
| && !(TREE_CODE (id_expression) == IDENTIFIER_NODE |
| && IDENTIFIER_TYPENAME_P (id_expression) |
| && dependent_type_p (TREE_TYPE (id_expression)))))) |
| { |
| /* If the qualifying type is non-dependent (and the name |
| does not name a conversion operator to a dependent |
| type), issue an error. */ |
| qualified_name_lookup_error (scope, id_expression); |
| return error_mark_node; |
| } |
| else if (!scope) |
| { |
| /* It may be resolved via Koenig lookup. */ |
| *idk = CP_ID_KIND_UNQUALIFIED; |
| return id_expression; |
| } |
| else |
| decl = id_expression; |
| } |
| /* If DECL is a variable that would be out of scope under |
| ANSI/ISO rules, but in scope in the ARM, name lookup |
| will succeed. Issue a diagnostic here. */ |
| else |
| decl = check_for_out_of_scope_variable (decl); |
| |
| /* Remember that the name was used in the definition of |
| the current class so that we can check later to see if |
| the meaning would have been different after the class |
| was entirely defined. */ |
| if (!scope && decl != error_mark_node) |
| maybe_note_name_used_in_class (id_expression, decl); |
| } |
| |
| /* If we didn't find anything, or what we found was a type, |
| then this wasn't really an id-expression. */ |
| if (TREE_CODE (decl) == TEMPLATE_DECL |
| && !DECL_FUNCTION_TEMPLATE_P (decl)) |
| { |
| *error_msg = "missing template arguments"; |
| return error_mark_node; |
| } |
| else if (TREE_CODE (decl) == TYPE_DECL |
| || TREE_CODE (decl) == NAMESPACE_DECL) |
| { |
| *error_msg = "expected primary-expression"; |
| return error_mark_node; |
| } |
| |
| /* If the name resolved to a template parameter, there is no |
| need to look it up again later. */ |
| if ((TREE_CODE (decl) == CONST_DECL && DECL_TEMPLATE_PARM_P (decl)) |
| || TREE_CODE (decl) == TEMPLATE_PARM_INDEX) |
| { |
| *idk = CP_ID_KIND_NONE; |
| if (TREE_CODE (decl) == TEMPLATE_PARM_INDEX) |
| decl = TEMPLATE_PARM_DECL (decl); |
| if (integral_constant_expression_p |
| && !dependent_type_p (TREE_TYPE (decl)) |
| && !INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (decl))) |
| { |
| if (!allow_non_integral_constant_expression_p) |
| error ("template parameter `%D' of type `%T' is not allowed in " |
| "an integral constant expression because it is not of " |
| "integral or enumeration type", decl, TREE_TYPE (decl)); |
| *non_integral_constant_expression_p = true; |
| } |
| return DECL_INITIAL (decl); |
| } |
| /* Similarly, we resolve enumeration constants to their |
| underlying values. */ |
| else if (TREE_CODE (decl) == CONST_DECL) |
| { |
| *idk = CP_ID_KIND_NONE; |
| if (!processing_template_decl) |
| return DECL_INITIAL (decl); |
| return decl; |
| } |
| else |
| { |
| bool dependent_p; |
| |
| /* If the declaration was explicitly qualified indicate |
| that. The semantics of `A::f(3)' are different than |
| `f(3)' if `f' is virtual. */ |
| *idk = (scope |
| ? CP_ID_KIND_QUALIFIED |
| : (TREE_CODE (decl) == TEMPLATE_ID_EXPR |
| ? CP_ID_KIND_TEMPLATE_ID |
| : CP_ID_KIND_UNQUALIFIED)); |
| |
| |
| /* [temp.dep.expr] |
| |
| An id-expression is type-dependent if it contains an |
| identifier that was declared with a dependent type. |
| |
| The standard is not very specific about an id-expression that |
| names a set of overloaded functions. What if some of them |
| have dependent types and some of them do not? Presumably, |
| such a name should be treated as a dependent name. */ |
| /* Assume the name is not dependent. */ |
| dependent_p = false; |
| if (!processing_template_decl) |
| /* No names are dependent outside a template. */ |
| ; |
| /* A template-id where the name of the template was not resolved |
| is definitely dependent. */ |
| else if (TREE_CODE (decl) == TEMPLATE_ID_EXPR |
| && (TREE_CODE (TREE_OPERAND (decl, 0)) |
| == IDENTIFIER_NODE)) |
| dependent_p = true; |
| /* For anything except an overloaded function, just check its |
| type. */ |
| else if (!is_overloaded_fn (decl)) |
| dependent_p |
| = dependent_type_p (TREE_TYPE (decl)); |
| /* For a set of overloaded functions, check each of the |
| functions. */ |
| else |
| { |
| tree fns = decl; |
| |
| if (BASELINK_P (fns)) |
| fns = BASELINK_FUNCTIONS (fns); |
| |
| /* For a template-id, check to see if the template |
| arguments are dependent. */ |
| if (TREE_CODE (fns) == TEMPLATE_ID_EXPR) |
| { |
| tree args = TREE_OPERAND (fns, 1); |
| dependent_p = any_dependent_template_arguments_p (args); |
| /* The functions are those referred to by the |
| template-id. */ |
| fns = TREE_OPERAND (fns, 0); |
| } |
| |
| /* If there are no dependent template arguments, go through |
| the overloaded functions. */ |
| while (fns && !dependent_p) |
| { |
| tree fn = OVL_CURRENT (fns); |
| |
| /* Member functions of dependent classes are |
| dependent. */ |
| if (TREE_CODE (fn) == FUNCTION_DECL |
| && type_dependent_expression_p (fn)) |
| dependent_p = true; |
| else if (TREE_CODE (fn) == TEMPLATE_DECL |
| && dependent_template_p (fn)) |
| dependent_p = true; |
| |
| fns = OVL_NEXT (fns); |
| } |
| } |
| |
| /* If the name was dependent on a template parameter, we will |
| resolve the name at instantiation time. */ |
| if (dependent_p) |
| { |
| /* Create a SCOPE_REF for qualified names, if the scope is |
| dependent. */ |
| if (scope) |
| { |
| if (TYPE_P (scope)) |
| *qualifying_class = scope; |
| /* Since this name was dependent, the expression isn't |
| constant -- yet. No error is issued because it might |
| be constant when things are instantiated. */ |
| if (integral_constant_expression_p) |
| *non_integral_constant_expression_p = true; |
| if (TYPE_P (scope) && dependent_type_p (scope)) |
| return build_nt (SCOPE_REF, scope, id_expression); |
| else if (TYPE_P (scope) && DECL_P (decl)) |
| return build (SCOPE_REF, TREE_TYPE (decl), scope, |
| id_expression); |
| else |
| return decl; |
| } |
| /* A TEMPLATE_ID already contains all the information we |
| need. */ |
| if (TREE_CODE (id_expression) == TEMPLATE_ID_EXPR) |
| return id_expression; |
| /* Since this name was dependent, the expression isn't |
| constant -- yet. No error is issued because it might be |
| constant when things are instantiated. */ |
| if (integral_constant_expression_p) |
| *non_integral_constant_expression_p = true; |
| *idk = CP_ID_KIND_UNQUALIFIED_DEPENDENT; |
| /* If we found a variable, then name lookup during the |
| instantiation will always resolve to the same VAR_DECL |
| (or an instantiation thereof). */ |
| if (TREE_CODE (decl) == VAR_DECL |
| || TREE_CODE (decl) == PARM_DECL) |
| return decl; |
| return id_expression; |
| } |
| |
| /* Only certain kinds of names are allowed in constant |
| expression. Enumerators and template parameters |
| have already been handled above. */ |
| if (integral_constant_expression_p |
| && !DECL_INTEGRAL_CONSTANT_VAR_P (decl)) |
| { |
| if (!allow_non_integral_constant_expression_p) |
| { |
| error ("`%D' cannot appear in a constant-expression", decl); |
| return error_mark_node; |
| } |
| *non_integral_constant_expression_p = true; |
| } |
| |
| if (TREE_CODE (decl) == NAMESPACE_DECL) |
| { |
| error ("use of namespace `%D' as expression", decl); |
| return error_mark_node; |
| } |
| else if (DECL_CLASS_TEMPLATE_P (decl)) |
| { |
| error ("use of class template `%T' as expression", decl); |
| return error_mark_node; |
| } |
| else if (TREE_CODE (decl) == TREE_LIST) |
| { |
| /* Ambiguous reference to base members. */ |
| error ("request for member `%D' is ambiguous in " |
| "multiple inheritance lattice", id_expression); |
| print_candidates (decl); |
| return error_mark_node; |
| } |
| |
| /* Mark variable-like entities as used. Functions are similarly |
| marked either below or after overload resolution. */ |
| if (TREE_CODE (decl) == VAR_DECL |
| || TREE_CODE (decl) == PARM_DECL |
| || TREE_CODE (decl) == RESULT_DECL) |
| mark_used (decl); |
| |
| if (scope) |
| { |
| decl = (adjust_result_of_qualified_name_lookup |
| (decl, scope, current_class_type)); |
| |
| if (TREE_CODE (decl) == FUNCTION_DECL) |
| mark_used (decl); |
| |
| if (TREE_CODE (decl) == FIELD_DECL || BASELINK_P (decl)) |
| *qualifying_class = scope; |
| else if (!processing_template_decl) |
| decl = convert_from_reference (decl); |
| else if (TYPE_P (scope)) |
| decl = build (SCOPE_REF, TREE_TYPE (decl), scope, decl); |
| } |
| else if (TREE_CODE (decl) == FIELD_DECL) |
| decl = finish_non_static_data_member (decl, current_class_ref, |
| /*qualifying_scope=*/NULL_TREE); |
| else if (is_overloaded_fn (decl)) |
| { |
| tree first_fn = OVL_CURRENT (decl); |
| |
| if (TREE_CODE (first_fn) == TEMPLATE_DECL) |
| first_fn = DECL_TEMPLATE_RESULT (first_fn); |
| |
| if (!really_overloaded_fn (decl)) |
| mark_used (first_fn); |
| |
| if (TREE_CODE (first_fn) == FUNCTION_DECL |
| && DECL_FUNCTION_MEMBER_P (first_fn) |
| && !shared_member_p (decl)) |
| { |
| /* A set of member functions. */ |
| decl = maybe_dummy_object (DECL_CONTEXT (first_fn), 0); |
| return finish_class_member_access_expr (decl, id_expression); |
| } |
| } |
| else |
| { |
| if (TREE_CODE (decl) == VAR_DECL |
| || TREE_CODE (decl) == PARM_DECL |
| || TREE_CODE (decl) == RESULT_DECL) |
| { |
| tree context = decl_function_context (decl); |
| |
| if (context != NULL_TREE && context != current_function_decl |
| && ! TREE_STATIC (decl)) |
| { |
| error ("use of %s from containing function", |
| (TREE_CODE (decl) == VAR_DECL |
| ? "`auto' variable" : "parameter")); |
| cp_error_at (" `%#D' declared here", decl); |
| return error_mark_node; |
| } |
| } |
| |
| if (DECL_P (decl) && DECL_NONLOCAL (decl) |
| && DECL_CLASS_SCOPE_P (decl) |
| && DECL_CONTEXT (decl) != current_class_type) |
| { |
| tree path; |
| |
| path = currently_open_derived_class (DECL_CONTEXT (decl)); |
| perform_or_defer_access_check (TYPE_BINFO (path), decl); |
| } |
| |
| if (! processing_template_decl) |
| decl = convert_from_reference (decl); |
| } |
| |
| /* Resolve references to variables of anonymous unions |
| into COMPONENT_REFs. */ |
| if (TREE_CODE (decl) == ALIAS_DECL) |
| decl = DECL_INITIAL (decl); |
| } |
| |
| if (TREE_DEPRECATED (decl)) |
| warn_deprecated_use (decl); |
| |
| return decl; |
| } |
| |
| /* Implement the __typeof keyword: Return the type of EXPR, suitable for |
| use as a type-specifier. */ |
| |
| tree |
| finish_typeof (tree expr) |
| { |
| tree type; |
| |
| if (type_dependent_expression_p (expr)) |
| { |
| type = make_aggr_type (TYPEOF_TYPE); |
| TYPE_FIELDS (type) = expr; |
| |
| return type; |
| } |
| |
| type = TREE_TYPE (expr); |
| |
| if (!type || type == unknown_type_node) |
| { |
| error ("type of `%E' is unknown", expr); |
| return error_mark_node; |
| } |
| |
| return type; |
| } |
| |
| /* Generate RTL for the statement T, and its substatements, and any |
| other statements at its nesting level. */ |
| |
| static void |
| cp_expand_stmt (tree t) |
| { |
| switch (TREE_CODE (t)) |
| { |
| case TRY_BLOCK: |
| genrtl_try_block (t); |
| break; |
| |
| case EH_SPEC_BLOCK: |
| genrtl_eh_spec_block (t); |
| break; |
| |
| case HANDLER: |
| genrtl_handler (t); |
| break; |
| |
| case USING_STMT: |
| break; |
| |
| default: |
| abort (); |
| break; |
| } |
| } |
| |
| /* Called from expand_body via walk_tree. Replace all AGGR_INIT_EXPRs |
| will equivalent CALL_EXPRs. */ |
| |
| static tree |
| simplify_aggr_init_exprs_r (tree* tp, |
| int* walk_subtrees, |
| void* data ATTRIBUTE_UNUSED) |
| { |
| /* We don't need to walk into types; there's nothing in a type that |
| needs simplification. (And, furthermore, there are places we |
| actively don't want to go. For example, we don't want to wander |
| into the default arguments for a FUNCTION_DECL that appears in a |
| CALL_EXPR.) */ |
| if (TYPE_P (*tp)) |
| { |
| *walk_subtrees = 0; |
| return NULL_TREE; |
| } |
| /* Only AGGR_INIT_EXPRs are interesting. */ |
| else if (TREE_CODE (*tp) != AGGR_INIT_EXPR) |
| return NULL_TREE; |
| |
| simplify_aggr_init_expr (tp); |
| |
| /* Keep iterating. */ |
| return NULL_TREE; |
| } |
| |
| /* Replace the AGGR_INIT_EXPR at *TP with an equivalent CALL_EXPR. This |
| function is broken out from the above for the benefit of the tree-ssa |
| project. */ |
| |
| void |
| simplify_aggr_init_expr (tree *tp) |
| { |
| tree aggr_init_expr = *tp; |
| |
| /* Form an appropriate CALL_EXPR. */ |
| tree fn = TREE_OPERAND (aggr_init_expr, 0); |
| tree args = TREE_OPERAND (aggr_init_expr, 1); |
| tree slot = TREE_OPERAND (aggr_init_expr, 2); |
| tree type = TREE_TYPE (aggr_init_expr); |
| |
| tree call_expr; |
| enum style_t { ctor, arg, pcc } style; |
| |
| if (AGGR_INIT_VIA_CTOR_P (aggr_init_expr)) |
| style = ctor; |
| #ifdef PCC_STATIC_STRUCT_RETURN |
| else if (1) |
| style = pcc; |
| #endif |
| else if (TREE_ADDRESSABLE (type)) |
| style = arg; |
| else |
| /* We shouldn't build an AGGR_INIT_EXPR if we don't need any special |
| handling. See build_cplus_new. */ |
| abort (); |
| |
| if (style == ctor || style == arg) |
| { |
| /* Pass the address of the slot. If this is a constructor, we |
| replace the first argument; otherwise, we tack on a new one. */ |
| tree addr; |
| |
| if (style == ctor) |
| args = TREE_CHAIN (args); |
| |
| cxx_mark_addressable (slot); |
| addr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (slot)), slot); |
| if (style == arg) |
| { |
| /* The return type might have different cv-quals from the slot. */ |
| tree fntype = TREE_TYPE (TREE_TYPE (fn)); |
| #ifdef ENABLE_CHECKING |
| if (TREE_CODE (fntype) != FUNCTION_TYPE |
| && TREE_CODE (fntype) != METHOD_TYPE) |
| abort (); |
| #endif |
| addr = convert (build_pointer_type (TREE_TYPE (fntype)), addr); |
| } |
| |
| args = tree_cons (NULL_TREE, addr, args); |
| } |
| |
| call_expr = build (CALL_EXPR, |
| TREE_TYPE (TREE_TYPE (TREE_TYPE (fn))), |
| fn, args, NULL_TREE); |
| |
| if (style == arg) |
| /* Tell the backend that we've added our return slot to the argument |
| list. */ |
| CALL_EXPR_HAS_RETURN_SLOT_ADDR (call_expr) = 1; |
| else if (style == pcc) |
| { |
| /* If we're using the non-reentrant PCC calling convention, then we |
| need to copy the returned value out of the static buffer into the |
| SLOT. */ |
| push_deferring_access_checks (dk_no_check); |
| call_expr = build_aggr_init (slot, call_expr, |
| DIRECT_BIND | LOOKUP_ONLYCONVERTING); |
| pop_deferring_access_checks (); |
| } |
| |
| /* We want to use the value of the initialized location as the |
| result. */ |
| call_expr = build (COMPOUND_EXPR, type, |
| call_expr, slot); |
| |
| /* Replace the AGGR_INIT_EXPR with the CALL_EXPR. */ |
| TREE_CHAIN (call_expr) = TREE_CHAIN (aggr_init_expr); |
| *tp = call_expr; |
| } |
| |
| /* Emit all thunks to FN that should be emitted when FN is emitted. */ |
| |
| static void |
| emit_associated_thunks (tree fn) |
| { |
| /* When we use vcall offsets, we emit thunks with the virtual |
| functions to which they thunk. The whole point of vcall offsets |
| is so that you can know statically the entire set of thunks that |
| will ever be needed for a given virtual function, thereby |
| enabling you to output all the thunks with the function itself. */ |
| if (DECL_VIRTUAL_P (fn)) |
| { |
| tree thunk; |
| |
| for (thunk = DECL_THUNKS (fn); thunk; thunk = TREE_CHAIN (thunk)) |
| { |
| if (!THUNK_ALIAS (thunk)) |
| { |
| use_thunk (thunk, /*emit_p=*/1); |
| if (DECL_RESULT_THUNK_P (thunk)) |
| { |
| tree probe; |
| |
| for (probe = DECL_THUNKS (thunk); |
| probe; probe = TREE_CHAIN (probe)) |
| use_thunk (probe, /*emit_p=*/1); |
| } |
| } |
| else |
| my_friendly_assert (!DECL_THUNKS (thunk), 20031023); |
| } |
| } |
| } |
| |
| /* Generate RTL for FN. */ |
| |
| void |
| expand_body (tree fn) |
| { |
| tree saved_function; |
| |
| /* Compute the appropriate object-file linkage for inline |
| functions. */ |
| if (DECL_DECLARED_INLINE_P (fn)) |
| import_export_decl (fn); |
| |
| /* If FN is external, then there's no point in generating RTL for |
| it. This situation can arise with an inline function under |
| `-fexternal-templates'; we instantiate the function, even though |
| we're not planning on emitting it, in case we get a chance to |
| inline it. */ |
| if (DECL_EXTERNAL (fn)) |
| return; |
| |
| /* ??? When is this needed? */ |
| saved_function = current_function_decl; |
| |
| /* Emit any thunks that should be emitted at the same time as FN. */ |
| emit_associated_thunks (fn); |
| |
| timevar_push (TV_INTEGRATION); |
| optimize_function (fn); |
| timevar_pop (TV_INTEGRATION); |
| |
| tree_rest_of_compilation (fn, function_depth > 1); |
| |
| current_function_decl = saved_function; |
| |
| extract_interface_info (); |
| |
| if (DECL_CLONED_FUNCTION_P (fn)) |
| { |
| /* If this is a clone, go through the other clones now and mark |
| their parameters used. We have to do that here, as we don't |
| know whether any particular clone will be expanded, and |
| therefore cannot pick one arbitrarily. */ |
| tree probe; |
| |
| for (probe = TREE_CHAIN (DECL_CLONED_FUNCTION (fn)); |
| probe && DECL_CLONED_FUNCTION_P (probe); |
| probe = TREE_CHAIN (probe)) |
| { |
| tree parms; |
| |
| for (parms = DECL_ARGUMENTS (probe); |
| parms; parms = TREE_CHAIN (parms)) |
| TREE_USED (parms) = 1; |
| } |
| } |
| } |
| |
| /* Generate RTL for FN. */ |
| |
| void |
| expand_or_defer_fn (tree fn) |
| { |
| /* When the parser calls us after finishing the body of a template |
| function, we don't really want to expand the body. */ |
| if (processing_template_decl) |
| { |
| /* Normally, collection only occurs in rest_of_compilation. So, |
| if we don't collect here, we never collect junk generated |
| during the processing of templates until we hit a |
| non-template function. */ |
| ggc_collect (); |
| return; |
| } |
| |
| /* Replace AGGR_INIT_EXPRs with appropriate CALL_EXPRs. */ |
| walk_tree_without_duplicates (&DECL_SAVED_TREE (fn), |
| simplify_aggr_init_exprs_r, |
| NULL); |
| |
| /* If this is a constructor or destructor body, we have to clone |
| it. */ |
| if (maybe_clone_body (fn)) |
| { |
| /* We don't want to process FN again, so pretend we've written |
| it out, even though we haven't. */ |
| TREE_ASM_WRITTEN (fn) = 1; |
| return; |
| } |
| |
| /* There's no reason to do any of the work here if we're only doing |
| semantic analysis; this code just generates RTL. */ |
| if (flag_syntax_only) |
| return; |
| |
| /* Compute the appropriate object-file linkage for inline functions. */ |
| if (DECL_DECLARED_INLINE_P (fn)) |
| import_export_decl (fn); |
| |
| /* If this function is marked with the constructor attribute, add it |
| to the list of functions to be called along with constructors |
| from static duration objects. */ |
| if (DECL_STATIC_CONSTRUCTOR (fn)) |
| static_ctors = tree_cons (NULL_TREE, fn, static_ctors); |
| |
| /* If this function is marked with the destructor attribute, add it |
| to the list of functions to be called along with destructors from |
| static duration objects. */ |
| if (DECL_STATIC_DESTRUCTOR (fn)) |
| static_dtors = tree_cons (NULL_TREE, fn, static_dtors); |
| |
| function_depth++; |
| |
| /* Expand or defer, at the whim of the compilation unit manager. */ |
| cgraph_finalize_function (fn, function_depth > 1); |
| |
| function_depth--; |
| } |
| |
| /* Helper function for walk_tree, used by finish_function to override all |
| the RETURN_STMTs and pertinent CLEANUP_STMTs for the named return |
| value optimization. */ |
| |
| tree |
| nullify_returns_r (tree* tp, int* walk_subtrees, void* data) |
| { |
| tree nrv = (tree) data; |
| |
| /* No need to walk into types. There wouldn't be any need to walk into |
| non-statements, except that we have to consider STMT_EXPRs. */ |
| if (TYPE_P (*tp)) |
| *walk_subtrees = 0; |
| else if (TREE_CODE (*tp) == RETURN_STMT) |
| RETURN_STMT_EXPR (*tp) = NULL_TREE; |
| else if (TREE_CODE (*tp) == CLEANUP_STMT |
| && CLEANUP_DECL (*tp) == nrv) |
| CLEANUP_EH_ONLY (*tp) = 1; |
| /* Replace the DECL_STMT for the NRV with an initialization of the |
| RESULT_DECL, if needed. */ |
| else if (TREE_CODE (*tp) == DECL_STMT |
| && DECL_STMT_DECL (*tp) == nrv) |
| { |
| tree init; |
| if (DECL_INITIAL (nrv) |
| && DECL_INITIAL (nrv) != error_mark_node) |
| { |
| init = build (INIT_EXPR, void_type_node, |
| DECL_RESULT (current_function_decl), |
| DECL_INITIAL (nrv)); |
| DECL_INITIAL (nrv) = error_mark_node; |
| } |
| else |
| init = NULL_TREE; |
| init = build_stmt (EXPR_STMT, init); |
| TREE_CHAIN (init) = TREE_CHAIN (*tp); |
| STMT_LINENO (init) = STMT_LINENO (*tp); |
| *tp = init; |
| } |
| |
| /* Keep iterating. */ |
| return NULL_TREE; |
| } |
| |
| /* Start generating the RTL for FN. */ |
| |
| void |
| cxx_expand_function_start (void) |
| { |
| /* Give our named return value the same RTL as our RESULT_DECL. */ |
| if (current_function_return_value) |
| COPY_DECL_RTL (DECL_RESULT (cfun->decl), current_function_return_value); |
| } |
| |
| /* Perform initialization related to this module. */ |
| |
| void |
| init_cp_semantics (void) |
| { |
| lang_expand_stmt = cp_expand_stmt; |
| } |
| |
| #include "gt-cp-semantics.h" |