From 07201249f09ddf4ade1f02d8208b624b5d927212 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sat, 15 Sep 2018 17:14:18 +0300 Subject: [PATCH] process nested obligations in autoderef This is a hack-fix to #53843, but I am worried it might break things because it makes the "inference pollution" problem worse. Fixes #53843 (but introduces a bug that someone might notice). --- src/librustc/traits/fulfill.rs | 26 +++++++++++++++--- src/librustc_typeck/check/autoderef.rs | 37 ++++++++++++++++---------- src/test/run-pass/issue-53843.rs | 34 +++++++++++++++++++++++ 3 files changed, 80 insertions(+), 17 deletions(-) create mode 100644 src/test/run-pass/issue-53843.rs diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index bc091a4e7e084..09c7bd679705a 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -61,6 +61,16 @@ pub struct FulfillmentContext<'tcx> { // type-lives-for-region constraints, and because the type // is well-formed, the constraints should hold. register_region_obligations: bool, + // Is it OK to register obligations into this infcx inside + // an infcx snapshot? + // + // The "primary fulfillment" in many cases in typeck lives + // outside of any snapshot, so any use of it inside a snapshot + // will lead to trouble and therefore is checked against, but + // other fulfillment contexts sometimes do live inside of + // a snapshot (they don't *straddle* a snapshot, so there + // is no trouble there). + usable_in_snapshot: bool } #[derive(Clone, Debug)] @@ -74,14 +84,24 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { pub fn new() -> FulfillmentContext<'tcx> { FulfillmentContext { predicates: ObligationForest::new(), - register_region_obligations: true + register_region_obligations: true, + usable_in_snapshot: false, + } + } + + pub fn new_in_snapshot() -> FulfillmentContext<'tcx> { + FulfillmentContext { + predicates: ObligationForest::new(), + register_region_obligations: true, + usable_in_snapshot: true, } } pub fn new_ignoring_regions() -> FulfillmentContext<'tcx> { FulfillmentContext { predicates: ObligationForest::new(), - register_region_obligations: false + register_region_obligations: false, + usable_in_snapshot: false } } @@ -195,7 +215,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { debug!("register_predicate_obligation(obligation={:?})", obligation); - assert!(!infcx.is_in_snapshot()); + assert!(!infcx.is_in_snapshot() || self.usable_in_snapshot); self.predicates.register_obligation(PendingPredicateObligation { obligation, diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs index 1b594342c9a71..7a84634d2a3ee 100644 --- a/src/librustc_typeck/check/autoderef.rs +++ b/src/librustc_typeck/check/autoderef.rs @@ -15,7 +15,7 @@ use super::method::MethodCallee; use rustc::infer::InferOk; use rustc::session::DiagnosticMessageId; -use rustc::traits; +use rustc::traits::{self, TraitEngine}; use rustc::ty::{self, Ty, TraitRef}; use rustc::ty::{ToPredicate, TypeFoldable}; use rustc::ty::adjustment::{Adjustment, Adjust, OverloadedDeref}; @@ -128,19 +128,28 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> { return None; } - let mut selcx = traits::SelectionContext::new(self.fcx); - let normalized_ty = traits::normalize_projection_type(&mut selcx, - self.fcx.param_env, - ty::ProjectionTy::from_ref_and_name( - tcx, - trait_ref, - Ident::from_str("Target"), - ), - cause, - 0, - &mut self.obligations); - - debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized_ty); + let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot(); + let normalized_ty = fulfillcx.normalize_projection_type( + &self.fcx, + self.fcx.param_env, + ty::ProjectionTy::from_ref_and_name( + tcx, + trait_ref, + Ident::from_str("Target"), + ), + cause); + if let Err(e) = fulfillcx.select_where_possible(&self.fcx) { + // This shouldn't happen, except for evaluate/fulfill mismatches, + // but that's not a reason for an ICE (`predicate_may_hold` is conservative + // by design). + debug!("overloaded_deref_ty: encountered errors {:?} while fulfilling", + e); + return None; + } + let obligations = fulfillcx.pending_obligations(); + debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", + ty, normalized_ty, obligations); + self.obligations.extend(obligations); Some(self.fcx.resolve_type_vars_if_possible(&normalized_ty)) } diff --git a/src/test/run-pass/issue-53843.rs b/src/test/run-pass/issue-53843.rs new file mode 100644 index 0000000000000..4b15ecb3e542b --- /dev/null +++ b/src/test/run-pass/issue-53843.rs @@ -0,0 +1,34 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ops::Deref; + +pub struct Pin

(P); + +impl Deref for Pin

+where + P: Deref, +{ + type Target = T; + + fn deref(&self) -> &T { + &*self.0 + } +} + +impl

Pin

{ + fn poll(self) {} +} + +fn main() { + let mut unit = (); + let pin = Pin(&mut unit); + pin.poll(); +}