Auto merge of #32258 - nikomatsakis:fewer-errors, r=arielb1

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.

Fixes #31997

cc @alexcrichton
r? @arielb1
This commit is contained in:
bors 2016-04-25 08:13:22 -07:00
commit 90318b8c22
20 changed files with 348 additions and 99 deletions

View File

@ -35,7 +35,7 @@ use ty::fold::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<bool>,
// 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

View File

@ -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)
}

View File

@ -624,6 +624,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();

View File

@ -155,6 +155,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)
@ -1533,6 +1539,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;
}
_ => {

View File

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

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,17 +113,37 @@ 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, 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)
}
}
}
fn report_cast_error<'a>(&self, fcx: &FnCtxt<'a, 'tcx>,
fn report_cast_error<'a>(&self,
fcx: &FnCtxt<'a, 'tcx>,
e: CastError) {
match e {
CastError::NeedViaPtr |
@ -186,6 +208,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;
@ -218,7 +295,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);
@ -403,3 +482,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 }
@ -1240,6 +1182,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> {
@ -1524,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,
@ -1771,16 +1706,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 +3188,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 +3392,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);
}
}
@ -3560,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);
@ -3568,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.
@ -3577,9 +3533,15 @@ 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);
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) => {
let typ = fcx.to_ty(&t);
@ -4408,8 +4370,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()];
}
}
@ -4769,9 +4735,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() {
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 {

View File

@ -384,6 +384,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

View File

@ -0,0 +1,19 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 compare with zero
}

View File

@ -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
let _ = E::A as bool;

View File

@ -0,0 +1,66 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<String>,
end: Option<String>,
}
fn main() {
let input = io::stdin();
let mut input = input.lock();
let mut map = HashMap::new();
//~^ ERROR E0433
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::<Vec<_>>();
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);
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<F, T>(x: F) -> Result<T, ()>
where F: FnMut() -> T, T: TheTrait,
{
unimplemented!()
}
fn foo() -> Result<(), ()> {
try!(closure(|| bar(0 as *mut _))); //~ ERROR unresolved name `bar`
Ok(())
}
fn main() { }

View File

@ -35,7 +35,7 @@ impl Node for Stuff {
fn iterate<N: Node, G: Graph<N>>(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();
}
}

View File

@ -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() { }

View File

@ -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
}
}

View File

@ -30,8 +30,7 @@ macro_rules! write {
}
macro_rules! cast {
($x:expr) => ($x as ())
//~^ ERROR non-scalar cast: `i32` as `()`
($x:expr) => ($x as ()) //~ ERROR non-scalar cast
}
fn main() {

View File

@ -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());
}

View File

@ -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(),
}
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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());
}