re PR c++/10931 (valid conversion static_cast<const unsigned int&>(lvalue-of-type...
authorMark Mitchell <mark@codesourcery.com>
Thu, 26 Jun 2003 00:07:09 +0000 (00:07 +0000)
committerMark Mitchell <mmitchel@gcc.gnu.org>
Thu, 26 Jun 2003 00:07:09 +0000 (00:07 +0000)
PR c++/10931
* g++.dg/expr/static_cast1.C: New test.

PR c++/10931
* call.c (convert_like): Pass issue_conversion_warnings.
(convert_like_with_context): Likewise.
(convert_like_real): Add issue_conversion_warnings parameter.
(perform_direct_initialization_if_possible): New function.
* cp-tree.h (perform_direct_initialization_if_possible): Declare it.
* typeck.c (check_for_casting_away_constness): New function.
(build_static_cast): Rewrite.

From-SVN: r68506

gcc/cp/ChangeLog
gcc/cp/call.c
gcc/cp/cp-tree.h
gcc/cp/typeck.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/expr/static_cast1.C [new file with mode: 0644]

index 65ca03d..d624446 100644 (file)
@@ -1,3 +1,14 @@
+2003-06-25  Mark Mitchell  <mark@codesourcery.com>
+
+       PR c++/10931
+       * call.c (convert_like): Pass issue_conversion_warnings.
+       (convert_like_with_context): Likewise.
+       (convert_like_real): Add issue_conversion_warnings parameter.
+       (perform_direct_initialization_if_possible): New function.
+       * cp-tree.h (perform_direct_initialization_if_possible): Declare it.
+       * typeck.c (check_for_casting_away_constness): New function.
+       (build_static_cast): Rewrite.
+       
 2003-06-24  Nathan Sidwell  <nathan@codesourcery.com>
 
        * call.c (enforce_access): Assert we get a binfo.
index 77ed3e8..41ad02d 100644 (file)
@@ -45,11 +45,13 @@ static int joust (struct z_candidate *, struct z_candidate *, bool);
 static int compare_ics (tree, tree);
 static tree build_over_call (struct z_candidate *, int);
 static tree build_java_interface_fn_ref (tree, tree);
-#define convert_like(CONV, EXPR) \
-  convert_like_real ((CONV), (EXPR), NULL_TREE, 0, 0)
-#define convert_like_with_context(CONV, EXPR, FN, ARGNO) \
-  convert_like_real ((CONV), (EXPR), (FN), (ARGNO), 0)
-static tree convert_like_real (tree, tree, tree, int, int);
+#define convert_like(CONV, EXPR)                               \
+  convert_like_real ((CONV), (EXPR), NULL_TREE, 0, 0,          \
+                    /*issue_conversion_warnings=*/true)
+#define convert_like_with_context(CONV, EXPR, FN, ARGNO)       \
+  convert_like_real ((CONV), (EXPR), (FN), (ARGNO), 0,                 \
+                    /*issue_conversion_warnings=*/true)
+static tree convert_like_real (tree, tree, tree, int, int, bool);
 static void op_error (enum tree_code, enum tree_code, tree, tree,
                            tree, const char *);
 static tree build_object_call (tree, tree);
@@ -4115,14 +4117,17 @@ enforce_access (tree basetype_path, tree decl)
   return true;
 }
 
-/* Perform the conversions in CONVS on the expression EXPR. 
-   FN and ARGNUM are used for diagnostics.  ARGNUM is zero based, -1
+/* Perform the conversions in CONVS on the expression EXPR.  FN and
+   ARGNUM are used for diagnostics.  ARGNUM is zero based, -1
    indicates the `this' argument of a method.  INNER is nonzero when
    being called to continue a conversion chain. It is negative when a
-   reference binding will be applied, positive otherwise.  */
+   reference binding will be applied, positive otherwise.  If
+   ISSUE_CONVERSION_WARNINGS is true, warnings about suspicious
+   conversions will be emitted if appropriate.  */
 
 static tree
-convert_like_real (tree convs, tree expr, tree fn, int argnum, int inner)
+convert_like_real (tree convs, tree expr, tree fn, int argnum, int inner,
+                  bool issue_conversion_warnings)
 {
   int savew, savee;
 
@@ -4138,11 +4143,13 @@ convert_like_real (tree convs, tree expr, tree fn, int argnum, int inner)
        {
          if (TREE_CODE (t) == USER_CONV || !ICS_BAD_FLAG (t))
            {
-             expr = convert_like_real (t, expr, fn, argnum, 1);
+             expr = convert_like_real (t, expr, fn, argnum, 1,
+                                       /*issue_conversion_warnings=*/false);
              break;
            }
          else if (TREE_CODE (t) == AMBIG_CONV)
-           return convert_like_real (t, expr, fn, argnum, 1);
+           return convert_like_real (t, expr, fn, argnum, 1,
+                                     /*issue_conversion_warnings=*/false);
          else if (TREE_CODE (t) == IDENTITY_CONV)
            break;
        }
@@ -4152,7 +4159,7 @@ convert_like_real (tree convs, tree expr, tree fn, int argnum, int inner)
       return cp_convert (totype, expr);
     }
   
-  if (!inner)
+  if (issue_conversion_warnings)
     expr = dubious_conversion_warnings
              (totype, expr, "argument", fn, argnum);
   switch (TREE_CODE (convs))
@@ -4250,7 +4257,8 @@ convert_like_real (tree convs, tree expr, tree fn, int argnum, int inner)
     };
 
   expr = convert_like_real (TREE_OPERAND (convs, 0), expr, fn, argnum,
-                            TREE_CODE (convs) == REF_BIND ? -1 : 1);
+                            TREE_CODE (convs) == REF_BIND ? -1 : 1,
+                           /*issue_conversion_warnings=*/false);
   if (expr == error_mark_node)
     return error_mark_node;
 
