OSDN Git Service

1999-11-25 Mark Mitchell <mark@codesourcery.com>
[pf3gnuchains/gcc-fork.git] / gcc / cp / typeck.c
index cb024af..0bfb99c 100644 (file)
@@ -34,6 +34,7 @@ Boston, MA 02111-1307, USA.  */
 #include "tree.h"
 #include "rtl.h"
 #include "cp-tree.h"
+#include "tm_p.h"
 #include "flags.h"
 #include "output.h"
 #include "expr.h"
@@ -63,6 +64,7 @@ static tree get_delta_difference PROTO((tree, tree, int));
 static int comp_cv_target_types PROTO((tree, tree, int));
 static void casts_away_constness_r PROTO((tree *, tree *));
 static int casts_away_constness PROTO ((tree, tree));
+static void maybe_warn_about_returning_address_of_local PROTO ((tree));
 
 /* Return the target type of TYPE, which means return T for:
    T*, T&, T[], T (...), and otherwise, just T.  */
@@ -1611,9 +1613,8 @@ expr_sizeof (e)
       return size_int (1);
     }
   /* It's illegal to say `sizeof (X::i)' for `i' a non-static data
-     member unless you're in a non-static member of X.  But, we used
-     to support this usage, so we still permit it unless we're being
-     pedantic.  [expr.prim]  */
+     member unless you're in a non-static member of X.  So hand off to
+     resolve_offset_ref.  [expr.prim]  */
   else if (TREE_CODE (e) == OFFSET_REF)
     e = resolve_offset_ref (e);
 
@@ -1788,6 +1789,11 @@ decay_conversion (exp)
       return cp_convert (ptrtype, adr);
     }
 
+  /* [basic.lval]: Class rvalues can have cv-qualified types; non-class
+     rvalues always have cv-unqualified types.  */
+  if (! CLASS_TYPE_P (type))
+    exp = cp_convert (TYPE_MAIN_VARIANT (type), exp);
+
   return exp;
 }
 
@@ -2088,12 +2094,12 @@ build_component_ref (datum, component, basetype_path, protect)
     }
 
   /* Look up component name in the structure type definition.  */
-  if (CLASSTYPE_VFIELD (basetype)
-      && DECL_NAME (CLASSTYPE_VFIELD (basetype)) == component)
+  if (TYPE_VFIELD (basetype)
+      && DECL_NAME (TYPE_VFIELD (basetype)) == component)
     /* Special-case this because if we use normal lookups in an ambiguous
        hierarchy, the compiler will abort (because vptr lookups are
        not supposed to be ambiguous.  */
-    field = CLASSTYPE_VFIELD (basetype);
+    field = TYPE_VFIELD (basetype);
   else if (TREE_CODE (component) == FIELD_DECL)
     field = component;
   else if (TREE_CODE (component) == TYPE_DECL)
@@ -2808,6 +2814,17 @@ get_member_function_from_ptrfunc (instance_ptrptr, function)
 
       tree instance_ptr = *instance_ptrptr;
 
+      if (instance_ptr == error_mark_node
+         && TREE_CODE (function) == PTRMEM_CST)
+       {
+         /* Extracting the function address from a pmf is only
+            allowed with -Wno-pmf-conversions. It only works for
+            pmf constants. */
+         e1 = build_addr_func (PTRMEM_CST_MEMBER (function));
+         e1 = convert (TYPE_PTRMEMFUNC_FN_TYPE (TREE_TYPE (function)), e1);
+         return e1;
+       }
+
       if (TREE_SIDE_EFFECTS (instance_ptr))
        instance_ptr = save_expr (instance_ptr);
 
@@ -2822,67 +2839,66 @@ get_member_function_from_ptrfunc (instance_ptrptr, function)
                                               NULL_TREE, 0));
       e3 = PFN_FROM_PTRMEMFUNC (function);
 
