Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stop leaking inference variables from snapshots #121365

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1058,7 +1058,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
3 changes: 2 additions & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if fn_sig.has_escaping_bound_vars() {
return fn_sig;
}
self.probe(|_| {
// We only return the normalized `fn_sig` if it does not contain infer vars.
self.probe_unchecked(|_| {
let ocx = ObligationCtxt::new(self);
let normalized_fn_sig =
ocx.normalize(&ObligationCause::dummy(), self.param_env, fn_sig);
Expand Down
14 changes: 13 additions & 1 deletion compiler/rustc_hir_typeck/src/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,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::infer::{self, InferCtxt, InferOk, PlugSnapshotLeaks};
use rustc_middle::query::Providers;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{self, GenericParamDefKind, Ty, TypeVisitableExt};
Expand Down Expand Up @@ -67,6 +67,13 @@
BadReturnType,
}

impl<'tcx> PlugSnapshotLeaks<'tcx> for MethodError<'tcx> {
fn plug_leaks(self, infcx: &InferCtxt<'tcx>) -> Self {
// TODO

Check failure on line 72 in compiler/rustc_hir_typeck/src/method/mod.rs

View workflow job for this annotation

GitHub Actions / PR - mingw-check-tidy

TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
self
}
}

// Contains a list of static methods that may apply, a list of unsatisfied trait predicates which
// could lead to matches if satisfied, and a list of not-in-scope traits which may work.
#[derive(Debug)]
Expand All @@ -86,6 +93,11 @@
Impl(DefId),
Trait(DefId /* trait id */),
}
impl<'tcx> PlugSnapshotLeaks<'tcx> for CandidateSource {
fn plug_leaks(self, _: &InferCtxt<'tcx>) -> Self {
self
}
}

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Determines whether the type `self_ty` supports a visible method named `method_name` or not.
Expand Down
23 changes: 22 additions & 1 deletion compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@
use rustc_infer::infer::canonical::{Canonical, QueryResponse};
use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
use rustc_infer::infer::DefineOpaqueTypes;
use rustc_infer::infer::InferCtxt;
use rustc_infer::infer::PlugSnapshotLeaks;
use rustc_infer::infer::{self, InferOk, TyCtxtInferExt};
use rustc_middle::middle::stability;
use rustc_middle::query::Providers;
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
use rustc_middle::ty::AssocItem;
use rustc_middle::ty::GenericParamDefKind;
use rustc_middle::ty::PolySubtypePredicate;
use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
use rustc_middle::ty::{GenericArgs, GenericArgsRef};
Expand Down Expand Up @@ -136,6 +139,12 @@
pub(crate) kind: CandidateKind<'tcx>,
pub(crate) import_ids: SmallVec<[LocalDefId; 1]>,
}
impl<'tcx> PlugSnapshotLeaks<'tcx> for Candidate<'tcx> {
fn plug_leaks(self, _: &InferCtxt<'tcx>) -> Self {
// TODO

Check failure on line 144 in compiler/rustc_hir_typeck/src/method/probe.rs

View workflow job for this annotation

GitHub Actions / PR - mingw-check-tidy

TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
self
}
}

#[derive(Debug, Clone)]
pub(crate) enum CandidateKind<'tcx> {
Expand All @@ -158,6 +167,11 @@
BadReturnType,
Match,
}
impl<'tcx> PlugSnapshotLeaks<'tcx> for ProbeResult {
fn plug_leaks(self, _: &infer::InferCtxt<'tcx>) -> Self {
self
}
}

/// When adjusting a receiver we often want to do one of
///
Expand Down Expand Up @@ -216,6 +230,13 @@
unstable_candidates: Vec<(Candidate<'tcx>, Symbol)>,
}

impl<'tcx> PlugSnapshotLeaks<'tcx> for Pick<'tcx> {
fn plug_leaks(self, _: &infer::InferCtxt<'tcx>) -> Self {
// TODO

Check failure on line 235 in compiler/rustc_hir_typeck/src/method/probe.rs

View workflow job for this annotation

GitHub Actions / PR - mingw-check-tidy

TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
self
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PickKind<'tcx> {
InherentImplPick,
Expand Down Expand Up @@ -355,7 +376,7 @@
.unwrap()
}

