Makefile.in (parse.o, decl.o): Also depend on $(srcdir)/../except.h $(srcdir)/.....
[gcc.git] / gcc / cp / expr.c
index b59fefe954e008e056fb06b8a19ec0a873fcd102..a14695ddd51ff03fbc4c2e829d88f9b526fbc967 100644 (file)
@@ -1,6 +1,6 @@
 /* Convert language-specific tree expression to rtl instructions,
    for GNU compiler.
-   Copyright (C) 1988, 1992, 1993 Free Software Foundation, Inc.
+   Copyright (C) 1988, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -16,22 +16,26 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 
 #include "config.h"
+#include <stdio.h>
 #include "rtl.h"
 #include "tree.h"
 #include "flags.h"
 #include "expr.h"
 #include "cp-tree.h"
 
-#undef NULL
-#define NULL 0
+static tree extract_aggr_init PROTO((tree, tree));
+static tree extract_scalar_init PROTO((tree, tree));
+static rtx cplus_expand_expr PROTO((tree, rtx, enum machine_mode,
+                                   enum expand_modifier));
 
 /* Hook used by expand_expr to expand language-specific tree codes.  */
 
-rtx
+static rtx
 cplus_expand_expr (exp, target, tmode, modifier)
      tree exp;
      rtx target;
@@ -41,11 +45,10 @@ cplus_expand_expr (exp, target, tmode, modifier)
   tree type = TREE_TYPE (exp);
   register enum machine_mode mode = TYPE_MODE (type);
   register enum tree_code code = TREE_CODE (exp);
-  rtx original_target = target;
   int ignore = target == const0_rtx;
 
   if (ignore)
-    target = 0, original_target = 0;
+    target = 0;
 
   /* No sense saving up arithmetic to be done
      if it's all in the wrong mode to form part of an address.
@@ -104,9 +107,10 @@ cplus_expand_expr (exp, target, tmode, modifier)
            && TREE_CODE (TREE_OPERAND (func, 0)) == FUNCTION_DECL
            && DECL_CONSTRUCTOR_P (TREE_OPERAND (func, 0)))
          {
-           type = TYPE_POINTER_TO (type);
+           type = build_pointer_type (type);
            /* Don't clobber a value that might be part of a default
               parameter value.  */
+           mark_addressable (slot);
            if (TREE_PERMANENT (args))
              args = tree_cons (0, build1 (ADDR_EXPR, type, slot),
                                TREE_CHAIN (args));
@@ -130,34 +134,52 @@ cplus_expand_expr (exp, target, tmode, modifier)
          }
        if (call_target)
          {
-           preserve_temp_slots (call_target);
-
            /* Make this a valid memory address now.  The code below assumes
               that it can compare rtx and make assumptions based on the
               result.  The assumptions are true only if the address was
               valid to begin with.  */
            call_target = validize_mem (call_target);
+
+           /* If this is a reference to a symbol, expand_inline_function
+              will do this transformation and return a different target
+              than the one we gave it, though functionally equivalent.  Do
+              the transformation here to avoid confusion.  */
+           if (! cse_not_expected && GET_CODE (call_target) == MEM
+               && GET_CODE (XEXP (call_target, 0)) == SYMBOL_REF)
+             {
+               call_target = gen_rtx
+                 (MEM, mode, memory_address (mode, XEXP (call_target, 0)));
+               MEM_IN_STRUCT_P (call_target) = 1;
+             }
          }
 
-       preserve_temp_slots (DECL_RTL (slot));
-       call_exp = build (CALL_EXPR, type, func, args, 0);
+       call_exp = build (CALL_EXPR, type, func, args, NULL_TREE);
        TREE_SIDE_EFFECTS (call_exp) = 1;
