cp-tree.h (check_return_expr): New function.
authorMark Mitchell <mark@codesourcery.com>
Sat, 25 Sep 1999 18:10:04 +0000 (18:10 +0000)
committerMark Mitchell <mmitchel@gcc.gnu.org>
Sat, 25 Sep 1999 18:10:04 +0000 (18:10 +0000)
* cp-tree.h (check_return_expr): New function.
* decl.c (finish_constructor_body): New function.
(pushdecl): Put global friend functions in namespace binding
level, not the class binding level.
(finish_destructor_body): Make sure the dtor_label is always
defined.  Fix typo in comment.
(finish_function): Move generation of constructor-termination code
to semantic-analysis time.  Move generation of implicit `main'
return value to semantic-analysis time.
* semantics.c (finish_return_stmt): Generate goto's to
ctor_label/dtor_label here.  Use check_return_expr to do semantic
analysis on the returned expression.
* typeck.c (maybe_warn_about_returning_address_of_local): New
function split out from c_expand_return.
(check_return_expr): Likewise.
(c_expand_return): Just generate the RTL for the return.

From-SVN: r29663

gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/semantics.c
gcc/cp/typeck.c

index 19485a5..0efaa14 100644 (file)
@@ -1,3 +1,22 @@
+1999-09-25  Mark Mitchell  <mark@codesourcery.com>
+
+       * cp-tree.h (check_return_expr): New function.
+       * decl.c (finish_constructor_body): New function.
+       (pushdecl): Put global friend functions in namespace binding
+       level, not the class binding level.
+       (finish_destructor_body): Make sure the dtor_label is always
+       defined.  Fix typo in comment.
+       (finish_function): Move generation of constructor-termination code
+       to semantic-analysis time.  Move generation of implicit `main'
+       return value to semantic-analysis time.
+       * semantics.c (finish_return_stmt): Generate goto's to
+       ctor_label/dtor_label here.  Use check_return_expr to do semantic
+       analysis on the returned expression.
+       * typeck.c (maybe_warn_about_returning_address_of_local): New
+       function split out from c_expand_return.
+       (check_return_expr): Likewise.
+       (c_expand_return): Just generate the RTL for the return.
+       
 1999-09-24  Mark Mitchell  <mark@codesourcery.com>
 
        * cp-tree.h (CPTI_CLEANUP_TYPE): New macro.
index 584600e..15c5099 100644 (file)
@@ -3949,6 +3949,7 @@ extern tree pfn_from_ptrmemfunc                 PROTO((tree));
 extern tree type_after_usual_arithmetic_conversions PROTO((tree, tree));
 extern tree composite_pointer_type              PROTO((tree, tree, tree, tree,
                                                       const char*));
+extern tree check_return_expr                   PROTO((tree));
 
 /* in typeck2.c */
 extern tree error_not_base_type                        PROTO((tree, tree));
index f456dbb..d5da38f 100644 (file)
@@ -180,6 +180,7 @@ static void save_function_data PROTO((tree));
 static void check_function_type PROTO((tree));
 static void destroy_local_static PROTO((tree));
 static void destroy_local_var PROTO((tree));
+static void finish_constructor_body PROTO((void));
 static void finish_destructor_body PROTO((void));
 
 #if defined (DEBUG_CP_BINDING_LEVELS)
@@ -4064,7 +4065,10 @@ pushdecl (x)
     }
 
   if (need_new_binding)
-    add_decl_to_level (x, current_binding_level);
+    add_decl_to_level (x, 
+                      DECL_NAMESPACE_SCOPE_P (x)
+                      ? NAMESPACE_LEVEL (CP_DECL_CONTEXT (x))
+                      : current_binding_level);
 
   return x;
 }
@@ -13329,9 +13333,26 @@ save_function_data (decl)
   f->cannot_inline = current_function_cannot_inline;
 }
 
+/* At the end of every constructor we generate to code to return
+   `this'.  Do that now.  */
+
+static void
+finish_constructor_body ()
+{
+  /* Any return from a constructor will end up here.  */
+  add_tree (build_min_nt (LABEL_STMT, ctor_label));
+
+  /* Clear CTOR_LABEL so that finish_return_stmt knows to really
+     generate the return, rather than a goto to CTOR_LABEL.  */
+  ctor_label = NULL_TREE;
+  /* In check_return_expr we translate an empty return from a
+     constructor to a return of `this'.  */
+  finish_return_stmt (NULL_TREE);
+}
+
 /* At the end of every destructor we generate code to restore virtual
    function tables to the values desired by base classes and to call
-   to base class destructors.  Do that now, for DECL.  */
+   to base class destructors.  Do that now.  */
 
 static void
 finish_destructor_body ()
@@ -13344,6 +13365,9 @@ finish_destructor_body ()
   /* Create a block to contain all the extra code.  */
   compound_stmt = begin_compound_stmt (/*has_no_scope=*/0);
 
+  /* Any return from a destructor will end up here.  */
+  add_tree (build_min_nt (LABEL_STMT, dtor_label));
+
   /* Generate the code to call destructor on base class.  If this
      destructor belongs to a class with virtual functions, then set
      the virtual function table pointer to represent the type of our
@@ -13372,13 +13396,12 @@ finish_destructor_body ()
          || TREE_OPERAND (exprstmt, 0) != integer_zero_node
          || TYPE_USES_VIRTUAL_BASECLASSES (current_class_type)))
     {
-      add_tree (build_min_nt (LABEL_STMT, dtor_label));
       if (exprstmt != void_zero_node)
        /* Don't call `expand_expr_stmt' if we're not going to do
           anything, since -Wall will give a diagnostic.  */
        finish_expr_stmt (exprstmt);
 
