From cc2a1c7bd8e3271919dad8719e53c9d3ff007e2a Mon Sep 17 00:00:00 2001 From: Paul Faria Date: Mon, 9 Oct 2017 22:16:57 -0400 Subject: [PATCH 01/10] Initial attempt at implementation of inference layout for nll --- src/librustc_mir/transform/nll/infer.rs | 206 ++++++++++++++++++++++++ src/librustc_mir/transform/nll/mod.rs | 23 ++- 2 files changed, 224 insertions(+), 5 deletions(-) create mode 100644 src/librustc_mir/transform/nll/infer.rs diff --git a/src/librustc_mir/transform/nll/infer.rs b/src/librustc_mir/transform/nll/infer.rs new file mode 100644 index 0000000000000..e5d8b2e137997 --- /dev/null +++ b/src/librustc_mir/transform/nll/infer.rs @@ -0,0 +1,206 @@ +use super::{Region, RegionIndex}; +use std::mem; +use rustc::infer::InferCtxt; +use rustc::mir::{Location, Mir}; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_data_structures::fx::FxHashSet; + +pub struct InferenceContext { + definitions: IndexVec, + constraints: IndexVec, + errors: IndexVec, +} + +pub struct InferenceError { + pub constraint_point: Location, + pub name: (), // TODO(nashenas88) RegionName +} + +newtype_index!(InferenceErrorIndex); + +struct VarDefinition { + name: (), // TODO(nashenas88) RegionName + value: Region, + capped: bool, +} + +impl VarDefinition { + pub fn new(value: Region) -> Self { + Self { + name: (), + value, + capped: false, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct Constraint { + sub: RegionIndex, + sup: RegionIndex, + point: Location, +} + +newtype_index!(ConstraintIndex); + +impl InferenceContext { + pub fn new(values: IndexVec) -> Self { + Self { + definitions: values.into_iter().map(VarDefinition::new).collect(), + constraints: IndexVec::new(), + errors: IndexVec::new(), + } + } + + pub fn cap_var(&mut self, v: RegionIndex) { + self.definitions[v].capped = true; + } + + pub fn add_live_point(&mut self, v: RegionIndex, point: Location) { + debug!("add_live_point({:?}, {:?})", v, point); + let definition = &mut self.definitions[v]; + if definition.value.add_point(point) { + if definition.capped { + self.errors.push(InferenceError { + constraint_point: point, + name: definition.name, + }); + } + } + } + + pub fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) { + debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point); + self.constraints.push(Constraint { sup, sub, point }); + } + + pub fn region(&self, v: RegionIndex) -> &Region { + &self.definitions[v].value + } + + pub fn solve<'a, 'gcx, 'tcx>( + &mut self, + infcx: InferCtxt<'a, 'gcx, 'tcx>, + mir: &'a Mir<'tcx>, + ) -> IndexVec + where + 'gcx: 'tcx + 'a, + 'tcx: 'a, + { + let mut changed = true; + let mut dfs = Dfs::new(infcx, mir); + while changed { + changed = false; + for constraint in &self.constraints { + let sub = &self.definitions[constraint.sub].value.clone(); + let sup_def = &self.definitions[constraint.sup]; + debug!("constraint: {:?}", constraint); + debug!(" sub (before): {:?}", sub); + debug!(" sup (before): {:?}", sup_def.value); + + if dfs.copy(sub, &mut sup_def.value, constraint.point) { + changed = true; + if sup_def.capped { + // This is kind of a hack, but when we add a + // constraint, the "point" is always the point + // AFTER the action that induced the + // constraint. So report the error on the + // action BEFORE that. + assert!(constraint.point.statement_index > 0); + let p = Location { + block: constraint.point.block, + statement_index: constraint.point.statement_index - 1, + }; + + self.errors.push(InferenceError { + constraint_point: p, + name: sup_def.name, + }); + } + } + + debug!(" sup (after) : {:?}", sup_def.value); + debug!(" changed : {:?}", changed); + } + debug!("\n"); + } + + mem::replace(&mut self.errors, IndexVec::new()) + } +} + +struct Dfs<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> { + infcx: InferCtxt<'a, 'gcx, 'tcx>, + mir: &'a Mir<'tcx>, +} + +impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> { + fn new(infcx: InferCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self { + Self { infcx, mir } + } + + fn copy( + &mut self, + from_region: &Region, + to_region: &mut Region, + start_point: Location, + ) -> bool { + let mut changed = false; + + let mut stack = vec![]; + let mut visited = FxHashSet(); + + stack.push(start_point); + while let Some(p) = stack.pop() { + debug!(" dfs: p={:?}", p); + + if !from_region.may_contain(p) { + debug!(" not in from-region"); + continue; + } + + if !visited.insert(p) { + debug!(" already visited"); + continue; + } + + changed |= to_region.add_point(p); + + let block_data = self.mir[p.block]; + let successor_points = if p.statement_index < block_data.statements.len() { + vec![Location { + statement_index: p.statement_index + 1, + ..p + }] + } else { + block_data.terminator() + .successors() + .iter() + .map(|&basic_block| Location { + statement_index: 0, + block: basic_block, + }) + .collect::>() + }; + + if successor_points.is_empty() { + // If we reach the END point in the graph, then copy + // over any skolemized end points in the `from_region` + // and make sure they are included in the `to_region`. + for region_decl in self.infcx.tcx.tables.borrow().free_region_map() { + // TODO(nashenas88) figure out skolemized_end points + let block = self.env.graph.skolemized_end(region_decl.name); + let skolemized_end_point = Location { + block, + statement_index: 0, + }; + changed |= to_region.add_point(skolemized_end_point); + } + } else { + stack.extend(successor_points); + } + } + + changed + } +} diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 4925b1fcfed28..ab3e16e4ebb69 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -8,13 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use self::infer::InferenceContext; use rustc::ty::TypeFoldable; use rustc::ty::subst::{Kind, Substs}; use rustc::ty::{Ty, TyCtxt, ClosureSubsts, RegionVid, RegionKind}; use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind}; use rustc::mir::visit::{MutVisitor, Lookup}; use rustc::mir::transform::{MirPass, MirSource}; -use rustc::infer::{self, InferCtxt}; +use rustc::infer::{self as rustc_infer, InferCtxt}; use rustc::util::nodemap::FxHashSet; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use syntax_pos::DUMMY_SP; @@ -24,6 +25,8 @@ use std::fmt; use util as mir_util; use self::mir_util::PassWhere; +mod infer; + #[allow(dead_code)] struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { lookup_map: HashMap, @@ -40,14 +43,14 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { } } - pub fn into_results(self) -> HashMap { - self.lookup_map + pub fn into_results(self) -> (HashMap, IndexVec) { + (self.lookup_map, self.regions) } fn renumber_regions(&mut self, value: &T) -> T where T: TypeFoldable<'tcx> { self.infcx.tcx.fold_regions(value, &mut false, |_region, _depth| { self.regions.push(Region::default()); - self.infcx.next_region_var(infer::MiscVariable(DUMMY_SP)) + self.infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP)) }) } @@ -157,7 +160,9 @@ impl MirPass for NLL { } Ok(()) }); - let _results = visitor.into_results(); + let (_lookup_map, regions) = visitor.into_results(); + let inference_context = InferenceContext::new(regions); + inference_context.solve(infcx, &renumbered_mir); }) } } @@ -173,6 +178,14 @@ impl fmt::Debug for Region { } } +impl Region { + pub fn add_point(&mut self, point: Location) -> bool { + self.points.insert(point) + } + pub fn may_contain(&self, point: Location) -> bool { + self.points.contains(&point) + } +} newtype_index!(RegionIndex); From 3372b899d894a4f5c05f6deaa25889b384a4a12f Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 10 Oct 2017 14:25:04 -0300 Subject: [PATCH 02/10] Mark free regions handling as TODO --- src/librustc_mir/transform/nll/infer.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/librustc_mir/transform/nll/infer.rs b/src/librustc_mir/transform/nll/infer.rs index e5d8b2e137997..3ffe6b9f4fc51 100644 --- a/src/librustc_mir/transform/nll/infer.rs +++ b/src/librustc_mir/transform/nll/infer.rs @@ -184,18 +184,19 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> { }; if successor_points.is_empty() { + // TODO handle free regions // If we reach the END point in the graph, then copy // over any skolemized end points in the `from_region` // and make sure they are included in the `to_region`. - for region_decl in self.infcx.tcx.tables.borrow().free_region_map() { - // TODO(nashenas88) figure out skolemized_end points - let block = self.env.graph.skolemized_end(region_decl.name); - let skolemized_end_point = Location { - block, - statement_index: 0, - }; - changed |= to_region.add_point(skolemized_end_point); - } + // for region_decl in self.infcx.tcx.tables.borrow().free_region_map() { + // // TODO(nashenas88) figure out skolemized_end points + // let block = self.env.graph.skolemized_end(region_decl.name); + // let skolemized_end_point = Location { + // block, + // statement_index: 0, + // }; + // changed |= to_region.add_point(skolemized_end_point); + // } } else { stack.extend(successor_points); } From 25aa6eaf300ae7089881fead11e74688872b903a Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 10 Oct 2017 14:25:39 -0300 Subject: [PATCH 03/10] Mark Region as public --- src/librustc_mir/transform/nll/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index ab3e16e4ebb69..34a7568d2100a 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -168,7 +168,7 @@ impl MirPass for NLL { } #[derive(Clone, Default, PartialEq, Eq)] -struct Region { +pub struct Region { points: FxHashSet, } From 2720f635a34eea0c614aa16ac67b3af2007f44de Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 10 Oct 2017 19:18:19 -0300 Subject: [PATCH 04/10] inteference_context should be mut --- src/librustc_mir/transform/nll/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 34a7568d2100a..986b308f48414 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -161,7 +161,7 @@ impl MirPass for NLL { Ok(()) }); let (_lookup_map, regions) = visitor.into_results(); - let inference_context = InferenceContext::new(regions); + let mut inference_context = InferenceContext::new(regions); inference_context.solve(infcx, &renumbered_mir); }) } From c0e6a15130228ffaa4e4a61f848a3e43acfdeba9 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 10 Oct 2017 19:23:16 -0300 Subject: [PATCH 05/10] sup_def is a mutable reference --- src/librustc_mir/transform/nll/infer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/transform/nll/infer.rs b/src/librustc_mir/transform/nll/infer.rs index 3ffe6b9f4fc51..8e08fc91735fb 100644 --- a/src/librustc_mir/transform/nll/infer.rs +++ b/src/librustc_mir/transform/nll/infer.rs @@ -93,7 +93,7 @@ impl InferenceContext { changed = false; for constraint in &self.constraints { let sub = &self.definitions[constraint.sub].value.clone(); - let sup_def = &self.definitions[constraint.sup]; + let sup_def = &mut self.definitions[constraint.sup]; debug!("constraint: {:?}", constraint); debug!(" sub (before): {:?}", sub); debug!(" sup (before): {:?}", sup_def.value); From 6c52275c2d191c501b4354fed712e35955ca3c5f Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 10 Oct 2017 19:23:30 -0300 Subject: [PATCH 06/10] borrow block_data instead of moving it --- src/librustc_mir/transform/nll/infer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/transform/nll/infer.rs b/src/librustc_mir/transform/nll/infer.rs index 8e08fc91735fb..92942c38f4a89 100644 --- a/src/librustc_mir/transform/nll/infer.rs +++ b/src/librustc_mir/transform/nll/infer.rs @@ -166,7 +166,7 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> { changed |= to_region.add_point(p); - let block_data = self.mir[p.block]; + let block_data = &self.mir[p.block]; let successor_points = if p.statement_index < block_data.statements.len() { vec![Location { statement_index: p.statement_index + 1, From 50ac63b9b179dca946c5fcbd50541359ccfc68c9 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 10 Oct 2017 19:37:34 -0300 Subject: [PATCH 07/10] Do not move infcx, just borrow it --- src/librustc_mir/transform/nll/infer.rs | 6 +++--- src/librustc_mir/transform/nll/mod.rs | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/librustc_mir/transform/nll/infer.rs b/src/librustc_mir/transform/nll/infer.rs index 92942c38f4a89..61e320d9702fe 100644 --- a/src/librustc_mir/transform/nll/infer.rs +++ b/src/librustc_mir/transform/nll/infer.rs @@ -80,7 +80,7 @@ impl InferenceContext { pub fn solve<'a, 'gcx, 'tcx>( &mut self, - infcx: InferCtxt<'a, 'gcx, 'tcx>, + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, ) -> IndexVec where @@ -130,12 +130,12 @@ impl InferenceContext { } struct Dfs<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> { - infcx: InferCtxt<'a, 'gcx, 'tcx>, + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, } impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> { - fn new(infcx: InferCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self { + fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self { Self { infcx, mir } } diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 986b308f48414..805e9c976e4f0 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -31,11 +31,12 @@ mod infer; struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { lookup_map: HashMap, regions: IndexVec, - infcx: InferCtxt<'a, 'gcx, 'tcx>, + #[allow(dead_code)] + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, } impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { - pub fn new(infcx: InferCtxt<'a, 'gcx, 'tcx>) -> Self { + pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self { NLLVisitor { infcx, lookup_map: HashMap::new(), @@ -150,7 +151,7 @@ impl MirPass for NLL { tcx.infer_ctxt().enter(|infcx| { // Clone mir so we can mutate it without disturbing the rest of the compiler let mut renumbered_mir = mir.clone(); - let mut visitor = NLLVisitor::new(infcx); + let mut visitor = NLLVisitor::new(&infcx); visitor.visit_mir(&mut renumbered_mir); mir_util::dump_mir(tcx, None, "nll", &0, source, mir, |pass_where, out| { if let PassWhere::BeforeCFG = pass_where { @@ -162,7 +163,7 @@ impl MirPass for NLL { }); let (_lookup_map, regions) = visitor.into_results(); let mut inference_context = InferenceContext::new(regions); - inference_context.solve(infcx, &renumbered_mir); + inference_context.solve(&infcx, &renumbered_mir); }) } } From 88b02ef857317313cebd3833e0b6e847789f67c0 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 10 Oct 2017 19:41:29 -0300 Subject: [PATCH 08/10] Add allow_dead code directive to unused methods --- src/librustc_mir/transform/nll/infer.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/librustc_mir/transform/nll/infer.rs b/src/librustc_mir/transform/nll/infer.rs index 61e320d9702fe..f3f4228b421ee 100644 --- a/src/librustc_mir/transform/nll/infer.rs +++ b/src/librustc_mir/transform/nll/infer.rs @@ -52,10 +52,12 @@ impl InferenceContext { } } + #[allow(dead_code)] pub fn cap_var(&mut self, v: RegionIndex) { self.definitions[v].capped = true; } + #[allow(dead_code)] pub fn add_live_point(&mut self, v: RegionIndex, point: Location) { debug!("add_live_point({:?}, {:?})", v, point); let definition = &mut self.definitions[v]; @@ -69,11 +71,13 @@ impl InferenceContext { } } + #[allow(dead_code)] pub fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) { debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point); self.constraints.push(Constraint { sup, sub, point }); } + #[allow(dead_code)] pub fn region(&self, v: RegionIndex) -> &Region { &self.definitions[v].value } @@ -130,6 +134,7 @@ impl InferenceContext { } struct Dfs<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> { + #[allow(dead_code)] infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, } From e99702f5700d5b74b05d8ac4b6441f6bce18ff87 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 11 Oct 2017 18:40:35 -0300 Subject: [PATCH 09/10] TODO -> FIXME --- src/librustc_mir/transform/nll/infer.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc_mir/transform/nll/infer.rs b/src/librustc_mir/transform/nll/infer.rs index f3f4228b421ee..2d613f9b4bd1b 100644 --- a/src/librustc_mir/transform/nll/infer.rs +++ b/src/librustc_mir/transform/nll/infer.rs @@ -13,13 +13,13 @@ pub struct InferenceContext { pub struct InferenceError { pub constraint_point: Location, - pub name: (), // TODO(nashenas88) RegionName + pub name: (), // FIXME(nashenas88) RegionName } newtype_index!(InferenceErrorIndex); struct VarDefinition { - name: (), // TODO(nashenas88) RegionName + name: (), // FIXME(nashenas88) RegionName value: Region, capped: bool, } @@ -189,12 +189,12 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> { }; if successor_points.is_empty() { - // TODO handle free regions + // FIXME handle free regions // If we reach the END point in the graph, then copy // over any skolemized end points in the `from_region` // and make sure they are included in the `to_region`. // for region_decl in self.infcx.tcx.tables.borrow().free_region_map() { - // // TODO(nashenas88) figure out skolemized_end points + // // FIXME(nashenas88) figure out skolemized_end points // let block = self.env.graph.skolemized_end(region_decl.name); // let skolemized_end_point = Location { // block, From 9603e240d4e90c73507bf09957199de6f25bbc71 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 11 Oct 2017 18:41:20 -0300 Subject: [PATCH 10/10] Add License to infer.rs --- src/librustc_mir/transform/nll/infer.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/librustc_mir/transform/nll/infer.rs b/src/librustc_mir/transform/nll/infer.rs index 2d613f9b4bd1b..e6e00f295ca11 100644 --- a/src/librustc_mir/transform/nll/infer.rs +++ b/src/librustc_mir/transform/nll/infer.rs @@ -1,3 +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. + use super::{Region, RegionIndex}; use std::mem; use rustc::infer::InferCtxt;