-      if (TYPE_SIZE (basetype) != NULL_TREE
-         && ! TYPE_VIRTUAL_P (basetype))
-       /* If basetype doesn't have virtual functions, don't emit code to
-          handle that case.  */
-       e1 = e3;
-      else
+      /* This used to avoid checking for virtual functions if basetype
+        has no virtual functions, according to an earlier ANSI draft.
+        With the final ISO C++ rules, such an optimization is
+        incorrect: A pointer to a derived member can be static_cast
+        to pointer-to-base-member, as long as the dynamic object
+        later has the right member. */
+
+      /* Promoting idx before saving it improves performance on RISC
+        targets.  Without promoting, the first compare used
+        load-with-sign-extend, while the second used normal load then
+        shift to sign-extend.  An optimizer flaw, perhaps, but it's
+        easier to make this change.  */
+      idx = save_expr (default_conversion
+                      (build_component_ref (function,
+                                            index_identifier,
+                                            NULL_TREE, 0)));
+      e1 = build_binary_op (GE_EXPR, idx, integer_zero_node);
+
+      /* Convert down to the right base, before using the instance.  */
+      instance = convert_pointer_to_real (basetype, instance_ptr);
+      if (instance == error_mark_node && instance_ptr != error_mark_node)
+       return instance;
+
+      vtbl = convert_pointer_to (ptr_type_node, instance);
+      delta2 = DELTA2_FROM_PTRMEMFUNC (function);
+      vtbl = build
+       (PLUS_EXPR,
+        build_pointer_type (build_pointer_type (vtable_entry_type)),
+        vtbl, cp_convert (ptrdiff_type_node, delta2));
+      vtbl = build_indirect_ref (vtbl, NULL_PTR);
+      aref = build_array_ref (vtbl, build_binary_op (MINUS_EXPR,
+                                                    idx,
+                                                    integer_one_node));
+      if (! flag_vtable_thunks)
        {
-         /* Promoting idx before saving it improves performance on RISC
-            targets.  Without promoting, the first compare used
-            load-with-sign-extend, while the second used normal load then
-            shift to sign-extend.  An optimizer flaw, perhaps, but it's
-            easier to make this change.  */
-         idx = save_expr (default_conversion
-                          (build_component_ref (function,
-                                                index_identifier,
-                                                NULL_TREE, 0)));
-         e1 = build_binary_op (GE_EXPR, idx, integer_zero_node);
-
-         /* Convert down to the right base, before using the instance.  */
-         instance = convert_pointer_to_real (basetype, instance_ptr);
-         if (instance == error_mark_node && instance_ptr != error_mark_node)
-           return instance;
-
-         vtbl = convert_pointer_to (ptr_type_node, instance);
-         delta2 = DELTA2_FROM_PTRMEMFUNC (function);
-         vtbl = build
+         aref = save_expr (aref);
+         
+         delta = build_binary_op
            (PLUS_EXPR,
-            build_pointer_type (build_pointer_type (vtable_entry_type)),
-            vtbl, cp_convert (ptrdiff_type_node, delta2));
-         vtbl = build_indirect_ref (vtbl, NULL_PTR);
-         aref = build_array_ref (vtbl, build_binary_op (MINUS_EXPR,
-                                                        idx,
-                                                        integer_one_node));
-         if (! flag_vtable_thunks)
-           {
-             aref = save_expr (aref);
-
-             delta = build_binary_op
-               (PLUS_EXPR,
-                build_conditional_expr (e1,
-                                        build_component_ref (aref,
-                                                             delta_identifier,
-                                                             NULL_TREE, 0),
-                                        integer_zero_node),
-                delta);
-           }
-
-         if (flag_vtable_thunks)
-           e2 = aref;
-         else
-           e2 = build_component_ref (aref, pfn_identifier, NULL_TREE, 0);
-         TREE_TYPE (e2) = TREE_TYPE (e3);
-         e1 = build_conditional_expr (e1, e2, e3);
-
-         /* Make sure this doesn't get evaluated first inside one of the
-            branches of the COND_EXPR.  */
-         if (TREE_CODE (instance_ptr) == SAVE_EXPR)
-           e1 = build (COMPOUND_EXPR, TREE_TYPE (e1),
-                       instance_ptr, e1);
+            build_conditional_expr (e1,
+                                    build_component_ref (aref,
+                                                         delta_identifier,
+                                                         NULL_TREE, 0),
+                                    integer_zero_node),
+            delta);
        }
 
