call.c (struct conversion): Add base_p.

* call.c (struct conversion): Add base_p.
	(convert_like): Add c_cast_p argument.
	(convert_like_with_conversion): Likewise.
	(build_conv): Clear base_p.
	(standard_conversion): Set it, for derived-to-base conversions.
	(convert_like_real): Add c_cast_p parameter.  Handle pointer
	conversions directly rather than relying on ocp_convert.
	(perform_direct_initialization_if_possible): Add c_cast_p
	parameter.
	* cp-tree.h (perform_direct_initialization_if_possible): Change
	prototype.
	(convert_member_func_to_ptr): New function.
	* typeck.c (check_for_casting_away_constness): Add diag_fn
	parameter.
	(build_static_cast_1): New function, split out from ...
	(build_static_cast): ... here.  Use build_static_cast_1.
	(build_reinterpret_cast_1): New function, split out from ...
	(build_reinterpret_cast): ... here.  Use build_reinterpret_cast_1.
	(build_const_cast_1): New function, split out from ...
	(build_const_cast): ... here.  Use build_const_cast_1.
	(build_c_cast): Rewrite to use build_const_cast_1,
	build_static_cast_1, and build_reinterpret_cast_1.
	(convert_member_func_to_ptr): New function.

	* g++.dg/conversion/reinterpret1.C: Adjust error markers.
	* g++.dg/conversion/const2.C: New test.
	* g++.dg/expr/reinterpret2.C: New test.
	* g++.dg/expr/reinterpret3.C: New test.
	* g++.dg/expr/cast2.C: New test.
	* g++.dg/expr/copy1.C: New test.
	* g++.dg/other/conversion1.C: Change error message.
	* g++.dg/parse/comma1.C: Use __extension__ to allow casts from
	function pointers to void *.
	* g++.old-deja/g++.mike/p10148.C: Likewise.

From-SVN: r89300
This commit is contained in:
Mark Mitchell 2004-10-19 23:24:20 +00:00 committed by Mark Mitchell
parent b55d574602
commit 33c25e5c5a
14 changed files with 589 additions and 304 deletions

View File

@ -1,3 +1,30 @@
2004-10-19 Mark Mitchell <mark@codesourcery.com>
PR c++/14035
* call.c (struct conversion): Add base_p.
(convert_like): Add c_cast_p argument.
(convert_like_with_conversion): Likewise.
(build_conv): Clear base_p.
(standard_conversion): Set it, for derived-to-base conversions.
(convert_like_real): Add c_cast_p parameter. Handle pointer
conversions directly rather than relying on ocp_convert.
(perform_direct_initialization_if_possible): Add c_cast_p
parameter.
* cp-tree.h (perform_direct_initialization_if_possible): Change
prototype.
(convert_member_func_to_ptr): New function.
* typeck.c (check_for_casting_away_constness): Add diag_fn
parameter.
(build_static_cast_1): New function, split out from ...
(build_static_cast): ... here. Use build_static_cast_1.
(build_reinterpret_cast_1): New function, split out from ...
(build_reinterpret_cast): ... here. Use build_reinterpret_cast_1.
(build_const_cast_1): New function, split out from ...
(build_const_cast): ... here. Use build_const_cast_1.
(build_c_cast): Rewrite to use build_const_cast_1,
build_static_cast_1, and build_reinterpret_cast_1.
(convert_member_func_to_ptr): New function.
2004-10-19 Paolo Bonzini <bonzini@gnu.org>
PR c++/18047

View File