@@ -6058,6 +6066,25 @@ perform_implicit_conversion (tree type, tree expr)
   return convert_like (conv, expr);
 }
 
+/* Convert EXPR to TYPE (as a direct-initialization) if that is
+   permitted.  If the conversion is valid, the converted expression is
+   returned.  Otherwise, NULL_TREE is returned.  */
+
+tree
+perform_direct_initialization_if_possible (tree type, tree expr)
+{
+  tree conv;
+  
+  if (type == error_mark_node || error_operand_p (expr))
+    return error_mark_node;
+  conv = implicit_conversion (type, TREE_TYPE (expr), expr,
+                             LOOKUP_NORMAL);
+  if (!conv || ICS_BAD_FLAG (conv))
+    return NULL_TREE;
+  return convert_like_real (conv, expr, NULL_TREE, 0, 0, 
+                           /*issue_conversion_warnings=*/false);
+}
+
 /* DECL is a VAR_DECL whose type is a REFERENCE_TYPE.  The reference
    is being bound to a temporary.  Create and return a new VAR_DECL
    with the indicated TYPE; this variable will store the value to
index 1527aa4..0e3a971 100644 (file)
@@ -3525,6 +3525,7 @@ extern tree initialize_reference (tree, tree, tree);
 extern tree make_temporary_var_for_ref_to_temp (tree, tree);
 extern tree strip_top_quals (tree);
 extern tree perform_implicit_conversion (tree, tree);
+extern tree perform_direct_initialization_if_possible (tree, tree);
 extern tree in_charge_arg_for_name (tree);
 extern tree build_cxx_call (tree, tree, tree);
 
index be53d45..c84cc84 100644 (file)
@@ -4766,11 +4766,24 @@ build_compound_expr (tree list)
   return build (COMPOUND_EXPR, TREE_TYPE (rest), first, rest);
 }
 
+/* Issue an error message if casting from SRC_TYPE to DEST_TYPE casts
+   away constness.  */
+
+static void
+check_for_casting_away_constness (tree src_type, tree dest_type)
+{
+  if (casts_away_constness (src_type, dest_type))
+    error ("static_cast from type `%T' to type `%T' casts away constness",
+          src_type, dest_type);
+}
+
+/* Return an expression representing static_cast<TYPE>(EXPR).  */
+
 tree
 build_static_cast (tree type, tree expr)
 {
   tree intype;
-  int ok;
+  tree result;
 
   if (type == error_mark_node || expr == error_mark_node)
     return error_mark_node;
@@ -4791,88 +4804,149 @@ build_static_cast (tree type, tree expr)
       && TREE_TYPE (expr) == TREE_TYPE (TREE_OPERAND (expr, 0)))
     expr = TREE_OPERAND (expr, 0);
 