+      if (flag_vtable_thunks)
+       e2 = aref;
+      else
+       e2 = build_component_ref (aref, pfn_identifier, NULL_TREE, 0);
+      TREE_TYPE (e2) = TREE_TYPE (e3);
+      e1 = build_conditional_expr (e1, e2, e3);
+      
+      /* Make sure this doesn't get evaluated first inside one of the
+        branches of the COND_EXPR.  */
+      if (TREE_CODE (instance_ptr) == SAVE_EXPR)
+       e1 = build (COMPOUND_EXPR, TREE_TYPE (e1),
+                   instance_ptr, e1);
+
       *instance_ptrptr = build (PLUS_EXPR, TREE_TYPE (instance_ptr),
                                instance_ptr, delta);
 
@@ -3001,7 +3017,8 @@ build_function_call_real (function, params, require_complete, flags)
 
   if (TREE_CODE (function) == ADDR_EXPR
       && TREE_CODE (TREE_OPERAND (function, 0)) == FUNCTION_DECL
-      && DECL_BUILT_IN (TREE_OPERAND (function, 0)))
+      && DECL_BUILT_IN (TREE_OPERAND (function, 0))
+      && DECL_BUILT_IN_CLASS (TREE_OPERAND (function, 0)) == BUILT_IN_NORMAL)
     switch (DECL_FUNCTION_CODE (TREE_OPERAND (function, 0)))
       {
       case BUILT_IN_ABS:
@@ -3563,6 +3580,9 @@ build_binary_op_nodefault (code, orig_op0, orig_op1, error_code)
 
     case EQ_EXPR:
     case NE_EXPR:
+      if (warn_float_equal && (code0 == REAL_TYPE || code1 == REAL_TYPE))
+       warning ("comparing floating point with == or != is unsafe");
+
       build_type = boolean_type_node; 
       if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE
           || code0 == COMPLEX_TYPE)
@@ -3678,8 +3698,8 @@ build_binary_op_nodefault (code, orig_op0, orig_op1, error_code)
                                   DECL_VINDEX (TREE_OPERAND (op1, 0)),
                                   integer_one_node);
              op1 = integer_zero_node;
-             delta21 = CLASSTYPE_VFIELD (TYPE_METHOD_BASETYPE
-                                         (TREE_TYPE (type1)));
+             delta21 = TYPE_VFIELD (TYPE_METHOD_BASETYPE
+                                    (TREE_TYPE (type1)));
              delta21 = DECL_FIELD_BITPOS (delta21);
              delta21 = size_binop (FLOOR_DIV_EXPR, delta21,
                                    size_int (BITS_PER_UNIT));
@@ -4548,7 +4568,6 @@ build_unary_op (code, xarg, noconvert)
                                     || code == POSTINCREMENT_EXPR)
                                    ? PLUS_EXPR : MINUS_EXPR),
                                   argtype, value, inc);
-             TREE_SIDE_EFFECTS (incremented) = 1;
 
              modify = build_modify_expr (arg, NOP_EXPR, incremented);
              compound = build (COMPOUND_EXPR, TREE_TYPE (arg), modify, value);
@@ -4586,7 +4605,6 @@ build_unary_op (code, xarg, noconvert)
                arg = stabilize_reference (arg);
                val = build (MODIFY_EXPR, TREE_TYPE (arg), arg,
                             boolean_true_node);
-               TREE_SIDE_EFFECTS (val) = 1;
                arg = save_expr (arg);
                val = build (COMPOUND_EXPR, TREE_TYPE (arg), val, arg);
                val = build (COMPOUND_EXPR, TREE_TYPE (arg), arg, val);
@@ -4772,7 +4790,7 @@ build_unary_op (code, xarg, noconvert)
       return fold (build1 (code, argtype, arg));
     }
 
-  error (errstring);
+  error ("%s", errstring);
   return error_mark_node;
 }
 
