From b023fcca3267fff93d91d559d3096e2defbc39fe Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 11 Apr 2016 15:21:36 -0400 Subject: [PATCH] move checking for unsized target type into `cast` It is odd to have this logic strewn about. This also means that all calls to `type_is_known_to_be_sized` are encapsulated in the cast code, in case we want to update that logic. --- src/librustc_typeck/check/cast.rs | 121 ++++++++++++++++++++++++------ src/librustc_typeck/check/mod.rs | 81 ++------------------ 2 files changed, 104 insertions(+), 98 deletions(-) diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index ea872a92dcf3c..1765f5dc2f207 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -45,12 +45,13 @@ use super::structurally_resolved_type; use lint; use hir::def_id::DefId; +use rustc::hir; +use rustc::traits; use rustc::ty::{self, Ty, TypeFoldable}; use rustc::ty::cast::{CastKind, CastTy}; -use syntax::codemap::Span; -use rustc::hir; use syntax::ast; - +use syntax::codemap::Span; +use util::common::ErrorReported; /// Reifies a cast check to be checked once we have full type information for /// a function context. @@ -58,6 +59,7 @@ pub struct CastCheck<'tcx> { expr: &'tcx hir::Expr, expr_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, + cast_span: Span, span: Span, } @@ -111,37 +113,35 @@ enum CastError { } impl<'tcx> CastCheck<'tcx> { - pub fn new(expr: &'tcx hir::Expr, expr_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, span: Span) - -> CastCheck<'tcx> { - CastCheck { + pub fn new<'a>(fcx: &FnCtxt<'a, 'tcx>, + expr: &'tcx hir::Expr, + expr_ty: Ty<'tcx>, + cast_ty: Ty<'tcx>, + cast_span: Span, + span: Span) + -> Result, ErrorReported> { + let check = CastCheck { expr: expr, expr_ty: expr_ty, cast_ty: cast_ty, + cast_span: cast_span, span: span, + }; + + // For better error messages, we try to check whether the + // target type is known to be sized now (we will also check + // later, once inference is more complete done). + if !fcx.type_is_known_to_be_sized(cast_ty, span) { + check.report_cast_to_unsized_type(fcx); + return Err(ErrorReported); } + + Ok(check) } fn report_cast_error<'a>(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError) { - // As a heuristic, don't report errors if there are unresolved - // inference variables floating around AND we've already - // reported some errors in this fn. It happens often that those - // inference variables are unresolved precisely *because* of - // the errors we've already reported. See #31997. - // - // Note: it's kind of annoying that we need this. Fallback is - // modified to push all unresolved inference variables to - // ty-err, but it's STILL possible to see fallback for - // integral/float variables, because those cannot be unified - // with ty-error. - if - fcx.infcx().is_tainted_by_errors() && - (self.cast_ty.has_infer_types() || self.expr_ty.has_infer_types()) - { - return; - } - match e { CastError::NeedViaPtr | CastError::NeedViaThinPtr | @@ -205,6 +205,61 @@ impl<'tcx> CastCheck<'tcx> { } } + fn report_cast_to_unsized_type<'a>(&self, + fcx: &FnCtxt<'a, 'tcx>) { + if + self.cast_ty.references_error() || + self.expr_ty.references_error() + { + return; + } + + let tstr = fcx.infcx().ty_to_string(self.cast_ty); + let mut err = fcx.type_error_struct(self.span, |actual| { + format!("cast to unsized type: `{}` as `{}`", actual, tstr) + }, self.expr_ty, None); + match self.expr_ty.sty { + ty::TyRef(_, ty::TypeAndMut { mutbl: mt, .. }) => { + let mtstr = match mt { + hir::MutMutable => "mut ", + hir::MutImmutable => "" + }; + if self.cast_ty.is_trait() { + match fcx.tcx().sess.codemap().span_to_snippet(self.cast_span) { + Ok(s) => { + err.span_suggestion(self.cast_span, + "try casting to a reference instead:", + format!("&{}{}", mtstr, s)); + }, + Err(_) => + span_help!(err, self.cast_span, + "did you mean `&{}{}`?", mtstr, tstr), + } + } else { + span_help!(err, self.span, + "consider using an implicit coercion to `&{}{}` instead", + mtstr, tstr); + } + } + ty::TyBox(..) => { + match fcx.tcx().sess.codemap().span_to_snippet(self.cast_span) { + Ok(s) => { + err.span_suggestion(self.cast_span, + "try casting to a `Box` instead:", + format!("Box<{}>", s)); + }, + Err(_) => + span_help!(err, self.cast_span, "did you mean `Box<{}>`?", tstr), + } + } + _ => { + span_help!(err, self.expr.span, + "consider using a box or reference as appropriate"); + } + } + err.emit(); + } + fn trivial_cast_lint<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) { let t_cast = self.cast_ty; let t_expr = self.expr_ty; @@ -237,7 +292,9 @@ impl<'tcx> CastCheck<'tcx> { debug!("check_cast({}, {:?} as {:?})", self.expr.id, self.expr_ty, self.cast_ty); - if self.expr_ty.references_error() || self.cast_ty.references_error() { + if !fcx.type_is_known_to_be_sized(self.cast_ty, self.span) { + self.report_cast_to_unsized_type(fcx); + } else if self.expr_ty.references_error() || self.cast_ty.references_error() { // No sense in giving duplicate error messages } else if self.try_coercion_cast(fcx) { self.trivial_cast_lint(fcx); @@ -422,3 +479,17 @@ impl<'tcx> CastCheck<'tcx> { } } + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + fn type_is_known_to_be_sized(&self, + ty: Ty<'tcx>, + span: Span) + -> bool + { + traits::type_known_to_meet_builtin_bound(self.infcx(), + ty, + ty::BoundSized, + span) + } +} + diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 31458eac9ef18..16d6aca07b535 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1076,64 +1076,6 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, } } -fn report_cast_to_unsized_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - span: Span, - t_span: Span, - e_span: Span, - t_cast: Ty<'tcx>, - t_expr: Ty<'tcx>, - id: ast::NodeId) { - if t_cast.references_error() || t_expr.references_error() { - return; - } - let tstr = fcx.infcx().ty_to_string(t_cast); - let mut err = fcx.type_error_struct(span, |actual| { - format!("cast to unsized type: `{}` as `{}`", actual, tstr) - }, t_expr, None); - match t_expr.sty { - ty::TyRef(_, ty::TypeAndMut { mutbl: mt, .. }) => { - let mtstr = match mt { - hir::MutMutable => "mut ", - hir::MutImmutable => "" - }; - if t_cast.is_trait() { - match fcx.tcx().sess.codemap().span_to_snippet(t_span) { - Ok(s) => { - err.span_suggestion(t_span, - "try casting to a reference instead:", - format!("&{}{}", mtstr, s)); - }, - Err(_) => - span_help!(err, t_span, - "did you mean `&{}{}`?", mtstr, tstr), - } - } else { - span_help!(err, span, - "consider using an implicit coercion to `&{}{}` instead", - mtstr, tstr); - } - } - ty::TyBox(..) => { - match fcx.tcx().sess.codemap().span_to_snippet(t_span) { - Ok(s) => { - err.span_suggestion(t_span, - "try casting to a `Box` instead:", - format!("Box<{}>", s)); - }, - Err(_) => - span_help!(err, t_span, "did you mean `Box<{}>`?", tstr), - } - } - _ => { - span_help!(err, e_span, - "consider using a box or reference as appropriate"); - } - } - err.emit(); - fcx.write_error(id); -} - - impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { fn tcx(&self) -> &TyCtxt<'tcx> { self.ccx.tcx } @@ -1528,17 +1470,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.require_type_is_sized(self.expr_ty(expr), expr.span, code); } - pub fn type_is_known_to_be_sized(&self, - ty: Ty<'tcx>, - span: Span) - -> bool - { - traits::type_known_to_meet_builtin_bound(self.infcx(), - ty, - ty::BoundSized, - span) - } - pub fn register_builtin_bound(&self, ty: Ty<'tcx>, builtin_bound: ty::BuiltinBound, @@ -3595,8 +3526,6 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // Eagerly check for some obvious errors. if t_expr.references_error() || t_cast.references_error() { fcx.write_error(id); - } else if !fcx.type_is_known_to_be_sized(t_cast, expr.span) { - report_cast_to_unsized_type(fcx, expr.span, t.span, e.span, t_cast, t_expr, id); } else { // Write a type for the whole expression, assuming everything is going // to work out Ok. @@ -3604,8 +3533,14 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // Defer other checks until we're done type checking. let mut deferred_cast_checks = fcx.inh.deferred_cast_checks.borrow_mut(); - let cast_check = cast::CastCheck::new(e, t_expr, t_cast, expr.span); - deferred_cast_checks.push(cast_check); + match cast::CastCheck::new(fcx, e, t_expr, t_cast, t.span, expr.span) { + Ok(cast_check) => { + deferred_cast_checks.push(cast_check); + } + Err(ErrorReported) => { + fcx.write_error(id); + } + } } } hir::ExprType(ref e, ref t) => {