Skip to content

stop leaking inference variables from snapshots #121365

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

Closed
wants to merge 1 commit into from
Closed
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
@@ -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
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
@@ -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);
14 changes: 13 additions & 1 deletion compiler/rustc_hir_typeck/src/method/mod.rs
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ use rustc_errors::{Applicability, DiagnosticBuilder, SubdiagnosticMessage};
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};
@@ -67,6 +67,13 @@ pub enum MethodError<'tcx> {
BadReturnType,
}

impl<'tcx> PlugSnapshotLeaks<'tcx> for MethodError<'tcx> {
fn plug_leaks(self, infcx: &InferCtxt<'tcx>) -> Self {
// TODO
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)]
@@ -86,6 +93,11 @@ pub enum CandidateSource {
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.
23 changes: 22 additions & 1 deletion compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
@@ -14,12 +14,15 @@ 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::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};
@@ -136,6 +139,12 @@ pub(crate) struct Candidate<'tcx> {
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
self
}
}

#[derive(Debug, Clone)]
pub(crate) enum CandidateKind<'tcx> {
@@ -158,6 +167,11 @@ enum ProbeResult {
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
///
@@ -216,6 +230,13 @@ pub struct Pick<'tcx> {
unstable_candidates: Vec<(Candidate<'tcx>, Symbol)>,
}

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

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

fn probe_op<OP, R>(
fn probe_op<OP, R: PlugSnapshotLeaks<'tcx>>(
&'a self,
span: Span,
mode: Mode,
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/infer/fudge.rs
Original file line number Diff line number Diff line change
@@ -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);
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::*;
@@ -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,
@@ -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;
@@ -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,
{
@@ -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
@@ -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)]
7 changes: 6 additions & 1 deletion compiler/rustc_infer/src/traits/project.rs
Original file line number Diff line number Diff line change
@@ -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},
@@ -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> {
28 changes: 13 additions & 15 deletions compiler/rustc_trait_selection/src/infer.rs
Original file line number Diff line number Diff line change
@@ -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,
}
}
}

Loading