Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c428535

Browse files
committedNov 8, 2016
introduce a fudge_regions_if_ok to address false region edges
Fixes #37655.
1 parent 38a959a commit c428535

File tree

6 files changed

+192
-46
lines changed

6 files changed

+192
-46
lines changed
 

‎src/librustc/infer/fudge.rs

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// Copyright 2012-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+
use ty::{self, TyCtxt};
12+
use ty::fold::{TypeFoldable, TypeFolder};
13+
14+
use super::InferCtxt;
15+
use super::RegionVariableOrigin;
16+
17+
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
18+
/// This rather funky routine is used while processing expected
19+
/// types. What happens here is that we want to propagate a
20+
/// coercion through the return type of a fn to its
21+
/// argument. Consider the type of `Option::Some`, which is
22+
/// basically `for<T> fn(T) -> Option<T>`. So if we have an
23+
/// expression `Some(&[1, 2, 3])`, and that has the expected type
24+
/// `Option<&[u32]>`, we would like to type check `&[1, 2, 3]`
25+
/// with the expectation of `&[u32]`. This will cause us to coerce
26+
/// from `&[u32; 3]` to `&[u32]` and make the users life more
27+
/// pleasant.
28+
///
29+
/// The way we do this is using `fudge_regions_if_ok`. What the
30+
/// routine actually does is to start a snapshot and execute the
31+
/// closure `f`. In our example above, what this closure will do
32+
/// is to unify the expectation (`Option<&[u32]>`) with the actual
33+
/// return type (`Option<?T>`, where `?T` represents the variable
34+
/// instantiated for `T`). This will cause `?T` to be unified
35+
/// with `&?a [u32]`, where `?a` is a fresh lifetime variable. The
36+
/// input type (`?T`) is then returned by `f()`.
37+
///
38+
/// At this point, `fudge_regions_if_ok` will normalize all type
39+
/// variables, converting `?T` to `&?a [u32]` and end the
40+
/// snapshot. The problem is that we can't just return this type
41+
/// out, because it references the region variable `?a`, and that
42+
/// region variable was popped when we popped the snapshot.
43+
///
44+
/// So what we do is to keep a list (`region_vars`, in the code below)
45+
/// of region variables created during the snapshot (here, `?a`). We
46+
/// fold the return value and replace any such regions with a *new*
47+
/// region variable (e.g., `?b`) and return the result (`&?b [u32]`).
48+
/// This can then be used as the expectation for the fn argument.
49+
///
50+
/// The important point here is that, for soundness purposes, the
51+
/// regions in question are not particularly important. We will
52+
/// use the expected types to guide coercions, but we will still
53+
/// type-check the resulting types from those coercions against
54+
/// the actual types (`?T`, `Option<?T`) -- and remember that
55+
/// after the snapshot is popped, the variable `?T` is no longer
56+
/// unified.
57+
///
58+
/// Assumptions:
59+
/// - no new type variables are created during `f()` (asserted
60+
/// below); this simplifies our logic since we don't have to
61+
/// check for escaping type variables
62+
pub fn fudge_regions_if_ok<T, E, F>(&self,
63+
origin: &RegionVariableOrigin,
64+
f: F) -> Result<T, E> where
65+
F: FnOnce() -> Result<T, E>,
66+
T: TypeFoldable<'tcx>,
67+
{
68+
let (region_vars, value) = self.probe(|snapshot| {
69+
let vars_at_start = self.type_variables.borrow().num_vars();
70+
71+
match f() {
72+
Ok(value) => {
73+
let value = self.resolve_type_vars_if_possible(&value);
74+
75+
// At this point, `value` could in principle refer
76+
// to regions that have been created during the
77+
// snapshot (we assert below that `f()` does not
78+
// create any new type variables, so there
79+
// shouldn't be any of those). Once we exit
80+
// `probe()`, those are going to be popped, so we
81+
// will have to eliminate any references to them.
82+
83+
assert_eq!(self.type_variables.borrow().num_vars(), vars_at_start,
84+
"type variables were created during fudge_regions_if_ok");
85+
let region_vars =
86+
self.region_vars.vars_created_since_snapshot(
87+
&snapshot.region_vars_snapshot);
88+
89+
Ok((region_vars, value))
90+
}
91+
Err(e) => Err(e),
92+
}
93+
})?;
94+
95+
// At this point, we need to replace any of the now-popped
96+
// region variables that appear in `value` with a fresh region
97+
// variable. We can't do this during the probe because they
98+
// would just get popped then too. =)
99+
100+
// Micro-optimization: if no variables have been created, then
101+
// `value` can't refer to any of them. =) So we can just return it.
102+
if region_vars.is_empty() {
103+
return Ok(value);
104+
}
105+
106+
let mut fudger = RegionFudger {
107+
infcx: self,
108+
region_vars: &region_vars,
109+
origin: origin
110+
};
111+
112+
Ok(value.fold_with(&mut fudger))
113+
}
114+
}
115+
116+
pub struct RegionFudger<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
117+
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
118+
region_vars: &'a Vec<ty::RegionVid>,
119+
origin: &'a RegionVariableOrigin,
120+
}
121+
122+
impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionFudger<'a, 'gcx, 'tcx> {
123+
fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> {
124+
self.infcx.tcx
125+
}
126+
127+
fn fold_region(&mut self, r: &'tcx ty::Region) -> &'tcx ty::Region {
128+
match *r {
129+
ty::ReVar(v) if self.region_vars.contains(&v) => {
130+
self.infcx.next_region_var(self.origin.clone())
131+
}
132+
_ => {
133+
r
134+
}
135+
}
136+
}
137+
}

‎src/librustc/infer/mod.rs