-       return_target = expand_expr (call_exp, call_target, mode, 0);
-       free_temp_slots ();
+       return_target = expand_call (call_exp, call_target, ignore);
        if (call_target == 0)
          {
            if (pcc_struct_return)
              {
-               tree init = build (RTL_EXPR, type, 0, return_target);
+               extern int flag_access_control;
+               int old_ac = flag_access_control;
+
+               tree init = build_decl (VAR_DECL, 0, type);
                TREE_ADDRESSABLE (init) = 1;
-               expand_aggr_init (slot, init, 0);
+               DECL_RTL (init) = return_target;
+
+               flag_access_control = 0;
+               expand_aggr_init (slot, init, 0, LOOKUP_ONLYCONVERTING);
+               flag_access_control = old_ac;
+
                if (TYPE_NEEDS_DESTRUCTOR (type))
                  {
-                   init = build (RTL_EXPR, build_reference_type (type), 0,
-                                 XEXP (return_target, 0));
+                   init = build_decl (VAR_DECL, 0,
+                                      build_reference_type (type));
+                   DECL_RTL (init) = XEXP (return_target, 0);
+
                    init = maybe_build_cleanup (convert_from_reference (init));
                    if (init != NULL_TREE)
-                     expand_expr (init, 0, 0, 0);
+                     expand_expr (init, const0_rtx, VOIDmode, 0);
                  }
                call_target = return_target = DECL_RTL (slot);
              }
@@ -167,7 +189,7 @@ cplus_expand_expr (exp, target, tmode, modifier)
 
        if (call_target != return_target)
          {
-           my_friendly_assert (! TYPE_NEEDS_CONSTRUCTING (type), 317);
+           my_friendly_assert (TYPE_HAS_TRIVIAL_INIT_REF (type), 317);
            if (GET_MODE (return_target) == BLKmode)
              emit_block_move (call_target, return_target, expr_size (exp),
                               TYPE_ALIGN (type) / BITS_PER_UNIT);
@@ -188,7 +210,7 @@ cplus_expand_expr (exp, target, tmode, modifier)
            init = build (RTL_EXPR, return_type, 0, call_target);
            /* We got back a reference to the type we want.  Now initialize
               target with that.  */
-           expand_aggr_init (slot, init, 0);
+           expand_aggr_init (slot, init, 0, LOOKUP_ONLYCONVERTING);
          }
 
        if (DECL_RTL (slot) != target)
@@ -214,6 +236,18 @@ cplus_expand_expr (exp, target, tmode, modifier)
     case THUNK_DECL:
       return DECL_RTL (exp);
 
+    case THROW_EXPR:
+      expand_throw (TREE_OPERAND (exp, 0));
+      return NULL;
+
+    case VEC_INIT_EXPR:
+      return expand_expr
+       (expand_vec_init
+        (NULL_TREE, TREE_OPERAND (exp, 0),
+         build_binary_op (MINUS_EXPR, TREE_OPERAND (exp, 2),
+                          integer_one_node, 1),
+         TREE_OPERAND (exp, 1), 0), target, tmode, modifier);
+
     default:
       break;
     }
@@ -253,19 +287,177 @@ fixup_result_decl (decl, result)
          REG_FUNCTION_VALUE_P (real_decl_result) = 1;
          result = real_decl_result;
        }
-      emit_move_insn (result, DECL_RTL (decl));
+      store_expr (decl, result, 0);
       emit_insn (gen_rtx (USE, VOIDmode, result));
     }
 }
 
