From b0345d1786ca8d215f0d980de85efb5a9b7571dd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Apr 2017 19:04:50 -0400 Subject: [PATCH 1/3] backport solution for #40951 --- src/librustc/infer/combine.rs | 43 ++++++--- src/librustc/infer/fudge.rs | 67 +++++++++----- src/librustc/infer/type_variable.rs | 90 ++++++++++++++++++- src/librustc/traits/error_reporting.rs | 15 +++- src/test/run-pass/issue-40951.rs | 20 +++++ .../run-pass/type-infer-generalize-ty-var.rs | 60 +++++++++++++ 6 files changed, 255 insertions(+), 40 deletions(-) create mode 100644 src/test/run-pass/issue-40951.rs create mode 100644 src/test/run-pass/type-infer-generalize-ty-var.rs diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs index 5d33d6e6d2e71..96bf8c39682c7 100644 --- a/src/librustc/infer/combine.rs +++ b/src/librustc/infer/combine.rs @@ -262,10 +262,16 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> { Ok(()) } - /// Attempts to generalize `ty` for the type variable `for_vid`. This checks for cycle -- that - /// is, whether the type `ty` references `for_vid`. If `make_region_vars` is true, it will also - /// replace all regions with fresh variables. Returns `TyError` in the case of a cycle, `Ok` + /// Attempts to generalize `ty` for the type variable `for_vid`. + /// This checks for cycle -- that is, whether the type `ty` + /// references `for_vid`. If `make_region_vars` is true, it will + /// also replace all regions/unbound-type-variables with fresh + /// variables. Returns `TyError` in the case of a cycle, `Ok` /// otherwise. + /// + /// Preconditions: + /// + /// - `for_vid` is a "root vid" fn generalize(&self, ty: Ty<'tcx>, for_vid: ty::TyVid, @@ -275,7 +281,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> { let mut generalize = Generalizer { infcx: self.infcx, span: self.trace.cause.span, - for_vid: for_vid, + for_vid_sub_root: self.infcx.type_variables.borrow_mut().sub_root_var(for_vid), make_region_vars: make_region_vars, cycle_detected: false }; @@ -291,7 +297,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> { struct Generalizer<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> { infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, span: Span, - for_vid: ty::TyVid, + for_vid_sub_root: ty::TyVid, make_region_vars: bool, cycle_detected: bool, } @@ -303,17 +309,17 @@ impl<'cx, 'gcx, 'tcx> ty::fold::TypeFolder<'gcx, 'tcx> for Generalizer<'cx, 'gcx fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { // Check to see whether the type we are genealizing references - // `vid`. At the same time, also update any type variables to - // the values that they are bound to. This is needed to truly - // check for cycles, but also just makes things readable. - // - // (In particular, you could have something like `$0 = Box<$1>` - // where `$1` has already been instantiated with `Box<$0>`) + // any other type variable related to `vid` via + // subtyping. This is basically our "occurs check", preventing + // us from creating infinitely sized types. match t.sty { ty::TyInfer(ty::TyVar(vid)) => { let mut variables = self.infcx.type_variables.borrow_mut(); let vid = variables.root_var(vid); - if vid == self.for_vid { + let sub_vid = variables.sub_root_var(vid); + if sub_vid == self.for_vid_sub_root { + // If sub-roots are equal, then `for_vid` and + // `vid` are related via subtyping. self.cycle_detected = true; self.tcx().types.err } else { @@ -322,7 +328,18 @@ impl<'cx, 'gcx, 'tcx> ty::fold::TypeFolder<'gcx, 'tcx> for Generalizer<'cx, 'gcx drop(variables); self.fold_ty(u) } - None => t, + None => { + if self.make_region_vars { + let origin = variables.origin(vid); + let new_var_id = variables.new_var(false, origin, None); + let u = self.tcx().mk_var(new_var_id); + debug!("generalize: replacing original vid={:?} with new={:?}", + vid, u); + u + } else { + t + } + } } } } diff --git a/src/librustc/infer/fudge.rs b/src/librustc/infer/fudge.rs index 806b94486615f..72b23a3bc181c 100644 --- a/src/librustc/infer/fudge.rs +++ b/src/librustc/infer/fudge.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use ty::{self, TyCtxt}; +use infer::type_variable::TypeVariableMap; +use ty::{self, Ty, TyCtxt}; use ty::fold::{TypeFoldable, TypeFolder}; use super::InferCtxt; @@ -54,57 +55,52 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// the actual types (`?T`, `Option(&self, origin: &RegionVariableOrigin, f: F) -> Result where F: FnOnce() -> Result, T: TypeFoldable<'tcx>, { - let (region_vars, value) = self.probe(|snapshot| { - let vars_at_start = self.type_variables.borrow().num_vars(); + debug!("fudge_regions_if_ok(origin={:?})", origin); + let (type_variables, region_vars, value) = self.probe(|snapshot| { match f() { Ok(value) => { let value = self.resolve_type_vars_if_possible(&value); // At this point, `value` could in principle refer - // to regions that have been created during the - // snapshot (we assert below that `f()` does not - // create any new type variables, so there - // shouldn't be any of those). Once we exit - // `probe()`, those are going to be popped, so we - // will have to eliminate any references to them. - - assert_eq!(self.type_variables.borrow().num_vars(), vars_at_start, - "type variables were created during fudge_regions_if_ok"); + // to types/regions that have been created during + // the snapshot. Once we exit `probe()`, those are + // going to be popped, so we will have to + // eliminate any references to them. + + let type_variables = + self.type_variables.borrow_mut().types_created_since_snapshot( + &snapshot.type_snapshot); let region_vars = self.region_vars.vars_created_since_snapshot( &snapshot.region_vars_snapshot); - Ok((region_vars, value)) + Ok((type_variables, region_vars, value)) } Err(e) => Err(e), } })?; // At this point, we need to replace any of the now-popped - // region variables that appear in `value` with a fresh region - // variable. We can't do this during the probe because they - // would just get popped then too. =) + // type/region variables that appear in `value` with a fresh + // variable of the appropriate kind. We can't do this during + // the probe because they would just get popped then too. =) // Micro-optimization: if no variables have been created, then // `value` can't refer to any of them. =) So we can just return it. - if region_vars.is_empty() { + if type_variables.is_empty() && region_vars.is_empty() { return Ok(value); } let mut fudger = RegionFudger { infcx: self, + type_variables: &type_variables, region_vars: ®ion_vars, origin: origin }; @@ -115,6 +111,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { pub struct RegionFudger<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + type_variables: &'a TypeVariableMap, region_vars: &'a Vec, origin: &'a RegionVariableOrigin, } @@ -124,6 +121,32 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionFudger<'a, 'gcx, 'tcx> { self.infcx.tcx } + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + match ty.sty { + ty::TyInfer(ty::InferTy::TyVar(vid)) => { + match self.type_variables.get(&vid) { + None => { + // This variable was created before the + // "fudging". Since we refresh all type + // variables to their binding anyhow, we know + // that it is unbound, so we can just return + // it. + debug_assert!(self.infcx.type_variables.borrow_mut().probe(vid).is_none()); + ty + } + + Some(&origin) => { + // This variable was created during the + // fudging. Recreate it with a fresh variable + // here. + self.infcx.next_ty_var(origin) + } + } + } + _ => ty.super_fold_with(self), + } + } + fn fold_region(&mut self, r: &'tcx ty::Region) -> &'tcx ty::Region { match *r { ty::ReVar(v) if self.region_vars.contains(&v) => { diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs index 9c8419d9546d2..6ff496fa88541 100644 --- a/src/librustc/infer/type_variable.rs +++ b/src/librustc/infer/type_variable.rs @@ -21,15 +21,39 @@ use std::cmp::min; use std::marker::PhantomData; use std::mem; use std::u32; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::snapshot_vec as sv; use rustc_data_structures::unify as ut; pub struct TypeVariableTable<'tcx> { values: sv::SnapshotVec>, + + /// Two variables are unified in `eq_relations` when we have a + /// constraint `?X == ?Y`. eq_relations: ut::UnificationTable, + + /// Two variables are unified in `eq_relations` when we have a + /// constraint `?X <: ?Y` *or* a constraint `?Y <: ?X`. This second + /// table exists only to help with the occurs check. In particular, + /// we want to report constraints like these as an occurs check + /// violation: + /// + /// ?1 <: ?3 + /// Box <: ?1 + /// + /// This works because `?1` and `?3` are unified in the + /// `sub_relations` relation (not in `eq_relations`). Then when we + /// process the `Box <: ?1` constraint, we do an occurs check + /// on `Box` and find a potential cycle. + /// + /// This is reasonable because, in Rust, subtypes have the same + /// "skeleton" and hence there is no possible type such that + /// (e.g.) `Box <: ?3` for any `?3`. + sub_relations: ut::UnificationTable, } /// Reasons to create a type inference variable +#[derive(Copy, Clone)] pub enum TypeVariableOrigin { MiscVariable(Span), NormalizeProjectionType(Span), @@ -42,8 +66,11 @@ pub enum TypeVariableOrigin { DivergingStmt(Span), DivergingBlockExpr(Span), LatticeVariable(Span), + Generalized(ty::TyVid), } +pub type TypeVariableMap = FxHashMap; + struct TypeVariableData<'tcx> { value: TypeVariableValue<'tcx>, origin: TypeVariableOrigin, @@ -72,6 +99,7 @@ pub struct Default<'tcx> { pub struct Snapshot { snapshot: sv::Snapshot, eq_snapshot: ut::Snapshot, + sub_snapshot: ut::Snapshot, } enum UndoEntry<'tcx> { @@ -106,6 +134,7 @@ impl<'tcx> TypeVariableTable<'tcx> { TypeVariableTable { values: sv::SnapshotVec::new(), eq_relations: ut::UnificationTable::new(), + sub_relations: ut::UnificationTable::new(), } } @@ -135,6 +164,7 @@ impl<'tcx> TypeVariableTable<'tcx> { let a = self.root_var(a); let b = self.root_var(b); if a != b { + self.sub_relations.union(a, b); if dir == EqTo { // a and b must be equal which we mark in the unification table let root = self.eq_relations.union(a, b); @@ -197,6 +227,7 @@ impl<'tcx> TypeVariableTable<'tcx> { origin: TypeVariableOrigin, default: Option>,) -> ty::TyVid { self.eq_relations.new_key(()); + self.sub_relations.new_key(()); let index = self.values.push(TypeVariableData { value: Bounded { relations: vec![], default: default }, origin: origin, @@ -211,15 +242,41 @@ impl<'tcx> TypeVariableTable<'tcx> { self.values.len() } + /// Returns the "root" variable of `vid` in the `eq_relations` + /// equivalence table. All type variables that have been equated + /// will yield the same root variable (per the union-find + /// algorithm), so `root_var(a) == root_var(b)` implies that `a == + /// b` (transitively). pub fn root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { self.eq_relations.find(vid) } + /// Returns the "root" variable of `vid` in the `sub_relations` + /// equivalence table. All type variables that have been are + /// related via equality or subtyping will yield the same root + /// variable (per the union-find algorithm), so `sub_root_var(a) + /// == sub_root_var(b)` implies that: + /// + /// exists X. (a <: X || X <: a) && (b <: X || X <: b) + pub fn sub_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { + self.sub_relations.find(vid) + } + + /// True if `a` and `b` have same "sub-root" (i.e., exists some + /// type X such that `forall i in {a, b}. (i <: X || X <: i)`. + pub fn sub_unified(&mut self, a: ty::TyVid, b: ty::TyVid) -> bool { + self.sub_root_var(a) == self.sub_root_var(b) + } + pub fn probe(&mut self, vid: ty::TyVid) -> Option> { let vid = self.root_var(vid); self.probe_root(vid) } + pub fn origin(&self, vid: ty::TyVid) -> TypeVariableOrigin { + self.values.get(vid.index as usize).origin.clone() + } + /// Retrieves the type of `vid` given that it is currently a root in the unification table pub fn probe_root(&mut self, vid: ty::TyVid) -> Option> { debug_assert!(self.root_var(vid) == vid); @@ -245,6 +302,7 @@ impl<'tcx> TypeVariableTable<'tcx> { Snapshot { snapshot: self.values.start_snapshot(), eq_snapshot: self.eq_relations.snapshot(), + sub_snapshot: self.sub_relations.snapshot(), } } @@ -260,13 +318,37 @@ impl<'tcx> TypeVariableTable<'tcx> { } }); - self.values.rollback_to(s.snapshot); - self.eq_relations.rollback_to(s.eq_snapshot); + let Snapshot { snapshot, eq_snapshot, sub_snapshot } = s; + self.values.rollback_to(snapshot); + self.eq_relations.rollback_to(eq_snapshot); + self.sub_relations.rollback_to(sub_snapshot); } pub fn commit(&mut self, s: Snapshot) { - self.values.commit(s.snapshot); - self.eq_relations.commit(s.eq_snapshot); + let Snapshot { snapshot, eq_snapshot, sub_snapshot } = s; + self.values.commit(snapshot); + self.eq_relations.commit(eq_snapshot); + self.sub_relations.commit(sub_snapshot); + } + + /// Returns a map `{V1 -> V2}`, where the keys `{V1}` are + /// ty-variables created during the snapshot, and the values + /// `{V2}` are the root variables that they were unified with, + /// along with their origin. + pub fn types_created_since_snapshot(&mut self, s: &Snapshot) -> TypeVariableMap { + let actions_since_snapshot = self.values.actions_since_snapshot(&s.snapshot); + + actions_since_snapshot + .iter() + .filter_map(|action| match action { + &sv::UndoLog::NewElem(index) => Some(ty::TyVid { index: index as u32 }), + _ => None, + }) + .map(|vid| { + let origin = self.values.get(vid.index as usize).origin.clone(); + (vid, origin) + }) + .collect() } pub fn types_escaping_snapshot(&mut self, s: &Snapshot) -> Vec> { diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 0e5c786cd8dcf..c46b0f67d97bf 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -68,6 +68,19 @@ struct FindLocalByTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { found_pattern: Option<&'a Pat>, } +impl<'a, 'gcx, 'tcx> FindLocalByTypeVisitor<'a, 'gcx, 'tcx> { + fn is_match(&self, ty: Ty<'tcx>) -> bool { + ty == *self.target_ty || match (&ty.sty, &self.target_ty.sty) { + (&ty::TyInfer(ty::TyVar(a_vid)), &ty::TyInfer(ty::TyVar(b_vid))) => + self.infcx.type_variables + .borrow_mut() + .sub_unified(a_vid, b_vid), + + _ => false, + } + } +} + impl<'a, 'gcx, 'tcx> Visitor<'a> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> { fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'a> { NestedVisitorMap::None @@ -76,7 +89,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'a> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> { fn visit_local(&mut self, local: &'a Local) { if let Some(&ty) = self.infcx.tables.borrow().node_types.get(&local.id) { let ty = self.infcx.resolve_type_vars_if_possible(&ty); - let is_match = ty.walk().any(|t| t == *self.target_ty); + let is_match = ty.walk().any(|t| self.is_match(t)); if is_match && self.found_pattern.is_none() { self.found_pattern = Some(&*local.pat); diff --git a/src/test/run-pass/issue-40951.rs b/src/test/run-pass/issue-40951.rs new file mode 100644 index 0000000000000..adc7101b16aa1 --- /dev/null +++ b/src/test/run-pass/issue-40951.rs @@ -0,0 +1,20 @@ +// Copyright 2016 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 #40951. + +const FOO: [&'static str; 1] = ["foo"]; + +fn find(t: &[T], element: &T) { } + +fn main() { + let x = format!("hi"); + find(&FOO, &&*x); +} diff --git a/src/test/run-pass/type-infer-generalize-ty-var.rs b/src/test/run-pass/type-infer-generalize-ty-var.rs new file mode 100644 index 0000000000000..d7fb85ca4842e --- /dev/null +++ b/src/test/run-pass/type-infer-generalize-ty-var.rs @@ -0,0 +1,60 @@ +// Copyright 2016 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 a scenario where we generate a constraint like `?1 <: &?2`. +// In such a case, it is important that we instantiate `?1` with `&?3` +// where `?3 <: ?2`, and not with `&?2`. This is a regression test for +// #18653. The important thing is that we build. + +use std::cell::RefCell; + +enum Wrap { + WrapSome(A), + WrapNone +} + +use Wrap::*; + +struct T; +struct U; + +trait Get { + fn get(&self) -> &T; +} + +impl Get for Wrap { + fn get(&self) -> &(MyShow + 'static) { + static x: usize = 42; + &x + } +} + +impl Get for Wrap { + fn get(&self) -> &usize { + static x: usize = 55; + &x + } +} + +trait MyShow { fn dummy(&self) { } } +impl<'a> MyShow for &'a (MyShow + 'a) { } +impl MyShow for usize { } +fn constrain<'a>(rc: RefCell<&'a (MyShow + 'a)>) { } + +fn main() { + let mut collection: Wrap<_> = WrapNone; + + { + let __arg0 = Get::get(&collection); + let __args_cell = RefCell::new(__arg0); + constrain(__args_cell); + } + collection = WrapSome(T); +} From b6da61fba018cdd953077fd799665a73e8899ba6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 13 Apr 2017 06:54:12 -0400 Subject: [PATCH 2/3] fix tests --- src/test/compile-fail/issue-12187-1.rs | 1 + src/test/compile-fail/issue-12187-2.rs | 1 + src/test/compile-fail/issue-7813.rs | 3 +-- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/compile-fail/issue-12187-1.rs b/src/test/compile-fail/issue-12187-1.rs index 346fae11070e1..6aeb9442c40ed 100644 --- a/src/test/compile-fail/issue-12187-1.rs +++ b/src/test/compile-fail/issue-12187-1.rs @@ -16,4 +16,5 @@ fn main() { let &v = new(); //~^ ERROR type annotations needed [E0282] //~| NOTE cannot infer type for `_` + //~| NOTE consider giving a type to pattern } diff --git a/src/test/compile-fail/issue-12187-2.rs b/src/test/compile-fail/issue-12187-2.rs index 848174d6fe1e0..d52ed06c4085d 100644 --- a/src/test/compile-fail/issue-12187-2.rs +++ b/src/test/compile-fail/issue-12187-2.rs @@ -16,4 +16,5 @@ fn main() { let &v = new(); //~^ ERROR type annotations needed [E0282] //~| NOTE cannot infer type for `_` + //~| NOTE consider giving a type to pattern } diff --git a/src/test/compile-fail/issue-7813.rs b/src/test/compile-fail/issue-7813.rs index fdd89058fd397..a81c210184682 100644 --- a/src/test/compile-fail/issue-7813.rs +++ b/src/test/compile-fail/issue-7813.rs @@ -9,8 +9,7 @@ // except according to those terms. fn main() { - let v = &[]; + let v = &[]; //~ NOTE consider giving `v` a type let it = v.iter(); //~ ERROR type annotations needed [E0282] //~| NOTE cannot infer type for `T` - //~| NOTE consider giving `it` a type } From d45f62f3e4b72db36897acd4c2a492b479cae3cb Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 11 Apr 2017 16:58:04 -0400 Subject: [PATCH 3/3] fix a bug in compiletest JSON parsing for duplicate errors In some cases, we give multiple primary spans, in which case we would report one `//~` annotation per primary span. That was very confusing because these things are reported to the user as a single error. UI tests would be better here. --- src/test/compile-fail/binop-move-semantics.rs | 2 -- src/test/compile-fail/borrowck/borrowck-lend-flow-loop.rs | 1 - .../compile-fail/borrowck/borrowck-mut-borrow-linear-errors.rs | 1 - src/test/compile-fail/issue-25579.rs | 1 - src/test/compile-fail/issue-38412.rs | 1 - src/test/compile-fail/lint-unused-imports.rs | 1 - src/tools/compiletest/src/json.rs | 1 + 7 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/test/compile-fail/binop-move-semantics.rs b/src/test/compile-fail/binop-move-semantics.rs index 0cc6ea3e984d9..cff0064497aff 100644 --- a/src/test/compile-fail/binop-move-semantics.rs +++ b/src/test/compile-fail/binop-move-semantics.rs @@ -62,7 +62,6 @@ fn mut_plus_immut() { &mut f + &f; //~ ERROR: cannot borrow `f` as immutable because it is also borrowed as mutable - //~^ cannot borrow `f` as immutable because it is also borrowed as mutable } fn immut_plus_mut() { @@ -71,7 +70,6 @@ fn immut_plus_mut() { &f + &mut f; //~ ERROR: cannot borrow `f` as mutable because it is also borrowed as immutable - //~^ cannot borrow `f` as mutable because it is also borrowed as immutable } fn main() {} diff --git a/src/test/compile-fail/borrowck/borrowck-lend-flow-loop.rs b/src/test/compile-fail/borrowck/borrowck-lend-flow-loop.rs index 56cbe0b187867..f09e7ffd7e4b7 100644 --- a/src/test/compile-fail/borrowck/borrowck-lend-flow-loop.rs +++ b/src/test/compile-fail/borrowck/borrowck-lend-flow-loop.rs @@ -109,7 +109,6 @@ fn while_aliased_mut_cond(cond: bool, cond2: bool) { borrow(&*v); //~ ERROR cannot borrow if cond2 { x = &mut v; //~ ERROR cannot borrow - //~^ ERROR cannot borrow } } } diff --git a/src/test/compile-fail/borrowck/borrowck-mut-borrow-linear-errors.rs b/src/test/compile-fail/borrowck/borrowck-mut-borrow-linear-errors.rs index f789d44016eb1..38e0e27a7b98e 100644 --- a/src/test/compile-fail/borrowck/borrowck-mut-borrow-linear-errors.rs +++ b/src/test/compile-fail/borrowck/borrowck-mut-borrow-linear-errors.rs @@ -19,7 +19,6 @@ fn main() { match 1 { 1 => { addr = &mut x; } //~^ ERROR cannot borrow `x` as mutable more than once at a time - //~| ERROR cannot borrow `x` as mutable more than once at a time 2 => { addr = &mut x; } //~^ ERROR cannot borrow `x` as mutable more than once at a time _ => { addr = &mut x; } diff --git a/src/test/compile-fail/issue-25579.rs b/src/test/compile-fail/issue-25579.rs index 849c9aa18c905..323ce3b0adf33 100644 --- a/src/test/compile-fail/issue-25579.rs +++ b/src/test/compile-fail/issue-25579.rs @@ -17,7 +17,6 @@ fn causes_ice(mut l: &mut Sexpression) { loop { match l { &mut Sexpression::Num(ref mut n) => {}, &mut Sexpression::Cons(ref mut expr) => { //~ ERROR cannot borrow `l.0` - //~| ERROR cannot borrow `l.0` l = &mut **expr; //~ ERROR cannot assign to `l` } }} diff --git a/src/test/compile-fail/issue-38412.rs b/src/test/compile-fail/issue-38412.rs index 3b62aaf2ab8e9..b4feadbacf740 100644 --- a/src/test/compile-fail/issue-38412.rs +++ b/src/test/compile-fail/issue-38412.rs @@ -11,7 +11,6 @@ fn main() { let Box(a) = loop { }; //~^ ERROR expected tuple struct/variant, found struct `Box` - //~| ERROR expected tuple struct/variant, found struct `Box` // (The below is a trick to allow compiler to infer a type for // variable `a` without attempting to ascribe a type to the diff --git a/src/test/compile-fail/lint-unused-imports.rs b/src/test/compile-fail/lint-unused-imports.rs index f6f7c210f466a..5bb2ab75c53fd 100644 --- a/src/test/compile-fail/lint-unused-imports.rs +++ b/src/test/compile-fail/lint-unused-imports.rs @@ -21,7 +21,6 @@ use std::fmt::{}; // Should get errors for both 'Some' and 'None' use std::option::Option::{Some, None}; //~^ ERROR unused imports: `None`, `Some` -//~| ERROR unused imports: `None`, `Some` use test::A; //~ ERROR unused import: `test::A` // Be sure that if we just bring some methods into scope that they're also diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs index d9da1bdc34858..06cbd9a3df416 100644 --- a/src/tools/compiletest/src/json.rs +++ b/src/tools/compiletest/src/json.rs @@ -97,6 +97,7 @@ fn push_expected_errors(expected_errors: &mut Vec, let primary_spans: Vec<_> = spans_in_this_file.iter() .cloned() .filter(|span| span.is_primary) + .take(1) // sometimes we have more than one showing up in the json; pick first .collect(); let primary_spans = if primary_spans.is_empty() { // subdiagnostics often don't have a span of their own;