-  if (TREE_CODE (type) == VOID_TYPE)
-    {
-      expr = convert_to_void (expr, /*implicit=*/NULL);
-      return expr;
-    }
+  intype = TREE_TYPE (expr);
 
-  if (TREE_CODE (type) == REFERENCE_TYPE)
-    return (convert_from_reference
-           (convert_to_reference (type, expr, CONV_STATIC|CONV_IMPLICIT,
-                                  LOOKUP_COMPLAIN, NULL_TREE)));
+  /* [expr.static.cast]
 
-  if (IS_AGGR_TYPE (type))
-    return build_cplus_new (type, (build_special_member_call
-                                  (NULL_TREE, complete_ctor_identifier, 
-                                   build_tree_list (NULL_TREE, expr),
-                                   TYPE_BINFO (type), LOOKUP_NORMAL)));
+     An expression e can be explicitly converted to a type T using a
+     static_cast of the form static_cast<T>(e) if the declaration T
+     t(e);" is well-formed, for some invented temporary variable
+     t.  */
+  result = perform_direct_initialization_if_possible (type, expr);
+  if (result)
+    return result;
   
-  intype = TREE_TYPE (expr);
+  /* [expr.static.cast]
 
-  /* FIXME handle casting to array type.  */
+     Any expression can be explicitly converted to type cv void.  */
+  if (TREE_CODE (type) == VOID_TYPE)
+    return convert_to_void (expr, /*implicit=*/NULL);
 
