From 33c25e5c5a69b096d8aafef5599bf93672dd3659 Mon Sep 17 00:00:00 2001 From: Mark Mitchell Date: Tue, 19 Oct 2004 23:24:20 +0000 Subject: [PATCH] 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 --- gcc/cp/ChangeLog | 27 + gcc/cp/call.c | 66 +- gcc/cp/cp-tree.h | 3 +- gcc/cp/cvt.c | 25 +- gcc/cp/typeck.c | 697 +++++++++++------- gcc/testsuite/ChangeLog | 14 + gcc/testsuite/g++.dg/conversion/const2.C | 11 + .../g++.dg/conversion/reinterpret1.C | 2 +- .../g++.dg/conversion/reinterpret2.C | 5 + .../g++.dg/conversion/reinterpret3.C | 7 + gcc/testsuite/g++.dg/expr/cast2.C | 5 + gcc/testsuite/g++.dg/expr/copy1.C | 28 + gcc/testsuite/g++.dg/other/conversion1.C | 2 +- gcc/testsuite/g++.dg/parse/comma1.C | 1 + 14 files changed, 589 insertions(+), 304 deletions(-) create mode 100644 gcc/testsuite/g++.dg/conversion/const2.C create mode 100644 gcc/testsuite/g++.dg/conversion/reinterpret2.C create mode 100644 gcc/testsuite/g++.dg/conversion/reinterpret3.C create mode 100644 gcc/testsuite/g++.dg/expr/cast2.C create mode 100644 gcc/testsuite/g++.dg/expr/copy1.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 325fbf3db0f..0247ca5df93 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,30 @@ +2004-10-19 Mark Mitchell + + 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 PR c++/18047 diff --git a/gcc/cp/call.c b/gcc/cp/call.c index f15fd2ccb4a..c535cbacca5 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -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; diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index b6c976baaa8..6534170392e 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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); diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c index 8e9cb957155..02230301117 100644 --- a/gcc/cp/cvt.c +++ b/gcc/cp/cvt.c @@ -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); diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index cb5a2548d8b..bff5c348e8d 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -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(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(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(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 (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 diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index e53453ea141..7b8fc4840f3 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,17 @@ +2004-10-19 Mark Mitchell + + 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 * gcc.dg/smod-1.c: Pass -mtune=i486 only on x86. diff --git a/gcc/testsuite/g++.dg/conversion/const2.C b/gcc/testsuite/g++.dg/conversion/const2.C new file mode 100644 index 00000000000..fd2be1f4444 --- /dev/null +++ b/gcc/testsuite/g++.dg/conversion/const2.C @@ -0,0 +1,11 @@ +struct B {}; +struct D : public B {}; + +typedef int B::*bm; +typedef int D::*dm; + +bm bp; + +void f() { + const_cast(bp); // { dg-error "" } +} diff --git a/gcc/testsuite/g++.dg/conversion/reinterpret1.C b/gcc/testsuite/g++.dg/conversion/reinterpret1.C index 72ec7501219..d4567822e2a 100644 --- a/gcc/testsuite/g++.dg/conversion/reinterpret1.C +++ b/gcc/testsuite/g++.dg/conversion/reinterpret1.C @@ -1,6 +1,6 @@ // PR c++/15076 -struct Y { Y(int &); }; // { dg-error "" } +struct Y { Y(int &); }; int v; Y y1(reinterpret_cast(v)); // { dg-error "" } diff --git a/gcc/testsuite/g++.dg/conversion/reinterpret2.C b/gcc/testsuite/g++.dg/conversion/reinterpret2.C new file mode 100644 index 00000000000..e9b2328b74d --- /dev/null +++ b/gcc/testsuite/g++.dg/conversion/reinterpret2.C @@ -0,0 +1,5 @@ +bool b; + +void f() { + reinterpret_cast(b); +} diff --git a/gcc/testsuite/g++.dg/conversion/reinterpret3.C b/gcc/testsuite/g++.dg/conversion/reinterpret3.C new file mode 100644 index 00000000000..62fb91444d4 --- /dev/null +++ b/gcc/testsuite/g++.dg/conversion/reinterpret3.C @@ -0,0 +1,7 @@ +struct S {}; + +S s; + +void f() { + reinterpret_cast(s); // { dg-error "" } +} diff --git a/gcc/testsuite/g++.dg/expr/cast2.C b/gcc/testsuite/g++.dg/expr/cast2.C new file mode 100644 index 00000000000..fbc2a2ccca4 --- /dev/null +++ b/gcc/testsuite/g++.dg/expr/cast2.C @@ -0,0 +1,5 @@ +void (*p)(); + +void f() { + (void *)p; // { dg-error "" } +} diff --git a/gcc/testsuite/g++.dg/expr/copy1.C b/gcc/testsuite/g++.dg/expr/copy1.C new file mode 100644 index 00000000000..57036d3f0ae --- /dev/null +++ b/gcc/testsuite/g++.dg/expr/copy1.C @@ -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); +} diff --git a/gcc/testsuite/g++.dg/other/conversion1.C b/gcc/testsuite/g++.dg/other/conversion1.C index 580dfd6eede..6573ef15ba8 100644 --- a/gcc/testsuite/g++.dg/other/conversion1.C +++ b/gcc/testsuite/g++.dg/other/conversion1.C @@ -13,5 +13,5 @@ int main() { long long m; - (void (QObject::*)()) m; // { dg-error "invalid conversion" "" } + (void (QObject::*)()) m; // { dg-error "invalid cast" "" } } diff --git a/gcc/testsuite/g++.dg/parse/comma1.C b/gcc/testsuite/g++.dg/parse/comma1.C index 33d222cbc04..4983535a587 100644 --- a/gcc/testsuite/g++.dg/parse/comma1.C +++ b/gcc/testsuite/g++.dg/parse/comma1.C @@ -1,4 +1,5 @@ // PR c++/14278 +// { dg-options "" } struct X { X (int p);