Skip to content

Commit 2a4b3fd

Browse files
committed
Auto merge of rust-lang#122189 - lcnr:probe-no-more-leak, r=oli-obk
snapshot: avoid leaking inference vars A first step towards fixing rust-lang#122188. There are still some FIXMEs left, most notably method probing. fixes rust-lang#122098 r? `@oli-obk`
2 parents 3b85d2c + b0ad8d7 commit 2a4b3fd

28 files changed

+639
-140
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1076,7 +1076,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10761076
let coerce = Coerce::new(self, cause, AllowTwoPhase::No);
10771077
coerce
10781078
.autoderef(rustc_span::DUMMY_SP, expr_ty)
1079-
.find_map(|(ty, steps)| self.probe(|_| coerce.unify(ty, target)).ok().map(|_| steps))
1079+
.find_map(|(ty, steps)| self.probe(|_| coerce.unify(ty, target).ok().map(|_| steps)))
10801080
}
10811081

10821082
/// Given a type, this function will calculate and return the type given

compiler/rustc_hir_typeck/src/method/mod.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use rustc_hir as hir;
1616
use rustc_hir::def::{CtorOf, DefKind, Namespace};
1717
use rustc_hir::def_id::DefId;
1818
use rustc_infer::infer::{self, InferOk};
19+
use rustc_infer::trivial_no_snapshot_leaks;
1920
use rustc_middle::query::Providers;
2021
use rustc_middle::traits::ObligationCause;
2122
use rustc_middle::ty::{self, GenericParamDefKind, Ty, TypeVisitableExt};
@@ -43,6 +44,8 @@ pub struct MethodCallee<'tcx> {
4344
pub sig: ty::FnSig<'tcx>,
4445
}
4546

47+
// FIXME(#122188): This is wrong, as this type may leak inference vars.
48+
trivial_no_snapshot_leaks!('tcx, MethodError<'tcx>);
4649
#[derive(Debug)]
4750
pub enum MethodError<'tcx> {
4851
// Did not find an applicable method, but we did find various near-misses that may work.
@@ -79,8 +82,9 @@ pub struct NoMatchData<'tcx> {
7982
pub mode: probe::Mode,
8083
}
8184

82-
// A pared down enum describing just the places from which a method
83-
// candidate can arise. Used for error reporting only.
85+
trivial_no_snapshot_leaks!('tcx, CandidateSource);
86+
/// A pared down enum describing just the places from which a method
87+
/// candidate can arise. Used for error reporting only.
8488
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
8589
pub enum CandidateSource {
8690
Impl(DefId),

compiler/rustc_hir_typeck/src/method/probe.rs

+8
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ use rustc_hir_analysis::autoderef::{self, Autoderef};
1313
use rustc_infer::infer::canonical::OriginalQueryValues;
1414
use rustc_infer::infer::canonical::{Canonical, QueryResponse};
1515
use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
16+
use rustc_infer::infer::snapshot::NoSnapshotLeaks;
1617
use rustc_infer::infer::DefineOpaqueTypes;
1718
use rustc_infer::infer::{self, InferOk, TyCtxtInferExt};
19+
use rustc_infer::trivial_no_snapshot_leaks;
1820
use rustc_middle::middle::stability;
1921
use rustc_middle::query::Providers;
2022
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
@@ -97,6 +99,8 @@ impl<'a, 'tcx> Deref for ProbeContext<'a, 'tcx> {
9799
}
98100
}
99101

102+
// FIXME(#122188): This is wrong as this type may leak inference variables.
103+
trivial_no_snapshot_leaks!('tcx, Candidate<'tcx>);
100104
#[derive(Debug, Clone)]
101105
pub(crate) struct Candidate<'tcx> {
102106
// Candidates are (I'm not quite sure, but they are mostly) basically
@@ -152,6 +156,7 @@ pub(crate) enum CandidateKind<'tcx> {
152156
),
153157
}
154158

