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:
parent
a4e0e6bbf5
commit
b023fcca32
2 changed files with 104 additions and 98 deletions
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue