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

New scoping rules for safe destruction #21022

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
5 changes: 4 additions & 1 deletion src/compiletest/compiletest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ pub fn parse_config(args: Vec<String> ) -> Config {
}

fn opt_path(m: &getopts::Matches, nm: &str) -> Path {
Path::new(m.opt_str(nm).unwrap())
match m.opt_str(nm) {
Some(s) => Path::new(s),
None => panic!("no option (=path) found for {}", nm),
}
}

let filter = if !matches.free.is_empty() {
Expand Down
6 changes: 4 additions & 2 deletions src/libregex/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,10 @@ impl<'r, 't> Nfa<'r, 't> {
};
let mut matched = false;
let ninsts = self.prog.insts.len();
let mut clist = &mut Threads::new(self.which, ninsts, ncaps);
let mut nlist = &mut Threads::new(self.which, ninsts, ncaps);
let mut cthread = Threads::new(self.which, ninsts, ncaps);
let mut nthread = Threads::new(self.which, ninsts, ncaps);
let mut clist = &mut cthread;
let mut nlist = &mut nthread;

let mut groups: Vec<_> = repeat(None).take(ncaps * 2).collect();

Expand Down
12 changes: 12 additions & 0 deletions src/librustc/metadata/tydecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,18 @@ fn parse_scope(st: &mut PState) -> region::CodeExtent {
let node_id = parse_uint(st) as ast::NodeId;
region::CodeExtent::Misc(node_id)
}
'D' => {
let node_id = parse_uint(st) as ast::NodeId;
region::CodeExtent::DestructionScope(node_id)
}
'B' => {
let node_id = parse_uint(st) as ast::NodeId;
let first_stmt_index = parse_uint(st);
let block_remainder = region::BlockRemainder {
block: node_id, first_statement_index: first_stmt_index,
};
region::CodeExtent::Remainder(block_remainder)
}
_ => panic!("parse_scope: bad input")
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/librustc/metadata/tyencode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,10 @@ pub fn enc_region(w: &mut SeekableMemWriter, cx: &ctxt, r: ty::Region) {

fn enc_scope(w: &mut SeekableMemWriter, _cx: &ctxt, scope: region::CodeExtent) {
match scope {
region::CodeExtent::Misc(node_id) => mywrite!(w, "M{}", node_id)
region::CodeExtent::Misc(node_id) => mywrite!(w, "M{}", node_id),
region::CodeExtent::Remainder(region::BlockRemainder {
block: b, first_statement_index: i }) => mywrite!(w, "B{}{}", b, i),
region::CodeExtent::DestructionScope(node_id) => mywrite!(w, "D{}", node_id),
}
}

Expand Down
28 changes: 25 additions & 3 deletions src/librustc/middle/infer/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
}
}
SubSupConflict(var_origin, _, sub_r, _, sup_r) => {
debug!("processing SubSupConflict");
debug!("processing SubSupConflict sub: {:?} sup: {:?}", sub_r, sup_r);
match free_regions_from_same_fn(self.tcx, sub_r, sup_r) {
Some(ref same_frs) => {
var_origins.push(var_origin);
Expand Down Expand Up @@ -721,6 +721,22 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
sup,
"");
}
infer::SafeDestructor(span) => {
self.tcx.sess.span_err(
span,
"unsafe use of destructor: destructor might be called \
while references are dead");
note_and_explain_region(
self.tcx,
"superregion: ",
sup,
"");
note_and_explain_region(
self.tcx,
"subregion: ",
sub,
"");
}
infer::BindingTypeIsNotValidAtDecl(span) => {
self.tcx.sess.span_err(
span,
Expand Down Expand Up @@ -818,6 +834,8 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
let scope_id = same_regions[0].scope_id;
let parent = self.tcx.map.get_parent(scope_id);
let parent_node = self.tcx.map.find(parent);
let taken = lifetimes_in_scope(self.tcx, scope_id);
let life_giver = LifeGiver::with_taken(&taken[]);
let node_inner = match parent_node {
Some(ref node) => match *node {
ast_map::NodeItem(ref item) => {
Expand Down Expand Up @@ -860,8 +878,6 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
};
let (fn_decl, generics, unsafety, ident, expl_self, span)
= node_inner.expect("expect item fn");
let taken = lifetimes_in_scope(self.tcx, scope_id);
let life_giver = LifeGiver::with_taken(&taken[]);
let rebuilder = Rebuilder::new(self.tcx, fn_decl, expl_self,
generics, same_regions, &life_giver);
let (fn_decl, expl_self, generics) = rebuilder.rebuild();
Expand Down Expand Up @@ -1641,6 +1657,12 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
&format!("...so that the declared lifetime parameter bounds \
are satisfied")[]);
}
infer::SafeDestructor(span) => {
self.tcx.sess.span_note(
span,
"...so that references are valid when the destructor \
runs")
}
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/librustc/middle/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@ pub enum SubregionOrigin<'tcx> {

// An auto-borrow that does not enclose the expr where it occurs
AutoBorrow(Span),

// Region constraint arriving from destructor safety
SafeDestructor(Span),
}

/// Times when we replace late-bound regions with variables:
Expand Down Expand Up @@ -1217,6 +1220,7 @@ impl<'tcx> SubregionOrigin<'tcx> {
CallReturn(a) => a,
AddrOf(a) => a,
AutoBorrow(a) => a,
SafeDestructor(a) => a,
}
}
}
Expand Down Expand Up @@ -1279,6 +1283,7 @@ impl<'tcx> Repr<'tcx> for SubregionOrigin<'tcx> {
CallReturn(a) => format!("CallReturn({})", a.repr(tcx)),
AddrOf(a) => format!("AddrOf({})", a.repr(tcx)),
AutoBorrow(a) => format!("AutoBorrow({})", a.repr(tcx)),
SafeDestructor(a) => format!("SafeDestructor({})", a.repr(tcx)),
}
}
}
Expand Down
61 changes: 50 additions & 11 deletions src/librustc/middle/infer/region_inference/graphviz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@
use graphviz as dot;