@@ -4961,10 +4979,6 @@ mark_addressable (exp)
            && DECL_RTL (x) != 0
            && ! DECL_IN_MEMORY_P (x))
          {
-           /* We thought this would make a good constant variable,
-              but we were wrong.  */
-           push_permanent_obstack ();
-
            TREE_ASM_WRITTEN (x) = 0;
            DECL_RTL (x) = 0;
            rest_of_decl_compilation (x, 0, 
@@ -4972,8 +4986,6 @@ mark_addressable (exp)
                                      0);
            TREE_ADDRESSABLE (x) = 1;
 
-           pop_obstacks ();
-
            return 1;
          }
        /* Caller should not be trying to mark initialized
@@ -4989,8 +5001,10 @@ mark_addressable (exp)
            && !DECL_ARTIFICIAL (x) && extra_warnings)
          cp_warning ("address requested for `%D', which is declared `register'",
                      x);
-       put_var_into_stack (x);
        TREE_ADDRESSABLE (x) = 1;
+       TREE_USED (x) = 1;
+       if (current_function && expanding_p)
+         put_var_into_stack (x);
        return 1;
 
       case FUNCTION_DECL:
@@ -5803,17 +5817,6 @@ build_modify_expr (lhs, modifycode, rhs)
        }
     }
 
-  /* check to see if there is an assignment to `this' */
-  if (lhs == current_class_ptr)
-    {
-      if (flag_this_is_variable > 0
-         && DECL_NAME (current_function_decl) != NULL_TREE
-         && (DECL_NAME (current_function_decl)
-             != constructor_name (current_class_type)))
-       warning ("assignment to `this' not in constructor or destructor");
-      current_function_just_assigned_this = 1;
-    }
-
   if (modifycode != INIT_EXPR)
     {
       /* Make modifycode now either a NOP_EXPR or an INIT_EXPR.  */
@@ -5876,7 +5879,8 @@ build_modify_expr (lhs, modifycode, rhs)
     {
       newrhs = convert_for_initialization (lhs, lhstype, newrhs, LOOKUP_NORMAL,
                                           "assignment", NULL_TREE, 0);
-      if (lhs == DECL_RESULT (current_function_decl))
+      if (current_function_decl && 
+         lhs == DECL_RESULT (current_function_decl))
        {
          if (DECL_INITIAL (lhs))
            warning ("return value from function receives multiple initializations");
@@ -6386,7 +6390,10 @@ convert_for_assignment (type, rhs, errtype, fndecl, parmnum)
   if (TREE_CODE (rhs) == TREE_LIST && TREE_VALUE (rhs) == error_mark_node)
     return error_mark_node;
 
-  /* Issue warnings about peculiar, but legal, uses of NULL.  */
+  /* Issue warnings about peculiar, but legal, uses of NULL.  We
+     do this *before* the call to decl_constant_value so as to
+     avoid duplicate warnings on code like `const int I = NULL;
+     f(I);'.  */
   if (ARITHMETIC_TYPE_P (type) && rhs == null_node)
     cp_warning ("converting NULL to non-pointer type");
 
@@ -6437,25 +6444,28 @@ convert_for_assignment (type, rhs, errtype, fndecl, parmnum)
      cv-unqualified type of the left operand.  */
   if (!can_convert_arg (type, rhstype, rhs))
     {
-      /* When -Wno-pmf-converions is use, we just silently allow
+      /* When -Wno-pmf-conversions is use, we just silently allow
         conversions from pointers-to-members to plain pointers.  If
         the conversion doesn't work, cp_convert will complain.  */
       if (!warn_pmf2ptr 
          && TYPE_PTR_P (type) 
          && TYPE_PTRMEMFUNC_P (rhstype))
        rhs = cp_convert (strip_top_quals (type), rhs);
-      /* If the right-hand side has unknown type, then it is an
-        overloaded function.  Call instantiate_type to get error
-        messages.  */
-      else if (rhstype == unknown_type_node)
-       instantiate_type (type, rhs, 1);
-      else if (fndecl)
-       cp_error ("cannot convert `%T' to `%T' for argument `%P' to `%D'",
-                 rhstype, type, parmnum, fndecl);
-      else
-       cp_error ("cannot convert `%T' to `%T' in %s", rhstype, type, 
-                 errtype);
-      return error_mark_node;
+      else 
+       {
+         /* If the right-hand side has unknown type, then it is an
+            overloaded function.  Call instantiate_type to get error
+            messages.  */
+         if (rhstype == unknown_type_node)
+           instantiate_type (type, rhs, 1);
+         else if (fndecl)
+           cp_error ("cannot convert `%T' to `%T' for argument `%P' to `%D'",
+                     rhstype, type, parmnum, fndecl);
+         else
+           cp_error ("cannot convert `%T' to `%T' in %s", rhstype, type, 
+                     errtype);
+         return error_mark_node;
+       }
     }
   return perform_implicit_conversion (strip_top_quals (type), rhs);
 }
@@ -6560,21 +6570,6 @@ convert_for_initialization (exp, type, rhs, flags, errtype, fndecl, parmnum)
   if (IS_AGGR_TYPE (type))
     return ocp_convert (type, rhs, CONV_IMPLICIT|CONV_FORCE_TEMP, flags);
 
-  if (type == TREE_TYPE (rhs))
-    {
-      /* Issue warnings about peculiar, but legal, uses of NULL.  We
-        do this *before* the call to decl_constant_value so as to
-        avoid duplicate warnings on code like `const int I = NULL;
-        f(I);'.  */
-      if (ARITHMETIC_TYPE_P (type) && rhs == null_node)
-       cp_warning ("converting NULL to non-pointer type");
-
-      if (TREE_READONLY_DECL_P (rhs))
-       rhs = decl_constant_value (rhs);
-
-      return rhs;
-    }
-
   return convert_for_assignment (type, rhs, errtype, fndecl, parmnum);
 }
 \f
@@ -6634,41 +6629,103 @@ c_expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
   emit_queue ();
 }
 \f
