Skip to content

Commit

Permalink
Auto merge of #122189 - lcnr:probe-no-more-leak, r=oli-obk
Browse files Browse the repository at this point in the history
snapshot: avoid leaking inference vars

A first step towards fixing #122188. There are still some FIXMEs left, most notably method probing.

fixes #122098

r? `@oli-obk`
  • Loading branch information
bors committed Mar 12, 2024
2 parents 3b85d2c + b0ad8d7 commit 2a4b3fd
Show file tree
Hide file tree
Showing 28 changed files with 639 additions and 140 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let coerce = Coerce::new(self, cause, AllowTwoPhase::No);
coerce
.autoderef(rustc_span::DUMMY_SP, expr_ty)
.find_map(|(ty, steps)| self.probe(|_| coerce.unify(ty, target)).ok().map(|_| steps))
.find_map(|(ty, steps)| self.probe(|_| coerce.unify(ty, target).ok().map(|_| steps)))
}

/// Given a type, this function will calculate and return the type given
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_hir_typeck/src/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Namespace};
use rustc_hir::def_id::DefId;
use rustc_infer::infer::{self, InferOk};
use rustc_infer::trivial_no_snapshot_leaks;
use rustc_middle::query::Providers;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{self, GenericParamDefKind, Ty, TypeVisitableExt};
Expand Down Expand Up @@ -43,6 +44,8 @@ pub struct MethodCallee<'tcx> {
pub sig: ty::FnSig<'tcx>,
}

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

// A pared down enum describing just the places from which a method
// candidate can arise. Used for error reporting only.
trivial_no_snapshot_leaks!('tcx, CandidateSource);
/// A pared down enum describing just the places from which a method
/// candidate can arise. Used for error reporting only.
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum CandidateSource {
Impl(DefId),
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ use rustc_hir_analysis::autoderef::{self, Autoderef};
use rustc_infer::infer::canonical::OriginalQueryValues;
use rustc_infer::infer::canonical::{Canonical, QueryResponse};
use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
use rustc_infer::infer::snapshot::NoSnapshotLeaks;
use rustc_infer::infer::DefineOpaqueTypes;
use rustc_infer::infer::{self, InferOk, TyCtxtInferExt};
use rustc_infer::trivial_no_snapshot_leaks;
use rustc_middle::middle::stability;
use rustc_middle::query::Providers;
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
Expand Down Expand Up @@ -97,6 +99,8 @@ impl<'a, 'tcx> Deref for ProbeContext<'a, 'tcx> {
}
}

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

trivial_no_snapshot_leaks!('tcx, ProbeResult);
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
enum ProbeResult {
NoMatch,
Expand Down Expand Up @@ -195,6 +200,8 @@ impl AutorefOrPtrAdjustment {
}
}

// FIXME(#122188): This is wrong as this type may leak inference variables.
trivial_no_snapshot_leaks!('tcx, Pick<'tcx>);
#[derive(Debug, Clone)]
pub struct Pick<'tcx> {
pub item: ty::AssocItem,
Expand Down Expand Up @@ -368,6 +375,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
op: OP,
) -> Result<R, MethodError<'tcx>>
where
R: NoSnapshotLeaks<'tcx>,
OP: FnOnce(ProbeContext<'_, 'tcx>) -> Result<R, MethodError<'tcx>>,
{
let mut orig_values = OriginalQueryValues::default();
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ mod projection;
pub mod region_constraints;
mod relate;
pub mod resolve;
pub(crate) mod snapshot;
pub mod snapshot;
pub mod type_variable;

#[must_use]
Expand Down
140 changes: 140 additions & 0 deletions compiler/rustc_infer/src/infer/snapshot/check_leaks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
use super::VariableLengths;
use crate::infer::InferCtxt;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{TypeSuperVisitable, TypeVisitor};
use std::ops::ControlFlow;

/// Check for leaking inference variables and placeholders
/// from snapshot. This is only used if `debug_assertions`
/// are enabled.
pub struct HasSnapshotLeaksVisitor {
universe: ty::UniverseIndex,
variable_lengths: VariableLengths,
}
impl HasSnapshotLeaksVisitor {
pub fn new<'tcx>(infcx: &InferCtxt<'tcx>) -> Self {
HasSnapshotLeaksVisitor {
universe: infcx.universe(),
variable_lengths: infcx.variable_lengths(),
}
}
}

fn continue_if(b: bool) -> ControlFlow<()> {
if b { ControlFlow::Continue(()) } else { ControlFlow::Break(()) }
}

impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasSnapshotLeaksVisitor {
type Result = ControlFlow<()>;

fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
match r.kind() {
ty::ReVar(var) => continue_if(var.as_usize() < self.variable_lengths.region_vars),
ty::RePlaceholder(p) => continue_if(self.universe.can_name(p.universe)),
ty::ReEarlyParam(_)
| ty::ReBound(_, _)
| ty::ReLateParam(_)
| ty::ReStatic
| ty::ReErased
| ty::ReError(_) => ControlFlow::Continue(()),
}
}
fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
match t.kind() {
ty::Infer(ty::TyVar(var)) => {
continue_if(var.as_usize() < self.variable_lengths.type_vars)
}
ty::Infer(ty::IntVar(var)) => {
continue_if(var.as_usize() < self.variable_lengths.int_vars)
}
ty::Infer(ty::FloatVar(var)) => {
continue_if(var.as_usize() < self.variable_lengths.float_vars)
}
ty::Placeholder(p) => continue_if(self.universe.can_name(p.universe)),
ty::Infer(ty::FreshTy(..) | ty::FreshIntTy(..) | ty::FreshFloatTy(..))
| ty::Bool
| ty::Char
| ty::Int(_)
| ty::Uint(_)
| ty::Float(_)
| ty::Adt(_, _)
| ty::Foreign(_)
| ty::Str
| ty::Array(_, _)
| ty::Slice(_)
| ty::RawPtr(_)
| ty::Ref(_, _, _)
| ty::FnDef(_, _)
| ty::FnPtr(_)
| ty::Dynamic(_, _, _)
| ty::Closure(_, _)
| ty::CoroutineClosure(_, _)
| ty::Coroutine(_, _)
| ty::CoroutineWitness(_, _)
| ty::Never
| ty::Tuple(_)
| ty::Alias(_, _)
| ty::Param(_)
| ty::Bound(_, _)
| ty::Error(_) => t.super_visit_with(self),
}
}
fn visit_const(&mut self, c: ty::Const<'tcx>) -> Self::Result {
match c.kind() {
ty::ConstKind::Infer(ty::InferConst::Var(var)) => {
continue_if(var.as_usize() < self.variable_lengths.const_vars)
}
// FIXME(const_trait_impl): need to handle effect vars here and in `fudge_inference_if_ok`.
ty::ConstKind::Infer(ty::InferConst::EffectVar(_)) => ControlFlow::Continue(()),
ty::ConstKind::Placeholder(p) => continue_if(self.universe.can_name(p.universe)),
ty::ConstKind::Infer(ty::InferConst::Fresh(_))
| ty::ConstKind::Param(_)
| ty::ConstKind::Bound(_, _)
| ty::ConstKind::Unevaluated(_)
| ty::ConstKind::Value(_)
| ty::ConstKind::Expr(_)
| ty::ConstKind::Error(_) => c.super_visit_with(self),
}
}
}

