Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] introduce a region unification table and use it in dropck #30242

Merged
merged 1 commit into from
Dec 12, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/librustc/middle/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
value.fold_with(&mut r)
}

pub fn resolve_type_and_region_vars_if_possible<T>(&self, value: &T) -> T
where T: TypeFoldable<'tcx>
{
let mut r = resolve::OpportunisticTypeAndRegionResolver::new(self);
value.fold_with(&mut r)
}

/// Resolves all type variables in `t` and then, if any were left
/// unresolved, substitutes an error type. This is used after the
/// main checking when doing a second pass before writeback. The
Expand Down
27 changes: 25 additions & 2 deletions src/librustc/middle/infer/region_inference/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub use self::VarValue::*;
use super::{RegionVariableOrigin, SubregionOrigin, TypeTrace, MiscVariable};

use rustc_data_structures::graph::{self, Direction, NodeIndex};
use rustc_data_structures::unify::{self, UnificationTable};
use middle::free_region::FreeRegionMap;
use middle::ty::{self, Ty};
use middle::ty::{BoundRegion, FreeRegion, Region, RegionVid};
Expand Down Expand Up @@ -234,15 +235,16 @@ pub struct RegionVarBindings<'a, 'tcx: 'a> {
// bound on a variable and so forth, which can never be rolled
// back.
undo_log: RefCell<Vec<UndoLogEntry>>,
unification_table: RefCell<UnificationTable<ty::RegionVid>>,

// This contains the results of inference. It begins as an empty
// option and only acquires a value after inference is complete.
values: RefCell<Option<Vec<VarValue>>>,
}

#[derive(Debug)]
pub struct RegionSnapshot {
length: usize,
region_snapshot: unify::Snapshot<ty::RegionVid>,
skolemization_count: u32,
}

Expand All @@ -260,6 +262,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
skolemization_count: Cell::new(0),
bound_count: Cell::new(0),
undo_log: RefCell::new(Vec::new()),
unification_table: RefCell::new(UnificationTable::new()),
}
}

Expand All @@ -273,6 +276,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
self.undo_log.borrow_mut().push(OpenSnapshot);
RegionSnapshot {
length: length,
region_snapshot: self.unification_table.borrow_mut().snapshot(),
skolemization_count: self.skolemization_count.get(),
}
}
Expand All @@ -289,6 +293,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
(*undo_log)[snapshot.length] = CommitedSnapshot;
}
self.skolemization_count.set(snapshot.skolemization_count);
self.unification_table.borrow_mut().commit(snapshot.region_snapshot);
}

pub fn rollback_to(&self, snapshot: RegionSnapshot) {
Expand Down Expand Up @@ -328,6 +333,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
let c = undo_log.pop().unwrap();
assert!(c == OpenSnapshot);
self.skolemization_count.set(snapshot.skolemization_count);
self.unification_table.borrow_mut()
.rollback_to(snapshot.region_snapshot);
}

pub fn num_vars(&self) -> u32 {
Expand All @@ -340,7 +347,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
pub fn new_region_var(&self, origin: RegionVariableOrigin) -> RegionVid {
let id = self.num_vars();
self.var_origins.borrow_mut().push(origin.clone());
let vid = RegionVid { index: id };
let vid = self.unification_table.borrow_mut().new_key(());
assert_eq!(vid.index, id);
if self.in_snapshot() {
self.undo_log.borrow_mut().push(AddVar(vid));
}
Expand Down Expand Up @@ -460,6 +468,10 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
// equating regions.
self.make_subregion(origin.clone(), sub, sup);
self.make_subregion(origin, sup, sub);

if let (ty::ReVar(sub), ty::ReVar(sup)) = (sub, sup) {
self.unification_table.borrow_mut().union(sub, sup);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I expected to not add two subregion edges here, but rather to "refresh" the regions in the inference propagation below.

}
}

Expand Down Expand Up @@ -568,6 +580,10 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
}
}

pub fn opportunistic_resolve_var(&self, rid: RegionVid) -> ty::Region {
ty::ReVar(self.unification_table.borrow_mut().find(rid))
}

fn combine_map(&self, t: CombineMapType) -> &RefCell<CombineMap> {
match t {
Glb => &self.glbs,
Expand Down Expand Up @@ -1312,6 +1328,13 @@ impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> {
}
}

impl fmt::Debug for RegionSnapshot {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "RegionSnapshot(length={},skolemization={})",
self.length, self.skolemization_count)
}
}

impl<'tcx> fmt::Debug for GenericKind<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Expand Down
35 changes: 35 additions & 0 deletions src/librustc/middle/infer/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,41 @@ impl<'a, 'tcx> ty::fold::TypeFolder<'tcx> for OpportunisticTypeResolver<'a, 'tcx
}
}

/// The opportunistic type and region resolver is similar to the
/// opportunistic type resolver, but also opportunistly resolves
/// regions. It is useful for canonicalization.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just always resolve regions?

pub struct OpportunisticTypeAndRegionResolver<'a, 'tcx:'a> {
infcx: &'a InferCtxt<'a, 'tcx>,
}

