From e7245252ccb5de3d8002a2cc3ecb25595ea23e90 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 25 Jan 2015 01:44:49 +0200 Subject: [PATCH] Cleanup check_cast. Fixes #21554 This also makes the cast error messages somewhat more uniform. --- src/librustc/middle/ty.rs | 1 - src/librustc_typeck/check/mod.rs | 186 ++++++++++-------- src/test/compile-fail/cast-from-nil.rs | 2 +- src/test/compile-fail/cast-to-nil.rs | 2 +- src/test/compile-fail/issue-10991.rs | 2 +- src/test/compile-fail/issue-21554.rs | 15 ++ .../typeck-cast-pointer-to-float.rs | 2 +- 7 files changed, 119 insertions(+), 91 deletions(-) create mode 100644 src/test/compile-fail/issue-21554.rs diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index bae41b78c08..3e0801d6598 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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 } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index a9f81d3a266..9489f0cc01b 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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 + 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); +} - 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); - - if ty::type_is_nil(t_e) { +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 from nil: `{}` as `{}`", - actual, - 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 `{}`", + 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); @@ -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); } diff --git a/src/test/compile-fail/cast-from-nil.rs b/src/test/compile-fail/cast-from-nil.rs index 558a5478718..4c6dcaccc9a 100644 --- a/src/test/compile-fail/cast-from-nil.rs +++ b/src/test/compile-fail/cast-from-nil.rs @@ -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); } diff --git a/src/test/compile-fail/cast-to-nil.rs b/src/test/compile-fail/cast-to-nil.rs index 1a5c0744f70..e5fd5bb33eb 100644 --- a/src/test/compile-fail/cast-to-nil.rs +++ b/src/test/compile-fail/cast-to-nil.rs @@ -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 (); } diff --git a/src/test/compile-fail/issue-10991.rs b/src/test/compile-fail/issue-10991.rs index 2913ddf395f..25060b94dcf 100644 --- a/src/test/compile-fail/issue-10991.rs +++ b/src/test/compile-fail/issue-10991.rs @@ -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` } diff --git a/src/test/compile-fail/issue-21554.rs b/src/test/compile-fail/issue-21554.rs new file mode 100644 index 00000000000..a2cac55033c --- /dev/null +++ b/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 or the MIT license +// , 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 +} diff --git a/src/test/compile-fail/typeck-cast-pointer-to-float.rs b/src/test/compile-fail/typeck-cast-pointer-to-float.rs index 22a0978ef7c..285a5dbee05 100644 --- a/src/test/compile-fail/typeck-cast-pointer-to-float.rs +++ b/src/test/compile-fail/typeck-cast-pointer-to-float.rs @@ -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` }