From d6871769be51415f6b248e64ddefea22871ad6f3 Mon Sep 17 00:00:00 2001 From: Mark Simulacrum Date: Tue, 20 Jun 2017 18:04:36 -0600 Subject: [PATCH 01/18] Fixes bootstrapping with custom cargo/rustc. config.mk is now always read when parsing the configuration to prevent this from reoccurring in the future, hopefully. --- src/bootstrap/bin/main.rs | 8 +------- src/bootstrap/config.rs | 10 ++++++++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/bootstrap/bin/main.rs b/src/bootstrap/bin/main.rs index 5ca5ce1648f2f..5ef18b89841f0 100644 --- a/src/bootstrap/bin/main.rs +++ b/src/bootstrap/bin/main.rs @@ -26,12 +26,6 @@ use bootstrap::{Flags, Config, Build}; fn main() { let args = env::args().skip(1).collect::>(); let flags = Flags::parse(&args); - let mut config = Config::parse(&flags.build, flags.config.clone()); - - // compat with `./configure` while we're still using that - if std::fs::metadata("config.mk").is_ok() { - config.update_with_config_mk(); - } - + let config = Config::parse(&flags.build, flags.config.clone()); Build::new(flags, config).build(); } diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index abad216d89be4..af10a5825805b 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -15,7 +15,7 @@ use std::collections::HashMap; use std::env; -use std::fs::File; +use std::fs::{self, File}; use std::io::prelude::*; use std::path::PathBuf; use std::process; @@ -404,6 +404,12 @@ impl Config { set(&mut config.rust_dist_src, t.src_tarball); } + + // compat with `./configure` while we're still using that + if fs::metadata("config.mk").is_ok() { + config.update_with_config_mk(); + } + return config } @@ -412,7 +418,7 @@ impl Config { /// While we still have `./configure` this implements the ability to decode /// that configuration into this. This isn't exactly a full-blown makefile /// parser, but hey it gets the job done! - pub fn update_with_config_mk(&mut self) { + fn update_with_config_mk(&mut self) { let mut config = String::new(); File::open("config.mk").unwrap().read_to_string(&mut config).unwrap(); for line in config.lines() { From d9b47c3364416b80e9048131e521257e0636a5de Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 18 Jun 2017 19:01:34 +0300 Subject: [PATCH 02/18] Backport fixes to LLVM 4.0 ARM codegen bugs So ARM had quite a few codegen bugs on LLVM 4.0 which are fixed on LLVM trunk. This backports 5 of them: r297871 - ARM: avoid clobbering register in v6 jump-table expansion. - fixes rust-lang/rust#42248 r294949 - [Thumb-1] TBB generation: spot redefinitions of index r295816 - [ARM] Fix constant islands pass. r300870 - [Thumb-1] Fix corner cases for compressed jump tables r302650 - [IfConversion] Add missing check in IfConversion/canFallThroughTo - unblocks rust-lang/rust#39409 --- src/llvm | 2 +- src/rustllvm/llvm-rebuild-trigger | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/llvm b/src/llvm index 1ef3b9128e1ba..8e1e6e6ff18a2 160000 --- a/src/llvm +++ b/src/llvm @@ -1 +1 @@ -Subproject commit 1ef3b9128e1baaed61b42d5b0de79dee100acf17 +Subproject commit 8e1e6e6ff18a24b417a5ec3254bb111b8a9b4713 diff --git a/src/rustllvm/llvm-rebuild-trigger b/src/rustllvm/llvm-rebuild-trigger index 70663f30e8f9e..792b496f1dbe9 100644 --- a/src/rustllvm/llvm-rebuild-trigger +++ b/src/rustllvm/llvm-rebuild-trigger @@ -1,4 +1,4 @@ # If this file is modified, then llvm will be (optionally) cleaned and then rebuilt. # The actual contents of this file do not matter, but to trigger a change on the # build bots then the contents should be changed so git updates the mtime. -2017-05-13 +2017-06-18 From 8551427b9f1d1daad29b06337324ecaa20a1fb18 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 18 Jun 2017 18:57:39 +0300 Subject: [PATCH 03/18] collector: apply param substs to closures cast to fn items Fixes #42718. --- src/librustc_trans/collector.rs | 4 ++++ src/test/run-pass/closure-to-fn-coercion.rs | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index da74ed88eaf85..d723cf325718e 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -493,6 +493,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { } mir::Rvalue::Cast(mir::CastKind::ClosureFnPointer, ref operand, _) => { let source_ty = operand.ty(self.mir, self.scx.tcx()); + let source_ty = self.scx.tcx().trans_apply_param_substs(self.param_substs, + &source_ty); match source_ty.sty { ty::TyClosure(def_id, substs) => { let instance = monomorphize::resolve_closure( @@ -543,6 +545,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { block: mir::BasicBlock, kind: &mir::TerminatorKind<'tcx>, location: Location) { + debug!("visiting terminator {:?} @ {:?}", kind, location); + let tcx = self.scx.tcx(); match *kind { mir::TerminatorKind::Call { ref func, .. } => { diff --git a/src/test/run-pass/closure-to-fn-coercion.rs b/src/test/run-pass/closure-to-fn-coercion.rs index 7fb26bdc9360d..343209143cc2f 100644 --- a/src/test/run-pass/closure-to-fn-coercion.rs +++ b/src/test/run-pass/closure-to-fn-coercion.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::mem; + const FOO: fn(u8) -> u8 = |v: u8| { v }; const BAR: [fn(&mut u32); 5] = [ @@ -21,6 +23,10 @@ fn func_specific() -> (fn() -> u32) { || return 42 } +fn generic(_: T) -> fn() -> usize { + || mem::size_of::() +} + fn main() { // Items assert_eq!(func_specific()(), 42); @@ -34,4 +40,5 @@ fn main() { assert_eq!({ BAR[2](&mut a); a }, 3); assert_eq!({ BAR[3](&mut a); a }, 6); assert_eq!({ BAR[4](&mut a); a }, 10); + assert_eq!(generic(0i8)(), 1); } From 433450ae7f24d9a23c8dbb395f0a91a944a1913f Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sat, 17 Jun 2017 10:28:31 +0000 Subject: [PATCH 04/18] resolve: fix perf bug. --- src/librustc_resolve/build_reduced_graph.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 597a62f86884b..3450f1496e524 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -523,7 +523,10 @@ impl<'a> Resolver<'a> { }; let kind = ModuleKind::Def(Def::Mod(def_id), name); - self.arenas.alloc_module(ModuleData::new(parent, kind, def_id, Mark::root(), DUMMY_SP)) + let module = + self.arenas.alloc_module(ModuleData::new(parent, kind, def_id, Mark::root(), DUMMY_SP)); + self.extern_module_map.insert((def_id, macros_only), module); + module } pub fn macro_def_scope(&mut self, expansion: Mark) -> Module<'a> { From d8fd8df767145e1ef153bfe8e16f10069373ec87 Mon Sep 17 00:00:00 2001 From: Mark Simulacrum Date: Fri, 16 Jun 2017 07:44:09 -0600 Subject: [PATCH 05/18] Use custom cargo/rustc paths when parsing flags. --- src/bootstrap/flags.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index f100baa5d2ca7..68b1dd11a47c5 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -237,11 +237,18 @@ Arguments: let cwd = t!(env::current_dir()); let paths = matches.free[1..].iter().map(|p| cwd.join(p)).collect::>(); + let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| { + if fs::metadata("config.toml").is_ok() { + Some(PathBuf::from("config.toml")) + } else { + None + } + }); // All subcommands can have an optional "Available paths" section if matches.opt_present("verbose") { let flags = Flags::parse(&["build".to_string()]); - let mut config = Config::default(); + let mut config = Config::parse(&flags.build, cfg_file.clone()); config.build = flags.build.clone(); let mut build = Build::new(flags, config); metadata::build(&mut build); @@ -302,14 +309,6 @@ Arguments: }; - let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| { - if fs::metadata("config.toml").is_ok() { - Some(PathBuf::from("config.toml")) - } else { - None - } - }); - let mut stage = matches.opt_str("stage").map(|j| j.parse().unwrap()); if matches.opt_present("incremental") { From f6e76be89eb3a10abd358490001a43c93b9bfcdf Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 14 Jun 2017 12:46:14 -0400 Subject: [PATCH 06/18] move `implied_bounds` into regionck --- src/librustc/middle/free_region.rs | 20 +--- src/librustc/ty/wf.rs | 128 --------------------- src/librustc_typeck/check/regionck.rs | 159 +++++++++++++++++++++++--- 3 files changed, 142 insertions(+), 165 deletions(-) diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs index 6a21bdc19e091..de738fba30e92 100644 --- a/src/librustc/middle/free_region.rs +++ b/src/librustc/middle/free_region.rs @@ -18,7 +18,6 @@ use hir::def_id::DefId; use middle::region::RegionMaps; use ty::{self, Lift, TyCtxt, Region}; -use ty::wf::ImpliedBound; use rustc_data_structures::transitive_relation::TransitiveRelation; /// Combines a `RegionMaps` (which governs relationships between @@ -136,23 +135,6 @@ impl<'tcx> FreeRegionMap<'tcx> { self.relation.is_empty() } - pub fn relate_free_regions_from_implied_bounds(&mut self, - implied_bounds: &[ImpliedBound<'tcx>]) - { - debug!("relate_free_regions_from_implied_bounds()"); - for implied_bound in implied_bounds { - debug!("implied bound: {:?}", implied_bound); - match *implied_bound { - ImpliedBound::RegionSubRegion(a, b) => { - self.relate_regions(a, b); - } - ImpliedBound::RegionSubParam(..) | - ImpliedBound::RegionSubProjection(..) => { - } - } - } - } - pub fn relate_free_regions_from_predicates(&mut self, predicates: &[ty::Predicate<'tcx>]) { debug!("relate_free_regions_from_predicates(predicates={:?})", predicates); @@ -177,7 +159,7 @@ impl<'tcx> FreeRegionMap<'tcx> { // Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`. // (with the exception that `'static: 'x` is not notable) - fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) { + pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) { if (is_free(sub) || *sub == ty::ReStatic) && is_free(sup) { self.relation.add(sub, sup) } diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs index aa2c9802e5473..2eb0acac4f7ec 100644 --- a/src/librustc/ty/wf.rs +++ b/src/librustc/ty/wf.rs @@ -10,7 +10,6 @@ use hir::def_id::DefId; use infer::InferCtxt; -use ty::outlives::Component; use ty::subst::Substs; use traits; use ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable}; @@ -107,133 +106,6 @@ pub fn predicate_obligations<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, wf.normalize() } -/// Implied bounds are region relationships that we deduce -/// automatically. The idea is that (e.g.) a caller must check that a -/// function's argument types are well-formed immediately before -/// calling that fn, and hence the *callee* can assume that its -/// argument types are well-formed. This may imply certain relationships -/// between generic parameters. For example: -/// -/// fn foo<'a,T>(x: &'a T) -/// -/// can only be called with a `'a` and `T` such that `&'a T` is WF. -/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. -#[derive(Debug)] -pub enum ImpliedBound<'tcx> { - RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), - RegionSubParam(ty::Region<'tcx>, ty::ParamTy), - RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>), -} - -/// Compute the implied bounds that a callee/impl can assume based on -/// the fact that caller/projector has ensured that `ty` is WF. See -/// the `ImpliedBound` type for more details. -pub fn implied_bounds<'a, 'gcx, 'tcx>( - infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - body_id: ast::NodeId, - ty: Ty<'tcx>, - span: Span) - -> Vec> -{ - // Sometimes when we ask what it takes for T: WF, we get back that - // U: WF is required; in that case, we push U onto this stack and - // process it next. Currently (at least) these resulting - // predicates are always guaranteed to be a subset of the original - // type, so we need not fear non-termination. - let mut wf_types = vec![ty]; - - let mut implied_bounds = vec![]; - - while let Some(ty) = wf_types.pop() { - // Compute the obligations for `ty` to be well-formed. If `ty` is - // an unresolved inference variable, just substituted an empty set - // -- because the return type here is going to be things we *add* - // to the environment, it's always ok for this set to be smaller - // than the ultimate set. (Note: normally there won't be - // unresolved inference variables here anyway, but there might be - // during typeck under some circumstances.) - let obligations = obligations(infcx, param_env, body_id, ty, span).unwrap_or(vec![]); - - // From the full set of obligations, just filter down to the - // region relationships. - implied_bounds.extend( - obligations - .into_iter() - .flat_map(|obligation| { - assert!(!obligation.has_escaping_regions()); - match obligation.predicate { - ty::Predicate::Trait(..) | - ty::Predicate::Equate(..) | - ty::Predicate::Subtype(..) | - ty::Predicate::Projection(..) | - ty::Predicate::ClosureKind(..) | - ty::Predicate::ObjectSafe(..) => - vec![], - - ty::Predicate::WellFormed(subty) => { - wf_types.push(subty); - vec![] - } - - ty::Predicate::RegionOutlives(ref data) => - match infcx.tcx.no_late_bound_regions(data) { - None => - vec![], - Some(ty::OutlivesPredicate(r_a, r_b)) => - vec![ImpliedBound::RegionSubRegion(r_b, r_a)], - }, - - ty::Predicate::TypeOutlives(ref data) => - match infcx.tcx.no_late_bound_regions(data) { - None => vec![], - Some(ty::OutlivesPredicate(ty_a, r_b)) => { - let ty_a = infcx.resolve_type_vars_if_possible(&ty_a); - let components = infcx.tcx.outlives_components(ty_a); - implied_bounds_from_components(r_b, components) - } - }, - }})); - } - - implied_bounds -} - -/// When we have an implied bound that `T: 'a`, we can further break -/// this down to determine what relationships would have to hold for -/// `T: 'a` to hold. We get to assume that the caller has validated -/// those relationships. -fn implied_bounds_from_components<'tcx>(sub_region: ty::Region<'tcx>, - sup_components: Vec>) - -> Vec> -{ - sup_components - .into_iter() - .flat_map(|component| { - match component { - Component::Region(r) => - vec![ImpliedBound::RegionSubRegion(sub_region, r)], - Component::Param(p) => - vec![ImpliedBound::RegionSubParam(sub_region, p)], - Component::Projection(p) => - vec![ImpliedBound::RegionSubProjection(sub_region, p)], - Component::EscapingProjection(_) => - // If the projection has escaping regions, don't - // try to infer any implied bounds even for its - // free components. This is conservative, because - // the caller will still have to prove that those - // free components outlive `sub_region`. But the - // idea is that the WAY that the caller proves - // that may change in the future and we want to - // give ourselves room to get smarter here. - vec![], - Component::UnresolvedInferenceVariable(..) => - vec![], - } - }) - .collect() -} - struct WfPredicates<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index b66e311f04c6b..2b0542d3fc2b5 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -94,7 +94,8 @@ use rustc::traits; use rustc::ty::{self, Ty, TypeFoldable}; use rustc::infer::{self, GenericKind, SubregionOrigin, VerifyBound}; use rustc::ty::adjustment; -use rustc::ty::wf::ImpliedBound; +use rustc::ty::outlives::Component; +use rustc::ty::wf; use std::mem; use std::ops::Deref; @@ -196,6 +197,24 @@ pub struct RegionCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { } +/// Implied bounds are region relationships that we deduce +/// automatically. The idea is that (e.g.) a caller must check that a +/// function's argument types are well-formed immediately before +/// calling that fn, and hence the *callee* can assume that its +/// argument types are well-formed. This may imply certain relationships +/// between generic parameters. For example: +/// +/// fn foo<'a,T>(x: &'a T) +/// +/// can only be called with a `'a` and `T` such that `&'a T` is WF. +/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. +#[derive(Debug)] +enum ImpliedBound<'tcx> { + RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), + RegionSubParam(ty::Region<'tcx>, ty::ParamTy), + RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>), +} + impl<'a, 'gcx, 'tcx> Deref for RegionCtxt<'a, 'gcx, 'tcx> { type Target = FnCtxt<'a, 'gcx, 'tcx>; fn deref(&self) -> &Self::Target { @@ -386,11 +405,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { for &ty in fn_sig_tys { let ty = self.resolve_type(ty); debug!("relate_free_regions(t={:?})", ty); - let implied_bounds = - ty::wf::implied_bounds(self, self.fcx.param_env, body_id, ty, span); - - // Record any relations between free regions that we observe into the free-region-map. - self.free_region_map.relate_free_regions_from_implied_bounds(&implied_bounds); + let implied_bounds = self.implied_bounds(body_id, ty, span); // But also record other relationships, such as `T:'x`, // that don't go into the free-region-map but which we use @@ -410,16 +425,18 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { ImpliedBound::RegionSubProjection(r_a, projection_b) => { self.region_bound_pairs.push((r_a, GenericKind::Projection(projection_b))); } - ImpliedBound::RegionSubRegion(..) => { + ImpliedBound::RegionSubRegion(r_a, r_b) => { // In principle, we could record (and take // advantage of) every relationship here, but // we are also free not to -- it simply means // strictly less that we can successfully type - // check. (It may also be that we should - // revise our inference system to be more - // general and to make use of *every* - // relationship that arises here, but - // presently we do not.) + // check. Right now we only look for things + // relationships between free regions. (It may + // also be that we should revise our inference + // system to be more general and to make use + // of *every* relationship that arises here, + // but presently we do not.) + self.free_region_map.relate_regions(r_a, r_b); } } } @@ -428,6 +445,112 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { debug!("<< relate_free_regions"); } + /// Compute the implied bounds that a callee/impl can assume based on + /// the fact that caller/projector has ensured that `ty` is WF. See + /// the `ImpliedBound` type for more details. + fn implied_bounds(&mut self, body_id: ast::NodeId, ty: Ty<'tcx>, span: Span) + -> Vec> { + // Sometimes when we ask what it takes for T: WF, we get back that + // U: WF is required; in that case, we push U onto this stack and + // process it next. Currently (at least) these resulting + // predicates are always guaranteed to be a subset of the original + // type, so we need not fear non-termination. + let mut wf_types = vec![ty]; + + let mut implied_bounds = vec![]; + + while let Some(ty) = wf_types.pop() { + // Compute the obligations for `ty` to be well-formed. If `ty` is + // an unresolved inference variable, just substituted an empty set + // -- because the return type here is going to be things we *add* + // to the environment, it's always ok for this set to be smaller + // than the ultimate set. (Note: normally there won't be + // unresolved inference variables here anyway, but there might be + // during typeck under some circumstances.) + let obligations = + wf::obligations(self, self.fcx.param_env, body_id, ty, span) + .unwrap_or(vec![]); + + // From the full set of obligations, just filter down to the + // region relationships. + implied_bounds.extend( + obligations + .into_iter() + .flat_map(|obligation| { + assert!(!obligation.has_escaping_regions()); + match obligation.predicate { + ty::Predicate::Trait(..) | + ty::Predicate::Equate(..) | + ty::Predicate::Subtype(..) | + ty::Predicate::Projection(..) | + ty::Predicate::ClosureKind(..) | + ty::Predicate::ObjectSafe(..) => + vec![], + + ty::Predicate::WellFormed(subty) => { + wf_types.push(subty); + vec![] + } + + ty::Predicate::RegionOutlives(ref data) => + match self.tcx.no_late_bound_regions(data) { + None => + vec![], + Some(ty::OutlivesPredicate(r_a, r_b)) => + vec![ImpliedBound::RegionSubRegion(r_b, r_a)], + }, + + ty::Predicate::TypeOutlives(ref data) => + match self.tcx.no_late_bound_regions(data) { + None => vec![], + Some(ty::OutlivesPredicate(ty_a, r_b)) => { + let ty_a = self.resolve_type_vars_if_possible(&ty_a); + let components = self.tcx.outlives_components(ty_a); + self.implied_bounds_from_components(r_b, components) + } + }, + }})); + } + + implied_bounds + } + + /// When we have an implied bound that `T: 'a`, we can further break + /// this down to determine what relationships would have to hold for + /// `T: 'a` to hold. We get to assume that the caller has validated + /// those relationships. + fn implied_bounds_from_components(&self, + sub_region: ty::Region<'tcx>, + sup_components: Vec>) + -> Vec> + { + sup_components + .into_iter() + .flat_map(|component| { + match component { + Component::Region(r) => + vec![ImpliedBound::RegionSubRegion(sub_region, r)], + Component::Param(p) => + vec![ImpliedBound::RegionSubParam(sub_region, p)], + Component::Projection(p) => + vec![ImpliedBound::RegionSubProjection(sub_region, p)], + Component::EscapingProjection(_) => + // If the projection has escaping regions, don't + // try to infer any implied bounds even for its + // free components. This is conservative, because + // the caller will still have to prove that those + // free components outlive `sub_region`. But the + // idea is that the WAY that the caller proves + // that may change in the future and we want to + // give ourselves room to get smarter here. + vec![], + Component::UnresolvedInferenceVariable(..) => + vec![], + } + }) + .collect() + } + fn resolve_regions_and_report_errors(&self) { self.fcx.resolve_regions_and_report_errors(self.subject_def_id, &self.region_maps, @@ -1357,25 +1480,25 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { fn components_must_outlive(&self, origin: infer::SubregionOrigin<'tcx>, - components: Vec>, + components: Vec>, region: ty::Region<'tcx>) { for component in components { let origin = origin.clone(); match component { - ty::outlives::Component::Region(region1) => { + Component::Region(region1) => { self.sub_regions(origin, region, region1); } - ty::outlives::Component::Param(param_ty) => { + Component::Param(param_ty) => { self.param_ty_must_outlive(origin, region, param_ty); } - ty::outlives::Component::Projection(projection_ty) => { + Component::Projection(projection_ty) => { self.projection_must_outlive(origin, region, projection_ty); } - ty::outlives::Component::EscapingProjection(subcomponents) => { + Component::EscapingProjection(subcomponents) => { self.components_must_outlive(origin, subcomponents, region); } - ty::outlives::Component::UnresolvedInferenceVariable(v) => { + Component::UnresolvedInferenceVariable(v) => { // ignore this, we presume it will yield an error // later, since if a type variable is not resolved by // this point it never will be From 785e82cd4958a4dffbbb2d3acf5fe10740a76855 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 14 Jun 2017 14:25:41 -0400 Subject: [PATCH 07/18] register the obligations from `wf::implied_bounds` Fixes #42552. Fixes #42545. --- src/librustc_typeck/check/mod.rs | 3 +- src/librustc_typeck/check/regionck.rs | 26 +++++++++++++++++ src/test/run-pass/issue-42552.rs | 40 +++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 src/test/run-pass/issue-42552.rs diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index c3bce8048796b..6e1c403cbc020 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -583,7 +583,8 @@ impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> { .register_predicate_obligation(self, obligation); } - fn register_predicates(&self, obligations: Vec>) { + fn register_predicates(&self, obligations: I) + where I: IntoIterator> { for obligation in obligations { self.register_predicate(obligation); } diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 2b0542d3fc2b5..037d172a4d608 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -471,6 +471,32 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { wf::obligations(self, self.fcx.param_env, body_id, ty, span) .unwrap_or(vec![]); + // NB: All of these predicates *ought* to be easily proven + // true. In fact, their correctness is (mostly) implied by + // other parts of the program. However, in #42552, we had + // an annoying scenario where: + // + // - Some `T::Foo` gets normalized, resulting in a + // variable `_1` and a `T: Trait` constraint + // (not sure why it couldn't immediately get + // solved). This result of `_1` got cached. + // - These obligations were dropped on the floor here, + // rather than being registered. + // - Then later we would get a request to normalize + // `T::Foo` which would result in `_1` being used from + // the cache, but hence without the `T: Trait` + // constraint. As a result, `_1` never gets resolved, + // and we get an ICE (in dropck). + // + // Therefore, we register any predicates involving + // inference variables. We restrict ourselves to those + // involving inference variables both for efficiency and + // to avoids duplicate errors that otherwise show up. + self.fcx.register_predicates( + obligations.iter() + .filter(|o| o.predicate.has_infer_types()) + .cloned()); + // From the full set of obligations, just filter down to the // region relationships. implied_bounds.extend( diff --git a/src/test/run-pass/issue-42552.rs b/src/test/run-pass/issue-42552.rs new file mode 100644 index 0000000000000..fd1265b7174f6 --- /dev/null +++ b/src/test/run-pass/issue-42552.rs @@ -0,0 +1,40 @@ +// 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 an obscure issue with the projection cache. + +fn into_iter(a: &I) -> Groups { + Groups { _a: a } +} + +pub struct Groups<'a, I: 'a> { + _a: &'a I, +} + +impl<'a, I: Iterator> Iterator for Groups<'a, I> { + type Item = Group<'a, I>; + fn next(&mut self) -> Option { + None + } +} + +pub struct Group<'a, I: Iterator + 'a> + where I::Item: 'a // <-- needed to trigger ICE! +{ + _phantom: &'a (), + _ice_trigger: I::Item, // <-- needed to trigger ICE! +} + + +fn main() { + let _ = into_iter(&[0].iter().map(|_| 0)).map(|grp| { + let _g = grp; + }); +} From 21b276f32c3a6d6c59c3650d17ded90409fb06b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 13 Jun 2017 18:36:01 +0200 Subject: [PATCH 08/18] Change the for-loop desugar so the `break` does not affect type inference. Fixes #42618 --- src/libcore/iter/mod.rs | 6 ++++-- src/librustc/hir/lowering.rs | 37 ++++++++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index ee81151348772..c91fd16391aaf 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -191,10 +191,12 @@ //! { //! let result = match IntoIterator::into_iter(values) { //! mut iter => loop { -//! let x = match iter.next() { -//! Some(val) => val, +//! let next; +//! match iter.next() { +//! Some(val) => next = val, //! None => break, //! }; +//! let x = next; //! let () = { println!("{}", x); }; //! }, //! }; diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 4c436fb640f01..00d42e3cc6585 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -2168,11 +2168,13 @@ impl<'a> LoweringContext<'a> { // let result = match ::std::iter::IntoIterator::into_iter() { // mut iter => { // [opt_ident]: loop { - // let = match ::std::iter::Iterator::next(&mut iter) { - // ::std::option::Option::Some(val) => val, + // let next; + // match ::std::iter::Iterator::next(&mut iter) { + // ::std::option::Option::Some(val) => next = val, // ::std::option::Option::None => break // }; - // SemiExpr(); + // let = next; + // StmtExpr(); // } // } // }; @@ -2184,13 +2186,18 @@ impl<'a> LoweringContext<'a> { let iter = self.str_to_ident("iter"); - // `::std::option::Option::Some(val) => val` + let next_ident = self.str_to_ident("next"); + let next_pat = self.pat_ident(e.span, next_ident); + + // `::std::option::Option::Some(val) => next = val` let pat_arm = { let val_ident = self.str_to_ident("val"); let val_pat = self.pat_ident(e.span, val_ident); let val_expr = P(self.expr_ident(e.span, val_ident, val_pat.id)); + let next_expr = P(self.expr_ident(e.span, next_ident, next_pat.id)); + let assign = P(self.expr(e.span, hir::ExprAssign(next_expr, val_expr), ThinVec::new())); let some_pat = self.pat_some(e.span, val_pat); - self.arm(hir_vec![some_pat], val_expr) + self.arm(hir_vec![some_pat], assign) }; // `::std::option::Option::None => break` @@ -2220,10 +2227,20 @@ impl<'a> LoweringContext<'a> { hir::MatchSource::ForLoopDesugar), ThinVec::new())) }; + let match_stmt = respan(e.span, hir::StmtExpr(match_expr, self.next_id())); + + let next_expr = P(self.expr_ident(e.span, next_ident, next_pat.id)); + + // `let next` + let next_let = self.stmt_let_pat(e.span, + None, + next_pat, + hir::LocalSource::ForLoopDesugar); + // `let = next` let pat = self.lower_pat(pat); let pat_let = self.stmt_let_pat(e.span, - match_expr, + Some(next_expr), pat, hir::LocalSource::ForLoopDesugar); @@ -2232,7 +2249,7 @@ impl<'a> LoweringContext<'a> { let body_expr = P(self.expr_block(body_block, ThinVec::new())); let body_stmt = respan(e.span, hir::StmtExpr(body_expr, self.next_id())); - let loop_block = P(self.block_all(e.span, hir_vec![pat_let, body_stmt], None)); + let loop_block = P(self.block_all(e.span, hir_vec![next_let, match_stmt, pat_let, body_stmt], None)); // `[opt_ident]: loop { ... }` let loop_expr = hir::ExprLoop(loop_block, self.lower_opt_sp_ident(opt_ident), @@ -2599,14 +2616,14 @@ impl<'a> LoweringContext<'a> { fn stmt_let_pat(&mut self, sp: Span, - ex: P, + ex: Option>, pat: P, source: hir::LocalSource) -> hir::Stmt { let local = P(hir::Local { pat: pat, ty: None, - init: Some(ex), + init: ex, id: self.next_id(), span: sp, attrs: ThinVec::new(), @@ -2624,7 +2641,7 @@ impl<'a> LoweringContext<'a> { self.pat_ident(sp, ident) }; let pat_id = pat.id; - (self.stmt_let_pat(sp, ex, pat, hir::LocalSource::Normal), pat_id) + (self.stmt_let_pat(sp, Some(ex), pat, hir::LocalSource::Normal), pat_id) } fn block_expr(&mut self, expr: P) -> hir::Block { From 8d6d9d12f1d529c69454289ea6f4d07f074013b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Wed, 14 Jun 2017 13:36:30 +0200 Subject: [PATCH 09/18] Fix formatting and add a test for destruction order of unbound values --- src/librustc/hir/lowering.rs | 15 +++++++--- .../for-loop-lifetime-of-unbound-values.rs | 28 +++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 src/test/run-pass/for-loop-lifetime-of-unbound-values.rs diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 00d42e3cc6585..485c41f7feb57 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -2188,14 +2188,16 @@ impl<'a> LoweringContext<'a> { let next_ident = self.str_to_ident("next"); let next_pat = self.pat_ident(e.span, next_ident); - + // `::std::option::Option::Some(val) => next = val` let pat_arm = { let val_ident = self.str_to_ident("val"); let val_pat = self.pat_ident(e.span, val_ident); let val_expr = P(self.expr_ident(e.span, val_ident, val_pat.id)); let next_expr = P(self.expr_ident(e.span, next_ident, next_pat.id)); - let assign = P(self.expr(e.span, hir::ExprAssign(next_expr, val_expr), ThinVec::new())); + let assign = P(self.expr(e.span, + hir::ExprAssign(next_expr, val_expr), + ThinVec::new())); let some_pat = self.pat_some(e.span, val_pat); self.arm(hir_vec![some_pat], assign) }; @@ -2230,7 +2232,7 @@ impl<'a> LoweringContext<'a> { let match_stmt = respan(e.span, hir::StmtExpr(match_expr, self.next_id())); let next_expr = P(self.expr_ident(e.span, next_ident, next_pat.id)); - + // `let next` let next_let = self.stmt_let_pat(e.span, None, @@ -2249,7 +2251,12 @@ impl<'a> LoweringContext<'a> { let body_expr = P(self.expr_block(body_block, ThinVec::new())); let body_stmt = respan(e.span, hir::StmtExpr(body_expr, self.next_id())); - let loop_block = P(self.block_all(e.span, hir_vec![next_let, match_stmt, pat_let, body_stmt], None)); + let loop_block = P(self.block_all(e.span, + hir_vec![next_let, + match_stmt, + pat_let, + body_stmt], + None)); // `[opt_ident]: loop { ... }` let loop_expr = hir::ExprLoop(loop_block, self.lower_opt_sp_ident(opt_ident), diff --git a/src/test/run-pass/for-loop-lifetime-of-unbound-values.rs b/src/test/run-pass/for-loop-lifetime-of-unbound-values.rs new file mode 100644 index 0000000000000..a273fb579fa0b --- /dev/null +++ b/src/test/run-pass/for-loop-lifetime-of-unbound-values.rs @@ -0,0 +1,28 @@ +use std::cell::Cell; + +struct Flag<'a>(&'a Cell); + +impl<'a> Drop for Flag<'a> { + fn drop(&mut self) { + self.0.set(false) + } +} + +fn main() { + let alive2 = Cell::new(true); + for _i in std::iter::once(Flag(&alive2)) { + // The Flag value should be alive in the for loop body + assert_eq!(alive2.get(), true); + } + // The Flag value should be dead outside of the loop + assert_eq!(alive2.get(), false); + + let alive = Cell::new(true); + for _ in std::iter::once(Flag(&alive)) { + // The Flag value should be alive in the for loop body even if it wasn't + // bound by the for loop + assert_eq!(alive.get(), true); + } + // The Flag value should be dead outside of the loop + assert_eq!(alive.get(), false); +} \ No newline at end of file From 0b1469acd74f3dead3a1088dcbbfdfdc7778d11d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Wed, 14 Jun 2017 19:26:42 +0200 Subject: [PATCH 10/18] Fix test formatting --- .../for-loop-lifetime-of-unbound-values.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/test/run-pass/for-loop-lifetime-of-unbound-values.rs b/src/test/run-pass/for-loop-lifetime-of-unbound-values.rs index a273fb579fa0b..a0562edfadd6c 100644 --- a/src/test/run-pass/for-loop-lifetime-of-unbound-values.rs +++ b/src/test/run-pass/for-loop-lifetime-of-unbound-values.rs @@ -1,11 +1,21 @@ +// Copyright 2017 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. + use std::cell::Cell; struct Flag<'a>(&'a Cell); impl<'a> Drop for Flag<'a> { - fn drop(&mut self) { - self.0.set(false) - } + fn drop(&mut self) { + self.0.set(false) + } } fn main() { @@ -16,7 +26,7 @@ fn main() { } // The Flag value should be dead outside of the loop assert_eq!(alive2.get(), false); - + let alive = Cell::new(true); for _ in std::iter::once(Flag(&alive)) { // The Flag value should be alive in the for loop body even if it wasn't From 818fa6e09c5b8009335208c2ba366d59b327dfa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 15 Jun 2017 02:09:53 +0200 Subject: [PATCH 11/18] Added more tests --- .../for-loop-unconstrained-element-type.rs | 13 +++++++++++++ .../for-loop-lifetime-of-unbound-values.rs | 2 +- ...op-unconstrained-element-type-i32-fallback.rs | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/test/compile-fail/for-loop-unconstrained-element-type.rs create mode 100644 src/test/run-pass/for-loop-unconstrained-element-type-i32-fallback.rs diff --git a/src/test/compile-fail/for-loop-unconstrained-element-type.rs b/src/test/compile-fail/for-loop-unconstrained-element-type.rs new file mode 100644 index 0000000000000..f7dccc7f2ac19 --- /dev/null +++ b/src/test/compile-fail/for-loop-unconstrained-element-type.rs @@ -0,0 +1,13 @@ +// Copyright 2017 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. + +fn main() { + for i in Vec::new() { } //~ ERROR type annotations needed +} diff --git a/src/test/run-pass/for-loop-lifetime-of-unbound-values.rs b/src/test/run-pass/for-loop-lifetime-of-unbound-values.rs index a0562edfadd6c..4653a493898ee 100644 --- a/src/test/run-pass/for-loop-lifetime-of-unbound-values.rs +++ b/src/test/run-pass/for-loop-lifetime-of-unbound-values.rs @@ -35,4 +35,4 @@ fn main() { } // The Flag value should be dead outside of the loop assert_eq!(alive.get(), false); -} \ No newline at end of file +} diff --git a/src/test/run-pass/for-loop-unconstrained-element-type-i32-fallback.rs b/src/test/run-pass/for-loop-unconstrained-element-type-i32-fallback.rs new file mode 100644 index 0000000000000..d9a876d9c9521 --- /dev/null +++ b/src/test/run-pass/for-loop-unconstrained-element-type-i32-fallback.rs @@ -0,0 +1,16 @@ +// Copyright 2017 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. + +fn main() { + let mut sum = 0; + for i in Vec::new() { + sum += i; + } +} From 7ded8a9066c996e0190d6ffc63a2a8bc1c410ac9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 15 Jun 2017 12:27:15 -0400 Subject: [PATCH 12/18] explain purpose of test --- .../compile-fail/for-loop-unconstrained-element-type.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/test/compile-fail/for-loop-unconstrained-element-type.rs b/src/test/compile-fail/for-loop-unconstrained-element-type.rs index f7dccc7f2ac19..dd09e4a79ecdc 100644 --- a/src/test/compile-fail/for-loop-unconstrained-element-type.rs +++ b/src/test/compile-fail/for-loop-unconstrained-element-type.rs @@ -8,6 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Test that `for` loops don't introduce artificial +// constraints on the type of the binding (`i`). +// Subtle changes in the desugaring can cause the +// type of elements in the vector to (incorrectly) +// fallback to `!` or `()`. + fn main() { for i in Vec::new() { } //~ ERROR type annotations needed } From b568ab6abb4d1e014e80b98181501773ca12f430 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 15 Jun 2017 12:28:07 -0400 Subject: [PATCH 13/18] document purpose of test --- src/test/run-pass/for-loop-lifetime-of-unbound-values.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/run-pass/for-loop-lifetime-of-unbound-values.rs b/src/test/run-pass/for-loop-lifetime-of-unbound-values.rs index 4653a493898ee..7a088b5133472 100644 --- a/src/test/run-pass/for-loop-lifetime-of-unbound-values.rs +++ b/src/test/run-pass/for-loop-lifetime-of-unbound-values.rs @@ -8,6 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Test when destructors run in a for loop. The intention is +// that the value for each iteration is dropped *after* the loop +// body has executed. This is true even when the value is assigned +// to a `_` pattern (and hence ignored). + use std::cell::Cell; struct Flag<'a>(&'a Cell); From 202263d7dccc12a4bf633b56d9e2ef2d5ff976c0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 15 Jun 2017 12:31:45 -0400 Subject: [PATCH 14/18] Create for-loop-unconstrained-element-type-i32-fallback.rs --- .../for-loop-unconstrained-element-type-i32-fallback.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/run-pass/for-loop-unconstrained-element-type-i32-fallback.rs b/src/test/run-pass/for-loop-unconstrained-element-type-i32-fallback.rs index d9a876d9c9521..b36afcf87b3ee 100644 --- a/src/test/run-pass/for-loop-unconstrained-element-type-i32-fallback.rs +++ b/src/test/run-pass/for-loop-unconstrained-element-type-i32-fallback.rs @@ -8,6 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Test that the type of `sum` falls back to `i32` here, +// and that the for loop desugaring doesn't inferfere with +// that. + fn main() { let mut sum = 0; for i in Vec::new() { From cc273a5807c2a1a4ace846e9970d387b381a175c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 15 Jun 2017 15:45:37 -0400 Subject: [PATCH 15/18] remove trailing whitespace --- src/test/compile-fail/for-loop-unconstrained-element-type.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/compile-fail/for-loop-unconstrained-element-type.rs b/src/test/compile-fail/for-loop-unconstrained-element-type.rs index dd09e4a79ecdc..fb5553166bafb 100644 --- a/src/test/compile-fail/for-loop-unconstrained-element-type.rs +++ b/src/test/compile-fail/for-loop-unconstrained-element-type.rs @@ -11,7 +11,7 @@ // Test that `for` loops don't introduce artificial // constraints on the type of the binding (`i`). // Subtle changes in the desugaring can cause the -// type of elements in the vector to (incorrectly) +// type of elements in the vector to (incorrectly) // fallback to `!` or `()`. fn main() { From f0a3296d98fc5bf9804f1ec38329bc4dfe1b4e42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sat, 17 Jun 2017 01:51:55 +0200 Subject: [PATCH 16/18] Make the `next` variable mutable to allow for ref mut in for patterns. --- src/librustc/hir/lowering.rs | 14 +++++++------- src/test/run-pass/for-loop-mut-ref-element.rs | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 src/test/run-pass/for-loop-mut-ref-element.rs diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 485c41f7feb57..38d6dacd4c44f 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -2168,12 +2168,12 @@ impl<'a> LoweringContext<'a> { // let result = match ::std::iter::IntoIterator::into_iter() { // mut iter => { // [opt_ident]: loop { - // let next; + // let mut _next; // match ::std::iter::Iterator::next(&mut iter) { - // ::std::option::Option::Some(val) => next = val, + // ::std::option::Option::Some(val) => _next = val, // ::std::option::Option::None => break // }; - // let = next; + // let = _next; // StmtExpr(); // } // } @@ -2186,8 +2186,8 @@ impl<'a> LoweringContext<'a> { let iter = self.str_to_ident("iter"); - let next_ident = self.str_to_ident("next"); - let next_pat = self.pat_ident(e.span, next_ident); + let next_ident = self.str_to_ident("_next"); + let next_pat = self.pat_ident_binding_mode(e.span, next_ident, hir::BindByValue(hir::MutMutable)); // `::std::option::Option::Some(val) => next = val` let pat_arm = { @@ -2233,13 +2233,13 @@ impl<'a> LoweringContext<'a> { let next_expr = P(self.expr_ident(e.span, next_ident, next_pat.id)); - // `let next` + // `let mut _next` let next_let = self.stmt_let_pat(e.span, None, next_pat, hir::LocalSource::ForLoopDesugar); - // `let = next` + // `let = _next` let pat = self.lower_pat(pat); let pat_let = self.stmt_let_pat(e.span, Some(next_expr), diff --git a/src/test/run-pass/for-loop-mut-ref-element.rs b/src/test/run-pass/for-loop-mut-ref-element.rs new file mode 100644 index 0000000000000..14ce23b07242c --- /dev/null +++ b/src/test/run-pass/for-loop-mut-ref-element.rs @@ -0,0 +1,15 @@ +// Copyright 2014 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. + +// Tests that for loops can bind elements as mutable references + +fn main() { + for ref mut _a in std::iter::once(true) {} +} \ No newline at end of file From 47ad9e1061a9c8e218c725d563de2177e23cf404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sat, 17 Jun 2017 14:46:37 +0200 Subject: [PATCH 17/18] Fix formatting --- src/librustc/hir/lowering.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 38d6dacd4c44f..c3f05928f66da 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -2187,7 +2187,9 @@ impl<'a> LoweringContext<'a> { let iter = self.str_to_ident("iter"); let next_ident = self.str_to_ident("_next"); - let next_pat = self.pat_ident_binding_mode(e.span, next_ident, hir::BindByValue(hir::MutMutable)); + let next_pat = self.pat_ident_binding_mode(e.span, + next_ident, + hir::BindByValue(hir::MutMutable)); // `::std::option::Option::Some(val) => next = val` let pat_arm = { From fc875777a793d54fb0045c3b171f588eedd03e98 Mon Sep 17 00:00:00 2001 From: arthurprs Date: Fri, 9 Jun 2017 20:23:38 +0200 Subject: [PATCH 18/18] Revert "Update jemalloc to 4.5.0" This reverts commit 65d0be3b7b540145c22409b1a79f7d263422e19b. --- src/jemalloc | 2 +- src/liballoc_jemalloc/build.rs | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/jemalloc b/src/jemalloc index 3288e0659c08f..11bfb0dcf85f7 160000 --- a/src/jemalloc +++ b/src/jemalloc @@ -1 +1 @@ -Subproject commit 3288e0659c08fb5006f6d6dd4b5675ed0c2c432a +Subproject commit 11bfb0dcf85f7aa92abd30524bb1e42e18d108c6 diff --git a/src/liballoc_jemalloc/build.rs b/src/liballoc_jemalloc/build.rs index 859e414a6fe7c..f3a0eebe6984d 100644 --- a/src/liballoc_jemalloc/build.rs +++ b/src/liballoc_jemalloc/build.rs @@ -93,7 +93,29 @@ fn main() { .env("AR", &ar) .env("RANLIB", format!("{} s", ar.display())); - if target.contains("ios") { + if target.contains("windows") { + // A bit of history here, this used to be --enable-lazy-lock added in + // #14006 which was filed with jemalloc in jemalloc/jemalloc#83 which + // was also reported to MinGW: + // + // http://sourceforge.net/p/mingw-w64/bugs/395/ + // + // When updating jemalloc to 4.0, however, it was found that binaries + // would exit with the status code STATUS_RESOURCE_NOT_OWNED indicating + // that a thread was unlocking a mutex it never locked. Disabling this + // "lazy lock" option seems to fix the issue, but it was enabled by + // default for MinGW targets in 13473c7 for jemalloc. + // + // As a result of all that, force disabling lazy lock on Windows, and + // after reading some code it at least *appears* that the initialization + // of mutexes is otherwise ok in jemalloc, so shouldn't cause problems + // hopefully... + // + // tl;dr: make windows behave like other platforms by disabling lazy + // locking, but requires passing an option due to a historical + // default with jemalloc. + cmd.arg("--disable-lazy-lock"); + } else if target.contains("ios") { cmd.arg("--disable-tls"); } else if target.contains("android") { // We force android to have prefixed symbols because apparently