Skip to content

Commit a6462ac

Browse files
committed
explicit leak check enum
1 parent 05b9fb6 commit a6462ac

File tree

1 file changed

+82
-51
lines changed

1 file changed

+82
-51
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

Lines changed: 82 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ fn success<'tcx>(
113113
Ok(InferOk { value: (adj, target), obligations })
114114
}
115115

116+
enum LeakCheck {
117+
Yes,
118+
Default,
119+
}
120+
116121
impl<'f, 'tcx> Coerce<'f, 'tcx> {
117122
fn new(
118123
fcx: &'f FnCtxt<'f, 'tcx>,
@@ -123,9 +128,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
123128
Coerce { fcx, cause, allow_two_phase, use_lub: false, coerce_never }
124129
}
125130

126-
fn unify_raw(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
131+
fn unify_raw(
132+
&self,
133+
a: Ty<'tcx>,
134+
b: Ty<'tcx>,
135+
leak_check: LeakCheck,
136+
) -> InferResult<'tcx, Ty<'tcx>> {
127137
debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub);
128-
self.commit_if_ok(|_| {
138+
self.commit_if_ok(|snapshot| {
139+
let outer_universe = self.infcx.universe();
140+
129141
let at = self.at(&self.cause, self.fcx.param_env);
130142

131143
let res = if self.use_lub {
@@ -138,7 +150,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
138150
// In the new solver, lazy norm may allow us to shallowly equate
139151
// more types, but we emit possibly impossible-to-satisfy obligations.
140152
// Filter these cases out to make sure our coercion is more accurate.
141-
match res {
153+
let res = match res {
142154
Ok(InferOk { value, obligations }) if self.next_trait_solver() => {
143155
let ocx = ObligationCtxt::new(self);
144156
ocx.register_obligations(obligations);
@@ -149,13 +161,35 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
149161
}
150162
}
151163
res => res,
164+
};
165+
166+
// We leak check here mostly because lub operations are
167+
// kind of scuffed around binders. Instead of computing an actual
168+
// lub'd binder we instead:
169+
// - Equate the binders
170+
// - Return the lhs of the lub operation
171+
//
172+
// This may lead to incomplete type inference for the resulting type
173+
// of a `match` or `if .. else`, etc. This is a backwards compat
174+
// hazard for if/when we start handling `lub` more correctly.
175+
//
176+
// In order to actually ensure that equating the binders *does*
177+
// result in equal binders, and that the lhs is actually a supertype
178+
// of the rhs, we must perform a leak check here.
179+
//
180+
// FIXME: Type relations should handle leak checks
181+
// themselves whenever a binder is entered.
182+
if matches!(leak_check, LeakCheck::Yes) {
183+
self.leak_check(outer_universe, Some(snapshot))?;
152184
}
185+
186+
res
153187
})
154188
}
155189

156190
/// Unify two types (using sub or lub).
157-
fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
158-
self.unify_raw(a, b)
191+
fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>, leak_check: LeakCheck) -> CoerceResult<'tcx> {
192+
self.unify_raw(a, b, leak_check)
159193
.and_then(|InferOk { value: ty, obligations }| success(vec![], ty, obligations))
160194
}
161195

@@ -166,8 +200,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
166200
b: Ty<'tcx>,
167201
adjustments: impl IntoIterator<Item = Adjustment<'tcx>>,
168202
final_adjustment: Adjust,
203+
leak_check: LeakCheck,
169204
) -> CoerceResult<'tcx> {
170-
self.unify_raw(a, b).and_then(|InferOk { value: ty, obligations }| {
205+
self.unify_raw(a, b, leak_check).and_then(|InferOk { value: ty, obligations }| {
171206
success(
172207
adjustments
173208
.into_iter()
@@ -196,7 +231,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
196231
);
197232
} else {
198233
// Otherwise the only coercion we can do is unification.
199-
return self.unify(a, b);
234+
return self.unify(a, b, LeakCheck::Default);
200235
}
201236
}
202237

@@ -266,7 +301,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
266301
}
267302
_ => {
268303
// Otherwise, just use unification rules.
269-
self.unify(a, b)
304+
self.unify(a, b, LeakCheck::Default)
270305
}
271306
}
272307
}
@@ -305,7 +340,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
305340
} else {
306341
// One unresolved type variable: just apply subtyping, we may be able
307342
// to do something useful.
308-
self.unify(a, b)
343+
self.unify(a, b, LeakCheck::Default)
309344
}
310345
}
311346

@@ -332,7 +367,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
332367
coerce_mutbls(mutbl, mutbl_b)?;
333368
(r_a, ty::TypeAndMut { ty, mutbl })
334369
}
335-
_ => return self.unify(a, b),
370+
_ => return self.unify(a, b, LeakCheck::Default),
336371
};
337372

