Skip to content

Commit 2cc7ad0

Browse files
author
Ariel Ben-Yehuda
committed
Prevent coercions from polluting the fulfillment context
This adds transaction support to fulfill. I can't use it in rust-lang#26282 because that would require nested transactions. This doesn't seem to cause compilation time to regress. Fixes rust-lang#24819.
1 parent 0d82fb5 commit 2cc7ad0

File tree

4 files changed

+94
-1
lines changed

4 files changed

+94
-1
lines changed

src/librustc/middle/traits/fulfill.rs

+61
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use middle::infer::InferCtxt;
1212
use middle::ty::{self, RegionEscape, Ty};
1313
use std::collections::HashSet;
1414
use std::default::Default;
15+
use std::mem;
1516
use syntax::ast;
1617
use util::common::ErrorReported;
1718
use util::ppaux::Repr;
@@ -80,6 +81,11 @@ pub struct FulfillmentContext<'tcx> {
8081
// obligations (otherwise, it's easy to fail to walk to a
8182
// particular node-id).
8283
region_obligations: NodeMap<Vec<RegionObligation<'tcx>>>,
84+
85+
// Stored versions of the fields for snapshots
86+
stored_region_obligations: Option<NodeMap<Vec<RegionObligation<'tcx>>>>,
87+
stored_predicates: Option<Vec<PredicateObligation<'tcx>>>,
88+
stored_duplicate_set: Option<HashSet<ty::Predicate<'tcx>>>
8389
}
8490

8591
#[derive(Clone)]
@@ -96,6 +102,51 @@ impl<'tcx> FulfillmentContext<'tcx> {
96102
predicates: Vec::new(),
97103
attempted_mark: 0,
98104
region_obligations: NodeMap(),
105+
106+
stored_region_obligations: None,
107+
stored_predicates: None,
108+
stored_duplicate_set: None
109+
}
110+
}
111+
112+
/// Begin a snapshot. This should be done in parallel with infcx
113+
/// snapshots.
114+
pub fn begin_transaction(&mut self) {
115+
assert!(self.stored_duplicate_set.is_none(), "nested transactions are not supported");
116+
debug!("begin_transaction");
117+
118+
self.stored_duplicate_set = Some(mem::replace(&mut self.duplicate_set,
119+
HashSet::new()));
120+
self.stored_predicates = Some(self.predicates.clone());
121+
self.stored_region_obligations = Some(mem::replace(&mut self.region_obligations,
122+
NodeMap()));
123+
}
124+
125+
/// Rolls the current transaction back
126+
pub fn rollback(&mut self) {
127+
assert!(self.stored_duplicate_set.is_some(), "rollback not within a transaction");
128+
debug!("rollback!");
129+
130+
self.duplicate_set = self.stored_duplicate_set.take().unwrap();
131+
self.predicates = self.stored_predicates.take().unwrap();
132+
self.region_obligations = self.stored_region_obligations.take().unwrap();
133+
}
134+
135+
/// Commits the current transaction
136+
pub fn commit(&mut self) {
137+
assert!(self.stored_duplicate_set.is_some(), "commit not within a transaction");
138+
debug!("commit!");
139+
140+
let transaction_duplicate_set = mem::replace(&mut self.duplicate_set,
141+
self.stored_duplicate_set.take().unwrap());
142+
let transaction_region_obligations = mem::replace(&mut self.region_obligations,
143+
self.stored_region_obligations.take().unwrap());
144+
145+
self.duplicate_set.extend(transaction_duplicate_set);
146+
self.predicates = self.stored_predicates.take().unwrap();
147+
148+
for (node, mut ros) in transaction_region_obligations {
149+
self.region_obligations.entry(node).or_insert(vec![]).append(&mut ros);
99150
}
100151
}
101152

@@ -170,6 +221,14 @@ impl<'tcx> FulfillmentContext<'tcx> {
170221
return;
171222
}
172223

224+
if let Some(ref duplicate_set) = self.stored_duplicate_set {
225+
if duplicate_set.contains(&obligation.predicate) {
226+
debug!("register_predicate({}) -- already seen before transaction, skip",
227+
obligation.repr(infcx.tcx));
228+
return;
229+
}
230+
}
231+
173232
debug!("register_predicate({})", obligation.repr(infcx.tcx));
174233
self.predicates.push(obligation);
175234
}
@@ -178,6 +237,8 @@ impl<'tcx> FulfillmentContext<'tcx> {
178237
body_id: ast::NodeId)
179238
-> &[RegionObligation<'tcx>]
180239
{
240+
assert!(self.stored_region_obligations.is_none(),
241+
"can't get region obligations within a transaction");
181242
match self.region_obligations.get(&body_id) {
182243
None => Default::default(),
183244
Some(vec) => vec,

src/librustc_typeck/check/coercion.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ pub fn mk_assignty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
434434
debug!("mk_assignty({} -> {})", a.repr(fcx.tcx()), b.repr(fcx.tcx()));
435435
let mut unsizing_obligations = vec![];
436436
let adjustment = try!(indent(|| {
437-
fcx.infcx().commit_if_ok(|_| {
437+
fcx.select_commit_if_ok(|_| {
438438
let coerce = Coerce {
439439
fcx: fcx,
440440
origin: infer::ExprAssignable(expr.span),

src/librustc_typeck/check/mod.rs

+16
Original file line numberDiff line numberDiff line change
@@ -1436,6 +1436,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14361436
self.inh.adjustments.borrow_mut().insert(node_id, adj);
14371437
}
14381438

1439+
/// A version of commit_if_ok that allows for registering trait obligations
1440+
fn select_commit_if_ok<T, E, F>(&self, f: F) -> Result<T, E> where
1441+
F: FnOnce(&infer::CombinedSnapshot) -> Result<T, E> {
1442+
self.inh.fulfillment_cx.borrow_mut().begin_transaction();
1443+
match self.infcx().commit_if_ok(f) {
1444+
Ok(o) => {
1445+
self.inh.fulfillment_cx.borrow_mut().commit();
1446+
Ok(o)
1447+
}
1448+
Err(e) => {
1449+
self.inh.fulfillment_cx.borrow_mut().rollback();
1450+
Err(e)
1451+
}
1452+
}
1453+
}
1454+
14391455
/// Basically whenever we are converting from a type scheme into
14401456
/// the fn body space, we always want to normalize associated
14411457
/// types as well. This function combines the two.

src/test/compile-fail/issue-24819.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
let mut v = Vec::new();
13+
foo(&mut v); //~ ERROR mismatched types
14+
}
15+
16+
fn foo(h: &mut ()) {}

0 commit comments

Comments
 (0)