From ccaa2f855e34028ff9be745ecc9803e720d34b5e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 15 Mar 2016 04:49:10 -0400 Subject: [PATCH 1/7] Suppress fallback and ambiguity errors If the infcx has observed other errors, then suppress both default type parameter fallback (which can be unreliable, as the full constraint set is not available) and errors related to unresovled variables (annoyingly, integer type variables cannot currently be unified with error, so that has to be a separate mechanism). Also add a flag to `infcx` to allow us to independently indicate when we have observed an error and hence should trigger this suppression mode. --- src/librustc/infer/mod.rs | 51 +++++++++++++++++-- src/librustc/infer/sub.rs | 1 + src/librustc/traits/error_reporting.rs | 6 +++ src/librustc_typeck/astconv.rs | 7 +++ src/librustc_typeck/check/_match.rs | 2 + src/librustc_typeck/check/cast.rs | 21 +++++++- src/librustc_typeck/check/mod.rs | 33 +++++++++++- src/librustc_typeck/collect.rs | 4 ++ src/test/compile-fail/cast-rfc0401-2.rs | 20 ++++++++ src/test/compile-fail/cast-rfc0401.rs | 2 +- .../{ => derived-errors}/issue-30580.rs | 0 .../derived-errors/issue-31997.rs | 27 ++++++++++ src/test/compile-fail/issue-26480.rs | 2 - 13 files changed, 166 insertions(+), 10 deletions(-) create mode 100644 src/test/compile-fail/cast-rfc0401-2.rs rename src/test/compile-fail/{ => derived-errors}/issue-30580.rs (100%) create mode 100644 src/test/compile-fail/derived-errors/issue-31997.rs diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 4c1216aa8626a..c6a329bd64f94 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -35,7 +35,7 @@ use ty::fold::{TypeFolder, TypeFoldable}; use ty::relate::{Relate, RelateResult, TypeRelation}; use traits::{self, PredicateObligations, ProjectionMode}; use rustc_data_structures::unify::{self, UnificationTable}; -use std::cell::{RefCell, Ref}; +use std::cell::{Cell, RefCell, Ref}; use std::fmt; use syntax::ast; use syntax::codemap; @@ -110,6 +110,25 @@ pub struct InferCtxt<'a, 'tcx: 'a> { // documentation for `ProjectionMode`. projection_mode: ProjectionMode, + // When an error occurs, we want to avoid reporting "derived" + // errors that are due to this original failure. Normally, we + // handle this with the `err_count_on_creation` count, which + // basically just tracks how many errors were reported when we + // started type-checking a fn and checks to see if any new errors + // have been reported since then. Not great, but it works. + // + // However, when errors originated in other passes -- notably + // resolve -- this heuristic breaks down. Therefore, we have this + // auxiliary flag that one can set whenever one creates a + // type-error that is due to an error in a prior pass. + // + // Don't read this flag directly, call `is_tainted_by_errors()` + // and `set_tainted_by_errors()`. + tainted_by_errors_flag: Cell, + + // Track how many errors were reported when this infcx is created. + // If the number of errors increases, that's also a sign (line + // `tained_by_errors`) to avoid reporting certain kinds of errors. err_count_on_creation: usize, } @@ -379,6 +398,7 @@ pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>, reported_trait_errors: RefCell::new(FnvHashSet()), normalize: false, projection_mode: projection_mode, + tainted_by_errors_flag: Cell::new(false), err_count_on_creation: tcx.sess.err_count() } } @@ -1128,15 +1148,36 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .map(|method| resolve_ty(method.ty))) } - pub fn errors_since_creation(&self) -> bool { - self.tcx.sess.err_count() - self.err_count_on_creation != 0 + /// True if errors have been reported since this infcx was + /// created. This is sometimes used as a heuristic to skip + /// reporting errors that often occur as a result of earlier + /// errors, but where it's hard to be 100% sure (e.g., unresolved + /// inference variables, regionck errors). + pub fn is_tainted_by_errors(&self) -> bool { + debug!("is_tainted_by_errors(err_count={}, err_count_on_creation={}, \ + tainted_by_errors_flag={})", + self.tcx.sess.err_count(), + self.err_count_on_creation, + self.tainted_by_errors_flag.get()); + + if self.tcx.sess.err_count() > self.err_count_on_creation { + return true; // errors reported since this infcx was made + } + self.tainted_by_errors_flag.get() + } + + /// Set the "tainted by errors" flag to true. We call this when we + /// observe an error from a prior pass. + pub fn set_tainted_by_errors(&self) { + debug!("set_tainted_by_errors()"); + self.tainted_by_errors_flag.set(true) } pub fn node_type(&self, id: ast::NodeId) -> Ty<'tcx> { match self.tables.borrow().node_types.get(&id) { Some(&t) => t, // FIXME - None if self.errors_since_creation() => + None if self.is_tainted_by_errors() => self.tcx.types.err, None => { bug!("no type for node {}: {} in fcx", @@ -1158,7 +1199,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { free_regions: &FreeRegionMap, subject_node_id: ast::NodeId) { let errors = self.region_vars.resolve_regions(free_regions, subject_node_id); - if !self.errors_since_creation() { + if !self.is_tainted_by_errors() { // As a heuristic, just skip reporting region errors // altogether if other errors have been reported while // this infcx was in use. This is totally hokey but diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs index ece8c0c696af8..71e1efe220f03 100644 --- a/src/librustc/infer/sub.rs +++ b/src/librustc/infer/sub.rs @@ -91,6 +91,7 @@ impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Sub<'a, 'tcx> { } (&ty::TyError, _) | (_, &ty::TyError) => { + infcx.set_tainted_by_errors(); Ok(self.tcx().types.err) } diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 286733c7c26a3..44cd05907df95 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -609,6 +609,12 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, predicate, obligation); + // Ambiguity errors are often caused as fallout from earlier + // errors. So just ignore them if this infcx is tainted. + if infcx.is_tainted_by_errors() { + return; + } + match predicate { ty::Predicate::Trait(ref data) => { let trait_ref = data.to_poly_trait_ref(); diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 21122e7095dd1..3f9f8a45272f8 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -154,6 +154,12 @@ pub trait AstConv<'tcx> { _trait_ref: ty::TraitRef<'tcx>, _item_name: ast::Name) -> Ty<'tcx>; + + /// Invoked when we encounter an error from some prior pass + /// (e.g. resolve) that is translated into a ty-error. This is + /// used to help suppress derived errors typeck might otherwise + /// report. + fn set_tainted_by_errors(&self); } pub fn ast_region_to_region(tcx: &TyCtxt, lifetime: &hir::Lifetime) @@ -1532,6 +1538,7 @@ fn base_def_to_ty<'tcx>(this: &AstConv<'tcx>, prim_ty_to_ty(tcx, base_segments, prim_ty) } Def::Err => { + this.set_tainted_by_errors(); return this.tcx().types.err; } _ => { diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 8dbd6496b6fb0..a7a04f4a85fe8 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -209,6 +209,7 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, let self_ty = fcx.to_ty(&qself.ty); let path_res = if let Some(&d) = tcx.def_map.borrow().get(&pat.id) { if d.base_def == Def::Err { + fcx.infcx().set_tainted_by_errors(); fcx.write_error(pat.id); return; } @@ -628,6 +629,7 @@ fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, let path_res = match tcx.def_map.borrow().get(&pat.id) { Some(&path_res) if path_res.base_def != Def::Err => path_res, _ => { + fcx.infcx().set_tainted_by_errors(); fcx.write_error(pat.id); if let Some(subpats) = subpats { diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 91cdb8d966d41..ea872a92dcf3c 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -121,8 +121,27 @@ impl<'tcx> CastCheck<'tcx> { } } - fn report_cast_error<'a>(&self, fcx: &FnCtxt<'a, 'tcx>, + 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 | diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 67b91f7838c66..5bc6184263c10 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1240,6 +1240,10 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { { self.normalize_associated_type(span, trait_ref, item_name) } + + fn set_tainted_by_errors(&self) { + self.infcx().set_tainted_by_errors() + } } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -1771,16 +1775,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn default_type_parameters(&self) { use rustc::ty::error::UnconstrainedNumeric::Neither; use rustc::ty::error::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat}; + + // Defaulting inference variables becomes very dubious if we have + // encountered type-checking errors. Therefore, if we think we saw + // some errors in this function, just resolve all uninstanted type + // varibles to TyError. + if self.infcx().is_tainted_by_errors() { + for ty in &self.infcx().unsolved_variables() { + if let ty::TyInfer(_) = self.infcx().shallow_resolve(ty).sty { + debug!("default_type_parameters: defaulting `{:?}` to error", ty); + demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.err); + } + } + return; + } + for ty in &self.infcx().unsolved_variables() { let resolved = self.infcx().resolve_type_vars_if_possible(ty); if self.infcx().type_var_diverges(resolved) { + debug!("default_type_parameters: defaulting `{:?}` to `()` because it diverges", + resolved); demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil()); } else { match self.infcx().type_is_unconstrained_numeric(resolved) { UnconstrainedInt => { + debug!("default_type_parameters: defaulting `{:?}` to `i32`", + resolved); demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.i32) }, UnconstrainedFloat => { + debug!("default_type_parameters: defaulting `{:?}` to `f32`", + resolved); demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.f64) } Neither => { } @@ -3232,6 +3257,7 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // Find the relevant variant let def = lookup_full_def(tcx, path.span, expr.id); if def == Def::Err { + fcx.infcx().set_tainted_by_errors(); check_struct_fields_on_error(fcx, expr.id, fields, base_expr); return; } @@ -3435,6 +3461,7 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr.span, id); } else { + fcx.infcx().set_tainted_by_errors(); fcx.write_ty(id, fcx.tcx().types.err); } } @@ -4408,8 +4435,12 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, Def::ForeignMod(..) | Def::Local(..) | Def::Label(..) | - Def::Upvar(..) | + Def::Upvar(..) => { + segment_spaces = vec![None; segments.len()]; + } + Def::Err => { + fcx.infcx().set_tainted_by_errors(); segment_spaces = vec![None; segments.len()]; } } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 2e1a684684375..39465d6ffe360 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -383,6 +383,10 @@ impl<'a, 'tcx> AstConv<'tcx> for ItemCtxt<'a, 'tcx> { { self.tcx().mk_projection(trait_ref, item_name) } + + fn set_tainted_by_errors(&self) { + // no obvious place to track this, just let it go + } } /// Interface used to find the bounds on a type parameter from within diff --git a/src/test/compile-fail/cast-rfc0401-2.rs b/src/test/compile-fail/cast-rfc0401-2.rs new file mode 100644 index 0000000000000..fdc250f994627 --- /dev/null +++ b/src/test/compile-fail/cast-rfc0401-2.rs @@ -0,0 +1,20 @@ +// 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. + +// RFC 401 test extracted into distinct file. This is because some the +// change to suppress "derived" errors wound up suppressing this error +// message, since the fallback for `3` doesn't occur. + +fn main() { + let _ = 3 as bool; + //~^ ERROR cannot cast as `bool` + //~| HELP see a detailed explanation + //~| HELP compare with zero +} diff --git a/src/test/compile-fail/cast-rfc0401.rs b/src/test/compile-fail/cast-rfc0401.rs index dcd49e34bb26c..fcfb5706e5d1a 100644 --- a/src/test/compile-fail/cast-rfc0401.rs +++ b/src/test/compile-fail/cast-rfc0401.rs @@ -58,7 +58,7 @@ fn main() let _ = f as *const u8; //~^ ERROR casting //~^^ HELP through a usize first - let _ = 3 as bool; + let _ = 3_i32 as bool; //~^ ERROR cannot cast as `bool` //~^^ HELP compare with zero //~^^^ HELP run `rustc --explain E0054` to see a detailed explanation diff --git a/src/test/compile-fail/issue-30580.rs b/src/test/compile-fail/derived-errors/issue-30580.rs similarity index 100% rename from src/test/compile-fail/issue-30580.rs rename to src/test/compile-fail/derived-errors/issue-30580.rs diff --git a/src/test/compile-fail/derived-errors/issue-31997.rs b/src/test/compile-fail/derived-errors/issue-31997.rs new file mode 100644 index 0000000000000..cf283f6d3e4b5 --- /dev/null +++ b/src/test/compile-fail/derived-errors/issue-31997.rs @@ -0,0 +1,27 @@ +// Copyright 2012 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. + +// Test that the resolve failure does not lead to downstream type errors. +// See issue #31997. + +trait TheTrait { } + +fn closure(x: F) -> Result + where F: FnMut() -> T, T: TheTrait, +{ + unimplemented!() +} + +fn foo() -> Result<(), ()> { + try!(closure(|| bar(0 as *mut _))); //~ ERROR unresolved name `bar` + Ok(()) +} + +fn main() { } diff --git a/src/test/compile-fail/issue-26480.rs b/src/test/compile-fail/issue-26480.rs index 903df42291c63..44aff62386018 100644 --- a/src/test/compile-fail/issue-26480.rs +++ b/src/test/compile-fail/issue-26480.rs @@ -31,7 +31,6 @@ macro_rules! write { macro_rules! cast { ($x:expr) => ($x as ()) - //~^ ERROR non-scalar cast: `i32` as `()` } fn main() { @@ -40,5 +39,4 @@ fn main() { //~^ NOTE in this expansion of write! cast!(2); - //~^ NOTE in this expansion of cast! } From a4e0e6bbf5835afc69ab5df383097a1d7b8293c5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 5 Apr 2016 18:56:24 -0400 Subject: [PATCH 2/7] avoid "type must be known here" errors if tainted --- src/librustc_typeck/check/mod.rs | 8 ++-- src/test/compile-fail/issue-31997.rs | 67 ++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 src/test/compile-fail/issue-31997.rs diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 5bc6184263c10..31458eac9ef18 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4800,9 +4800,11 @@ fn structurally_resolve_type_or_else<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, // If not, error. if alternative.is_ty_var() || alternative.references_error() { - fcx.type_error_message(sp, |_actual| { - "the type of this value must be known in this context".to_string() - }, ty, None); + if !fcx.infcx().is_tainted_by_errors() { + fcx.type_error_message(sp, |_actual| { + "the type of this value must be known in this context".to_string() + }, ty, None); + } demand::suptype(fcx, sp, fcx.tcx().types.err, ty); ty = fcx.tcx().types.err; } else { diff --git a/src/test/compile-fail/issue-31997.rs b/src/test/compile-fail/issue-31997.rs new file mode 100644 index 0000000000000..2d78382fea3ee --- /dev/null +++ b/src/test/compile-fail/issue-31997.rs @@ -0,0 +1,67 @@ +// Copyright 2012 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. + +// Regression test for this example from #31997 -- main goal is to +// emit as minimal and precise an error set as possible. Ideally, we'd +// only emit the E0433 error below, but right now we emit two. + +use std::io::prelude::*; +// use std::collections::HashMap; +use std::io; + +#[derive(Debug)] +struct Instance { + name: String, + start: Option, + end: Option, +} + +fn main() { + let input = io::stdin(); + let mut input = input.lock(); + + let mut map = HashMap::new(); + //~^ ERROR E0433 + //~| ERROR E0425 + + for line in input.lines() { + let line = line.unwrap(); + println!("process: {}", line); + let mut parts = line.splitn(2, ":"); + let _logfile = parts.next().unwrap(); + let rest = parts.next().unwrap(); + let mut parts = line.split(" [-] "); + + let stamp = parts.next().unwrap(); + + let rest = parts.next().unwrap(); + let words = rest.split_whitespace().collect::>(); + + let instance = words.iter().find(|a| a.starts_with("i-")).unwrap(); + let name = words[1].to_owned(); + let mut entry = map.entry(instance.to_owned()).or_insert(Instance { + name: name, + start: None, + end: None, + }); + + if rest.contains("terminating") { + assert!(entry.end.is_none()); + entry.end = Some(stamp.to_string()); + } + if rest.contains("waiting for") { + assert!(entry.start.is_none()); + entry.start = Some(stamp.to_string()); + } + + } + + println!("{:?}", map); +} From b023fcca3267fff93d91d559d3096e2defbc39fe Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 11 Apr 2016 15:21:36 -0400 Subject: [PATCH 3/7] 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) => { From 47d3b801ef707bba7437a8830e1bc285b638dced Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 11 Apr 2016 15:28:23 -0400 Subject: [PATCH 4/7] move regr test into same directory --- .../{issue-31997.rs => derived-errors/issue-31997-1.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/test/compile-fail/{issue-31997.rs => derived-errors/issue-31997-1.rs} (100%) diff --git a/src/test/compile-fail/issue-31997.rs b/src/test/compile-fail/derived-errors/issue-31997-1.rs similarity index 100% rename from src/test/compile-fail/issue-31997.rs rename to src/test/compile-fail/derived-errors/issue-31997-1.rs From 2c9dfafa572272a758357d6bd5d51c0b22a9fdd3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 12 Apr 2016 12:19:13 -0400 Subject: [PATCH 5/7] fix various error messages --- src/test/compile-fail/derived-errors/issue-31997-1.rs | 1 - src/test/compile-fail/issue-13853.rs | 2 +- src/test/compile-fail/issue-19692.rs | 2 +- src/test/compile-fail/issue-20261.rs | 1 - src/test/compile-fail/issue-26480.rs | 3 ++- src/test/compile-fail/issue-3973.rs | 2 +- src/test/compile-fail/issue-7092.rs | 2 +- 7 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/test/compile-fail/derived-errors/issue-31997-1.rs b/src/test/compile-fail/derived-errors/issue-31997-1.rs index 2d78382fea3ee..7d79c48c06ae2 100644 --- a/src/test/compile-fail/derived-errors/issue-31997-1.rs +++ b/src/test/compile-fail/derived-errors/issue-31997-1.rs @@ -29,7 +29,6 @@ fn main() { let mut map = HashMap::new(); //~^ ERROR E0433 - //~| ERROR E0425 for line in input.lines() { let line = line.unwrap(); diff --git a/src/test/compile-fail/issue-13853.rs b/src/test/compile-fail/issue-13853.rs index 7643310298da3..86a6bdfd4dde4 100644 --- a/src/test/compile-fail/issue-13853.rs +++ b/src/test/compile-fail/issue-13853.rs @@ -35,7 +35,7 @@ impl Node for Stuff { fn iterate>(graph: &G) { for node in graph.iter() { //~ ERROR no method named `iter` found - node.zomg(); //~ error: the type of this value must be known in this context + node.zomg(); } } diff --git a/src/test/compile-fail/issue-19692.rs b/src/test/compile-fail/issue-19692.rs index 53ad241687894..ca1715445e526 100644 --- a/src/test/compile-fail/issue-19692.rs +++ b/src/test/compile-fail/issue-19692.rs @@ -12,7 +12,7 @@ struct Homura; fn akemi(homura: Homura) { let Some(ref madoka) = Some(homura.kaname()); //~ ERROR no method named `kaname` found - madoka.clone(); //~ ERROR the type of this value must be known + madoka.clone(); } fn main() { } diff --git a/src/test/compile-fail/issue-20261.rs b/src/test/compile-fail/issue-20261.rs index 09044b5b5055d..2f1910b26bbef 100644 --- a/src/test/compile-fail/issue-20261.rs +++ b/src/test/compile-fail/issue-20261.rs @@ -11,6 +11,5 @@ fn main() { for (ref i,) in [].iter() { //~ ERROR mismatched types i.clone(); - //~^ ERROR: the type of this value must be known in this context } } diff --git a/src/test/compile-fail/issue-26480.rs b/src/test/compile-fail/issue-26480.rs index 44aff62386018..23e4ffb1f3076 100644 --- a/src/test/compile-fail/issue-26480.rs +++ b/src/test/compile-fail/issue-26480.rs @@ -30,7 +30,7 @@ macro_rules! write { } macro_rules! cast { - ($x:expr) => ($x as ()) + ($x:expr) => ($x as ()) //~ ERROR non-scalar cast } fn main() { @@ -39,4 +39,5 @@ fn main() { //~^ NOTE in this expansion of write! cast!(2); + //~^ NOTE in this expansion of cast! } diff --git a/src/test/compile-fail/issue-3973.rs b/src/test/compile-fail/issue-3973.rs index 54eb2a9082955..92456760b0508 100644 --- a/src/test/compile-fail/issue-3973.rs +++ b/src/test/compile-fail/issue-3973.rs @@ -31,5 +31,5 @@ impl ToString_ for Point { fn main() { let p = Point::new(0.0, 0.0); //~^ ERROR no associated item named `new` found for type `Point` in the current scope - println!("{}", p.to_string()); //~ ERROR type of this value must be known + println!("{}", p.to_string()); } diff --git a/src/test/compile-fail/issue-7092.rs b/src/test/compile-fail/issue-7092.rs index 4a278bbdeb04a..4acbcb165ff08 100644 --- a/src/test/compile-fail/issue-7092.rs +++ b/src/test/compile-fail/issue-7092.rs @@ -19,7 +19,7 @@ fn foo(x: Whatever) { //~| found `std::option::Option<_>` //~| expected enum `Whatever` //~| found enum `std::option::Option` - field.access(), //~ ERROR the type of this value must be known in this context + field.access(), } } From 89bbd2c8b75f31f06496158e8e309557cfeaeed5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 14 Apr 2016 09:57:55 -0400 Subject: [PATCH 6/7] Be a bit more constrained in our early check Do not require the target type to be fully known, either. This allows code like `let x: *const () = 0 as _` to work (see regression test). --- src/librustc_typeck/check/cast.rs | 19 +++++++++++-------- src/librustc_typeck/check/mod.rs | 2 +- src/test/run-pass/cast-to-infer-ty.rs | 17 +++++++++++++++++ 3 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 src/test/run-pass/cast-to-infer-ty.rs diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 1765f5dc2f207..922c411ce8cd8 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -128,15 +128,18 @@ impl<'tcx> CastCheck<'tcx> { 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); + // For better error messages, check for some obviously unsized + // cases now. We do a more thorough check at the end, once + // inference is more completely known. + match cast_ty.sty { + ty::TyTrait(..) | ty::TySlice(..) => { + check.report_cast_to_unsized_type(fcx); + Err(ErrorReported) + } + _ => { + Ok(check) + } } - - Ok(check) } fn report_cast_error<'a>(&self, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 16d6aca07b535..74ccd44891d26 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3518,7 +3518,7 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // Find the type of `e`. Supply hints based on the type we are casting to, // if appropriate. let t_cast = fcx.to_ty(t); - let t_cast = structurally_resolved_type(fcx, expr.span, t_cast); + let t_cast = fcx.infcx().resolve_type_vars_if_possible(&t_cast); check_expr_with_expectation(fcx, e, ExpectCastableToType(t_cast)); let t_expr = fcx.expr_ty(e); let t_cast = fcx.infcx().resolve_type_vars_if_possible(&t_cast); diff --git a/src/test/run-pass/cast-to-infer-ty.rs b/src/test/run-pass/cast-to-infer-ty.rs new file mode 100644 index 0000000000000..2aa0d9c62fb41 --- /dev/null +++ b/src/test/run-pass/cast-to-infer-ty.rs @@ -0,0 +1,17 @@ +// Copyright 2012 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. + +// Check that we allow a cast to `_` so long as the target type can be +// inferred elsewhere. + +pub fn main() { + let i: *const i32 = 0 as _; + assert!(i.is_null()); +} From b3d54a2c0e793768cd9ce4a7887a75e3ec356cb5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 25 Apr 2016 09:38:06 -0400 Subject: [PATCH 7/7] patch test due to changes from compiletest-json --- src/test/compile-fail/cast-rfc0401-2.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/compile-fail/cast-rfc0401-2.rs b/src/test/compile-fail/cast-rfc0401-2.rs index fdc250f994627..1598a9aa1fc41 100644 --- a/src/test/compile-fail/cast-rfc0401-2.rs +++ b/src/test/compile-fail/cast-rfc0401-2.rs @@ -15,6 +15,5 @@ fn main() { let _ = 3 as bool; //~^ ERROR cannot cast as `bool` - //~| HELP see a detailed explanation //~| HELP compare with zero }