+1-43
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ mod bivariate;
5050
mod combine;
5151
mod equate;
5252
pub mod error_reporting;
53+
mod fudge;
5354
mod glb;
5455
mod higher_ranked;
5556
pub mod lattice;
@@ -986,49 +987,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
986987
r
987988
}
988989

989-
/// Execute `f` and commit only the region bindings if successful.
990-
/// The function f must be very careful not to leak any non-region
991-
/// variables that get created.
992-
pub fn commit_regions_if_ok<T, E, F>(&self, f: F) -> Result<T, E> where
993-
F: FnOnce() -> Result<T, E>
994-
{
995-
debug!("commit_regions_if_ok()");
996-
let CombinedSnapshot { projection_cache_snapshot,
997-
type_snapshot,
998-
int_snapshot,
999-
float_snapshot,
1000-
region_vars_snapshot,
1001-
obligations_in_snapshot } = self.start_snapshot();
1002-
1003-
let r = self.commit_if_ok(|_| f());
1004-
1005-
debug!("commit_regions_if_ok: rolling back everything but regions");
1006-
1007-
assert!(!self.obligations_in_snapshot.get());
1008-
self.obligations_in_snapshot.set(obligations_in_snapshot);
1009-
1010-
// Roll back any non-region bindings - they should be resolved
1011-
// inside `f`, with, e.g. `resolve_type_vars_if_possible`.
1012-
self.projection_cache
1013-
.borrow_mut()
1014-
.rollback_to(projection_cache_snapshot);
1015-
self.type_variables
1016-
.borrow_mut()
1017-
.rollback_to(type_snapshot);
1018-
self.int_unification_table
1019-
.borrow_mut()
1020-
.rollback_to(int_snapshot);
1021-
self.float_unification_table
1022-
.borrow_mut()
1023-
.rollback_to(float_snapshot);
1024-
1025-
// Commit region vars that may escape through resolved types.
1026-
self.region_vars
1027-
.commit(region_vars_snapshot);
1028-
1029-
r
1030-
}
1031-
1032990
/// Execute `f` then unroll any bindings it creates
1033991
pub fn probe<R, F>(&self, f: F) -> R where
1034992
F: FnOnce(&CombinedSnapshot) -> R,

‎src/librustc/infer/type_variable.rs

+4
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,10 @@ impl<'tcx> TypeVariableTable<'tcx> {
184184
v
185185
}
186186

187+
pub fn num_vars(&self) -> usize {
188+
self.values.len()
189+
}
190+
187191
pub fn root_var(&mut self, vid: ty::TyVid) -> ty::TyVid {
188192
self.eq_relations.find(vid)
189193
}

‎src/librustc_typeck/check/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ use fmt_macros::{Parser, Piece, Position};
8686
use hir::def::{Def, CtorKind, PathResolution};
8787
use hir::def_id::{DefId, LOCAL_CRATE};
8888
use hir::pat_util;
89-
use rustc::infer::{self, InferCtxt, InferOk, TypeOrigin, TypeTrace, type_variable};
89+
use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin,
90+
TypeOrigin, TypeTrace, type_variable};
9091
use rustc::ty::subst::{Kind, Subst, Substs};
9192
use rustc::traits::{self, Reveal};
9293
use rustc::ty::{ParamTy, ParameterEnvironment};
@@ -2752,7 +2753,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
27522753
formal_args: &[Ty<'tcx>])
27532754
-> Vec<Ty<'tcx>> {
27542755
let expected_args = expected_ret.only_has_type(self).and_then(|ret_ty| {
2755-
self.commit_regions_if_ok(|| {
2756+
self.fudge_regions_if_ok(&RegionVariableOrigin::Coercion(call_span), || {
27562757
// Attempt to apply a subtyping relationship between the formal
27572758
// return type (likely containing type variables if the function
27582759
// is polymorphic) and the expected return type.

‎src/librustc_typeck/check/regionck.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1149,7 +1149,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
11491149
autoderefs: usize,
11501150
autoref: &adjustment::AutoBorrow<'tcx>)
11511151
{
1152-
debug!("link_autoref(autoref={:?})", autoref);
1152+
debug!("link_autoref(autoderefs={}, autoref={:?})", autoderefs, autoref);
11531153
let mc = mc::MemCategorizationContext::new(self);
11541154
let expr_cmt = ignore_err!(mc.cat_expr_autoderefd(expr, autoderefs));
11551155
debug!("expr_cmt={:?}", expr_cmt);

‎src/test/run-pass/issue-37655.rs

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright 2016 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+
// Regression test for #37655. The problem was a false edge created by
12+
// coercion that wound up requiring that `'a` (in `split()`) outlive
13+
// `'b`, which shouldn't be necessary.
14+
15+
#![allow(warnings)]
16+
17+
trait SliceExt<T> {
18+
type Item;
19+
20+
fn get_me<I>(&self, index: I) -> &I::Output
21+
where I: SliceIndex<Self::Item>;
22+
}
23+
24+
impl<T> SliceExt<T> for [T] {
25+
type Item = T;
26+
27+
fn get_me<I>(&self, index: I) -> &I::Output
28+
where I: SliceIndex<T>
29+
{
30+
panic!()
31+
}
32+
}
33+
34+
pub trait SliceIndex<T> {
35+
type Output: ?Sized;
36+
}
37+
38+
impl<T> SliceIndex<T> for usize {
39+
type Output = T;
40+
}
41+
42+
fn foo<'a, 'b>(split: &'b [&'a [u8]]) -> &'a [u8] {
43+
split.get_me(0)
44+
}
45+
46+
fn main() { }

0 commit comments

Comments
 (0)
Please sign in to comment.