re PR c++/10931 (valid conversion static_cast<const unsigned int&>(lvalue-of-type-int) is rejected)

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
This commit is contained in:
Mark Mitchell 2003-06-26 00:07:09 +00:00 committed by Mark Mitchell
parent 22c7c85ebc
commit 3fe18f1d47
6 changed files with 212 additions and 89 deletions

View 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.

View 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

View 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);

View 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;
}
if (TREE_CODE (type) == REFERENCE_TYPE)
return (convert_from_reference
(convert_to_reference (type, expr, CONV_STATIC|CONV_IMPLICIT,
LOOKUP_COMPLAIN, NULL_TREE)));
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)));
intype = TREE_TYPE (expr);
/* FIXME handle casting to array type. */
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]
The static_cast operator shall not be used to cast away
constness. */
if (ok && casts_away_constness (intype, type))
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;
/* [expr.static.cast]
Any expression can be explicitly converted to type cv void. */
if (TREE_CODE (type) == VOID_TYPE)
return convert_to_void (expr, /*implicit=*/NULL);
/* [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))
{
error ("static_cast from type `%T' to type `%T' casts away constness",
intype, type);
return error_mark_node;
/* 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);
}
if (ok)
return build_c_cast (type, expr);
/* [expr.static.cast]
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]
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;

View 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.

View File

@ -0,0 +1,5 @@
void foo(int x)
{
static_cast<const unsigned int&>(x);
}