Skip to content

Commit

Permalink
Cleanup check_cast. Fixes #21554
Browse files Browse the repository at this point in the history
This also makes the cast error messages somewhat more uniform.
  • Loading branch information
arielb1 committed Jan 24, 2015
1 parent 76fbb35 commit e724525
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 91 deletions.
1 change: 0 additions & 1 deletion src/librustc/middle/ty.rs
Expand Up @@ -3128,7 +3128,6 @@ pub fn type_is_scalar(ty: Ty) -> bool {
ty_bool | ty_char | ty_int(_) | ty_float(_) | ty_uint(_) |
ty_infer(IntVar(_)) | ty_infer(FloatVar(_)) |
ty_bare_fn(..) | ty_ptr(_) => true,
ty_tup(ref tys) if tys.is_empty() => true,
_ => false
}
}
Expand Down
186 changes: 100 additions & 86 deletions src/librustc_typeck/check/mod.rs
Expand Up @@ -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 {
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 \
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);
Expand All @@ -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) {
Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -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);
}

Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/cast-from-nil.rs
Expand Up @@ -8,5 +8,5 @@
// option. This file may not be copied, modified, or distributed
// 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); }
2 changes: 1 addition & 1 deletion src/test/compile-fail/cast-to-nil.rs
Expand Up @@ -8,5 +8,5 @@
// option. This file may not be copied, modified, or distributed
// 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 (); }
2 changes: 1 addition & 1 deletion src/test/compile-fail/issue-10991.rs
Expand Up @@ -10,5 +10,5 @@

fn main() {
let nil = ();
let _t = nil as usize; //~ ERROR: cast from nil: `()` as `usize`
let _t = nil as usize; //~ ERROR: non-scalar cast: `()` as `usize`
}
15 changes: 15 additions & 0 deletions src/test/compile-fail/issue-21554.rs
@@ -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
}
2 changes: 1 addition & 1 deletion src/test/compile-fail/typeck-cast-pointer-to-float.rs
Expand Up @@ -11,5 +11,5 @@
fn main() {
let x : i16 = 22;
((&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`
}

0 comments on commit e724525

Please sign in to comment.