Skip to content

Commit 75307c2

Browse files
committed
Auto merge of #97648 - lcnr:bound-var-replacer, r=jackh726
cleanup bound variable handling each commit should be pretty self-contained and hopefully straightforward to review. I've added 677ec23a8dbf8ff5f1c03ccebd46f8b85e5ec1fc so that we can stop returning the region map from `replace_bound_vars_with_fresh_vars` in the following commit. r? `@nikomatsakis` or `@jackh726`
2 parents b9f3bdf + efdf948 commit 75307c2

File tree

22 files changed

+186
-191
lines changed

22 files changed

+186
-191
lines changed

compiler/rustc_borrowck/src/type_check/input_output.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
6060
// Replace the bound items in the fn sig with fresh
6161
// variables, so that they represent the view from
6262
// "inside" the closure.
63-
self.infcx
64-
.replace_bound_vars_with_fresh_vars(
65-
body.span,
66-
LateBoundRegionConversionTime::FnCall,
67-
poly_sig,
68-
)
69-
.0
63+
self.infcx.replace_bound_vars_with_fresh_vars(
64+
body.span,
65+
LateBoundRegionConversionTime::FnCall,
66+
poly_sig,
67+
)
7068
},
7169
);
7270
}

compiler/rustc_borrowck/src/type_check/mod.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc_infer::infer::outlives::env::RegionBoundPairs;
2020
use rustc_infer::infer::region_constraints::RegionConstraintData;
2121
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
2222
use rustc_infer::infer::{
23-
InferCtxt, InferOk, LateBoundRegionConversionTime, NllRegionVariableOrigin,
23+
InferCtxt, InferOk, LateBoundRegion, LateBoundRegionConversionTime, NllRegionVariableOrigin,
2424
};
2525
use rustc_middle::mir::tcx::PlaceTy;
2626
use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
@@ -1436,11 +1436,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
14361436
return;
14371437
}
14381438
};
1439-
let (sig, map) = self.infcx.replace_bound_vars_with_fresh_vars(
1440-
term.source_info.span,
1441-
LateBoundRegionConversionTime::FnCall,
1442-
sig,
1443-
);
1439+
let (sig, map) = tcx.replace_late_bound_regions(sig, |br| {
1440+
self.infcx.next_region_var(LateBoundRegion(
1441+
term.source_info.span,
1442+
br.kind,
1443+
LateBoundRegionConversionTime::FnCall,
1444+
))
1445+
});
14441446
debug!(?sig);
14451447
let sig = self.normalize(sig, term_location);
14461448
self.check_call_dest(body, term, &sig, *destination, target, term_location);

compiler/rustc_infer/src/infer/canonical/substitute.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,6 @@ where
8686
c => bug!("{:?} is a const but value is {:?}", bound_ct, c),
8787
};
8888

89-
tcx.replace_escaping_bound_vars(value, fld_r, fld_t, fld_c)
89+
tcx.replace_escaping_bound_vars_uncached(value, fld_r, fld_t, fld_c)
9090
}
9191
}

compiler/rustc_infer/src/infer/equate.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -153,12 +153,12 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
153153
{
154154
if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() {
155155
self.fields.higher_ranked_sub(a, b, self.a_is_expected)?;
156-
self.fields.higher_ranked_sub(b, a, self.a_is_expected)
156+
self.fields.higher_ranked_sub(b, a, self.a_is_expected)?;
157157
} else {
158158
// Fast path for the common case.
159159
self.relate(a.skip_binder(), b.skip_binder())?;
160-
Ok(a)
161160
}
161+
Ok(a)
162162
}
163163
}
164164

compiler/rustc_infer/src/infer/higher_ranked/mod.rs

+43-51
Original file line numberDiff line numberDiff line change
@@ -3,78 +3,85 @@
33
44
use super::combine::CombineFields;
55
use super::{HigherRankedType, InferCtxt};
6-
76
use crate::infer::CombinedSnapshot;
87
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
98
use rustc_middle::ty::{self, Binder, TypeFoldable};
109

