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.
This commit is contained in:
Niko Matsakis 2016-04-11 15:21:36 -04:00
parent a4e0e6bbf5
commit b023fcca32
2 changed files with 104 additions and 98 deletions

View file

@ -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<CastCheck<'tcx>, 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)
}
}

View file

@ -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) => {