159+
trivial_no_snapshot_leaks!('tcx, ProbeResult);
155160
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
156161
enum ProbeResult {
157162
NoMatch,
@@ -195,6 +200,8 @@ impl AutorefOrPtrAdjustment {
195200
}
196201
}
197202

203+
// FIXME(#122188): This is wrong as this type may leak inference variables.
204+
trivial_no_snapshot_leaks!('tcx, Pick<'tcx>);
198205
#[derive(Debug, Clone)]
199206
pub struct Pick<'tcx> {
200207
pub item: ty::AssocItem,
@@ -368,6 +375,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
368375
op: OP,
369376
) -> Result<R, MethodError<'tcx>>
370377
where
378+
R: NoSnapshotLeaks<'tcx>,
371379
OP: FnOnce(ProbeContext<'_, 'tcx>) -> Result<R, MethodError<'tcx>>,
372380
{
373381
let mut orig_values = OriginalQueryValues::default();

compiler/rustc_infer/src/infer/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ mod projection;
6262
pub mod region_constraints;
6363
mod relate;
6464
pub mod resolve;
65-
pub(crate) mod snapshot;
65+
pub mod snapshot;
6666
pub mod type_variable;
6767

6868
#[must_use]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
use super::VariableLengths;
2+
use crate::infer::InferCtxt;
3+
use rustc_middle::ty::{self, Ty, TyCtxt};
4+
use rustc_middle::ty::{TypeSuperVisitable, TypeVisitor};
5+
use std::ops::ControlFlow;
6+
7+
/// Check for leaking inference variables and placeholders
8+
/// from snapshot. This is only used if `debug_assertions`
9+
/// are enabled.
10+
pub struct HasSnapshotLeaksVisitor {
11+
universe: ty::UniverseIndex,
12+
variable_lengths: VariableLengths,
13+
}
14+
impl HasSnapshotLeaksVisitor {
15+
pub fn new<'tcx>(infcx: &InferCtxt<'tcx>) -> Self {
16+
HasSnapshotLeaksVisitor {
17+
universe: infcx.universe(),
18+
variable_lengths: infcx.variable_lengths(),
19+
}
20+
}
21+
}
22+
23+
fn continue_if(b: bool) -> ControlFlow<()> {
24+
if b { ControlFlow::Continue(()) } else { ControlFlow::Break(()) }
25+
}
26+
27+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasSnapshotLeaksVisitor {
28+
type Result = ControlFlow<()>;
29+
30+
fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
31+
match r.kind() {
32+
ty::ReVar(var) => continue_if(var.as_usize() < self.variable_lengths.region_vars),
33+
ty::RePlaceholder(p) => continue_if(self.universe.can_name(p.universe)),
34+
ty::ReEarlyParam(_)
35+
| ty::ReBound(_, _)
36+
| ty::ReLateParam(_)
37+
| ty::ReStatic
38+
| ty::ReErased
39+
| ty::ReError(_) => ControlFlow::Continue(()),
40+
}
41+
}
42+
fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
43+
match t.kind() {
44+
ty::Infer(ty::TyVar(var)) => {
45+
continue_if(var.as_usize() < self.variable_lengths.type_vars)
46+
}
47+
ty::Infer(ty::IntVar(var)) => {
48+
continue_if(var.as_usize() < self.variable_lengths.int_vars)
49+
}
50+
ty::Infer(ty::FloatVar(var)) => {
51+
continue_if(var.as_usize() < self.variable_lengths.float_vars)
52+
}
53+
ty::Placeholder(p) => continue_if(self.universe.can_name(p.universe)),
54+
ty::Infer(ty::FreshTy(..) | ty::FreshIntTy(..) | ty::FreshFloatTy(..))
55+
| ty::Bool
56+
| ty::Char
57+
| ty::Int(_)
58+
| ty::Uint(_)
59+
| ty::Float(_)
60+
| ty::Adt(_, _)
61+
| ty::Foreign(_)
62+
| ty::Str
63+
| ty::Array(_, _)
64+
| ty::Slice(_)
65+
| ty::RawPtr(_)
66+
| ty::Ref(_, _, _)
67+
| ty::FnDef(_, _)
68+
| ty::FnPtr(_)
69+
| ty::Dynamic(_, _, _)
70+
| ty::Closure(_, _)
71+
| ty::CoroutineClosure(_, _)
72+
| ty::Coroutine(_, _)
73+
| ty::CoroutineWitness(_, _)
74+
| ty::Never
75+
| ty::Tuple(_)
76+
| ty::Alias(_, _)
77+
| ty::Param(_)
78+
| ty::Bound(_, _)
79+
| ty::Error(_) => t.super_visit_with(self),
80+
}
81+
}
82+
fn visit_const(&mut self, c: ty::Const<'tcx>) -> Self::Result {
83+
match c.kind() {
84+
ty::ConstKind::Infer(ty::InferConst::Var(var)) => {
85+
continue_if(var.as_usize() < self.variable_lengths.const_vars)
86+
}
87+
// FIXME(const_trait_impl): need to handle effect vars here and in `fudge_inference_if_ok`.
88+
ty::ConstKind::Infer(ty::InferConst::EffectVar(_)) => ControlFlow::Continue(()),
89+
ty::ConstKind::Placeholder(p) => continue_if(self.universe.can_name(p.universe)),
90+
ty::ConstKind::Infer(ty::InferConst::Fresh(_))
91+
| ty::ConstKind::Param(_)
92+
| ty::ConstKind::Bound(_, _)
93+
| ty::ConstKind::Unevaluated(_)
94+
| ty::ConstKind::Value(_)
95+
| ty::ConstKind::Expr(_)
96+
| ty::ConstKind::Error(_) => c.super_visit_with(self),
97+
}
98+
}
99+
}
100+
101+
#[macro_export]
102+
#[cfg(debug_assertions)]
103+
macro_rules! type_foldable_verify_no_snapshot_leaks {
104+
($tcx:lifetime, $t:ty) => {
105+
const _: () = {
106+
use rustc_middle::ty::TypeVisitable;
107+
use $crate::infer::snapshot::check_leaks::HasSnapshotLeaksVisitor;
108+
use $crate::infer::InferCtxt;
109+
impl<$tcx> $crate::infer::snapshot::NoSnapshotLeaks<$tcx> for $t {
110+
type StartData = HasSnapshotLeaksVisitor;
111+
type EndData = ($t, HasSnapshotLeaksVisitor);
112+
fn snapshot_start_data(infcx: &$crate::infer::InferCtxt<$tcx>) -> Self::StartData {
113+
HasSnapshotLeaksVisitor::new(infcx)
114+
}
115+
fn end_of_snapshot(
116+
_: &InferCtxt<'tcx>,
117+
value: $t,
118+
visitor: Self::StartData,
119+
) -> Self::EndData {
120+
(value, visitor)
121+
}
122+
fn avoid_leaks(_: &InferCtxt<$tcx>, (value, mut visitor): Self::EndData) -> Self {
123+
if value.visit_with(&mut visitor).is_break() {
124+
bug!("leaking vars from snapshot: {value:?}");
125+
}
126+
127+
value
128+
}
129+
}
130+
};
131+
};
132+
}
133+
134+
#[macro_export]
135+
#[cfg(not(debug_assertions))]
136+
macro_rules! type_foldable_verify_no_snapshot_leaks {
137+
($tcx:lifetime, $t:ty) => {
138+
trivial_no_snapshot_leaks!($tcx, $t);
139+
};
140+
}

0 commit comments

Comments
 (0)