1110
impl<'a, 'tcx> CombineFields<'a, 'tcx> {
11+
/// Checks whether `for<..> sub <: for<..> sup` holds.
12+
///
13+
/// For this to hold, **all** instantiations of the super type
14+
/// have to be a super type of **at least one** instantiation of
15+
/// the subtype.
16+
///
17+
/// This is implemented by first entering a new universe.
18+
/// We then replace all bound variables in `sup` with placeholders,
19+
/// and all bound variables in `sup` with inference vars.
20+
/// We can then just relate the two resulting types as normal.
21+
///
22+
/// Note: this is a subtle algorithm. For a full explanation, please see
23+
/// the [rustc dev guide][rd]
24+
///
25+
/// [rd]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html
1226
#[instrument(skip(self), level = "debug")]
1327
pub fn higher_ranked_sub<T>(
1428
&mut self,
15-
a: Binder<'tcx, T>,
16-
b: Binder<'tcx, T>,
17-
a_is_expected: bool,
18-
) -> RelateResult<'tcx, Binder<'tcx, T>>
29+
sub: Binder<'tcx, T>,
30+
sup: Binder<'tcx, T>,
31+
sub_is_expected: bool,
32+
) -> RelateResult<'tcx, ()>
1933
where
2034
T: Relate<'tcx>,
2135
{
22-
// Rather than checking the subtype relationship between `a` and `b`
23-
// as-is, we need to do some extra work here in order to make sure
24-
// that function subtyping works correctly with respect to regions
25-
//
26-
// Note: this is a subtle algorithm. For a full explanation, please see
27-
// the rustc dev guide:
28-
// <https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html>
29-
3036
let span = self.trace.cause.span;
3137

3238
self.infcx.commit_if_ok(|_| {
3339
// First, we instantiate each bound region in the supertype with a
34-
// fresh placeholder region.
35-
let b_prime = self.infcx.replace_bound_vars_with_placeholders(b);
40+
// fresh placeholder region. Note that this automatically creates
41+
// a new universe if needed.
42+
let sup_prime = self.infcx.replace_bound_vars_with_placeholders(sup);
3643

3744
// Next, we instantiate each bound region in the subtype
3845
// with a fresh region variable. These region variables --
3946
// but no other pre-existing region variables -- can name
4047
// the placeholders.
41-
let (a_prime, _) =
42-
self.infcx.replace_bound_vars_with_fresh_vars(span, HigherRankedType, a);
48+
let sub_prime =
49+
self.infcx.replace_bound_vars_with_fresh_vars(span, HigherRankedType, sub);
4350

44-
debug!("a_prime={:?}", a_prime);
45-
debug!("b_prime={:?}", b_prime);
51+
debug!("a_prime={:?}", sub_prime);
52+
debug!("b_prime={:?}", sup_prime);
4653

4754
// Compare types now that bound regions have been replaced.
48-
let result = self.sub(a_is_expected).relate(a_prime, b_prime)?;
49-
50-
debug!("higher_ranked_sub: OK result={:?}", result);
55+
let result = self.sub(sub_is_expected).relate(sub_prime, sup_prime)?;
5156

52-
// We related `a_prime` and `b_prime`, which just had any bound vars
53-
// replaced with placeholders or infer vars, respectively. Relating
54-
// them should not introduce new bound vars.
55-
Ok(ty::Binder::dummy(result))
57+
debug!("higher_ranked_sub: OK result={result:?}");
58+
// NOTE: returning the result here would be dangerous as it contains
59+
// placeholders which **must not** be named afterwards.
60+
Ok(())
5661
})
5762
}
5863
}
5964