-  ok = 0;
-  if (IS_AGGR_TYPE (intype)
-      ? can_convert_arg (type, intype, expr)
-      : can_convert_arg (strip_all_pointer_quals (type),
-                         strip_all_pointer_quals (intype), expr))
-    /* This is a standard conversion.  */
-    ok = 1;
-  else if (TYPE_PTROB_P (type) && TYPE_PTROB_P (intype))
-    {
-      /* They're pointers to objects. They must be aggregates that
-         are related non-virtually.  */
-      base_kind kind;
-      
-      if (IS_AGGR_TYPE (TREE_TYPE (type)) && IS_AGGR_TYPE (TREE_TYPE (intype))
-         && lookup_base (TREE_TYPE (type), TREE_TYPE (intype),
-                         ba_ignore | ba_quiet, &kind)
-         && kind != bk_via_virtual)
-       ok = 1;
-    }
-  else if (TYPE_PTRMEM_P (type) && TYPE_PTRMEM_P (intype))
-    {
-      /* They're pointers to members. The pointed to objects must be
-        the same (ignoring CV qualifiers), and the containing classes
-        must be related non-virtually.  */
-      base_kind kind;
-      
-      if (same_type_p
-         (strip_all_pointer_quals (TREE_TYPE (TREE_TYPE (type))),
-          strip_all_pointer_quals (TREE_TYPE (TREE_TYPE (intype))))
-         && (lookup_base (TYPE_OFFSET_BASETYPE (TREE_TYPE (intype)),
-                          TYPE_OFFSET_BASETYPE (TREE_TYPE (type)),
-                          ba_ignore | ba_quiet, &kind))
-         && kind != bk_via_virtual)
-       ok = 1;
-    }
-  else if (TREE_CODE (intype) != BOOLEAN_TYPE
-          && TREE_CODE (type) != ARRAY_TYPE
-          && TREE_CODE (type) != FUNCTION_TYPE
-          && can_convert (intype, strip_all_pointer_quals (type)))
-    ok = 1;
-  else if (TREE_CODE (intype) == ENUMERAL_TYPE
-           && TREE_CODE (type) == ENUMERAL_TYPE)
-    /* DR 128: "A value of integral _or enumeration_ type can be explicitly
-       converted to an enumeration type."
-       The integral to enumeration will be accepted by the previous clause.
-       We need to explicitly check for enumeration to enumeration.  */
-    ok = 1;
+  /* [expr.static.cast]
+
+     An lvalue of type "cv1 B", where B is a class type, can be cast
+     to type "reference to cv2 D", where D is a class derived (clause
+     _class.derived_) from B, if a valid standard conversion from
+     "pointer to D" to "pointer to B" exists (_conv.ptr_), cv2 is the
+     same cv-qualification as, or greater cv-qualification than, cv1,
+     and B is not a virtual base class of D.  */
+  if (TREE_CODE (type) == REFERENCE_TYPE
+      && CLASS_TYPE_P (TREE_TYPE (type))
+      && CLASS_TYPE_P (intype)
+      && real_non_cast_lvalue_p (expr)
+      && DERIVED_FROM_P (intype, TREE_TYPE (type))
+      && can_convert (build_pointer_type (TYPE_MAIN_VARIANT (intype)),
+                     build_pointer_type (TYPE_MAIN_VARIANT 
+                                         (TREE_TYPE (type))))
+      && at_least_as_qualified_p (TREE_TYPE (type), intype))
+    {
+      /* At this point we have checked all of the conditions except
+        that B is not a virtual base class of D.  That will be
+        checked by build_base_path.  */
+      tree base = lookup_base (TREE_TYPE (type), intype, ba_any, NULL);
+
+      /* Convert from B* to D*.  */
+      expr = build_base_path (MINUS_EXPR, build_address (expr), 
+                             base, /*nonnull=*/false);
+      /* Convert the pointer to a reference.  */
+      return build_nop (type, expr);
+    }
 
   /* [expr.static.cast]
 
-     The static_cast operator shall not be used to cast away
-     constness.  */
-  if (ok && casts_away_constness (intype, type))
-    {
-      error ("static_cast from type `%T' to type `%T' casts away constness",
-               intype, type);
-      return error_mark_node;
+     The inverse of any standard conversion sequence (clause _conv_),
+     other than the lvalue-to-rvalue (_conv.lval_), array-to-pointer
+     (_conv.array_), function-to-pointer (_conv.func_), and boolean
+     (_conv.bool_) conversions, can be performed explicitly using
+     static_cast subject to the restriction that the explicit
+     conversion does not cast away constness (_expr.const.cast_), and
+     the following additional rules for specific cases:  */
+  /* For reference, the conversions not excluded are: integral
+     promotions, floating point promotion, integral conversions,
+     floating point conversions, floating-integral conversions,
+     pointer conversions, and pointer to member conversions.  */
+  if ((ARITHMETIC_TYPE_P (type) && ARITHMETIC_TYPE_P (intype))
+      /* DR 128
+
+         A value of integral _or enumeration_ type can be explicitly
+        converted to an enumeration type.  */
+      || (INTEGRAL_OR_ENUMERATION_TYPE_P (type)
+         && INTEGRAL_OR_ENUMERATION_TYPE_P (intype)))
+      /* Really, build_c_cast should defer to this function rather
+        than the other way around.  */
+      return build_c_cast (type, expr);
+  if (TYPE_PTR_P (type) && TYPE_PTR_P (intype)
+      && CLASS_TYPE_P (TREE_TYPE (type))
+      && CLASS_TYPE_P (TREE_TYPE (intype))
+      && can_convert (build_pointer_type (TYPE_MAIN_VARIANT 
+                                         (TREE_TYPE (intype))), 
+                     build_pointer_type (TYPE_MAIN_VARIANT 
+                                         (TREE_TYPE (type)))))
+    {
+      tree base;
+
+      check_for_casting_away_constness (intype, type);
+      base = lookup_base (TREE_TYPE (type), TREE_TYPE (intype), 
+                         ba_check | ba_quiet, NULL);
+      return build_base_path (MINUS_EXPR, expr, base, /*nonnull=*/false);
+    }
+  if ((TYPE_PTRMEM_P (type) && TYPE_PTRMEM_P (intype))
+      || (TYPE_PTRMEMFUNC_P (type) && TYPE_PTRMEMFUNC_P (intype)))
+    {
+      tree c1;
+      tree c2;
+      tree t1;
+      tree t2;
+
+      c1 = TYPE_PTRMEM_CLASS_TYPE (intype);
+      c2 = TYPE_PTRMEM_CLASS_TYPE (type);
+
+      if (TYPE_PTRMEM_P (type))
+       {
+         t1 = (build_ptrmem_type 
+               (c1,
+                TYPE_MAIN_VARIANT (TYPE_PTRMEM_POINTED_TO_TYPE (intype))));
+         t2 = (build_ptrmem_type 
+               (c2,
+                TYPE_MAIN_VARIANT (TYPE_PTRMEM_POINTED_TO_TYPE (type))));
+       }
+      else
+       {
+         t1 = intype;
+         t2 = type;
+       }
+      if (can_convert (t1, t2))
+       {
+         check_for_casting_away_constness (intype, type);
+         if (TYPE_PTRMEM_P (type))
+           {
+             if (TREE_CODE (expr) == PTRMEM_CST)
+               expr = cplus_expand_constant (expr);
+             expr = cp_build_binary_op (PLUS_EXPR, 
+                                        cp_convert (ptrdiff_type_node, expr),
+                                        get_delta_difference (c1, c2, 
+                                                              /*force=*/1));
+             return build_nop (type, expr);
+           }
+         else
+           return build_ptrmemfunc (TYPE_PTRMEMFUNC_FN_TYPE (type), expr, 
+                                    /*force=*/1);
+       }
     }
+    
+  /* [expr.static.cast]
 
-  if (ok)
-    return build_c_cast (type, expr);
+     An rvalue of type "pointer to cv void" can be explicitly
+     converted to a pointer to object type.  A value of type pointer
+     to object converted to "pointer to cv void" and back to the
+     original pointer type will have its original value.  */
+  if (TREE_CODE (intype) == POINTER_TYPE 
+      && VOID_TYPE_P (TREE_TYPE (intype))
+      && TYPE_PTROB_P (type))
+    {
+      check_for_casting_away_constness (intype, type);
+      return build_nop (type, expr);
+    }
 
   error ("invalid static_cast from type `%T' to type `%T'", intype, type);
   return error_mark_node;
index ffed97e..8ae0158 100644 (file)
@@ -1,3 +1,8 @@
+2003-06-25  Mark Mitchell  <mark@codesourcery.com>
+
+       PR c++/10931
+       * g++.dg/expr/static_cast1.C: New test.
+       
 2003-06-25  Josef Zlomek  <zlomekj@suse.cz>
 
        * gcc.dg/20030625-1.c: New test.
diff --git a/gcc/testsuite/g++.dg/expr/static_cast1.C b/gcc/testsuite/g++.dg/expr/static_cast1.C
new file mode 100644 (file)
index 0000000..f1d88bf
--- /dev/null
@@ -0,0 +1,5 @@
+void foo(int x)
+{
+    static_cast<const unsigned int&>(x);
+}
+