-/* Expand a C `return' statement.
-   RETVAL is the expression for what to return,
-   or a null pointer for `return;' with no value.
+/* If RETVAL is the address of, or a reference to, a local variable or
+   temporary give an appropraite warning.  */
 
-   C++: upon seeing a `return', we must call destructors on all
-   variables in scope which had constructors called on them.
-   This means that if in a destructor, the base class destructors
-   must be called before returning.
+static void
+maybe_warn_about_returning_address_of_local (retval)
+     tree retval;
+{
+  tree valtype = TREE_TYPE (DECL_RESULT (current_function_decl));
 
-   The RETURN statement in C++ has initialization semantics.  */
+  if (TREE_CODE (valtype) == REFERENCE_TYPE)
+    {
+      tree whats_returned;
 
-void
-c_expand_return (retval)
+      /* Sort through common things to see what it is
+        we are returning.  */
+      whats_returned = retval;
+      if (TREE_CODE (whats_returned) == COMPOUND_EXPR)
+       {
+         whats_returned = TREE_OPERAND (whats_returned, 1);
+         if (TREE_CODE (whats_returned) == ADDR_EXPR)
+           whats_returned = TREE_OPERAND (whats_returned, 0);
+       }
+      while (TREE_CODE (whats_returned) == CONVERT_EXPR
+            || TREE_CODE (whats_returned) == NOP_EXPR)
+       whats_returned = TREE_OPERAND (whats_returned, 0);
+      if (TREE_CODE (whats_returned) == ADDR_EXPR)
+       {
+         whats_returned = TREE_OPERAND (whats_returned, 0);
+         while (TREE_CODE (whats_returned) == AGGR_INIT_EXPR
+                || TREE_CODE (whats_returned) == TARGET_EXPR)
+           {
+             /* Get the target.  */
+             whats_returned = TREE_OPERAND (whats_returned, 0);
+             warning ("returning reference to temporary");
+           }
+       }
+
+      if (TREE_CODE (whats_returned) == VAR_DECL 
+         && DECL_NAME (whats_returned))
+       {
+         if (TEMP_NAME_P (DECL_NAME (whats_returned)))
+           warning ("reference to non-lvalue returned");
+         else if (TREE_CODE (TREE_TYPE (whats_returned)) != REFERENCE_TYPE
+                  && DECL_FUNCTION_SCOPE_P (whats_returned)
+                  && !(TREE_STATIC (whats_returned)
+                       || TREE_PUBLIC (whats_returned)))
+           cp_warning_at ("reference to local variable `%D' returned", 
+                          whats_returned);
+       }
+    }
+  else if (TREE_CODE (retval) == ADDR_EXPR)
+    {
+      tree whats_returned = TREE_OPERAND (retval, 0);
+
+      if (TREE_CODE (whats_returned) == VAR_DECL
+         && DECL_NAME (whats_returned)
+         && DECL_FUNCTION_SCOPE_P (whats_returned)
+         && !(TREE_STATIC (whats_returned)
+              || TREE_PUBLIC (whats_returned)))
+       cp_warning_at ("address of local variable `%D' returned", 
+                      whats_returned);
+    }
+}
+
+/* Check that returning RETVAL from the current function is legal.
+   Return an expression explicitly showing all conversions required to
+   change RETVAL into the function return type, and to assign it to
+   the DECL_RESULT for the function.  */
+
+tree
+check_return_expr (retval)
      tree retval;
 {
-  tree result = DECL_RESULT (current_function_decl);
-  tree valtype = TREE_TYPE (result);
-
+  tree result;
+  /* The type actually returned by the function, after any
+     promotions.  */
+  tree valtype;
+  int fn_returns_value_p;
+
+  /* A `volatile' function is one that isn't supposed to return, ever.
+     (This is a G++ extension, used to get better code for functions
+     that call the `volatile' function.)  */
   if (TREE_THIS_VOLATILE (current_function_decl))
     warning ("function declared `noreturn' has a `return' statement");
 
+  /* Check for various simple errors.  */
   if (retval == error_mark_node)
     {
+      /* If an error occurred, there's nothing to do.  */
       current_function_returns_null = 1;
-      return;
+      return error_mark_node;
     }
-
-  if (dtor_label)
+  else if (dtor_label)
     {
       if (retval)
        error ("returning a value from a destructor");
-
-      /* Can't just return from a destructor.  */
-      expand_goto (dtor_label);
-      return;
+      return NULL_TREE;
     }
   else if (in_function_try_handler
           && DECL_CONSTRUCTOR_P (current_function_decl))
@@ -6676,8 +6733,57 @@ c_expand_return (retval)
       /* If a return statement appears in a handler of the
          function-try-block of a constructor, the program is ill-formed. */
       error ("cannot return from a handler of a function-try-block of a constructor");
-      return;
+      return error_mark_node;
+    }
+  else if (retval && DECL_CONSTRUCTOR_P (current_function_decl))
+    /* You can't return a value from a constructor.  */
+    error ("returning a value from a constructor");
+
+  /* Constructors actually always return `this', even though in C++
+     you can't return a value from a constructor.  */
+  if (DECL_CONSTRUCTOR_P (current_function_decl))
+    retval = current_class_ptr;
+
+  /* When no explicit return-value is given in a function with a named
+     return value, the named return value is used.  */
+  result = DECL_RESULT (current_function_decl);
+  valtype = TREE_TYPE (result);
+  my_friendly_assert (valtype != NULL_TREE, 19990924);
+  fn_returns_value_p = !same_type_p (valtype, void_type_node);
+  if (!retval && DECL_NAME (result) && fn_returns_value_p)
+    retval = result;
+
+  /* Check for a return statement with no return value in a function
+     that's supposed to return a value.  */
+  if (!retval && fn_returns_value_p)
+    {
+      pedwarn ("`return' with no value, in function returning non-void");
+      /* Clear this, so finish_function won't say that we reach the
+        end of a non-void function (which we don't, we gave a
+        return!).  */
+      current_function_returns_null = 0;
+    }
+  /* Check for a return statement with a value in a function that
+     isn't supposed to return a value.  */
+  else if (retval && !fn_returns_value_p)
+    {     
+      if (same_type_p (TREE_TYPE (retval), void_type_node))
+       /* You can return a `void' value from a function of `void'
+          type.  In that case, we have to evaluate the expression for
+          its side-effects.  */
+         finish_expr_stmt (retval);
+      else
+       pedwarn ("`return' with a value, in function returning void");
+
+      current_function_returns_null = 1;
+
+      /* There's really no value to return, after all.  */
+      return NULL_TREE;
     }