6065
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
6166
/// Replaces all bound variables (lifetimes, types, and constants) bound by
62-
/// `binder` with placeholder variables.
67+
/// `binder` with placeholder variables in a new universe. This means that the
68+
/// new placeholders can only be named by inference variables created after
69+
/// this method has been called.
6370
///
6471
/// This is the first step of checking subtyping when higher-ranked things are involved.
6572
/// For more details visit the relevant sections of the [rustc dev guide].
6673
///
6774
/// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html
75+
#[instrument(level = "debug", skip(self))]
6876
pub fn replace_bound_vars_with_placeholders<T>(&self, binder: ty::Binder<'tcx, T>) -> T
6977
where
70-
T: TypeFoldable<'tcx>,
78+
T: TypeFoldable<'tcx> + Copy,
7179
{
72-
// Figure out what the next universe will be, but don't actually create
73-
// it until after we've done the substitution (in particular there may
74-
// be no bound variables). This is a performance optimization, since the
75-
// leak check for example can be skipped if no new universes are created
76-
// (i.e., if there are no placeholders).
77-
let next_universe = self.universe().next_universe();
80+
if let Some(inner) = binder.no_bound_vars() {
81+
return inner;
82+
}
83+
84+
let next_universe = self.create_next_universe();
7885

7986
let fld_r = |br: ty::BoundRegion| {
8087
self.tcx.mk_region(ty::RePlaceholder(ty::PlaceholderRegion {
@@ -100,23 +107,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
100107
})
101108
};
102109

103-
let (result, map) = self.tcx.replace_bound_vars(binder, fld_r, fld_t, fld_c);
104-
105-
// If there were higher-ranked regions to replace, then actually create
106-
// the next universe (this avoids needlessly creating universes).
107-
if !map.is_empty() {
108-
let n_u = self.create_next_universe();
109-
assert_eq!(n_u, next_universe);
110-
}
111-
112-
debug!(
113-
"replace_bound_vars_with_placeholders(\
114-
next_universe={:?}, \
115-
result={:?}, \
116-
map={:?})",
117-
next_universe, result, map,
118-
);
119-
110+
let result = self.tcx.replace_bound_vars_uncached(binder, fld_r, fld_t, fld_c);
111+
debug!(?next_universe, ?result);
120112
result
121113
}
122114

compiler/rustc_infer/src/infer/mod.rs

+29-15
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ use rustc_span::symbol::Symbol;
3333
use rustc_span::Span;
3434

3535
use std::cell::{Cell, Ref, RefCell};
36-
use std::collections::BTreeMap;
3736
use std::fmt;
3837

3938
use self::combine::CombineFields;
@@ -1524,25 +1523,40 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
15241523
span: Span,
15251524
lbrct: LateBoundRegionConversionTime,
15261525
value: ty::Binder<'tcx, T>,
1527-
) -> (T, BTreeMap<ty::BoundRegion, ty::Region<'tcx>>)
1526+
) -> T
15281527
where
1529-
T: TypeFoldable<'tcx>,
1528+
T: TypeFoldable<'tcx> + Copy,
15301529
{
1531-
let fld_r =
1532-
|br: ty::BoundRegion| self.next_region_var(LateBoundRegion(span, br.kind, lbrct));
1533-
let fld_t = |_| {
1534-
self.next_ty_var(TypeVariableOrigin {
1535-
kind: TypeVariableOriginKind::MiscVariable,
1536-
span,
1530+
if let Some(inner) = value.no_bound_vars() {
1531+
return inner;
1532+
}
1533+
1534+
let mut region_map = FxHashMap::default();
1535+
let fld_r = |br: ty::BoundRegion| {
1536+
*region_map
1537+
.entry(br)
1538+
.or_insert_with(|| self.next_region_var(LateBoundRegion(span, br.kind, lbrct)))
1539+
};
1540+
1541+
let mut ty_map = FxHashMap::default();
1542+
let fld_t = |bt: ty::BoundTy| {
1543+
*ty_map.entry(bt).or_insert_with(|| {
1544+
self.next_ty_var(TypeVariableOrigin {
1545+
kind: TypeVariableOriginKind::MiscVariable,
1546+
span,
1547+
})
15371548
})
15381549
};
1539-
let fld_c = |_, ty| {
1540-
self.next_const_var(
1541-
ty,
1542-
ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span },
1543-
)
1550+
let mut ct_map = FxHashMap::default();
1551+
let fld_c = |bc: ty::BoundVar, ty| {
1552+
*ct_map.entry(bc).or_insert_with(|| {
1553+
self.next_const_var(
1554+
ty,
1555+
ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span },
1556+
)
1557+
})
15441558
};
1545-
self.tcx.replace_bound_vars(value, fld_r, fld_t, fld_c)
1559+
self.tcx.replace_bound_vars_uncached(value, fld_r, fld_t, fld_c)
15461560
}
15471561

15481562
/// See the [`region_constraints::RegionConstraintCollector::verify_generic_bound`] method.

compiler/rustc_infer/src/infer/sub.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,8 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> {
198198
where
199199
T: Relate<'tcx>,
200200
{
201-
self.fields.higher_ranked_sub(a, b, self.a_is_expected)
201+
self.fields.higher_ranked_sub(a, b, self.a_is_expected)?;
202+
Ok(a)
202203
}
203204
}
204205

0 commit comments

Comments
 (0)