1
Fork 0

Cleanup check_cast. Fixes #21554

This also makes the cast error messages somewhat more uniform.
This commit is contained in:
Ariel Ben-Yehuda 2015-01-25 01:44:49 +02:00
parent 76fbb35831
commit e7245252cc
7 changed files with 119 additions and 91 deletions

View file

@ -3128,7 +3128,6 @@ pub fn type_is_scalar(ty: Ty) -> bool {
ty_bool | ty_char | ty_int(_) | ty_float(_) | ty_uint(_) | ty_bool | ty_char | ty_int(_) | ty_float(_) | ty_uint(_) |
ty_infer(IntVar(_)) | ty_infer(FloatVar(_)) | ty_infer(IntVar(_)) | ty_infer(FloatVar(_)) |
ty_bare_fn(..) | ty_ptr(_) => true, ty_bare_fn(..) | ty_ptr(_) => true,
ty_tup(ref tys) if tys.is_empty() => true,
_ => false _ => false
} }
} }

View file

@ -993,86 +993,65 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
} }
} }
fn check_cast(fcx: &FnCtxt, fn report_cast_to_unsized_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
cast_expr: &ast::Expr, span: Span,
e: &ast::Expr, t_span: Span,
t: &ast::Ty) { e_span: Span,
let id = cast_expr.id; t_1: Ty<'tcx>,
let span = cast_expr.span; t_e: Ty<'tcx>,
id: ast::NodeId) {
// Find the type of `e`. Supply hints based on the type we are casting to, let tstr = fcx.infcx().ty_to_string(t_1);
// if appropriate. fcx.type_error_message(span, |actual| {
let t_1 = fcx.to_ty(t); format!("cast to unsized type: `{}` as `{}`", actual, tstr)
let t_1 = structurally_resolved_type(fcx, span, t_1); }, t_e, None);
match t_e.sty {
check_expr_with_expectation(fcx, e, ExpectCastableToType(t_1)); ty::ty_rptr(_, ty::mt { mutbl: mt, .. }) => {
let mtstr = match mt {
let t_e = fcx.expr_ty(e); ast::MutMutable => "mut ",
ast::MutImmutable => ""
debug!("t_1={}", fcx.infcx().ty_to_string(t_1)); };
debug!("t_e={}", fcx.infcx().ty_to_string(t_e)); if ty::type_is_trait(t_1) {
span_help!(fcx.tcx().sess, t_span, "did you mean `&{}{}`?", mtstr, tstr);
if ty::type_is_error(t_e) { } else {
fcx.write_error(id); span_help!(fcx.tcx().sess, span,
return "consider using an implicit coercion to `&{}{}` instead",
} mtstr, tstr);
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");
} }
} }
fcx.write_error(id); ty::ty_uniq(..) => {
return 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);
}
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); fn check_cast_inner<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
let t_e = structurally_resolved_type(fcx, span, t_e); span: Span,
t_1: Ty<'tcx>,
if ty::type_is_nil(t_e) { 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| { fcx.type_error_message(span, |actual| {
format!("cast from nil: `{}` as `{}`", format!("illegal cast; cast through an \
actual, integer first: `{}` as `{}`",
fcx.infcx().ty_to_string(t_1))
}, t_e, None);
} else if ty::type_is_nil(t_1) {
fcx.type_error_message(span, |actual| {
format!("cast to nil: `{}` as `{}`",
actual, actual,
fcx.infcx().ty_to_string(t_1)) fcx.infcx().ty_to_string(t_1))
}, t_e, None); }, t_e, None);
} }
let t_e_is_bare_fn_item = ty::type_is_bare_fn_item(t_e); 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_scalar = ty::type_is_scalar(t_1);
let t_1_is_char = ty::type_is_char(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 // 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; 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 { if t_e_is_bare_fn_item && t_1_is_bare_fn {
demand::coerce(fcx, e.span, t_1, &*e); 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 { } else if t_1_is_char {
let t_e = fcx.infcx().shallow_resolve(t_e); let t_e = fcx.infcx().shallow_resolve(t_e);
if t_e.sty != ty::ty_uint(ast::TyU8) { 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 { } else if t_1.sty == ty::ty_bool {
span_err!(fcx.tcx().sess, span, E0054, span_err!(fcx.tcx().sess, span, E0054,
"cannot cast as `bool`, compare with zero instead"); "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) { } 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, fn types_compatible<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span,
t1: Ty<'tcx>, t2: Ty<'tcx>) -> bool { t1: Ty<'tcx>, t2: Ty<'tcx>) -> bool {
@ -1145,7 +1125,7 @@ fn check_cast(fcx: &FnCtxt,
demand::coerce(fcx, e.span, t_1, &*e); 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 If more type combinations should be supported than are
supported here, then file an enhancement issue and supported here, then file an enhancement issue and
@ -1156,15 +1136,49 @@ fn check_cast(fcx: &FnCtxt,
actual, actual,
fcx.infcx().ty_to_string(t_1)) fcx.infcx().ty_to_string(t_1))
}, t_e, None); }, 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", fn check_cast(fcx: &FnCtxt,
actual, cast_expr: &ast::Expr,
fcx.infcx().ty_to_string(t_1)) e: &ast::Expr,
}, t_e, None); 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); fcx.write_ty(id, t_1);
} }

View file

@ -8,5 +8,5 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// error-pattern: cast from nil: `()` as `u32` // error-pattern: non-scalar cast: `()` as `u32`
fn main() { let u = (assert!(true) as u32); } fn main() { let u = (assert!(true) as u32); }

View file

@ -8,5 +8,5 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// error-pattern: cast to nil: `u32` as `()` // error-pattern: non-scalar cast: `u32` as `()`
fn main() { let u = 0u32 as (); } fn main() { let u = 0u32 as (); }

View file

@ -10,5 +10,5 @@
fn main() { fn main() {
let nil = (); let nil = ();
let _t = nil as usize; //~ ERROR: cast from nil: `()` as `usize` let _t = nil as usize; //~ ERROR: non-scalar cast: `()` as `usize`
} }

View file

@ -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
}

View file

@ -11,5 +11,5 @@
fn main() { fn main() {
let x : i16 = 22; let x : i16 = 22;
((&x) as *const i16) as f32; ((&x) as *const i16) as f32;
//~^ ERROR: cannot cast from pointer to float directly: `*const i16` as `f32` //~^ ERROR illegal cast; cast through an integer first: `*const i16` as `f32`
} }