@ -92,6 +92,9 @@ struct conversion {
copy constructor must be accessible, even though it is not being
used. */
BOOL_BITFIELD check_copy_constructor_p : 1;
/* If KIND is ck_ptr, true to indicate that a conversion from a
pointer-to-derived to pointer-to-base is being performed. */
BOOL_BITFIELD base_p : 1;
/* The type of the expression resulting from the conversion. */
tree type;
union {
@ -125,12 +128,15 @@ static int compare_ics (conversion *, conversion *);
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, \
/*issue_conversion_warnings=*/true)
convert_like_real ((CONV), (EXPR), NULL_TREE, 0, 0, \
/*issue_conversion_warnings=*/true, \
/*c_cast_p=*/false)
#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 (conversion *, tree, tree, int, int, bool);
convert_like_real ((CONV), (EXPR), (FN), (ARGNO), 0, \
/*issue_conversion_warnings=*/true, \
/*c_cast_p=*/false)
static tree convert_like_real (conversion *, tree, tree, int, int, bool,
bool);
static void op_error (enum tree_code, enum tree_code, tree, tree,
tree, const char *);
static tree build_object_call (tree, tree);
@ -528,6 +534,7 @@ build_conv (conversion_kind code, tree type, conversion *from)
t->rank = rank;
t->user_conv_p = (code == ck_user || from->user_conv_p);
t->bad_p = from->bad_p;
t->base_p = false;
return t;
}
@ -721,6 +728,7 @@ standard_conversion (tree to, tree from, tree expr)
cp_type_quals (TREE_TYPE (from)));
from = build_pointer_type (from);
conv = build_conv (ck_ptr, from, conv);
conv->base_p = true;
}
if (tcode == POINTER_TYPE)
@ -4113,11 +4121,14 @@ build_temp (tree expr, tree type, int flags,
being called to continue a conversion chain. It is negative when a
reference binding will be applied, positive otherwise. If
ISSUE_CONVERSION_WARNINGS is true, warnings about suspicious
conversions will be emitted if appropriate. */
conversions will be emitted if appropriate. If C_CAST_P is true,
this conversion is coming from a C-style cast; in that case,
conversions to inaccessible bases are permitted. */
static tree
convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
int inner, bool issue_conversion_warnings)
int inner, bool issue_conversion_warnings,
bool c_cast_p)
{
tree totype = convs->type;
void (*diagnostic_fn)(const char *, ...);
@ -4133,12 +4144,14 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
if (t->kind == ck_user || !t->bad_p)
{
expr = convert_like_real (t, expr, fn, argnum, 1,
/*issue_conversion_warnings=*/false);
/*issue_conversion_warnings=*/false,
/*c_cast_p=*/false);
break;
}
else if (t->kind == ck_ambig)
return convert_like_real (t, expr, fn, argnum, 1,
/*issue_conversion_warnings=*/false);
/*issue_conversion_warnings=*/false,
/*c_cast_p=*/false);
else if (t->kind == ck_identity)
break;
}
@ -4237,7 +4250,8 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
expr = convert_like_real (convs->u.next, expr, fn, argnum,
convs->kind == ck_ref_bind ? -1 : 1,
/*issue_conversion_warnings=*/false);
/*issue_conversion_warnings=*/false,
c_cast_p);
if (expr == error_mark_node)
return error_mark_node;
@ -4321,7 +4335,22 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
/* Warn about deprecated conversion if appropriate. */
string_conv_p (totype, expr, 1);
break;
case ck_ptr:
if (convs->base_p)
{
tree binfo;
binfo = lookup_base (TREE_TYPE (TREE_TYPE (expr)),
TREE_TYPE (totype),
c_cast_p ? ba_unique : ba_check,
NULL);
if (binfo == error_mark_node)
return error_mark_node;
expr = build_base_path (PLUS_EXPR, expr, binfo, /*nonnull=*/0);
}
return build_nop (totype, expr);
default:
break;
}
@ -6273,10 +6302,15 @@ perform_implicit_conversion (tree type, tree 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, except in the case
that TYPE is a class type; in that case, an error is issued. */
that TYPE is a class type; in that case, an error is issued. If
C_CAST_P is ttrue, then this direction initialization is taking
place as part of a static_cast being attempted as part of a C-style
cast. */
tree
perform_direct_initialization_if_possible (tree type, tree expr)
perform_direct_initialization_if_possible (tree type,
tree expr,
bool c_cast_p)
{
conversion *conv;
void *p;
@ -6308,7 +6342,8 @@ perform_direct_initialization_if_possible (tree type, tree expr)
expr = NULL_TREE;
else
expr = convert_like_real (conv, expr, NULL_TREE, 0, 0,
/*issue_conversion_warnings=*/false);
/*issue_conversion_warnings=*/false,
c_cast_p);
/* Free all the conversions we allocated. */
obstack_free (&conversion_obstack, p);
@ -6449,7 +6484,8 @@ initialize_reference (tree type, tree expr, tree decl, tree *cleanup)
expr = convert_like_real (conv, expr,
/*fn=*/NULL_TREE, /*argnum=*/0,
/*inner=*/-1,
/*issue_conversion_warnings=*/true);
/*issue_conversion_warnings=*/true,
/*c_cast_p=*/false);
if (!real_lvalue_p (expr))
{
tree init;

View File

@ -3585,7 +3585,7 @@ extern tree initialize_reference (tree, 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 perform_direct_initialization_if_possible (tree, tree, bool);
extern tree in_charge_arg_for_name (tree);
extern tree build_cxx_call (tree, tree);
#ifdef ENABLE_CHECKING
@ -4290,6 +4290,7 @@ extern tree build_nop (tree, tree);
extern tree non_reference (tree);
extern tree lookup_anon_field (tree, tree);
extern bool invalid_nonstatic_memfn_p (tree);
extern tree convert_member_func_to_ptr (tree, tree);
/* in typeck2.c */
extern void require_complete_eh_spec_types (tree, tree);

View File

@ -107,28 +107,9 @@ cp_convert_to_pointer (tree type, tree expr, bool force)
&& (TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE
|| VOID_TYPE_P (TREE_TYPE (type))))
{
/* Allow an implicit this pointer for pointer to member
functions. */
if (TYPE_PTRMEMFUNC_P (intype))
{
if (pedantic || warn_pmf2ptr)
pedwarn ("converting from `%T' to `%T'", intype, type);
if (TREE_CODE (expr) == PTRMEM_CST)
expr = build_address (PTRMEM_CST_MEMBER (expr));
else
{
tree decl = maybe_dummy_object (TYPE_PTRMEM_CLASS_TYPE (intype),
0);
decl = build_address (decl);
expr = get_member_function_from_ptrfunc (&decl, expr);
}
}
else if (TREE_CODE (TREE_TYPE (expr)) == METHOD_TYPE)
{
if (pedantic || warn_pmf2ptr)
pedwarn ("converting from `%T' to `%T'", intype, type);
expr = build_addr_func (expr);
}
if (TYPE_PTRMEMFUNC_P (intype)
|| TREE_CODE (intype) == METHOD_TYPE)
return convert_member_func_to_ptr (type, expr);
if (TREE_CODE (TREE_TYPE (expr)) == POINTER_TYPE)
return build_nop (type, expr);
intype = TREE_TYPE (expr);

View File

@ -4468,47 +4468,58 @@ build_compound_expr (tree lhs, tree rhs)
return build2 (COMPOUND_EXPR, TREE_TYPE (rhs), lhs, rhs);
}
/* Issue an error message if casting from SRC_TYPE to DEST_TYPE casts
away constness. DESCRIPTION explains what operation is taking
place. */
/* Issue a diagnostic message if casting from SRC_TYPE to DEST_TYPE
casts away constness. DIAG_FN gives the function to call if we
need to issue a diagnostic; if it is NULL, no diagnostic will be
issued. DESCRIPTION explains what operation is taking place. */
static void
check_for_casting_away_constness (tree src_type, tree dest_type,
void (*diag_fn)(const char *, ...),
const char *description)
{
if (casts_away_constness (src_type, dest_type))
if (diag_fn && casts_away_constness (src_type, dest_type))
error ("%s from type %qT to type %qT casts away constness",
description, src_type, dest_type);
}
/* Return an expression representing static_cast<TYPE>(EXPR). */
/* Perform a static_cast from EXPR to TYPE. When C_CAST_P is true,
this static_cast is being attempted as one of the possible casts
allowed by a C-style cast. (In that case, accessibility of base
classes is not considered, and it is OK to cast away
constness.) Return the result of the cast. *VALID_P is set to
indicate whether or not the cast was valid. */
tree
build_static_cast (tree type, tree expr)
static tree
build_static_cast_1 (tree type, tree expr, bool c_cast_p,
bool *valid_p)
{
tree intype;
tree result;
tree orig;
void (*diag_fn)(const char*, ...);
const char *desc;
if (type == error_mark_node || expr == error_mark_node)
return error_mark_node;
if (processing_template_decl)
{
expr = build_min (STATIC_CAST_EXPR, type, expr);
/* We don't know if it will or will not have side effects. */
TREE_SIDE_EFFECTS (expr) = 1;
return expr;
}
/* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.
Strip such NOP_EXPRs if VALUE is being used in non-lvalue context. */
if (TREE_CODE (type) != REFERENCE_TYPE
&& TREE_CODE (expr) == NOP_EXPR
&& TREE_TYPE (expr) == TREE_TYPE (TREE_OPERAND (expr, 0)))
expr = TREE_OPERAND (expr, 0);
/* Assume the cast is valid. */
*valid_p = true;
intype = TREE_TYPE (expr);
/* Determine what to do when casting away constness. */
if (c_cast_p)
{
/* C-style casts are allowed to cast away constness. With
WARN_CAST_QUAL, we still want to issue a warning. */
diag_fn = warn_cast_qual ? warning : NULL;
desc = "cast";
}
else
{
/* A static_cast may not cast away constness. */
diag_fn = error;
desc = "static_cast";
}
/* [expr.static.cast]
An lvalue of type "cv1 B", where B is a class type, can be cast
@ -4536,12 +4547,20 @@ build_static_cast (tree type, tree expr)
&& 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))
&& (c_cast_p
|| at_least_as_qualified_p (TREE_TYPE (type), intype)))
{
tree base;
/* There is a standard conversion from "D*" to "B*" even if "B"
is ambiguous or inaccessible. Therefore, we ask lookup_base
to check these conditions. */
tree base = lookup_base (TREE_TYPE (type), intype, ba_check, NULL);
is ambiguous or inaccessible. If this is really a
static_cast, then we check both for inaccessibility and
ambiguity. However, if this is a static_cast being performed
because the user wrote a C-style cast, then accessibility is
not considered. */
base = lookup_base (TREE_TYPE (type), intype,
c_cast_p ? ba_unique : ba_check,
NULL);
/* Convert from "B*" to "D*". This function will check that "B"
is not a virtual base of "D". */
@ -4552,16 +4571,28 @@ build_static_cast (tree type, tree expr)
return convert_from_reference (build_nop (type, expr));
}
orig = expr;
/* [expr.static.cast]
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);
result = perform_direct_initialization_if_possible (type, expr,
c_cast_p);
if (result)
{
result = convert_from_reference (result);
/* Ignore any integer overflow caused by the cast. */
if (TREE_CODE (result) == INTEGER_CST
&& CONSTANT_CLASS_P (orig))
{
TREE_OVERFLOW (result) = TREE_OVERFLOW (orig);
TREE_CONSTANT_OVERFLOW (result)
= TREE_CONSTANT_OVERFLOW (orig);
}
/* [expr.static.cast]
If T is a reference type, the result is an lvalue; otherwise,
@ -4598,10 +4629,20 @@ build_static_cast (tree type, tree expr)
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);
{
expr = decl_constant_value (expr);
expr = ocp_convert (type, expr, CONV_C_CAST, LOOKUP_NORMAL);
/* Ignore any integer overflow caused by the cast. */
if (TREE_CODE (expr) == INTEGER_CST
&& CONSTANT_CLASS_P (orig))
{
TREE_OVERFLOW (expr) = TREE_OVERFLOW (orig);
TREE_CONSTANT_OVERFLOW (expr) = TREE_CONSTANT_OVERFLOW (orig);
}
return expr;
}
if (TYPE_PTR_P (type) && TYPE_PTR_P (intype)
&& CLASS_TYPE_P (TREE_TYPE (type))
&& CLASS_TYPE_P (TREE_TYPE (intype))
@ -4612,8 +4653,10 @@ build_static_cast (tree type, tree expr)
{
tree base;
check_for_casting_away_constness (intype, type, "static_cast");
base = lookup_base (TREE_TYPE (type), TREE_TYPE (intype), ba_check,
if (!c_cast_p)
check_for_casting_away_constness (intype, type, diag_fn, desc);
base = lookup_base (TREE_TYPE (type), TREE_TYPE (intype),
c_cast_p ? ba_unique : ba_check,
NULL);
return build_base_path (MINUS_EXPR, expr, base, /*nonnull=*/false);
}
@ -4645,7 +4688,8 @@ build_static_cast (tree type, tree expr)
}
if (can_convert (t1, t2))
{
check_for_casting_away_constness (intype, type, "static_cast");
if (!c_cast_p)
check_for_casting_away_constness (intype, type, diag_fn, desc);
if (TYPE_PTRMEM_P (type))
{
tree delta;
@ -4675,19 +4719,227 @@ build_static_cast (tree type, tree expr)
&& VOID_TYPE_P (TREE_TYPE (intype))
&& TYPE_PTROB_P (type))
{
check_for_casting_away_constness (intype, type, "static_cast");
if (!c_cast_p)
check_for_casting_away_constness (intype, type, diag_fn, desc);
return build_nop (type, expr);
}
error ("invalid static_cast from type %qT to type %qT", intype, type);
*valid_p = false;
return error_mark_node;
}
/* Return an expression representing static_cast<TYPE>(EXPR). */
tree
build_static_cast (tree type, tree expr)
{
tree result;
bool valid_p;
if (type == error_mark_node || expr == error_mark_node)
return error_mark_node;
if (processing_template_decl)
{
expr = build_min (STATIC_CAST_EXPR, type, expr);
/* We don't know if it will or will not have side effects. */
TREE_SIDE_EFFECTS (expr) = 1;
return expr;
}
/* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.
Strip such NOP_EXPRs if VALUE is being used in non-lvalue context. */
if (TREE_CODE (type) != REFERENCE_TYPE
&& TREE_CODE (expr) == NOP_EXPR
&& TREE_TYPE (expr) == TREE_TYPE (TREE_OPERAND (expr, 0)))
expr = TREE_OPERAND (expr, 0);
result = build_static_cast_1 (type, expr, /*c_cast_p=*/false, &valid_p);
if (valid_p)
return result;
error ("invalid static_cast from type %qT to type %qT",
TREE_TYPE (expr), type);
return error_mark_node;
}
/* EXPR is an expression with member function or pointer-to-member
function type. TYPE is a pointer type. Converting EXPR to TYPE is
not permitted by ISO C++, but we accept it in some modes. If we
are not in one of those modes, issue a diagnostic. Return the
converted expression. */
tree
convert_member_func_to_ptr (tree type, tree expr)
{
tree intype;
tree decl;
intype = TREE_TYPE (expr);
gcc_assert (TYPE_PTRMEMFUNC_P (intype)
|| TREE_CODE (intype) == METHOD_TYPE);
if (pedantic || warn_pmf2ptr)
pedwarn ("converting from `%T' to `%T'", intype, type);
if (TREE_CODE (intype) == METHOD_TYPE)
expr = build_addr_func (expr);
else if (TREE_CODE (expr) == PTRMEM_CST)
expr = build_address (PTRMEM_CST_MEMBER (expr));
else
{
decl = maybe_dummy_object (TYPE_PTRMEM_CLASS_TYPE (intype), 0);
decl = build_address (decl);
expr = get_member_function_from_ptrfunc (&decl, expr);
}
return build_nop (type, expr);
}
/* Return a representation for a reinterpret_cast from EXPR to TYPE.
If C_CAST_P is true, this reinterpret cast is being done as part of
a C-style cast. If VALID_P is non-NULL, *VALID_P is set to
indicate whether or not reinterpret_cast was valid. */
static tree
build_reinterpret_cast_1 (tree type, tree expr, bool c_cast_p,
bool *valid_p)
{
tree intype;
/* Assume the cast is invalid. */
if (valid_p)
*valid_p = true;
if (type == error_mark_node || error_operand_p (expr))
return error_mark_node;
intype = TREE_TYPE (expr);
/* [expr.reinterpret.cast]
An lvalue expression of type T1 can be cast to the type
"reference to T2" if an expression of type "pointer to T1" can be
explicitly converted to the type "pointer to T2" using a
reinterpret_cast. */
if (TREE_CODE (type) == REFERENCE_TYPE)
{
if (! real_lvalue_p (expr))
{
error ("invalid cast of an rvalue expression of type "
"%qT to type %qT",
intype, type);
return error_mark_node;
}
/* Warn about a reinterpret_cast from "A*" to "B&" if "A" and
"B" are related class types; the reinterpret_cast does not
adjust the pointer. */
if (TYPE_PTR_P (intype)
&& (comptypes (TREE_TYPE (intype), TREE_TYPE (type),
COMPARE_BASE | COMPARE_DERIVED)))
warning ("casting `%T' to `%T' does not dereference pointer",
intype, type);
expr = build_unary_op (ADDR_EXPR, expr, 0);
if (expr != error_mark_node)
expr = build_reinterpret_cast_1
(build_pointer_type (TREE_TYPE (type)), expr, c_cast_p,
valid_p);
if (expr != error_mark_node)
expr = build_indirect_ref (expr, 0);
return expr;
}
/* As a G++ extension, we consider conversions from member
functions, and pointers to member functions to
pointer-to-function and pointer-to-void types. If
-Wno-pmf-conversions has not been specified,
convert_member_func_to_ptr will issue an error message. */
if ((TYPE_PTRMEMFUNC_P (intype)
|| TREE_CODE (intype) == METHOD_TYPE)
&& TYPE_PTR_P (type)
&& (TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE
|| VOID_TYPE_P (TREE_TYPE (type))))
return convert_member_func_to_ptr (type, expr);
/* If the cast is not to a reference type, the lvalue-to-rvale,
array-to-pointer, and function-to-pointer conversions are
performed. */
expr = decay_conversion (expr);
/* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.
Strip such NOP_EXPRs if VALUE is being used in non-lvalue context. */
if (TREE_CODE (expr) == NOP_EXPR
&& TREE_TYPE (expr) == TREE_TYPE (TREE_OPERAND (expr, 0)))
expr = TREE_OPERAND (expr, 0);
if (error_operand_p (expr))
return error_mark_node;
intype = TREE_TYPE (expr);
/* [expr.reinterpret.cast]
A pointer can be converted to any integral type large enough to
hold it. */
if (CP_INTEGRAL_TYPE_P (type) && TYPE_PTR_P (intype))
{
if (TYPE_PRECISION (type) < TYPE_PRECISION (intype))
pedwarn ("cast from %qT to %qT loses precision",
intype, type);
}
/* [expr.reinterpret.cast]
A value of integral or enumeration type can be explicitly
converted to a pointer. */
else if (TYPE_PTR_P (type) && INTEGRAL_OR_ENUMERATION_TYPE_P (intype))
/* OK */
;
else if ((TYPE_PTRFN_P (type) && TYPE_PTRFN_P (intype))
|| (TYPE_PTRMEMFUNC_P (type) && TYPE_PTRMEMFUNC_P (intype)))
{
expr = decl_constant_value (expr);
return fold_if_not_in_template (build_nop (type, expr));
}
else if ((TYPE_PTRMEM_P (type) && TYPE_PTRMEM_P (intype))
|| (TYPE_PTROBV_P (type) && TYPE_PTROBV_P (intype)))
{
if (!c_cast_p)
check_for_casting_away_constness (intype, type, error,
"reinterpret_cast");
/* Warn about possible alignment problems. */
if (STRICT_ALIGNMENT && warn_cast_align
&& !VOID_TYPE_P (type)
&& TREE_CODE (TREE_TYPE (intype)) != FUNCTION_TYPE
&& COMPLETE_TYPE_P (TREE_TYPE (type))
&& COMPLETE_TYPE_P (TREE_TYPE (intype))
&& TYPE_ALIGN (TREE_TYPE (type)) > TYPE_ALIGN (TREE_TYPE (intype)))
warning ("cast from %qT to %qT increases required alignment of "
"target type",
intype, type);
expr = decl_constant_value (expr);
return fold_if_not_in_template (build_nop (type, expr));
}
else if ((TYPE_PTRFN_P (type) && TYPE_PTROBV_P (intype))
|| (TYPE_PTRFN_P (intype) && TYPE_PTROBV_P (type)))
{
if (pedantic || !c_cast_p)
pedwarn ("ISO C++ forbids casting between pointer-to-function and pointer-to-object");
expr = decl_constant_value (expr);
return fold_if_not_in_template (build_nop (type, expr));
}
else
{
if (valid_p)
*valid_p = false;
error ("invalid cast from type %qT to type %qT", intype, type);
return error_mark_node;
}
return cp_convert (type, expr);
}
tree
build_reinterpret_cast (tree type, tree expr)
{
tree intype;
if (type == error_mark_node || expr == error_mark_node)
return error_mark_node;
@ -4702,85 +4954,120 @@ build_reinterpret_cast (tree type, tree expr)
return t;
}
if (TREE_CODE (type) != REFERENCE_TYPE)
{
expr = decay_conversion (expr);
return build_reinterpret_cast_1 (type, expr, /*c_cast_p=*/false,
/*valid_p=*/NULL);
}
/* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.
Strip such NOP_EXPRs if VALUE is being used in non-lvalue context. */
if (TREE_CODE (expr) == NOP_EXPR
&& TREE_TYPE (expr) == TREE_TYPE (TREE_OPERAND (expr, 0)))
expr = TREE_OPERAND (expr, 0);
/* Perform a const_cast from EXPR to TYPE. If the cast is valid,
return an appropriate expression. Otherwise, return
error_mark_node. If the cast is not valid, and COMPLAIN is true,
then a diagnostic will be issued. If VALID_P is non-NULL, its
value upon return will indicate whether or not the conversion
succeeded. */
static tree
build_const_cast_1 (tree dst_type, tree expr, bool complain,
bool *valid_p)
{
tree src_type;
tree reference_type;
/* Callers are responsible for handling error_mark_node as a
destination type. */
gcc_assert (dst_type != error_mark_node);
/* In a template, callers should be building syntactic
representations of casts, not using this machinery. */
gcc_assert (!processing_template_decl);
/* Assume the conversion is invalid. */
if (valid_p)
*valid_p = false;
if (!POINTER_TYPE_P (dst_type) && !TYPE_PTRMEM_P (dst_type))
{
if (complain)
error ("invalid use of const_cast with type %qT, "
"which is not a pointer, "
"reference, nor a pointer-to-data-member type", dst_type);
return error_mark_node;
}
intype = TREE_TYPE (expr);
if (intype == error_mark_node)
return error_mark_node;
if (TREE_CODE (type) == REFERENCE_TYPE)
if (TREE_CODE (TREE_TYPE (dst_type)) == FUNCTION_TYPE)
{
if (complain)
error ("invalid use of const_cast with type %qT, which is a pointer "
"or reference to a function type", dst_type);
return error_mark_node;
}
src_type = TREE_TYPE (expr);
/* Expressions do not really have reference types. */
if (TREE_CODE (src_type) == REFERENCE_TYPE)
src_type = TREE_TYPE (src_type);
/* [expr.const.cast]
An lvalue of type T1 can be explicitly converted to an lvalue of
type T2 using the cast const_cast<T2&> (where T1 and T2 are object
types) if a pointer to T1 can be explicitly converted to the type
pointer to T2 using a const_cast. */
if (TREE_CODE (dst_type) == REFERENCE_TYPE)
{
reference_type = dst_type;
if (! real_lvalue_p (expr))
{
error ("invalid reinterpret_cast of an rvalue expression of type "
"%qT to type %qT", intype, type);
if (complain)
error ("invalid const_cast of an rvalue of type %qT to type %qT",
src_type, dst_type);
return error_mark_node;
}
expr = build_unary_op (ADDR_EXPR, expr, 0);
if (expr != error_mark_node)
expr = build_reinterpret_cast
(build_pointer_type (TREE_TYPE (type)), expr);
if (expr != error_mark_node)
expr = build_indirect_ref (expr, 0);
return expr;
}
else if (same_type_ignoring_top_level_qualifiers_p (intype, type))
return build_static_cast (type, expr);
if (TYPE_PTR_P (type) && (TREE_CODE (intype) == INTEGER_TYPE
|| TREE_CODE (intype) == ENUMERAL_TYPE))
/* OK */;
else if (TREE_CODE (type) == INTEGER_TYPE && TYPE_PTR_P (intype))
{
if (TYPE_PRECISION (type) < TYPE_PRECISION (intype))
pedwarn ("reinterpret_cast from %qT to %qT loses precision",
intype, type);
}
else if ((TYPE_PTRFN_P (type) && TYPE_PTRFN_P (intype))
|| (TYPE_PTRMEMFUNC_P (type) && TYPE_PTRMEMFUNC_P (intype)))
{
expr = decl_constant_value (expr);
return fold_if_not_in_template (build_nop (type, expr));
}
else if ((TYPE_PTRMEM_P (type) && TYPE_PTRMEM_P (intype))
|| (TYPE_PTROBV_P (type) && TYPE_PTROBV_P (intype)))
{
check_for_casting_away_constness (intype, type, "reinterpret_cast");
expr = decl_constant_value (expr);
return fold_if_not_in_template (build_nop (type, expr));
}
else if ((TYPE_PTRFN_P (type) && TYPE_PTROBV_P (intype))
|| (TYPE_PTRFN_P (intype) && TYPE_PTROBV_P (type)))
{
pedwarn ("ISO C++ forbids casting between pointer-to-function and pointer-to-object");
expr = decl_constant_value (expr);
return fold_if_not_in_template (build_nop (type, expr));
dst_type = build_pointer_type (TREE_TYPE (dst_type));
src_type = build_pointer_type (src_type);
}
else
{
error ("invalid reinterpret_cast from type %qT to type %qT",
intype, type);
return error_mark_node;
reference_type = NULL_TREE;
/* If the destination type is not a reference type, the
lvalue-to-rvalue, array-to-pointer, and function-to-pointer
conversions are performed. */
src_type = type_decays_to (src_type);
if (src_type == error_mark_node)
return error_mark_node;
}
return cp_convert (type, expr);
if ((TYPE_PTR_P (src_type) || TYPE_PTRMEM_P (src_type))
&& comp_ptr_ttypes_const (dst_type, src_type))
{
if (valid_p)
*valid_p = true;
if (reference_type)
{
expr = build_unary_op (ADDR_EXPR, expr, 0);
expr = build_nop (reference_type, expr);
return convert_from_reference (expr);
}
else
{
expr = decay_conversion (expr);
/* build_c_cast puts on a NOP_EXPR to make the result not an
lvalue. Strip such NOP_EXPRs if VALUE is being used in
non-lvalue context. */
if (TREE_CODE (expr) == NOP_EXPR
&& TREE_TYPE (expr) == TREE_TYPE (TREE_OPERAND (expr, 0)))
expr = TREE_OPERAND (expr, 0);
return build_nop (dst_type, expr);
}
}
if (complain)
error ("invalid const_cast from type %qT to type %qT",
src_type, dst_type);
return error_mark_node;
}
tree
build_const_cast (tree type, tree expr)
{
tree intype;
if (type == error_mark_node || expr == error_mark_node)
return error_mark_node;
@ -4795,69 +5082,21 @@ build_const_cast (tree type, tree expr)
return t;
}
if (!POINTER_TYPE_P (type) && !TYPE_PTRMEM_P (type))
error ("invalid use of const_cast with type %qT, which is not a pointer, "
"reference, nor a pointer-to-data-member type", type);
else if (TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE)
{
error ("invalid use of const_cast with type %qT, which is a pointer "
"or reference to a function type", type);
return error_mark_node;
}
if (TREE_CODE (type) != REFERENCE_TYPE)
{
expr = decay_conversion (expr);
/* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.
Strip such NOP_EXPRs if VALUE is being used in non-lvalue context. */
if (TREE_CODE (expr) == NOP_EXPR
&& TREE_TYPE (expr) == TREE_TYPE (TREE_OPERAND (expr, 0)))
expr = TREE_OPERAND (expr, 0);
}
intype = TREE_TYPE (expr);
if (same_type_ignoring_top_level_qualifiers_p (intype, type))
return build_static_cast (type, expr);
else if (TREE_CODE (type) == REFERENCE_TYPE)
{
if (! real_lvalue_p (expr))
{
error ("invalid const_cast of an rvalue of type %qT to type %qT",
intype, type);
return error_mark_node;
}
if (comp_ptr_ttypes_const (TREE_TYPE (type), intype))
{
expr = build_unary_op (ADDR_EXPR, expr, 0);
expr = build1 (NOP_EXPR, type, expr);
return convert_from_reference (expr);
}
}
else if (((TREE_CODE (type) == POINTER_TYPE
&& TREE_CODE (intype) == POINTER_TYPE)
|| (TYPE_PTRMEM_P (type) && TYPE_PTRMEM_P (intype)))
&& comp_ptr_ttypes_const (TREE_TYPE (type), TREE_TYPE (intype)))
return cp_convert (type, expr);
error ("invalid const_cast from type %qT to type %qT", intype, type);
return error_mark_node;
return build_const_cast_1 (type, expr, /*complain=*/true,
/*valid_p=*/NULL);
}
/* Build an expression representing a cast to type TYPE of expression EXPR.
ALLOW_NONCONVERTING is true if we should allow non-converting constructors
when doing the cast. */
/* Build an expression representing an explicit C-style cast to type
TYPE of expression EXPR. */
tree
build_c_cast (tree type, tree expr)
{
tree value = expr;
tree otype;
tree result;
bool valid_p;
if (type == error_mark_node || expr == error_mark_node)
if (type == error_mark_node || error_operand_p (expr))
return error_mark_node;
if (processing_template_decl)
@ -4906,118 +5145,48 @@ build_c_cast (tree type, tree expr)
return error_mark_node;
}
if (TREE_CODE (type) == VOID_TYPE)
/* A C-style cast can be a const_cast. */
result = build_const_cast_1 (type, value, /*complain=*/false,
&valid_p);
if (valid_p)
return result;
/* Or a static cast. */
result = build_static_cast_1 (type, value, /*c_cast_p=*/true,
&valid_p);
/* Or a reinterpret_cast. */
if (!valid_p)
result = build_reinterpret_cast_1 (type, value, /*c_cast_p=*/true,
&valid_p);
/* The static_cast or reinterpret_cast may be followed by a
const_cast. */
if (valid_p
/* A valid cast may result in errors if, for example, a
conversion to am ambiguous base class is required. */
&& !error_operand_p (result))
{
/* Conversion to void does not cause any of the normal function to
* pointer, array to pointer and lvalue to rvalue decays. */
value = convert_to_void (value, /*implicit=*/NULL);
return value;
}
tree result_type;
if (!complete_type_or_else (type, NULL_TREE))
return error_mark_node;
/* Convert functions and arrays to pointers and
convert references to their expanded types,
but don't convert any other types. If, however, we are
casting to a class type, there's no reason to do this: the
cast will only succeed if there is a converting constructor,
and the default conversions will be done at that point. In
fact, doing the default conversion here is actually harmful
in cases like this:
typedef int A[2];
struct S { S(const A&); };
since we don't want the array-to-pointer conversion done. */
if (!IS_AGGR_TYPE (type))
{
if (TREE_CODE (TREE_TYPE (value)) == FUNCTION_TYPE
|| (TREE_CODE (TREE_TYPE (value)) == METHOD_TYPE
/* Don't do the default conversion on a ->* expression. */
&& ! (TREE_CODE (type) == POINTER_TYPE
&& bound_pmf_p (value)))
|| TREE_CODE (TREE_TYPE (value)) == ARRAY_TYPE
|| TREE_CODE (TREE_TYPE (value)) == REFERENCE_TYPE)
value = decay_conversion (value);
}
else if (TREE_CODE (TREE_TYPE (value)) == REFERENCE_TYPE)
/* However, even for class types, we still need to strip away
the reference type, since the call to convert_force below
does not expect the input expression to be of reference
type. */
value = convert_from_reference (value);
otype = TREE_TYPE (value);
/* Optionally warn about potentially worrisome casts. */
if (warn_cast_qual
&& TREE_CODE (type) == POINTER_TYPE
&& TREE_CODE (otype) == POINTER_TYPE
&& !at_least_as_qualified_p (TREE_TYPE (type),
TREE_TYPE (otype)))
warning ("cast from %qT to %qT discards qualifiers from pointer "
"target type",
otype, type);
if (TREE_CODE (type) == INTEGER_TYPE
&& TYPE_PTR_P (otype)
&& TYPE_PRECISION (type) != TYPE_PRECISION (otype))
warning ("cast from pointer to integer of different size");
if (TYPE_PTR_P (type)
&& TREE_CODE (otype) == INTEGER_TYPE
&& TYPE_PRECISION (type) != TYPE_PRECISION (otype)
/* Don't warn about converting any constant. */
&& !TREE_CONSTANT (value))
warning ("cast to pointer from integer of different size");
if (TREE_CODE (type) == REFERENCE_TYPE)
value = (convert_from_reference
(convert_to_reference (type, value, CONV_C_CAST,
LOOKUP_COMPLAIN, NULL_TREE)));
else
{
tree ovalue;
value = decl_constant_value (value);
ovalue = value;
value = convert_force (type, value, CONV_C_CAST);
/* Ignore any integer overflow caused by the cast. */
if (TREE_CODE (value) == INTEGER_CST)
/* Non-class rvalues always have cv-unqualified type. */
if (!CLASS_TYPE_P (type))
type = TYPE_MAIN_VARIANT (type);
result_type = TREE_TYPE (result);
if (!CLASS_TYPE_P (result_type))
result_type = TYPE_MAIN_VARIANT (result_type);
/* If the type of RESULT does not match TYPE, perform a
const_cast to make it match. If the static_cast or
reinterpret_cast succeeded, we will differ by at most
cv-qualification, so the follow-on const_cast is guaranteed
to succeed. */
if (!same_type_p (non_reference (type), non_reference (result_type)))
{
TREE_OVERFLOW (value) = TREE_OVERFLOW (ovalue);
if (CONSTANT_CLASS_P (ovalue))
TREE_CONSTANT_OVERFLOW (value) = TREE_CONSTANT_OVERFLOW (ovalue);
result = build_const_cast_1 (type, result, false, &valid_p);
gcc_assert (valid_p);
}
return result;
}
/* Warn about possible alignment problems. Do this here when we will have
instantiated any necessary template types. */
if (STRICT_ALIGNMENT && warn_cast_align
&& TREE_CODE (type) == POINTER_TYPE
&& TREE_CODE (otype) == POINTER_TYPE
&& TREE_CODE (TREE_TYPE (otype)) != VOID_TYPE
&& TREE_CODE (TREE_TYPE (otype)) != FUNCTION_TYPE
&& COMPLETE_TYPE_P (TREE_TYPE (otype))
&& COMPLETE_TYPE_P (TREE_TYPE (type))
&& TYPE_ALIGN (TREE_TYPE (type)) > TYPE_ALIGN (TREE_TYPE (otype)))
warning ("cast from %qT to %qT increases required alignment of "
"target type",
otype, type);
/* Always produce some operator for an explicit cast,
so we can tell (for -pedantic) that the cast is no lvalue. */
if (TREE_CODE (type) != REFERENCE_TYPE && value == expr
&& real_lvalue_p (value))
value = non_lvalue (value);
return value;
return error_mark_node;
}
/* Build an assignment expression of lvalue LHS from value RHS.
@ -6260,7 +6429,7 @@ cp_has_mutable_p (tree type)
}
/* Subroutine of casts_away_constness. Make T1 and T2 point at
exemplar types such that casting T1 to T2 is casting away castness
exemplar types such that casting T1 to T2 is casting away constness
if and only if there is no implicit conversion from T1 to T2. */
static void