#[macro_export]
#[cfg(debug_assertions)]
macro_rules! type_foldable_verify_no_snapshot_leaks {
($tcx:lifetime, $t:ty) => {
const _: () = {
use rustc_middle::ty::TypeVisitable;
use $crate::infer::snapshot::check_leaks::HasSnapshotLeaksVisitor;
use $crate::infer::InferCtxt;
impl<$tcx> $crate::infer::snapshot::NoSnapshotLeaks<$tcx> for $t {
type StartData = HasSnapshotLeaksVisitor;
type EndData = ($t, HasSnapshotLeaksVisitor);
fn snapshot_start_data(infcx: &$crate::infer::InferCtxt<$tcx>) -> Self::StartData {
HasSnapshotLeaksVisitor::new(infcx)
}
fn end_of_snapshot(
_: &InferCtxt<'tcx>,
value: $t,
visitor: Self::StartData,
) -> Self::EndData {
(value, visitor)
}
fn avoid_leaks(_: &InferCtxt<$tcx>, (value, mut visitor): Self::EndData) -> Self {
if value.visit_with(&mut visitor).is_break() {
bug!("leaking vars from snapshot: {value:?}");
}

value
}
}
};
};
}

#[macro_export]
#[cfg(not(debug_assertions))]
macro_rules! type_foldable_verify_no_snapshot_leaks {
($tcx:lifetime, $t:ty) => {
trivial_no_snapshot_leaks!($tcx, $t);
};
}
Loading

0 comments on commit 2a4b3fd

Please sign in to comment.