impl<'a, 'tcx> OpportunisticTypeAndRegionResolver<'a, 'tcx> {
pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self {
OpportunisticTypeAndRegionResolver { infcx: infcx }
}
}

impl<'a, 'tcx> ty::fold::TypeFolder<'tcx> for OpportunisticTypeAndRegionResolver<'a, 'tcx> {
fn tcx(&self) -> &ty::ctxt<'tcx> {
self.infcx.tcx
}

fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
if !t.needs_infer() {
t // micro-optimize -- if there is nothing in this type that this fold affects...
} else {
let t0 = self.infcx.shallow_resolve(t);
ty::fold::super_fold_ty(self, t0)
}
}

fn fold_region(&mut self, r: ty::Region) -> ty::Region {
match r {
ty::ReVar(rid) => self.infcx.region_vars.opportunistic_resolve_var(rid),
_ => r,
}
}
}

///////////////////////////////////////////////////////////////////////////
// FULL TYPE RESOLUTION

Expand Down
7 changes: 7 additions & 0 deletions src/librustc/middle/infer/unify_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ impl UnifyKey for ty::IntVid {
fn tag(_: Option<ty::IntVid>) -> &'static str { "IntVid" }
}

impl UnifyKey for ty::RegionVid {
type Value = ();
fn index(&self) -> u32 { self.index }
fn from_index(i: u32) -> ty::RegionVid { ty::RegionVid { index: i } }
fn tag(_: Option<ty::RegionVid>) -> &'static str { "RegionVid" }
}

impl<'tcx> ToType<'tcx> for IntVarValue {
fn to_type(&self, tcx: &ty::ctxt<'tcx>) -> Ty<'tcx> {
match *self {
Expand Down
25 changes: 16 additions & 9 deletions src/librustc_typeck/check/dropck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ use middle::region;
use middle::subst::{self, Subst};
use middle::traits;
use middle::ty::{self, Ty};
use util::nodemap::FnvHashSet;

use syntax::ast;
use syntax::codemap::{self, Span};
Expand Down Expand Up @@ -280,7 +279,7 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>
rcx: rcx,
span: span,
parent_scope: parent_scope,
breadcrumbs: FnvHashSet()
breadcrumbs: Vec::new(),
},
TypeContext::Root,
typ,
Expand Down Expand Up @@ -341,7 +340,7 @@ enum TypeContext {
struct DropckContext<'a, 'b: 'a, 'tcx: 'b> {
rcx: &'a mut Rcx<'b, 'tcx>,
/// types that have already been traversed
breadcrumbs: FnvHashSet<Ty<'tcx>>,
breadcrumbs: Vec<Ty<'tcx>>,
/// span for error reporting
span: Span,
/// the scope reachable dtorck types must outlive
Expand All @@ -356,6 +355,8 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'tcx>(
depth: usize) -> Result<(), Error<'tcx>>
{
let tcx = cx.rcx.tcx();
let ty = cx.rcx.infcx().resolve_type_and_region_vars_if_possible(&ty);

// Issue #22443: Watch out for overflow. While we are careful to
// handle regular types properly, non-regular ones cause problems.
let recursion_limit = tcx.sess.recursion_limit.get();
Expand All @@ -366,13 +367,19 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'tcx>(
return Err(Error::Overflow(context, ty))
}

if !cx.breadcrumbs.insert(ty) {
debug!("iterate_over_potentially_unsafe_regions_in_type \
{}ty: {} scope: {:?} - cached",
(0..depth).map(|_| ' ').collect::<String>(),
ty, cx.parent_scope);
return Ok(()); // we already visited this type
for breadcrumb in &mut cx.breadcrumbs {
*breadcrumb =
cx.rcx.infcx().resolve_type_and_region_vars_if_possible(breadcrumb);
if *breadcrumb == ty {
debug!("iterate_over_potentially_unsafe_regions_in_type \
{}ty: {} scope: {:?} - cached",
(0..depth).map(|_| ' ').collect::<String>(),
ty, cx.parent_scope);
return Ok(()); // we already visited this type
}
}
cx.breadcrumbs.push(ty);

debug!("iterate_over_potentially_unsafe_regions_in_type \
{}ty: {} scope: {:?}",
(0..depth).map(|_| ' ').collect::<String>(),
Expand Down
33 changes: 33 additions & 0 deletions src/test/run-pass/issue-29844.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// 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.

use std::sync::Arc;

pub struct DescriptorSet<'a> {
pub slots: Vec<AttachInfo<'a, Resources>>
}

pub trait ResourcesTrait<'r>: Sized {
type DescriptorSet: 'r;
}

pub struct Resources;

impl<'a> ResourcesTrait<'a> for Resources {
type DescriptorSet = DescriptorSet<'a>;
}

pub enum AttachInfo<'a, R: ResourcesTrait<'a>> {
NextDescriptorSet(Arc<R::DescriptorSet>)
}

fn main() {
let _x = DescriptorSet {slots: Vec::new()};
}