+  else if (!retval)
+    /* Remember that this function can sometimes return without a
+       value.  */
+    current_function_returns_null = 1;
 
   /* Only operator new(...) throw(), can return NULL [expr.new/13].  */
   if ((DECL_NAME (current_function_decl) == ansi_opname[(int) NEW_EXPR]
@@ -6685,44 +6791,6 @@ c_expand_return (retval)
       && !TYPE_NOTHROW_P (TREE_TYPE (current_function_decl))
       && null_ptr_cst_p (retval))
     cp_warning ("operator new should throw an exception, not return NULL");
-  
-  if (retval == NULL_TREE)
-    {
-      /* A non-named return value does not count.  */
-
-      if (DECL_CONSTRUCTOR_P (current_function_decl))
-       retval = current_class_ptr;
-      else if (DECL_NAME (result) != NULL_TREE
-              && TREE_CODE (valtype) != VOID_TYPE)
-       retval = result;
-      else
-       {
-         current_function_returns_null = 1;
-
-         if (valtype != NULL_TREE && TREE_CODE (valtype) != VOID_TYPE)
-           {
-             if (DECL_NAME (DECL_RESULT (current_function_decl)) == NULL_TREE)
-               {
-                 pedwarn ("`return' with no value, in function returning non-void");
-                 /* Clear this, so finish_function won't say that we
-                    reach the end of a non-void function (which we don't,
-                    we gave a return!).  */
-                 current_function_returns_null = 0;
-               }
-           }
-
-         expand_null_return ();
-         return;
-       }
-    }
-  else if (DECL_CONSTRUCTOR_P (current_function_decl))
-    {
-      if (flag_this_is_variable)
-       error ("return from a constructor: use `this = ...' instead");
-      else
-       error ("returning a value from a constructor");
-      retval = current_class_ptr;
-    }
 
   /* Effective C++ rule 15.  See also start_function.  */
   if (warn_ecpp
@@ -6730,137 +6798,73 @@ c_expand_return (retval)
       && retval != current_class_ref)
     cp_warning ("`operator=' should return a reference to `*this'");
 
-  if (valtype == NULL_TREE || TREE_CODE (valtype) == VOID_TYPE)
-    {
-      current_function_returns_null = 1;
-      if (TREE_CODE (TREE_TYPE (retval)) != VOID_TYPE)
-       pedwarn ("`return' with a value, in function returning void");
-      expand_return (retval);
-      return;
-    }
-  
-  /* Now deal with possible C++ hair:
-     (1) Compute the return value.
-     (2) If there are aggregate values with destructors which
-     must be cleaned up, clean them (taking care
-     not to clobber the return value).
-     (3) If an X(X&) constructor is defined, the return
-     value must be returned via that.  */
-
-  if (retval == result
-      || DECL_CONSTRUCTOR_P (current_function_decl))
-    /* It's already done for us.  */;
-  else if (TREE_CODE (TREE_TYPE (retval)) == VOID_TYPE)
-    {
-      pedwarn ("return of void value in function returning non-void");
-      expand_expr_stmt (retval);
-      retval = 0;
-    }
+  /* We don't need to do any conversions when there's nothing being
+     returned.  */
+  if (!retval)
+    return NULL_TREE;
+
+  /* Do any required conversions.  */
+  if (retval == result || DECL_CONSTRUCTOR_P (current_function_decl))
+    /* No conversions are required.  */
+    ;
   else
     {
+      /* The type the function is declared to return.  */
       tree functype = TREE_TYPE (TREE_TYPE (current_function_decl));
 
       /* First convert the value to the function's return type, then
         to the type of return value's location to handle the
          case that functype is thiner than the valtype. */
-
       retval = convert_for_initialization
        (NULL_TREE, functype, retval, LOOKUP_NORMAL|LOOKUP_ONLYCONVERTING,
         "return", NULL_TREE, 0);
-
       retval = convert (valtype, retval);
 
+      /* If the conversion failed, treat this just like `return;'.  */
       if (retval == error_mark_node)
-       {
-         /* Avoid warning about control reaching end of function.  */
-         expand_null_return ();
-         return;
-       }
-
+       return NULL_TREE;
       /* We can't initialize a register from a AGGR_INIT_EXPR.  */
       else if (! current_function_returns_struct
               && TREE_CODE (retval) == TARGET_EXPR
               && TREE_CODE (TREE_OPERAND (retval, 1)) == AGGR_INIT_EXPR)
        retval = build (COMPOUND_EXPR, TREE_TYPE (retval), retval,
                        TREE_OPERAND (retval, 0));
+      else
+       maybe_warn_about_returning_address_of_local (retval);
+    }
+  
+  /* Actually copy the value returned into the appropriate location.  */
+  if (retval && retval != result)
+    retval = build (INIT_EXPR, TREE_TYPE (result), result, retval);
 
-      /* Add some useful error checking for C++.  */
-      else if (TREE_CODE (valtype) == REFERENCE_TYPE)
-       {
-         tree whats_returned;
-
-         /* Sort through common things to see what it is
-            we are returning.  */
-         whats_returned = retval;
-         if (TREE_CODE (whats_returned) == COMPOUND_EXPR)
-           {
-             whats_returned = TREE_OPERAND (whats_returned, 1);
-             if (TREE_CODE (whats_returned) == ADDR_EXPR)
-               whats_returned = TREE_OPERAND (whats_returned, 0);
-           }
-         while (TREE_CODE (whats_returned) == CONVERT_EXPR
-                || TREE_CODE (whats_returned) == NOP_EXPR)
-           whats_returned = TREE_OPERAND (whats_returned, 0);
-         if (TREE_CODE (whats_returned) == ADDR_EXPR)
-           {
-             whats_returned = TREE_OPERAND (whats_returned, 0);
-             while (TREE_CODE (whats_returned) == AGGR_INIT_EXPR
-                    || TREE_CODE (whats_returned) == TARGET_EXPR)
-               {
-                 /* Get the target.  */
-                 whats_returned = TREE_OPERAND (whats_returned, 0);
-                 warning ("returning reference to temporary");
-               }
-           }
+  /* All done.  Remember that this function did return a value.  */
+  current_function_returns_value = 1;
+  return retval;
+}
 
-         if (TREE_CODE (whats_returned) == VAR_DECL && DECL_NAME (whats_returned))
-           {
-             if (TEMP_NAME_P (DECL_NAME (whats_returned)))
-               warning ("reference to non-lvalue returned");
-             else if (TREE_CODE (TREE_TYPE (whats_returned)) != REFERENCE_TYPE
-                      && DECL_FUNCTION_SCOPE_P (whats_returned)
-                      && !(TREE_STATIC (whats_returned)
-                           || TREE_PUBLIC (whats_returned)))
-               cp_warning_at ("reference to local variable `%D' returned", whats_returned);
-           }
-       }
-      else if (TREE_CODE (retval) == ADDR_EXPR)
-       {
-         tree whats_returned = TREE_OPERAND (retval, 0);
-
-         if (TREE_CODE (whats_returned) == VAR_DECL
-             && DECL_NAME (whats_returned)
-             && DECL_FUNCTION_SCOPE_P (whats_returned)
-             && !(TREE_STATIC (whats_returned)
-                  || TREE_PUBLIC (whats_returned)))
-           cp_warning_at ("address of local variable `%D' returned", whats_returned);
-       }
-    }
+/* Expand a C `return' statement.
+   RETVAL is the expression for what to return,
+   or a null pointer for `return;' with no value.
 
-  if (retval != NULL_TREE
-      && TREE_CODE_CLASS (TREE_CODE (retval)) == 'd'
-      && ! in_control_zone_p ())
-    current_function_return_value = retval;
+   C++: upon seeing a `return', we must call destructors on all
+   variables in scope which had constructors called on them.
+   This means that if in a destructor, the base class destructors
+   must be called before returning.
 
-  if (ctor_label && TREE_CODE (ctor_label) != ERROR_MARK)
-    {
-      /* Here RETVAL is CURRENT_CLASS_PTR, so there's nothing to do.  */
-      expand_goto (ctor_label);
-    }
+   The RETURN statement in C++ has initialization semantics.  */
 
-  if (retval && retval != result)
+void
+c_expand_return (retval)
+     tree retval;
+{
+  if (!retval)
+    expand_null_return ();
+  else
     {
-      result = build (INIT_EXPR, TREE_TYPE (result), result, retval);
-      TREE_SIDE_EFFECTS (result) = 1;
+      expand_start_target_temps ();
+      expand_return (retval);
+      expand_end_target_temps ();
     }
-
-  expand_start_target_temps ();
-
-  expand_return (result);
-
-  expand_end_target_temps ();
-
-  current_function_returns_value = 1;
 }
 \f
 /* Start a C switch statement, testing expression EXP.