use middle::ty;
use middle::region::CodeExtent;
use super::Constraint;
use middle::infer::SubregionOrigin;
use middle::infer::region_inference::RegionVarBindings;
use util::nodemap::{FnvHashMap, FnvHashSet};
use util::ppaux::Repr;

use std::borrow::Cow;
use std::collections::hash_map::Entry::Vacant;
use std::io::{self, File};
use std::os;
Expand Down Expand Up @@ -120,13 +122,18 @@ struct ConstraintGraph<'a, 'tcx: 'a> {
node_ids: FnvHashMap<Node, uint>,
}

#[derive(Clone, Hash, PartialEq, Eq, Show)]
#[derive(Clone, Hash, PartialEq, Eq, Show, Copy)]
enum Node {
RegionVid(ty::RegionVid),
Region(ty::Region),
}

type Edge = Constraint;
// type Edge = Constraint;
#[derive(Clone, PartialEq, Eq, Show, Copy)]
enum Edge {
Constraint(Constraint),
EnclScope(CodeExtent, CodeExtent),
}

impl<'a, 'tcx> ConstraintGraph<'a, 'tcx> {
fn new(tcx: &'a ty::ctxt<'tcx>,
Expand All @@ -146,6 +153,11 @@ impl<'a, 'tcx> ConstraintGraph<'a, 'tcx> {
add_node(n1);
add_node(n2);
}

tcx.region_maps.each_encl_scope(|&mut: sub, sup| {
add_node(Node::Region(ty::ReScope(*sub)));
add_node(Node::Region(ty::ReScope(*sup)));
});
}

ConstraintGraph { tcx: tcx,
Expand All @@ -160,7 +172,17 @@ impl<'a, 'tcx> dot::Labeller<'a, Node, Edge> for ConstraintGraph<'a, 'tcx> {
dot::Id::new(self.graph_name.as_slice()).unwrap()
}
fn node_id(&self, n: &Node) -> dot::Id {
dot::Id::new(format!("node_{}", self.node_ids.get(n).unwrap())).unwrap()
let node_id = match self.node_ids.get(n) {
Some(node_id) => node_id,
None => panic!("no node_id found for node: {:?}", n),
};
let name = |&:| format!("node_{}", node_id);
match dot::Id::new(name()) {
Ok(id) => id,
Err(()) => {
panic!("failed to create graphviz node identified by {}", name());
}
}
}
fn node_label(&self, n: &Node) -> dot::LabelText {
match *n {
Expand All @@ -171,7 +193,12 @@ impl<'a, 'tcx> dot::Labeller<'a, Node, Edge> for ConstraintGraph<'a, 'tcx> {
}
}
fn edge_label(&self, e: &Edge) -> dot::LabelText {
dot::LabelText::label(format!("{}", self.map.get(e).unwrap().repr(self.tcx)))
match *e {
Edge::Constraint(ref c) =>
dot::LabelText::label(format!("{}", self.map.get(c).unwrap().repr(self.tcx))),
Edge::EnclScope(..) =>
dot::LabelText::label(format!("(enclosed)")),
}
}
}

