Skip to content

Commit

Permalink
move checking for unsized target type into cast
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nikomatsakis committed Apr 12, 2016
1 parent a4e0e6b commit b023fcc
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 98 deletions.
121 changes: 96 additions & 25 deletions src/librustc_typeck/check/cast.rs
Expand Up @@ -45,19 +45,21 @@ 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.
pub struct CastCheck<'tcx> {
expr: &'tcx hir::Expr,
expr_ty: Ty<'tcx>,
cast_ty: Ty<'tcx>,
cast_span: Span,
span: Span,
}

Expand Down Expand Up @@ -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 |
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
}
}

81 changes: 8 additions & 73 deletions src/librustc_typeck/check/mod.rs
Expand Up @@ -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 }

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -3595,17 +3526,21 @@ 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.
fcx.write_ty(id, t_cast);

// 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) => {
Expand Down

0 comments on commit b023fcc

Please sign in to comment.