fn probe_op<OP, R>(
fn probe_op<OP, R: PlugSnapshotLeaks<'tcx>>(
&'a self,
span: Span,
mode: Mode,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/infer/fudge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ impl<'tcx> InferCtxt<'tcx> {
T: TypeFoldable<TyCtxt<'tcx>>,
{
let variable_lengths = self.variable_lengths();
let (mut fudger, value) = self.probe(|_| {
let (mut fudger, value) = self.probe_unchecked(|_| {
match f() {
Ok(value) => {
let value = self.resolve_vars_if_possible(value);
Expand Down
151 changes: 144 additions & 7 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
pub use self::at::DefineOpaqueTypes;
pub use self::freshen::TypeFreshener;
pub use self::lexical_region_resolve::RegionResolutionError;
use self::opaque_types::OpaqueTypeStorage;
pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog};
pub use self::BoundRegionConversionTime::*;
pub use self::RegionVariableOrigin::*;
pub use self::SubregionOrigin::*;
Expand All @@ -10,9 +12,7 @@ use rustc_data_structures::captures::Captures;
use rustc_data_structures::undo_log::UndoLogs;
use rustc_middle::infer::unify_key::EffectVarValue;
use rustc_middle::infer::unify_key::{ConstVidKey, EffectVidKey};

use self::opaque_types::OpaqueTypeStorage;
pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog};
use rustc_middle::ty::TypeVisitable;

use crate::traits::{
self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine, TraitEngineExt,
Expand Down Expand Up @@ -45,6 +45,7 @@ use rustc_span::Span;

use std::cell::{Cell, RefCell};
use std::fmt;
use std::ops::ControlFlow;

use self::error_reporting::TypeErrCtxt;
use self::free_regions::RegionRelations;
Expand Down Expand Up @@ -890,24 +891,34 @@ impl<'tcx> InferCtxt<'tcx> {
pub fn commit_if_ok<T, E, F>(&self, f: F) -> Result<T, E>
where
F: FnOnce(&CombinedSnapshot<'tcx>) -> Result<T, E>,
E: PlugSnapshotLeaks<'tcx>,
{
let snapshot = self.start_snapshot();
let r = f(&snapshot);
debug!("commit_if_ok() -- r.is_ok() = {}", r.is_ok());
match r {
Ok(_) => {
Ok(value) => {
self.commit_from(snapshot);
Some(value)
}
Err(_) => {
Err(e) => {
self.rollback_to(snapshot);
Err(e.plug_leaks(self))
}
}
r
}

/// Execute `f` then unroll any bindings it creates.
#[instrument(skip(self, f), level = "debug")]
pub fn probe<R, F>(&self, f: F) -> R
pub fn probe<R: PlugSnapshotLeaks<'tcx>, F>(&self, f: F) -> R
where
F: FnOnce(&CombinedSnapshot<'tcx>) -> R,
{
let r = self.probe_unchecked(f);
r.plug_leaks(self)
}

pub fn probe_unchecked<R, F>(&self, f: F) -> R
where
F: FnOnce(&CombinedSnapshot<'tcx>) -> R,
{
Expand Down Expand Up @@ -2134,3 +2145,129 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>(

args.fold_with(&mut ReplaceParamAndInferWithPlaceholder { tcx, idx: 0 })
}

pub trait PlugSnapshotLeaks<'tcx> {
fn plug_leaks(self, infcx: &InferCtxt<'tcx>) -> Self;
}

macro_rules! noop_plug_leaks {
(<$tcx:lifetime> $($ty:ty),*$(,)?) => {
$(
impl<$tcx> PlugSnapshotLeaks<$tcx> for $ty {
fn plug_leaks(self, _: &InferCtxt<$tcx>) -> Self {
self
}
}
)*
};
}

noop_plug_leaks!(<'tcx>
!,
(),
bool,
usize,
DefId,
rustc_middle::ty::AssocItem,
rustc_middle::traits::query::NoSolution,
rustc_middle::traits::solve::Certainty,
rustc_middle::traits::EvaluationResult,
rustc_middle::traits::BuiltinImplSource,
rustc_middle::traits::query::MethodAutoderefStepsResult<'tcx>,
rustc_span::symbol::Ident,
ErrorGuaranteed,
rustc_middle::traits::OverflowError,
);

impl<'tcx, T: PlugSnapshotLeaks<'tcx>> PlugSnapshotLeaks<'tcx> for Option<T> {
fn plug_leaks(self, infcx: &InferCtxt<'tcx>) -> Self {
self.map(|v| v.plug_leaks(infcx))
}
}

impl<'tcx, T0, T1> PlugSnapshotLeaks<'tcx> for (T0, T1)
where
T0: PlugSnapshotLeaks<'tcx>,
T1: PlugSnapshotLeaks<'tcx>,
{
fn plug_leaks(self, infcx: &InferCtxt<'tcx>) -> Self {
(self.0.plug_leaks(infcx), self.1.plug_leaks(infcx))
}
}

impl<'tcx, V: TypeVisitable<TyCtxt<'tcx>>> PlugSnapshotLeaks<'tcx> for Canonical<'tcx, V> {
fn plug_leaks(self, _: &InferCtxt<'tcx>) -> Self {
debug_assert!(!self.has_infer() && !self.has_placeholders());
self
}
}

impl<'tcx, T, E> PlugSnapshotLeaks<'tcx> for Result<T, E>
where
T: PlugSnapshotLeaks<'tcx>,
E: PlugSnapshotLeaks<'tcx>,
{
fn plug_leaks(self, infcx: &InferCtxt<'tcx>) -> Self {
match self {
Ok(v) => Ok(v.plug_leaks(infcx)),
Err(e) => Err(e.plug_leaks(infcx)),
}
}
}

impl<'tcx, T, E> PlugSnapshotLeaks<'tcx> for ControlFlow<T, E>
where
T: PlugSnapshotLeaks<'tcx>,
E: PlugSnapshotLeaks<'tcx>,
{
fn plug_leaks(self, infcx: &InferCtxt<'tcx>) -> Self {
match self {
ControlFlow::Continue(c) => ControlFlow::Continue(c.plug_leaks(infcx)),
ControlFlow::Break(b) => ControlFlow::Break(b.plug_leaks(infcx)),
}
}
}

impl<'tcx, T: PlugSnapshotLeaks<'tcx>> PlugSnapshotLeaks<'tcx> for Vec<T> {
fn plug_leaks(self, infcx: &InferCtxt<'tcx>) -> Self {
self.into_iter().map(|e| e.plug_leaks(infcx)).collect()
}
}

impl<'tcx> PlugSnapshotLeaks<'tcx> for TypeError<'tcx> {
fn plug_leaks(self, _: &InferCtxt<'tcx>) -> Self {
match self {
TypeError::Mismatch
| TypeError::ConstnessMismatch(_)
| TypeError::PolarityMismatch(_)
| TypeError::UnsafetyMismatch(_)
| TypeError::AbiMismatch(_)
| TypeError::Mutability
| TypeError::ArgumentMutability(_)
| TypeError::TupleSize(_)
| TypeError::FixedArraySize(_)
| TypeError::ArgCount
| TypeError::FieldMisMatch(_, _)
| TypeError::RegionsPlaceholderMismatch
| TypeError::IntMismatch(_)
| TypeError::FloatMismatch(_)
| TypeError::Traits(_)
| TypeError::VariadicMismatch(_)
| TypeError::ProjectionMismatched(_)
| TypeError::IntrinsicCast
| TypeError::TargetFeatureCast(_) => {
debug_assert!(!self.has_infer() && !self.has_placeholders());
self
}

TypeError::RegionsDoesNotOutlive(_, _)
| TypeError::RegionsInsufficientlyPolymorphic(_, _)
| TypeError::Sorts(_)
| TypeError::ArgumentSorts(_, _)
| TypeError::CyclicTy(_)
| TypeError::CyclicConst(_)
| TypeError::ExistentialMismatch(_)
| TypeError::ConstMismatch(_) => TypeError::Mismatch,
}
}
}
1 change: 1 addition & 0 deletions compiler/rustc_infer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#![feature(extend_one)]
#![feature(let_chains)]
#![feature(if_let_guard)]
#![feature(never_type)]
#![feature(iterator_try_collect)]
#![cfg_attr(bootstrap, feature(min_specialization))]
#![feature(try_blocks)]
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_infer/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use super::PredicateObligation;

use crate::infer::InferCtxtUndoLogs;
use crate::infer::{InferCtxt, InferCtxtUndoLogs, PlugSnapshotLeaks};

use rustc_data_structures::{
snapshot_map::{self, SnapshotMapRef, SnapshotMapStorage},
Expand All @@ -19,6 +19,11 @@ pub(crate) type UndoLog<'tcx> =
pub struct MismatchedProjectionTypes<'tcx> {
pub err: ty::error::TypeError<'tcx>,
}
impl<'tcx> PlugSnapshotLeaks<'tcx> for MismatchedProjectionTypes<'tcx> {
fn plug_leaks(self, infcx: &InferCtxt<'tcx>) -> Self {
MismatchedProjectionTypes { err: self.err.plug_leaks(infcx) }
}
}

#[derive(Clone)]
pub struct Normalized<'tcx, T> {
Expand Down
28 changes: 13 additions & 15 deletions compiler/rustc_trait_selection/src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,22 +86,20 @@ impl<'tcx> InferCtxt<'tcx> {
ty: Ty<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Option<Vec<traits::FulfillmentError<'tcx>>> {
self.probe(|_snapshot| {
let mut selcx = SelectionContext::new(self);
match selcx.select(&Obligation::new(
self.tcx,
ObligationCause::dummy(),
param_env,
ty::TraitRef::new(self.tcx, trait_def_id, [ty]),
)) {
Ok(Some(selection)) => {
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(self);
fulfill_cx.register_predicate_obligations(self, selection.nested_obligations());
Some(fulfill_cx.select_all_or_error(self))
}
Ok(None) | Err(_) => None,
let mut selcx = SelectionContext::new(self);
match selcx.select(&Obligation::new(
self.tcx,
ObligationCause::dummy(),
param_env,
ty::TraitRef::new(self.tcx, trait_def_id, [ty]),
)) {
Ok(Some(selection)) => {
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(self);
fulfill_cx.register_predicate_obligations(self, selection.nested_obligations());
Some(fulfill_cx.select_all_or_error(self))
}
})
Ok(None) | Err(_) => None,
}
}
}

Expand Down
Loading
Loading