Expand All @@ -186,28 +213,40 @@ fn constraint_to_nodes(c: &Constraint) -> (Node, Node) {
}
}

fn edge_to_nodes(e: &Edge) -> (Node, Node) {
match *e {
Edge::Constraint(ref c) => constraint_to_nodes(c),
Edge::EnclScope(sub, sup) => {
(Node::Region(ty::ReScope(sub)), Node::Region(ty::ReScope(sup)))
}
}
}

impl<'a, 'tcx> dot::GraphWalk<'a, Node, Edge> for ConstraintGraph<'a, 'tcx> {
fn nodes(&self) -> dot::Nodes<Node> {
let mut set = FnvHashSet::new();
for constraint in self.map.keys() {
let (n1, n2) = constraint_to_nodes(constraint);
set.insert(n1);
set.insert(n2);
for node in self.node_ids.keys() {
set.insert(*node);
}
debug!("constraint graph has {} nodes", set.len());
set.into_iter().collect()
}
fn edges(&self) -> dot::Edges<Edge> {
debug!("constraint graph has {} edges", self.map.len());
self.map.keys().map(|e|*e).collect()
let mut v : Vec<_> = self.map.keys().map(|e| Edge::Constraint(*e)).collect();
self.tcx.region_maps.each_encl_scope(|&mut: sub, sup| {
v.push(Edge::EnclScope(*sub, *sup))
});
debug!("region graph has {} edges", v.len());
Cow::Owned(v)
}
fn source(&self, edge: &Edge) -> Node {
let (n1, _) = constraint_to_nodes(edge);
let (n1, _) = edge_to_nodes(edge);
debug!("edge {:?} has source {:?}", edge, n1);
n1
}
fn target(&self, edge: &Edge) -> Node {
let (_, n2) = constraint_to_nodes(edge);
let (_, n2) = edge_to_nodes(edge);
debug!("edge {:?} has target {:?}", edge, n2);
n2
}
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/middle/infer/region_inference/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1398,6 +1398,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
for upper_bound in upper_bounds.iter() {
if !self.is_subregion_of(lower_bound.region,
upper_bound.region) {
debug!("pushing SubSupConflict sub: {:?} sup: {:?}",
lower_bound.region, upper_bound.region);
errors.push(SubSupConflict(
(*self.var_origins.borrow())[node_idx.index as uint].clone(),
lower_bound.origin.clone(),
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1535,7 +1535,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
let fn_ret =
ty::liberate_late_bound_regions(
self.ir.tcx,
CodeExtent::from_node_id(body.id),
CodeExtent::DestructionScope(body.id),
&self.fn_ret(id));

match fn_ret {
Expand Down
5 changes: 4 additions & 1 deletion src/librustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,10 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {

// Region of environment pointer
let env_region = ty::ReFree(ty::FreeRegion {
scope: region::CodeExtent::from_node_id(fn_body_id),
// The environment of a closure is guaranteed to
// outlive any bindings introduced in the body of the
// closure itself.
scope: region::CodeExtent::DestructionScope(fn_body_id),
bound_region: ty::BrEnv
});

Expand Down
Loading