338373
// Look at each step in the `Deref` chain and check if
@@ -383,7 +418,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
383418
// the `Target` type with the pointee of `b`. This is necessary
384419
// to properly account for the differing variances of the pointees
385420
// of `&` vs `&mut` references.
386-
match self.unify_raw(autorefd_deref_ty, b) {
421+
match self.unify_raw(autorefd_deref_ty, b, LeakCheck::Default) {
387422
Ok(ok) => Some(ok),
388423
Err(err) => {
389424
if first_error.is_none() {
@@ -580,6 +615,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
580615
target,
581616
reborrow.into_iter().flat_map(|(deref, autoref)| [deref, autoref]),
582617
Adjust::Pointer(PointerCoercion::Unsize),
618+
LeakCheck::Default,
583619
)?;
584620

585621
// Create an obligation for `Source: CoerceUnsized<Target>`.
@@ -794,7 +830,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
794830

795831
// To complete the reborrow, we need to make sure we can unify the inner types, and if so we
796832
// add the adjustments.
797-
self.unify_and(a, b, [], Adjust::ReborrowPin(mut_b))
833+
self.unify_and(a, b, [], Adjust::ReborrowPin(mut_b), LeakCheck::Default)
798834
}
799835

800836
fn coerce_from_safe_fn(
@@ -805,39 +841,26 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
805841
) -> CoerceResult<'tcx> {
806842
debug_assert!(self.shallow_resolve(b) == b);
807843

808-
self.commit_if_ok(|snapshot| {
809-
let outer_universe = self.infcx.universe();
810-
811-
let result = if let ty::FnPtr(_, hdr_b) = b.kind()
812-
&& fn_ty_a.safety().is_safe()
813-
&& hdr_b.safety.is_unsafe()
814-
{
815-
let unsafe_a = self.tcx.safe_to_unsafe_fn_ty(fn_ty_a);
816-
self.unify_and(
817-
unsafe_a,
818-
b,
819-
adjustment
820-
.map(|kind| Adjustment { kind, target: Ty::new_fn_ptr(self.tcx, fn_ty_a) }),
821-
Adjust::Pointer(PointerCoercion::UnsafeFnPointer),
822-
)
823-
} else {
824-
let a = Ty::new_fn_ptr(self.tcx, fn_ty_a);
825-
match adjustment {
826-
Some(adjust) => self.unify_and(a, b, [], adjust),
827-
None => self.unify(a, b),
828-
}
829-
};
830-
831-
// FIXME(#73154): This is a hack. Currently LUB can generate
832-
// unsolvable constraints. Additionally, it returns `a`
833-
// unconditionally, even when the "LUB" is `b`. In the future, we
834-
// want the coerced type to be the actual supertype of these two,
835-
// but for now, we want to just error to ensure we don't lock
836-
// ourselves into a specific behavior with NLL.
837-
self.leak_check(outer_universe, Some(snapshot))?;
838-
839-
result
840-
})
844+
if let ty::FnPtr(_, hdr_b) = b.kind()
845+
&& fn_ty_a.safety().is_safe()
846+
&& hdr_b.safety.is_unsafe()
847+
{
848+
let unsafe_a = self.tcx.safe_to_unsafe_fn_ty(fn_ty_a);
849+
self.unify_and(
850+
unsafe_a,
851+
b,
852+
adjustment
853+
.map(|kind| Adjustment { kind, target: Ty::new_fn_ptr(self.tcx, fn_ty_a) }),
854+
Adjust::Pointer(PointerCoercion::UnsafeFnPointer),
855+
LeakCheck::Yes,
856+
)
857+
} else {
858+
let a = Ty::new_fn_ptr(self.tcx, fn_ty_a);
859+
match adjustment {
860+
Some(adjust) => self.unify_and(a, b, [], adjust, LeakCheck::Yes),
861+
None => self.unify(a, b, LeakCheck::Yes),
862+
}
863+
}
841864
}
842865

843866
fn coerce_from_fn_pointer(
@@ -901,7 +924,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
901924
obligations.extend(o2);
902925
Ok(InferOk { value, obligations })
903926
}
904-
_ => self.unify(a, b),
927+
_ => self.unify(a, b, LeakCheck::Default),
905928
}
906929
}
907930

@@ -946,9 +969,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
946969
b,
947970
[],
948971
Adjust::Pointer(PointerCoercion::ClosureFnPointer(safety)),
972+
LeakCheck::Default,
949973
)
950974
}
951-
_ => self.unify(a, b),
975+
_ => self.unify(a, b, LeakCheck::Default),
952976
}
953977
}
954978

@@ -965,7 +989,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
965989
let (is_ref, mt_a) = match *a.kind() {
966990
ty::Ref(_, ty, mutbl) => (true, ty::TypeAndMut { ty, mutbl }),
967991
ty::RawPtr(ty, mutbl) => (false, ty::TypeAndMut { ty, mutbl }),
968-
_ => return self.unify(a, b),
992+
_ => return self.unify(a, b, LeakCheck::Default),
969993
};
970994
coerce_mutbls(mt_a.mutbl, mutbl_b)?;
971995

@@ -980,11 +1004,18 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
9801004
b,
9811005
[Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }],
9821006
Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)),
1007+
LeakCheck::Default,
9831008
)
9841009
} else if mt_a.mutbl != mutbl_b {
985-
self.unify_and(a_raw, b, [], Adjust::Pointer(PointerCoercion::MutToConstPointer))
1010+
self.unify_and(
1011+
a_raw,
1012+
b,
1013+
[],
1014+
Adjust::Pointer(PointerCoercion::MutToConstPointer),
1015+
LeakCheck::Default,
1016+
)
9861017
} else {
987-
self.unify(a_raw, b)
1018+
self.unify(a_raw, b, LeakCheck::Default)
9881019
}
9891020
}
9901021
}
@@ -1083,7 +1114,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10831114
// We don't ever need two-phase here since we throw out the result of the coercion.
10841115
let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true);
10851116
coerce.autoderef(DUMMY_SP, expr_ty).find_map(|(ty, steps)| {
1086-
self.probe(|_| coerce.unify_raw(ty, target)).ok().map(|_| steps)
1117+
self.probe(|_| coerce.unify_raw(ty, target, LeakCheck::Default)).ok().map(|_| steps)
10871118
})
10881119
}
10891120

0 commit comments

Comments
 (0)