-/* Return nonzero iff DECL is memory-based.  The DECL_RTL of
-   certain const variables might be a CONST_INT, or a REG
-   in some cases.  We cannot use `memory_operand' as a test
-   here because on most RISC machines, a variable's address
-   is not, by itself, a legitimate address.  */
+/* Expand this initialization inline and see if it's simple enough that
+   it can be done at compile-time.  */
+
+static tree
+extract_aggr_init (decl, init)
+     tree decl, init;
+{
+  return 0;
+}
+
+static tree
+extract_scalar_init (decl, init)
+     tree decl, init;
+{
+  rtx value, insns, insn;
+  extern struct obstack temporary_obstack;
+  tree t = NULL_TREE;
+
+  push_obstacks (&temporary_obstack, &temporary_obstack);
+  start_sequence ();
+  value = expand_expr (init, NULL_RTX, VOIDmode, 0);
+  insns = get_insns ();
+  end_sequence ();
+  reg_scan (insns, max_reg_num (), 0);
+  jump_optimize (insns, 0, 0, 1);
+  pop_obstacks ();
+
+  for (insn = insns; insn; insn = NEXT_INSN (insn))
+    {
+      rtx r, to;
+
+      if (GET_CODE (insn) == NOTE)
+       continue;
+      else if (GET_CODE (insn) != INSN)
+       return 0;
+
+      r = PATTERN (insn);
+      if (GET_CODE (r) != SET)
+       return 0;
+
+      to = XEXP (r, 0);
+
+      if (! (to == value
+            || (GET_CODE (to) == SUBREG && XEXP (to, 0) == value)))
+       return 0;
+
+      r = XEXP (r, 1);
+
+      switch (GET_CODE (r))
+       {
+       case CONST_INT:
+         t = build_int_2 (XEXP (r, 0), 0);
+         break;
+       default:
+         return 0;
+       }
+    }
+
+  return t; 
+}
+
 int
-decl_in_memory_p (decl)
-     tree decl;
+extract_init (decl, init)
+     tree decl, init;
 {
-  return DECL_RTL (decl) != 0 && GET_CODE (DECL_RTL (decl)) == MEM;
+  return 0;
+
+#if 0
+  if (IS_AGGR_TYPE (TREE_TYPE (decl))
+      || TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
+    init = extract_aggr_init (decl, init);
+  else
+    init = extract_scalar_init (decl, init);
+
+  if (init == NULL_TREE)
+    return 0;
+
+  DECL_INITIAL (decl) = init;
+  return 1;
+#endif
+}
+
+void
+do_case (start, end)
+     tree start, end;
+{
+  tree value1 = NULL_TREE, value2 = NULL_TREE, label;
+
+  if (start != NULL_TREE && TREE_TYPE (start) != NULL_TREE 
+      && POINTER_TYPE_P (TREE_TYPE (start)))
+    error ("pointers are not permitted as case values");
+
+  if (end && pedantic)
+    pedwarn ("ANSI C++ forbids range expressions in switch statement");
+
+  if (processing_template_decl)
+    {
+      add_tree (build_min_nt (CASE_LABEL, start, end));
+      return;
+    }
+
+  if (start)
+    value1 = check_cp_case_value (start);
+  if (end)
+    value2 = check_cp_case_value (end);
+  
+  label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+
+  if (value1 != error_mark_node
+      && value2 != error_mark_node)
+    {
+      tree duplicate;
+      int success;
+
+      if (end)
+       success = pushcase_range (value1, value2, convert_and_check,
+                                 label, &duplicate);
+      else if (start)
+       success = pushcase (value1, convert_and_check, label, &duplicate);
+      else
+       success = pushcase (NULL_TREE, 0, label, &duplicate);
+
+      if (success == 1)
+       {
+         if (end)
+           error ("case label not within a switch statement");
+         else if (start)
+           cp_error ("case label `%E' not within a switch statement", start);
+         else
+           error ("default label not within a switch statement");
+       }
+      else if (success == 2)
+       {
+         if (end)
+           {
+             error ("duplicate (or overlapping) case value");
+             cp_error_at ("this is the first entry overlapping that value",
+                          duplicate);
+           }
+         else if (start)
+           {
+             cp_error ("duplicate case value `%E'", start);
+             cp_error_at ("previously used here", duplicate);
+           }
+         else
+           {
+             error ("multiple default labels in one switch");
+             cp_error_at ("this is the first default label", duplicate);
+           }
+       }
+      else if (success == 3)
+       warning ("case value out of range");
+      else if (success == 4)
+       warning ("empty range specified");
+      else if (success == 5)
+       {
+         if (end)
+           error ("case label within scope of cleanup or variable array");
+         else if (! start)
+           error ("`default' label within scope of cleanup or variable array");
+         else
+           cp_error ("case label `%E' within scope of cleanup or variable array", start);
+       }
+    }
+  if (start)
+    define_case_label (label);
+  else
+    define_case_label (NULL_TREE);
 }