View File

@ -1,3 +1,17 @@
2004-10-19 Mark Mitchell <mark@codesourcery.com>
PR c++/14035
* g++.dg/conversion/reinterpret1.C: Adjust error markers.
* g++.dg/conversion/const2.C: New test.
* g++.dg/expr/reinterpret2.C: New test.
* g++.dg/expr/reinterpret3.C: New test.
* g++.dg/expr/cast2.C: New test.
* g++.dg/expr/copy1.C: New test.
* g++.dg/other/conversion1.C: Change error message.
* g++.dg/parse/comma1.C: Use __extension__ to allow casts from
function pointers to void *.
* g++.old-deja/g++.mike/p10148.C: Likewise.
2004-10-19 Eric Botcazou <ebotcazou@libertysurf.fr>
* gcc.dg/smod-1.c: Pass -mtune=i486 only on x86.

View File

@ -0,0 +1,11 @@
struct B {};
struct D : public B {};
typedef int B::*bm;
typedef int D::*dm;
bm bp;
void f() {
const_cast<dm>(bp); // { dg-error "" }
}

View File

@ -1,6 +1,6 @@
// PR c++/15076
struct Y { Y(int &); }; // { dg-error "" }
struct Y { Y(int &); };
int v;
Y y1(reinterpret_cast<int>(v)); // { dg-error "" }

View File

@ -0,0 +1,5 @@
bool b;
void f() {
reinterpret_cast<void*>(b);
}

View File

@ -0,0 +1,7 @@
struct S {};
S s;
void f() {
reinterpret_cast<const S>(s); // { dg-error "" }
}

View File

@ -0,0 +1,5 @@
void (*p)();
void f() {
(void *)p; // { dg-error "" }
}

View File

@ -0,0 +1,28 @@
// PR c++/14035
// { dg-do run }
extern "C" void abort();
struct Blob {
int x, y;
Blob() { }
Blob(const Blob &b) { abort (); }
};
struct Blobby : public Blob { };
struct Wooly {
operator const Blobby & ()
{
return myBlobby;
}
Blobby myBlobby;
};
void catcher(const Blob &blo)
{ }
int main()
{
Wooly wooly;
catcher((const Blob &)wooly);
}

View File

@ -13,5 +13,5 @@ int main()
{
long long m;
(void (QObject::*)()) m; // { dg-error "invalid conversion" "" }
(void (QObject::*)()) m; // { dg-error "invalid cast" "" }
}

View File

@ -1,4 +1,5 @@
// PR c++/14278
// { dg-options "" }
struct X {
X (int p);