-      /* Run destructor on all virtual baseclasses.  */
+      /* Run destructors for all virtual baseclasses.  */
       if (TYPE_USES_VIRTUAL_BASECLASSES (current_class_type))
        {
          tree vbases = nreverse (copy_list (CLASSTYPE_VBASECLASSES (current_class_type)));
@@ -13496,10 +13519,23 @@ finish_function (lineno, flags)
 
   if (building_stmt_tree ())
     {
-      if (DECL_CONSTRUCTOR_P (fndecl) && call_poplevel)
-       do_poplevel ();
+      if (DECL_CONSTRUCTOR_P (fndecl))
+       {
+         finish_constructor_body ();
+         if (call_poplevel)
+           do_poplevel ();
+       }
       else if (DECL_DESTRUCTOR_P (fndecl) && !processing_template_decl)
        finish_destructor_body ();
+      else if (DECL_MAIN_P (fndecl))
+       {
+         /* Make it so that `main' always returns 0 by default.  */
+#ifdef VMS
+         finish_return_stmt (integer_one_node);
+#else
+         finish_return_stmt (integer_zero_node);
+#endif
+       }
 
       /* Finish dealing with exception specifiers.  */
       if (flag_exceptions && !processing_template_decl
@@ -13535,28 +13571,11 @@ finish_function (lineno, flags)
        ;
       else if (DECL_CONSTRUCTOR_P (fndecl))
        {
-         /* This is where the body of the constructor begins.  All
-            subobjects have been fully constructed at this point.  */
+         /* All subobjects have been fully constructed at this point.  */
          end_protect_partials ();
 
-         /* This is where the body of the constructor ends.  */
-         expand_label (ctor_label);
-         ctor_label = NULL_TREE;
-
          if (call_poplevel)
            do_poplevel ();
-
-         /* c_expand_return knows to return 'this' from a constructor.  */
-         c_expand_return (NULL_TREE);
-       }
-      else if (DECL_MAIN_P (fndecl))
-       {
-         /* Make it so that `main' always returns 0 by default.  */
-#ifdef VMS
-         c_expand_return (integer_one_node);
-#else
-         c_expand_return (integer_zero_node);
-#endif
        }
       else if (return_label != NULL_RTX
               && flag_this_is_variable <= 0
index cd48570..ac7856b 100644 (file)
@@ -371,6 +371,33 @@ void
 finish_return_stmt (expr)
      tree expr;
 {
+  if (doing_semantic_analysis_p () && !processing_template_decl)
+    expr = check_return_expr (expr);
+
+  if (doing_semantic_analysis_p () && !processing_template_decl)
+    {
+      if (DECL_CONSTRUCTOR_P (current_function_decl) && ctor_label)
+       {
+         /* Even returns without a value in a constructor must return
+            `this'.  We accomplish this by sending all returns in a
+            constructor to the CTOR_LABEL; finish_function emits code to
+            return a value there.  When we finally generate the real
+            return statement, CTOR_LABEL is no longer set, and we fall
+            through into the normal return-processing code below.  */
+         finish_goto_stmt (ctor_label);
+         return;
+       }
+      else 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; finsh_function emits
+            code to return a value there.  */
+         finish_goto_stmt (dtor_label);
+         return;
+       }
+    }
+
   if (building_stmt_tree ())
     add_tree (build_min_nt (RETURN_STMT, expr));
   else
index 80be5f8..6a18b11 100644 (file)
@@ -64,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.  */
@@ -6639,41 +6640,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))
@@ -6681,8 +6744,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]
@@ -6690,41 +6802,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))
-    {
-      error ("returning a value from a constructor");
-      retval = current_class_ptr;
-    }
 
   /* Effective C++ rule 15.  See also start_function.  */
   if (warn_ecpp
@@ -6732,137 +6809,76 @@ 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));
-
-      /* 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");
-               }
-           }
-
-         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);
-       }
-    }
-
-  if (retval != NULL_TREE
-      && TREE_CODE_CLASS (TREE_CODE (retval)) == 'd'
-      && ! in_control_zone_p ())
-    current_function_return_value = retval;
-
-  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);
+      else
+       maybe_warn_about_returning_address_of_local (retval);
     }
-
+  
+  /* Actually copy the value returned into the appropriate location.  */
   if (retval && retval != result)
     {
-      result = build (INIT_EXPR, TREE_TYPE (result), result, retval);
-      TREE_SIDE_EFFECTS (result) = 1;
+      retval = build (INIT_EXPR, TREE_TYPE (result), result, retval);
+      TREE_SIDE_EFFECTS (retval) = 1;
     }
 
-  expand_start_target_temps ();
+  /* All done.  Remember that this function did return a value.  */
+  current_function_returns_value = 1;
+  return retval;
+}
+
+/* Expand a C `return' statement.
+   RETVAL is the expression for what to return,
+   or a null pointer for `return;' with no value.
 
-  expand_return (result);
+   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.
 
-  expand_end_target_temps ();
+   The RETURN statement in C++ has initialization semantics.  */
 
-  current_function_returns_value = 1;
+void
+c_expand_return (retval)
+     tree retval;
+{
+  if (!retval)
+    expand_null_return ();
+  else
+    {
+      expand_start_target_temps ();
+      expand_return (retval);
+      expand_end_target_temps ();
+    }
 }
 \f
 /* Start a C switch statement, testing expression EXP.