-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Cleanup check_cast. Fixes #21554 #21606
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -993,86 +993,65 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, | |
} | ||
} | ||
|
||
fn check_cast(fcx: &FnCtxt, | ||
cast_expr: &ast::Expr, | ||
e: &ast::Expr, | ||
t: &ast::Ty) { | ||
let id = cast_expr.id; | ||
let span = cast_expr.span; | ||
|
||
// Find the type of `e`. Supply hints based on the type we are casting to, | ||
// if appropriate. | ||
let t_1 = fcx.to_ty(t); | ||
let t_1 = structurally_resolved_type(fcx, span, t_1); | ||
|
||
check_expr_with_expectation(fcx, e, ExpectCastableToType(t_1)); | ||
|
||
let t_e = fcx.expr_ty(e); | ||
|
||
debug!("t_1={}", fcx.infcx().ty_to_string(t_1)); | ||
debug!("t_e={}", fcx.infcx().ty_to_string(t_e)); | ||
|
||
if ty::type_is_error(t_e) { | ||
fcx.write_error(id); | ||
return | ||
} | ||
|
||
if !fcx.type_is_known_to_be_sized(t_1, cast_expr.span) { | ||
let tstr = fcx.infcx().ty_to_string(t_1); | ||
fcx.type_error_message(span, |actual| { | ||
format!("cast to unsized type: `{}` as `{}`", actual, tstr) | ||
}, t_e, None); | ||
match t_e.sty { | ||
ty::ty_rptr(_, ty::mt { mutbl: mt, .. }) => { | ||
let mtstr = match mt { | ||
ast::MutMutable => "mut ", | ||
ast::MutImmutable => "" | ||
}; | ||
if ty::type_is_trait(t_1) { | ||
span_help!(fcx.tcx().sess, t.span, "did you mean `&{}{}`?", mtstr, tstr); | ||
} else { | ||
span_help!(fcx.tcx().sess, span, | ||
"consider using an implicit coercion to `&{}{}` instead", | ||
mtstr, tstr); | ||
} | ||
} | ||
ty::ty_uniq(..) => { | ||
span_help!(fcx.tcx().sess, t.span, "did you mean `Box<{}>`?", tstr); | ||
} | ||
_ => { | ||
span_help!(fcx.tcx().sess, e.span, | ||
"consider using a box or reference as appropriate"); | ||
fn report_cast_to_unsized_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, | ||
span: Span, | ||
t_span: Span, | ||
e_span: Span, | ||
t_1: Ty<'tcx>, | ||
t_e: Ty<'tcx>, | ||
id: ast::NodeId) { | ||
let tstr = fcx.infcx().ty_to_string(t_1); | ||
fcx.type_error_message(span, |actual| { | ||
format!("cast to unsized type: `{}` as `{}`", actual, tstr) | ||
}, t_e, None); | ||
match t_e.sty { | ||
ty::ty_rptr(_, ty::mt { mutbl: mt, .. }) => { | ||
let mtstr = match mt { | ||
ast::MutMutable => "mut ", | ||
ast::MutImmutable => "" | ||
}; | ||
if ty::type_is_trait(t_1) { | ||
span_help!(fcx.tcx().sess, t_span, "did you mean `&{}{}`?", mtstr, tstr); | ||
} else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems removing this causes the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, wait, sorry, this function is changed in more than just an internal refactoring. |
||
span_help!(fcx.tcx().sess, span, | ||
"consider using an implicit coercion to `&{}{}` instead", | ||
mtstr, tstr); | ||
} | ||
} | ||
fcx.write_error(id); | ||
return | ||
} | ||
|
||
if ty::type_is_trait(t_1) { | ||
// This will be looked up later on. | ||
vtable::check_object_cast(fcx, cast_expr, e, t_1); | ||
fcx.write_ty(id, t_1); | ||
return | ||
ty::ty_uniq(..) => { | ||
span_help!(fcx.tcx().sess, t_span, "did you mean `Box<{}>`?", tstr); | ||
} | ||
_ => { | ||
span_help!(fcx.tcx().sess, e_span, | ||
"consider using a box or reference as appropriate"); | ||
} | ||
} | ||
fcx.write_error(id); | ||
} | ||
|
||
let t_1 = structurally_resolved_type(fcx, span, t_1); | ||
let t_e = structurally_resolved_type(fcx, span, t_e); | ||
|
||
if ty::type_is_nil(t_e) { | ||
fcx.type_error_message(span, |actual| { | ||
format!("cast from nil: `{}` as `{}`", | ||
actual, | ||
fcx.infcx().ty_to_string(t_1)) | ||
}, t_e, None); | ||
} else if ty::type_is_nil(t_1) { | ||
fn check_cast_inner<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, | ||
span: Span, | ||
t_1: Ty<'tcx>, | ||
t_e: Ty<'tcx>, | ||
e: &ast::Expr) { | ||
fn cast_through_integer_err<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, | ||
span: Span, | ||
t_1: Ty<'tcx>, | ||
t_e: Ty<'tcx>) { | ||
fcx.type_error_message(span, |actual| { | ||
format!("cast to nil: `{}` as `{}`", | ||
format!("illegal cast; cast through an \ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just copied it |
||
integer first: `{}` as `{}`", | ||
actual, | ||
fcx.infcx().ty_to_string(t_1)) | ||
}, t_e, None); | ||
} | ||
|
||
let t_e_is_bare_fn_item = ty::type_is_bare_fn_item(t_e); | ||
let t_e_is_scalar = ty::type_is_scalar(t_e); | ||
let t_e_is_integral = ty::type_is_integral(t_e); | ||
let t_e_is_float = ty::type_is_floating_point(t_e); | ||
let t_e_is_c_enum = ty::type_is_c_like_enum(fcx.tcx(), t_e); | ||
|
||
let t_1_is_scalar = ty::type_is_scalar(t_1); | ||
let t_1_is_char = ty::type_is_char(t_1); | ||
|
@@ -1081,18 +1060,9 @@ fn check_cast(fcx: &FnCtxt, | |
|
||
// casts to scalars other than `char` and `bare fn` are trivial | ||
let t_1_is_trivial = t_1_is_scalar && !t_1_is_char && !t_1_is_bare_fn; | ||
|
||
if t_e_is_bare_fn_item && t_1_is_bare_fn { | ||
demand::coerce(fcx, e.span, t_1, &*e); | ||
} else if ty::type_is_c_like_enum(fcx.tcx(), t_e) && t_1_is_trivial { | ||
if t_1_is_float || ty::type_is_unsafe_ptr(t_1) { | ||
fcx.type_error_message(span, |actual| { | ||
format!("illegal cast; cast through an \ | ||
integer first: `{}` as `{}`", | ||
actual, | ||
fcx.infcx().ty_to_string(t_1)) | ||
}, t_e, None); | ||
} | ||
// casts from C-like enums are allowed | ||
} else if t_1_is_char { | ||
let t_e = fcx.infcx().shallow_resolve(t_e); | ||
if t_e.sty != ty::ty_uint(ast::TyU8) { | ||
|
@@ -1104,6 +1074,16 @@ fn check_cast(fcx: &FnCtxt, | |
} else if t_1.sty == ty::ty_bool { | ||
span_err!(fcx.tcx().sess, span, E0054, | ||
"cannot cast as `bool`, compare with zero instead"); | ||
} else if t_1_is_float && (t_e_is_scalar || t_e_is_c_enum) && !( | ||
t_e_is_integral || t_e_is_float || t_e.sty == ty::ty_bool) { | ||
// Casts to float must go through an integer or boolean | ||
cast_through_integer_err(fcx, span, t_1, t_e) | ||
} else if t_e_is_c_enum && t_1_is_trivial { | ||
if ty::type_is_unsafe_ptr(t_1) { | ||
// ... and likewise with C enum -> *T | ||
cast_through_integer_err(fcx, span, t_1, t_e) | ||
} | ||
// casts from C-like enums are allowed | ||
} else if ty::type_is_region_ptr(t_e) && ty::type_is_unsafe_ptr(t_1) { | ||
fn types_compatible<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span, | ||
t1: Ty<'tcx>, t2: Ty<'tcx>) -> bool { | ||
|
@@ -1145,7 +1125,7 @@ fn check_cast(fcx: &FnCtxt, | |
demand::coerce(fcx, e.span, t_1, &*e); | ||
} | ||
} | ||
} else if !(ty::type_is_scalar(t_e) && t_1_is_trivial) { | ||
} else if !(t_e_is_scalar && t_1_is_trivial) { | ||
/* | ||
If more type combinations should be supported than are | ||
supported here, then file an enhancement issue and | ||
|
@@ -1156,15 +1136,49 @@ fn check_cast(fcx: &FnCtxt, | |
actual, | ||
fcx.infcx().ty_to_string(t_1)) | ||
}, t_e, None); | ||
} else if ty::type_is_unsafe_ptr(t_e) && t_1_is_float { | ||
fcx.type_error_message(span, |actual| { | ||
format!("cannot cast from pointer to float directly: `{}` as `{}`; cast through an \ | ||
integer first", | ||
actual, | ||
fcx.infcx().ty_to_string(t_1)) | ||
}, t_e, None); | ||
} | ||
} | ||
|
||
fn check_cast(fcx: &FnCtxt, | ||
cast_expr: &ast::Expr, | ||
e: &ast::Expr, | ||
t: &ast::Ty) { | ||
let id = cast_expr.id; | ||
let span = cast_expr.span; | ||
|
||
// Find the type of `e`. Supply hints based on the type we are casting to, | ||
// if appropriate. | ||
let t_1 = fcx.to_ty(t); | ||
let t_1 = structurally_resolved_type(fcx, span, t_1); | ||
|
||
check_expr_with_expectation(fcx, e, ExpectCastableToType(t_1)); | ||
|
||
let t_e = fcx.expr_ty(e); | ||
|
||
debug!("t_1={}", fcx.infcx().ty_to_string(t_1)); | ||
debug!("t_e={}", fcx.infcx().ty_to_string(t_e)); | ||
|
||
if ty::type_is_error(t_e) { | ||
fcx.write_error(id); | ||
return | ||
} | ||
|
||
if !fcx.type_is_known_to_be_sized(t_1, cast_expr.span) { | ||
report_cast_to_unsized_type(fcx, span, t.span, e.span, t_1, t_e, id); | ||
return | ||
} | ||
|
||
if ty::type_is_trait(t_1) { | ||
// This will be looked up later on. | ||
vtable::check_object_cast(fcx, cast_expr, e, t_1); | ||
fcx.write_ty(id, t_1); | ||
return | ||
} | ||
|
||
let t_1 = structurally_resolved_type(fcx, span, t_1); | ||
let t_e = structurally_resolved_type(fcx, span, t_e); | ||
|
||
check_cast_inner(fcx, span, t_1, t_e, e); | ||
fcx.write_ty(id, t_1); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
struct Inches(i32); | ||
|
||
fn main() { | ||
Inches as f32; //~ ERROR illegal cast; cast through an integer first | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is
()
not scalar?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to me that this might have non-local consequence, beyond just changing these error messages slightly, maybe just changing the internals of the cast checking would be more reliable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unit structs aren't scalars. Unit enums aren't scalars.
((),)
isn't a scalar. I checked the other places that use this function – they don't care.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, good; checking convinces me.