| /* 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 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 GNU CC. |
| |
| GNU CC 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. |
| |
| GNU CC 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 GNU CC; 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 "tree.h" |
| #include "cp-tree.h" |
| #include "except.h" |
| #include "lex.h" |
| #include "toplev.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. */ |
| |
| /* When parsing a template, LAST_TREE contains the last statement |
| parsed. These are chained together through the TREE_CHAIN field, |
| but often need to be re-organized since the parse is performed |
| bottom-up. This macro makes LAST_TREE the indicated SUBSTMT of |
| STMT. */ |
| |
| #define RECHAIN_STMTS(stmt, substmt, last) \ |
| do { \ |
| substmt = last; \ |
| TREE_CHAIN (stmt) = NULL_TREE; \ |
| last_tree = stmt; \ |
| } while (0) |
| |
| #define RECHAIN_STMTS_FROM_LAST(stmt, substmt) \ |
| RECHAIN_STMTS (stmt, substmt, last_tree) |
| |
| #define RECHAIN_STMTS_FROM_CHAIN(stmt, substmt) \ |
| RECHAIN_STMTS (stmt, substmt, TREE_CHAIN (stmt)) |
| |
| /* Finish an expression-statement, whose EXPRESSION is as indicated. */ |
| |
| void |
| finish_expr_stmt (expr) |
| tree expr; |
| { |
| if (expr != NULL_TREE) |
| { |
| if (!processing_template_decl) |
| { |
| emit_line_note (input_filename, lineno); |
| /* Do default conversion if safe and possibly important, |
| in case within ({...}). */ |
| if ((TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE |
| && lvalue_p (expr)) |
| || TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE) |
| expr = default_conversion (expr); |
| } |
| |
| cplus_expand_expr_stmt (expr); |
| clear_momentary (); |
| } |
| |
| finish_stmt (); |
| } |
| |
| /* Begin an if-statement. Returns a newly created IF_STMT if |
| appropriate. */ |
| |
| tree |
| begin_if_stmt () |
| { |
| tree r; |
| |
| if (processing_template_decl) |
| { |
| r = build_min_nt (IF_STMT, NULL_TREE, NULL_TREE, NULL_TREE); |
| add_tree (r); |
| } |
| else |
| r = NULL_TREE; |
| |
| do_pushlevel (); |
| |
| return r; |
| } |
| |
| /* Process the COND of an if-statement, which may be given by |
| IF_STMT. */ |
| |
| void |
| finish_if_stmt_cond (cond, if_stmt) |
| tree cond; |
| tree if_stmt; |
| { |
| if (processing_template_decl) |
| { |
| if (last_tree != if_stmt) |
| RECHAIN_STMTS_FROM_LAST (if_stmt, IF_COND (if_stmt)); |
| else |
| IF_COND (if_stmt) = cond; |
| } |
| else |
| { |
| emit_line_note (input_filename, lineno); |
| expand_start_cond (condition_conversion (cond), 0); |
| } |
| } |
| |
| /* Finish the then-clause of an if-statement, which may be given by |
| IF_STMT. */ |
| |
| tree |
| finish_then_clause (if_stmt) |
| tree if_stmt; |
| { |
| if (processing_template_decl) |
| { |
| RECHAIN_STMTS_FROM_CHAIN (if_stmt, |
| THEN_CLAUSE (if_stmt)); |
| last_tree = if_stmt; |
| return if_stmt; |
| } |
| else |
| return NULL_TREE; |
| } |
| |
| /* Begin the else-clause of an if-statement. */ |
| |
| void |
| begin_else_clause () |
| { |
| if (!processing_template_decl) |
| expand_start_else (); |
| } |
| |
| /* Finish the else-clause of an if-statement, which may be given by |
| IF_STMT. */ |
| |
| void |
| finish_else_clause (if_stmt) |
| tree if_stmt; |
| { |
| if (processing_template_decl) |
| RECHAIN_STMTS_FROM_CHAIN (if_stmt, ELSE_CLAUSE (if_stmt)); |
| } |
| |
| /* Finsh an if-statement. */ |
| |
| void |
| finish_if_stmt () |
| { |
| if (!processing_template_decl) |
| expand_end_cond (); |
| |
| do_poplevel (); |
| finish_stmt (); |
| } |
| |
| /* Begin a while-statement. Returns a newly created WHILE_STMT if |
| appropriate. */ |
| |
| tree |
| begin_while_stmt () |
| { |
| tree r; |
| |
| if (processing_template_decl) |
| { |
| r = build_min_nt (WHILE_STMT, NULL_TREE, NULL_TREE); |
| add_tree (r); |
| } |
| else |
| { |
| emit_nop (); |
| emit_line_note (input_filename, lineno); |
| expand_start_loop (1); |
| r = NULL_TREE; |
| } |
| |
| do_pushlevel (); |
| |
| return r; |
| } |
| |
| /* Process the COND of an if-statement, which may be given by |
| WHILE_STMT. */ |
| |
| void |
| finish_while_stmt_cond (cond, while_stmt) |
| tree cond; |
| tree while_stmt; |
| { |
| if (processing_template_decl) |
| { |
| if (last_tree != while_stmt) |
| RECHAIN_STMTS_FROM_LAST (while_stmt, |
| WHILE_COND (while_stmt)); |
| else |
| TREE_OPERAND (while_stmt, 0) = cond; |
| } |
| else |
| { |
| emit_line_note (input_filename, lineno); |
| expand_exit_loop_if_false (0, condition_conversion (cond)); |
| } |
| |
| /* If COND wasn't a declaration, clear out the |
| block we made for it and start a new one here so the |
| optimization in expand_end_loop will work. */ |
| if (getdecls () == NULL_TREE) |
| { |
| do_poplevel (); |
| do_pushlevel (); |
| } |
| } |
| |
| /* Finish a while-statement, which may be given by WHILE_STMT. */ |
| |
| void |
| finish_while_stmt (while_stmt) |
| tree while_stmt; |
| { |
| do_poplevel (); |
| |
| if (processing_template_decl) |
| RECHAIN_STMTS_FROM_CHAIN (while_stmt, WHILE_BODY (while_stmt)); |
| else |
| expand_end_loop (); |
| finish_stmt (); |
| } |
| |
| /* Begin a do-statement. Returns a newly created DO_STMT if |
| appropriate. */ |
| |
| tree |
| begin_do_stmt () |
| { |
| if (processing_template_decl) |
| { |
| tree r = build_min_nt (DO_STMT, NULL_TREE, NULL_TREE); |
| add_tree (r); |
| return r; |
| } |
| else |
| { |
| emit_nop (); |
| emit_line_note (input_filename, lineno); |
| expand_start_loop_continue_elsewhere (1); |
| return NULL_TREE; |
| } |
| } |
| |
| /* Finish the body of a do-statement, which may be given by DO_STMT. */ |
| |
| void |
| finish_do_body (do_stmt) |
| tree do_stmt; |
| { |
| if (processing_template_decl) |
| RECHAIN_STMTS_FROM_CHAIN (do_stmt, DO_BODY (do_stmt)); |
| else |
| expand_loop_continue_here (); |
| } |
| |
| /* Finish a do-statement, which may be given by DO_STMT, and whose |
| COND is as indicated. */ |
| |
| void |
| finish_do_stmt (cond, do_stmt) |
| tree cond; |
| tree do_stmt; |
| { |
| if (processing_template_decl) |
| DO_COND (do_stmt) = cond; |
| else |
| { |
| emit_line_note (input_filename, lineno); |
| expand_exit_loop_if_false (0, condition_conversion (cond)); |
| expand_end_loop (); |
| } |
| |
| clear_momentary (); |
| finish_stmt (); |
| } |
| |
| /* Finish a return-statement. The EXPRESSION returned, if any, is as |
| indicated. */ |
| |
| void |
| finish_return_stmt (expr) |
| tree expr; |
| { |
| emit_line_note (input_filename, lineno); |
| c_expand_return (expr); |
| finish_stmt (); |
| } |
| |
| /* Begin a for-statement. Returns a new FOR_STMT if appropriate. */ |
| |
| tree |
| begin_for_stmt () |
| { |
| tree r; |
| |
| if (processing_template_decl) |
| { |
| r = build_min_nt (FOR_STMT, NULL_TREE, NULL_TREE, |
| NULL_TREE, NULL_TREE); |
| add_tree (r); |
| } |
| else |
| r = NULL_TREE; |
| |
| if (flag_new_for_scope > 0) |
| { |
| do_pushlevel (); |
| note_level_for_for (); |
| } |
| |
| return r; |
| } |
| |
| /* Finish the for-init-statement of a for-statement, which may be |
| given by FOR_STMT. */ |
| |
| void |
| finish_for_init_stmt (for_stmt) |
| tree for_stmt; |
| { |
| if (processing_template_decl) |
| { |
| if (last_tree != for_stmt) |
| RECHAIN_STMTS_FROM_CHAIN (for_stmt, FOR_INIT_STMT (for_stmt)); |
| } |
| else |
| { |
| emit_nop (); |
| emit_line_note (input_filename, lineno); |
| expand_start_loop_continue_elsewhere (1); |
| } |
| |
| do_pushlevel (); |
| } |
| |
| /* Finish the COND of a for-statement, which may be given by |
| FOR_STMT. */ |
| |
| void |
| finish_for_cond (cond, for_stmt) |
| tree cond; |
| tree for_stmt; |
| { |
| if (processing_template_decl) |
| { |
| if (last_tree != for_stmt) |
| RECHAIN_STMTS_FROM_LAST (for_stmt, FOR_COND (for_stmt)); |
| else |
| FOR_COND (for_stmt) = cond; |
| } |
| else |
| { |
| emit_line_note (input_filename, lineno); |
| if (cond) |
| expand_exit_loop_if_false (0, condition_conversion (cond)); |
| } |
| |
| /* If the cond wasn't a declaration, clear out the |
| block we made for it and start a new one here so the |
| optimization in expand_end_loop will work. */ |
| if (getdecls () == NULL_TREE) |
| { |
| do_poplevel (); |
| do_pushlevel (); |
| } |
| } |
| |
| /* Finish the increment-EXPRESSION in a for-statement, which may be |
| given by FOR_STMT. */ |
| |
| void |
| finish_for_expr (expr, for_stmt) |
| tree expr; |
| tree for_stmt; |
| { |
| if (processing_template_decl) |
| FOR_EXPR (for_stmt) = expr; |
| |
| /* Don't let the tree nodes for EXPR be discarded |
| by clear_momentary during the parsing of the next stmt. */ |
| push_momentary (); |
| } |
| |
| /* 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 (expr, for_stmt) |
| tree expr; |
| tree for_stmt; |
| { |
| /* Pop the scope for the body of the loop. */ |
| do_poplevel (); |
| |
| if (processing_template_decl) |
| RECHAIN_STMTS_FROM_CHAIN (for_stmt, FOR_BODY (for_stmt)); |
| else |
| { |
| emit_line_note (input_filename, lineno); |
| expand_loop_continue_here (); |
| if (expr) |
| cplus_expand_expr_stmt (expr); |
| expand_end_loop (); |
| } |
| |
| pop_momentary (); |
| |
| if (flag_new_for_scope > 0) |
| do_poplevel (); |
| |
| finish_stmt (); |
| } |
| |
| /* Finish a break-statement. */ |
| |
| void |
| finish_break_stmt () |
| { |
| emit_line_note (input_filename, lineno); |
| if (processing_template_decl) |
| add_tree (build_min_nt (BREAK_STMT)); |
| else if ( ! expand_exit_something ()) |
| cp_error ("break statement not within loop or switch"); |
| } |
| |
| /* Finish a continue-statement. */ |
| |
| void |
| finish_continue_stmt () |
| { |
| emit_line_note (input_filename, lineno); |
| if (processing_template_decl) |
| add_tree (build_min_nt (CONTINUE_STMT)); |
| else if (! expand_continue_loop (0)) |
| cp_error ("continue statement not within a loop"); |
| } |
| |
| /* Begin a switch-statement. */ |
| |
| void |
| begin_switch_stmt () |
| { |
| do_pushlevel (); |
| } |
| |
| /* Finish the cond of a switch-statement. Returns a new |
| SWITCH_STMT if appropriate. */ |
| |
| tree |
| finish_switch_cond (cond) |
| tree cond; |
| { |
| tree r; |
| |
| if (processing_template_decl) |
| { |
| r = build_min_nt (SWITCH_STMT, cond, NULL_TREE); |
| add_tree (r); |
| } |
| else |
| { |
| emit_line_note (input_filename, lineno); |
| c_expand_start_case (cond); |
| r = NULL_TREE; |
| } |
| push_switch (); |
| |
| /* Don't let the tree nodes for COND be discarded by |
| clear_momentary during the parsing of the next stmt. */ |
| push_momentary (); |
| |
| return r; |
| } |
| |
| /* 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 (cond, switch_stmt) |
| tree cond; |
| tree switch_stmt; |
| { |
| if (processing_template_decl) |
| RECHAIN_STMTS_FROM_CHAIN (switch_stmt, SWITCH_BODY (switch_stmt)); |
| else |
| expand_end_case (cond); |
| pop_momentary (); |
| pop_switch (); |
| do_poplevel (); |
| finish_stmt (); |
| } |
| |
| /* Finish a case-label. */ |
| |
| void |
| finish_case_label (low_value, high_value) |
| tree low_value; |
| tree high_value; |
| { |
| do_case (low_value, high_value); |
| } |
| |
| |
| /* Finish a goto-statement. */ |
| |
| void |
| finish_goto_stmt (destination) |
| tree destination; |
| { |
| if (processing_template_decl) |
| add_tree (build_min_nt (GOTO_STMT, destination)); |
| else |
| { |
| emit_line_note (input_filename, lineno); |
| |
| if (TREE_CODE (destination) == IDENTIFIER_NODE) |
| { |
| tree decl = lookup_label (destination); |
| TREE_USED (decl) = 1; |
| expand_goto (decl); |
| } |
| else |
| expand_computed_goto (destination); |
| } |
| } |
| |
| /* Begin a try-block. Returns a newly-created TRY_BLOCK if |
| appropriate. */ |
| |
| tree |
| begin_try_block () |
| { |
| if (processing_template_decl) |
| { |
| tree r = build_min_nt (TRY_BLOCK, NULL_TREE, |
| NULL_TREE); |
| add_tree (r); |
| return r; |
| } |
| else |
| { |
| emit_line_note (input_filename, lineno); |
| expand_start_try_stmts (); |
| return NULL_TREE; |
| } |
| } |
| |
| /* Finish a try-block, which may be given by TRY_BLOCK. */ |
| |
| void |
| finish_try_block (try_block) |
| tree try_block; |
| { |
| if (processing_template_decl) |
| RECHAIN_STMTS_FROM_LAST (try_block, TRY_STMTS (try_block)); |
| else |
| { |
| expand_start_all_catch (); |
| } |
| } |
| |
| /* Finish a handler-sequence for a try-block, which may be given by |
| TRY_BLOCK. */ |
| |
| void |
| finish_handler_sequence (try_block) |
| tree try_block; |
| { |
| if (processing_template_decl) |
| RECHAIN_STMTS_FROM_CHAIN (try_block, TRY_HANDLERS (try_block)); |
| else |
| { |
| expand_end_all_catch (); |
| } |
| } |
| |
| /* Begin a handler. Returns a HANDLER if appropriate. */ |
| |
| tree |
| begin_handler () |
| { |
| tree r; |
| |
| if (processing_template_decl) |
| { |
| r = build_min_nt (HANDLER, NULL_TREE, NULL_TREE); |
| add_tree (r); |
| } |
| else |
| r = NULL_TREE; |
| |
| do_pushlevel (); |
| |
| return r; |
| } |
| |
| /* Finish the handler-parameters for a handler, which may be given by |
| HANDLER. */ |
| |
| void |
| finish_handler_parms (handler) |
| tree handler; |
| { |
| if (processing_template_decl) |
| RECHAIN_STMTS_FROM_CHAIN (handler, HANDLER_PARMS (handler)); |
| } |
| |
| /* Finish a handler, which may be given by HANDLER. */ |
| |
| void |
| finish_handler (handler) |
| tree handler; |
| { |
| if (processing_template_decl) |
| RECHAIN_STMTS_FROM_CHAIN (handler, HANDLER_BODY (handler)); |
| else |
| expand_end_catch_block (); |
| |
| do_poplevel (); |
| } |
| |
| /* Begin a compound-statement. If HAS_NO_SCOPE is non-zero, the |
| compound-statement does not define a scope. Returns a new |
| COMPOUND_STMT if appropriate. */ |
| |
| tree |
| begin_compound_stmt (has_no_scope) |
| int has_no_scope; |
| { |
| tree r; |
| |
| if (processing_template_decl) |
| { |
| r = build_min_nt (COMPOUND_STMT, NULL_TREE); |
| add_tree (r); |
| if (has_no_scope) |
| COMPOUND_STMT_NO_SCOPE (r) = 1; |
| } |
| else |
| r = NULL_TREE; |
| |
| if (!has_no_scope) |
| do_pushlevel (); |
| |
| return r; |
| } |
| |
| |
| /* Finish a compound-statement, which may be given by COMPOUND_STMT. |
| If HAS_NO_SCOPE is non-zero, the compound statement does not define |
| a scope. */ |
| |
| tree |
| finish_compound_stmt (has_no_scope, compound_stmt) |
| int has_no_scope; |
| tree compound_stmt; |
| { |
| tree r; |
| |
| if (!has_no_scope) |
| r = do_poplevel (); |
| else |
| r = NULL_TREE; |
| |
| if (processing_template_decl) |
| RECHAIN_STMTS_FROM_CHAIN (compound_stmt, |
| COMPOUND_BODY (compound_stmt)); |
| |
| finish_stmt (); |
| |
| return r; |
| } |
| |
| /* Finish an asm-statement, whose components are a CV_QUALIFIER, a |
| STRING, some OUTPUT_OPERANDS, some INPUT_OPERANDS, and some |
| CLOBBERS. */ |
| |
| void |
| finish_asm_stmt (cv_qualifier, string, output_operands, |
| input_operands, clobbers) |
| tree cv_qualifier; |
| tree string; |
| tree output_operands; |
| tree input_operands; |
| tree clobbers; |
| { |
| if (TREE_CHAIN (string)) |
| string = combine_strings (string); |
| |
| if (processing_template_decl) |
| { |
| tree r = build_min_nt (ASM_STMT, cv_qualifier, string, |
| output_operands, input_operands, |
| clobbers); |
| add_tree (r); |
| } |
| else |
| { |
| emit_line_note (input_filename, lineno); |
| if (output_operands != NULL_TREE || input_operands != NULL_TREE |
| || clobbers != NULL_TREE) |
| { |
| if (cv_qualifier != NULL_TREE |
| && cv_qualifier != ridpointers[(int) RID_VOLATILE]) |
| cp_warning ("%s qualifier ignored on asm", |
| IDENTIFIER_POINTER (cv_qualifier)); |
| |
| c_expand_asm_operands (string, output_operands, |
| input_operands, |
| clobbers, |
| cv_qualifier |
| == ridpointers[(int) RID_VOLATILE], |
| input_filename, lineno); |
| } |
| else |
| { |
| if (cv_qualifier != NULL_TREE) |
| cp_warning ("%s qualifier ignored on asm", |
| IDENTIFIER_POINTER (cv_qualifier)); |
| expand_asm (string); |
| } |
| |
| finish_stmt (); |
| } |
| } |
| |
| /* Finish a parenthesized expression EXPR. */ |
| |
| tree |
| finish_parenthesized_expr (expr) |
| tree expr; |
| { |
| if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (TREE_CODE (expr)))) |
| /* This inhibits warnings in truthvalue_conversion. */ |
| C_SET_EXP_ORIGINAL_CODE (expr, ERROR_MARK); |
| |
| return expr; |
| } |
| |
| /* Begin a statement-expression. The value returned must be passed to |
| finish_stmt_expr. */ |
| |
| tree |
| begin_stmt_expr () |
| { |
| keep_next_level (); |
| /* If we're processing_template_decl, then the upcoming compound |
| statement will be chained onto the tree structure, starting at |
| last_tree. We return last_tree so that we can later unhook the |
| compound statement. */ |
| return processing_template_decl ? last_tree : expand_start_stmt_expr(); |
| } |
| |
| /* Finish a statement-expression. RTL_EXPR should be the value |
| returned by the previous begin_stmt_expr; EXPR is the |
| statement-expression. Returns an expression representing the |
| statement-expression. */ |
| |
| tree |
| finish_stmt_expr (rtl_expr, expr) |
| tree rtl_expr; |
| tree expr; |
| { |
| tree result; |
| |
| if (!processing_template_decl) |
| { |
| rtl_expr = expand_end_stmt_expr (rtl_expr); |
| /* The statements have side effects, so the group does. */ |
| TREE_SIDE_EFFECTS (rtl_expr) = 1; |
| } |
| |
| if (TREE_CODE (expr) == BLOCK) |
| { |
| /* Make a BIND_EXPR for the BLOCK already made. */ |
| if (processing_template_decl) |
| result = build (BIND_EXPR, NULL_TREE, |
| NULL_TREE, last_tree, expr); |
| else |
| result = build (BIND_EXPR, TREE_TYPE (rtl_expr), |
| NULL_TREE, rtl_expr, expr); |
| |
| /* Remove the block from the tree at this point. |
| It gets put back at the proper place |
| when the BIND_EXPR is expanded. */ |
| delete_block (expr); |
| } |
| else |
| result = expr; |
| |
| if (processing_template_decl) |
| { |
| /* Remove the compound statement from the tree structure; it is |
| now saved in the BIND_EXPR. */ |
| last_tree = rtl_expr; |
| TREE_CHAIN (last_tree) = NULL_TREE; |
| } |
| |
| return result; |
| } |
| |
| /* Finish a call to FN with ARGS. Returns a representation of the |
| call. */ |
| |
| tree |
| finish_call_expr (fn, args, koenig) |
| tree fn; |
| tree args; |
| int koenig; |
| { |
| tree result; |
| |
| if (koenig) |
| { |
| if (TREE_CODE (fn) == BIT_NOT_EXPR) |
| fn = build_x_unary_op (BIT_NOT_EXPR, TREE_OPERAND (fn, 0)); |
| else if (TREE_CODE (fn) != TEMPLATE_ID_EXPR) |
| fn = do_identifier (fn, 2, args); |
| } |
| result = build_x_function_call (fn, args, current_class_ref); |
| |
| if (TREE_CODE (result) == CALL_EXPR |
| && TREE_TYPE (result) != void_type_node) |
| result = require_complete_type (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.) */ |
| |
| tree |
| finish_increment_expr (expr, code) |
| tree expr; |
| enum tree_code code; |
| { |
| /* If we get an OFFSET_REF, turn it into what it really means (e.g., |
| a COMPONENT_REF). This way if we've got, say, a reference to a |
| static member that's being operated on, we don't end up trying to |
| find a member operator for the class it's in. */ |
| |
| if (TREE_CODE (expr) == OFFSET_REF) |
| expr = resolve_offset_ref (expr); |
| return build_x_unary_op (code, expr); |
| } |
| |
| /* Finish a use of `this'. Returns an expression for `this'. */ |
| |
| tree |
| finish_this_expr () |
| { |
| tree result; |
| |
| if (current_class_ptr) |
| { |
| #ifdef WARNING_ABOUT_CCD |
| TREE_USED (current_class_ptr) = 1; |
| #endif |
| 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 member function call using OBJECT and ARGS as arguments to |
| FN. Returns an expression for the call. */ |
| |
| tree |
| finish_object_call_expr (fn, object, args) |
| tree fn; |
| tree object; |
| tree args; |
| { |
| #if 0 |
| /* This is a future direction of this code, but because |
| build_x_function_call cannot always undo what is done in |
| build_component_ref entirely yet, we cannot do this. */ |
| |
| tree real_fn = build_component_ref (object, fn, NULL_TREE, 1); |
| return finish_call_expr (real_fn, args); |
| #else |
| if (TREE_CODE (fn) == TYPE_DECL) |
| { |
| if (processing_template_decl) |
| /* This can happen on code like: |
| |
| class X; |
| template <class T> void f(T t) { |
| t.X(); |
| } |
| |
| We just grab the underlying IDENTIFIER. */ |
| fn = DECL_NAME (fn); |
| else |
| { |
| cp_error ("calling type `%T' like a method", fn); |
| return error_mark_node; |
| } |
| } |
| |
| return build_method_call (object, fn, args, NULL_TREE, LOOKUP_NORMAL); |
| #endif |
| } |
| |
| /* Finish a qualified member function call using OBJECT and ARGS as |
| arguments to FN. Returns an expressino for the call. */ |
| |
| tree |
| finish_qualified_object_call_expr (fn, object, args) |
| tree fn; |
| tree object; |
| tree args; |
| { |
| if (IS_SIGNATURE (TREE_OPERAND (fn, 0))) |
| { |
| warning ("signature name in scope resolution ignored"); |
| return finish_object_call_expr (TREE_OPERAND (fn, 1), object, args); |
| } |
| else |
| return build_scoped_method_call (object, TREE_OPERAND (fn, 0), |
| TREE_OPERAND (fn, 1), args); |
| } |
| |
| /* Finish a pseudo-destructor call expression of OBJECT, with SCOPE |
| being the scope, if any, of DESTRUCTOR. Returns an expression for |
| the call. */ |
| |
| tree |
| finish_pseudo_destructor_call_expr (object, scope, destructor) |
| tree object; |
| tree scope; |
| tree destructor; |
| { |
| if (scope && scope != destructor) |
| cp_error ("destructor specifier `%T::~%T()' must have matching names", |
| scope, destructor); |
| |
| if ((scope == NULL_TREE || IDENTIFIER_GLOBAL_VALUE (destructor)) |
| && (TREE_CODE (TREE_TYPE (object)) != |
| TREE_CODE (TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (destructor))))) |
| cp_error ("`%E' is not of type `%T'", object, destructor); |
| |
| return cp_convert (void_type_node, object); |
| } |
| |
| /* Finish a call to a globally qualified member function FN using |
| ARGS. Returns an expression for the call. */ |
| |
| tree |
| finish_globally_qualified_member_call_expr (fn, args) |
| tree fn; |
| tree args; |
| { |
| if (processing_template_decl) |
| return build_min_nt (CALL_EXPR, copy_to_permanent (fn), args, |
| NULL_TREE); |
| else |
| return build_member_call (TREE_OPERAND (fn, 0), |
| TREE_OPERAND (fn, 1), |
| args); |
| } |
| |
| /* Finish an expression taking the address of LABEL. Returns an |
| expression for the address. */ |
| |
| tree |
| finish_label_address_expr (label) |
| tree label; |
| { |
| tree result; |
| |
| label = lookup_label (label); |
| if (label == NULL_TREE) |
| result = null_pointer_node; |
| else |
| { |
| TREE_USED (label) = 1; |
| result = build1 (ADDR_EXPR, ptr_type_node, label); |
| TREE_CONSTANT (result) = 1; |
| } |
| |
| return result; |
| } |
| |
| /* Finish an expression of the form CODE EXPR. */ |
| |
| tree |
| finish_unary_op_expr (code, expr) |
| enum tree_code code; |
| tree expr; |
| { |
| tree result = build_x_unary_op (code, expr); |
| if (code == NEGATE_EXPR && TREE_CODE (expr) == INTEGER_CST) |
| TREE_NEGATED_INT (result) = 1; |
| overflow_warning (result); |
| return result; |
| } |
| |
| /* Finish an id-expression. */ |
| |
| tree |
| finish_id_expr (expr) |
| tree expr; |
| { |
| if (TREE_CODE (expr) == IDENTIFIER_NODE) |
| expr = do_identifier (expr, 1, NULL_TREE); |
| |
| return expr; |
| } |
| |
| /* Begin a new-placement. */ |
| |
| int |
| begin_new_placement () |
| { |
| /* The arguments to a placement new might be passed to a |
| deallocation function, in the event that the allocation throws an |
| exception. Since we don't expand exception handlers until the |
| end of a function, we must make sure the arguments stay around |
| that long. */ |
| return suspend_momentary (); |
| } |
| |
| /* Finish a new-placement. The ARGS are the placement arguments. The |
| COOKIE is the value returned by the previous call to |
| begin_new_placement. */ |
| |
| tree |
| finish_new_placement (args, cookie) |
| tree args; |
| int cookie; |
| { |
| resume_momentary (cookie); |
| return args; |
| } |
| |
| /* Begin a function defniition declared with DECL_SPECS and |
| DECLARATOR. Returns non-zero if the function-declaration is |
| legal. */ |
| |
| int |
| begin_function_definition (decl_specs, declarator) |
| tree decl_specs; |
| tree declarator; |
| { |
| tree specs; |
| tree attrs; |
| split_specs_attrs (decl_specs, &specs, &attrs); |
| if (!start_function (specs, declarator, attrs, 0)) |
| return 0; |
| |
| reinit_parse_for_function (); |
| return 1; |
| } |
| |
| /* Begin a constructor declarator of the form `SCOPE::NAME'. Returns |
| a SCOPE_REF. */ |
| |
| tree |
| begin_constructor_declarator (scope, name) |
| tree scope; |
| tree name; |
| { |
| tree result = build_parse_node (SCOPE_REF, scope, name); |
| |
| if (scope != current_class_type) |
| { |
| push_nested_class (scope, 3); |
| TREE_COMPLEXITY (result) = current_class_depth; |
| } |
| |
| return result; |
| } |
| |
| /* Finish an init-declarator. Returns a DECL. */ |
| |
| tree |
| finish_declarator (declarator, declspecs, attributes, |
| prefix_attributes, initialized) |
| tree declarator; |
| tree declspecs; |
| tree attributes; |
| tree prefix_attributes; |
| int initialized; |
| { |
| return start_decl (declarator, declspecs, initialized, attributes, |
| prefix_attributes); |
| } |
| |
| /* Finish a translation unit. */ |
| |
| void |
| finish_translation_unit () |
| { |
| /* In case there were missing closebraces, |
| get us back to the global binding level. */ |
| while (! toplevel_bindings_p ()) |
| poplevel (0, 0, 0); |
| while (current_namespace != global_namespace) |
| pop_namespace (); |
| finish_file (); |
| } |
| |
| /* Finish a template type parameter, specified as AGGR IDENTIFIER. |
| Returns the parameter. */ |
| |
| tree |
| finish_template_type_parm (aggr, identifier) |
| tree aggr; |
| tree identifier; |
| { |
| if (aggr == signature_type_node) |
| sorry ("signature as template type parameter"); |
| else 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 (aggr, identifier) |
| 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; |
| SET_DECL_ARTIFICIAL (decl); |
| end_template_decl (); |
| |
| return finish_template_type_parm (aggr, tmpl); |
| } |
| |
| /* Finish a parameter list, indicated by PARMS. If ELLIPSIS is |
| non-zero, the parameter list was terminated by a `...'. */ |
| |
| tree |
| finish_parmlist (parms, ellipsis) |
| tree parms; |
| int ellipsis; |
| { |
| if (!ellipsis) |
| chainon (parms, void_list_node); |
| /* We mark the PARMS as a parmlist so that declarator processing can |
| disambiguate certain constructs. */ |
| if (parms != NULL_TREE) |
| TREE_PARMLIST (parms) = 1; |
| |
| return parms; |
| } |
| |
| /* Begin a class definition, as indicated by T. */ |
| |
| tree |
| begin_class_definition (t) |
| tree t; |
| { |
| tree new_type = t; |
| |
| push_obstacks_nochange (); |
| end_temporary_allocation (); |
| |
| if (t == error_mark_node |
| || ! IS_AGGR_TYPE (t)) |
| { |
| t = new_type = make_lang_type (RECORD_TYPE); |
| pushtag (make_anon_name (), t, 0); |
| } |
| if (TYPE_SIZE (t)) |
| duplicate_tag_error (t); |
| if (TYPE_SIZE (t) || TYPE_BEING_DEFINED (t)) |
| { |
| t = make_lang_type (TREE_CODE (t)); |
| pushtag (TYPE_IDENTIFIER (t), t, 0); |
| new_type = t; |
| } |
| if (processing_template_decl && TYPE_CONTEXT (t) |
| && TREE_CODE (TYPE_CONTEXT (t)) != NAMESPACE_DECL |
| && ! current_class_type) |
| push_template_decl (TYPE_STUB_DECL (t)); |
| pushclass (t, 0); |
| TYPE_BEING_DEFINED (t) = 1; |
| if (IS_AGGR_TYPE (t) && CLASSTYPE_USE_TEMPLATE (t)) |
| { |
| if (CLASSTYPE_IMPLICIT_INSTANTIATION (t) |
| && TYPE_SIZE (t) == NULL_TREE) |
| { |
| SET_CLASSTYPE_TEMPLATE_SPECIALIZATION (t); |
| if (processing_template_decl) |
| push_template_decl (TYPE_MAIN_DECL (t)); |
| } |
| else if (CLASSTYPE_TEMPLATE_INSTANTIATION (t)) |
| cp_error ("specialization after instantiation of `%T'", t); |
| } |
| /* Reset the interface data, at the earliest possible |
| moment, as it might have been set via a class foo; |
| before. */ |
| /* Don't change signatures. */ |
| if (! IS_SIGNATURE (t)) |
| { |
| extern tree pending_vtables; |
| int needs_writing; |
| tree name = TYPE_IDENTIFIER (t); |
| |
| if (! ANON_AGGRNAME_P (name)) |
| { |
| CLASSTYPE_INTERFACE_ONLY (t) = interface_only; |
| SET_CLASSTYPE_INTERFACE_UNKNOWN_X |
| (t, interface_unknown); |
| } |
| |
| /* Record how to set the access of this class's |
| virtual functions. If write_virtuals == 2 or 3, then |
| inline virtuals are ``extern inline''. */ |
| switch (write_virtuals) |
| { |
| case 0: |
| case 1: |
| needs_writing = 1; |
| break; |
| case 2: |
| needs_writing = !! value_member (name, pending_vtables); |
| break; |
| case 3: |
| needs_writing = ! CLASSTYPE_INTERFACE_ONLY (t) |
| && CLASSTYPE_INTERFACE_KNOWN (t); |
| break; |
| default: |
| needs_writing = 0; |
| } |
| CLASSTYPE_VTABLE_NEEDS_WRITING (t) = needs_writing; |
| } |
| #if 0 |
| t = TYPE_IDENTIFIER ($<ttype>0); |
| if (t && IDENTIFIER_TEMPLATE (t)) |
| overload_template_name (t, 1); |
| #endif |
| reset_specialization(); |
| |
| /* In case this is a local class within a template |
| function, we save the current tree structure so |
| that we can get it back later. */ |
| begin_tree (); |
| |
| return new_type; |
| } |
| |
| /* Finish a class definition T, with the indicated COMPONENTS, and |
| with the indicate ATTRIBUTES. If SEMI, the definition is |
| immediately followed by a semicolon. Returns the type. */ |
| |
| tree |
| finish_class_definition (t, components, attributes, semi) |
| tree t; |
| tree components; |
| tree attributes; |
| int semi; |
| { |
| #if 0 |
| /* Need to rework class nesting in the presence of nested classes, |
| etc. */ |
| shadow_tag (CLASSTYPE_AS_LIST (t)); */ |
| #endif |
| |
| /* finish_struct nukes this anyway; if finish_exception does too, |
| then it can go. */ |
| if (semi) |
| note_got_semicolon (t); |
| |
| /* If we got any attributes in class_head, xref_tag will stick them in |
| TREE_TYPE of the type. Grab them now. */ |
| attributes = chainon (TREE_TYPE (t), attributes); |
| TREE_TYPE (t) = NULL_TREE; |
| |
| if (TREE_CODE (t) == ENUMERAL_TYPE) |
| ; |
| else |
| { |
| t = finish_struct (t, components, attributes, semi); |
| if (semi) |
| note_got_semicolon (t); |
| } |
| |
| pop_obstacks (); |
| |
| if (! semi) |
| check_for_missing_semicolon (t); |
| if (current_scope () == current_function_decl) |
| do_pending_defargs (); |
| |
| return t; |
| } |
| |
| /* Finish processing the default argument expressions cached during |
| the processing of a class definition. */ |
| |
| void |
| finish_default_args () |
| { |
| if (pending_inlines |
| && current_scope () == current_function_decl) |
| do_pending_inlines (); |
| } |
| |
| /* Finish processing the inline function definitions cached during the |
| processing of a class definition. */ |
| |
| void |
| begin_inline_definitions () |
| { |
| if (current_class_type == NULL_TREE) |
| clear_inline_text_obstack (); |
| |
| /* Undo the begin_tree in begin_class_definition. */ |
| end_tree (); |
| } |
| |
| /* Finish processing the declaration of a member class template |
| TYPES whose template parameters are given by PARMS. */ |
| |
| tree |
| finish_member_class_template (parms, types) |
| tree parms; |
| tree types; |
| { |
| note_list_got_semicolon (types); |
| grok_x_components (types, NULL_TREE); |
| 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 (parms, types); |
| /* As with other component type declarations, we do |
| not store the new DECL on the list of |
| component_decls. */ |
| return NULL_TREE; |
| } |