about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAriel Ben-Yehuda <ariel.byd@gmail.com>2018-09-15 17:14:18 +0300
committerAriel Ben-Yehuda <ariel.byd@gmail.com>2018-12-14 18:18:51 +0200
commit07201249f09ddf4ade1f02d8208b624b5d927212 (patch)
treefde6f67e0f78c62fdd0266ac370d3738c2c93840
parentf4b07e0713b2d82417968db08cd0575734cdac0d (diff)
downloadrust-07201249f09ddf4ade1f02d8208b624b5d927212.tar.gz
rust-07201249f09ddf4ade1f02d8208b624b5d927212.zip
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).
-rw-r--r--src/librustc/traits/fulfill.rs26
-rw-r--r--src/librustc_typeck/check/autoderef.rs37
-rw-r--r--src/test/run-pass/issue-53843.rs34
3 files changed, 80 insertions, 17 deletions
diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs
index bc091a4e7e0..09c7bd67970 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 1b594342c9a..7a84634d2a3 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 00000000000..4b15ecb3e54
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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>(P);
+
+impl<P, T> Deref for Pin<P>
+where
+    P: Deref<Target=T>,
+{
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        &*self.0
+    }
+}
+
+impl<P> Pin<P> {
+    fn poll(self) {}
+}
+
+fn main() {
+    let mut unit = ();
+    let pin = Pin(&mut unit);
+    pin.poll();
+}