about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-11-02 22:58:01 -0700
committerGitHub <noreply@github.com>2016-11-02 22:58:01 -0700
commitac919fcd9d4a958baf99b2f2ed5c3d38a2ebf9d0 (patch)
tree88e3435d49f2f1e670cf8eba613210b4ddb495e2
parentf9f45c6dacd0f0d0a44473931291a4fa6bbb4ddc (diff)
parentb4f910d9004f09620ef5b1aff5d676c1dab7d42f (diff)
downloadrust-ac919fcd9d4a958baf99b2f2ed5c3d38a2ebf9d0.tar.gz
rust-ac919fcd9d4a958baf99b2f2ed5c3d38a2ebf9d0.zip
Auto merge of #37541 - nikomatsakis:issue-37291, r=brson
Use impl obligations as initial environment for specialization

This corrects a small regression in specialization that crept in, I think as part of the refactoring to introduce arenas. I also made an experiment (in the last commit) to cleanup the code to be more aggressive about normalization. As the commit log notes, I am not 100% sure that this is correct, but it feels safer, and I think that at worst it yields *more* ICEs (as opposed to admitting faulty code). I'll schedule a crater run to check beyond the testbase.

Fixes #37291.

r? @aturon
-rw-r--r--src/librustc/traits/specialize/mod.rs37
-rw-r--r--src/test/run-pass/issue-37291/auxiliary/lib.rs52
-rw-r--r--src/test/run-pass/issue-37291/main.rs29
3 files changed, 97 insertions, 21 deletions
diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs
index e37425901c8..24cafa7f725 100644
--- a/src/librustc/traits/specialize/mod.rs
+++ b/src/librustc/traits/specialize/mod.rs
@@ -25,7 +25,7 @@ use hir::def_id::DefId;
 use infer::{InferCtxt, TypeOrigin};
 use middle::region;
 use ty::subst::{Subst, Substs};
-use traits::{self, Reveal, ObligationCause, Normalized};
+use traits::{self, Reveal, ObligationCause};
 use ty::{self, TyCtxt, TypeFoldable};
 use syntax_pos::DUMMY_SP;
 
@@ -148,6 +148,8 @@ pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 pub fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                              impl1_def_id: DefId,
                              impl2_def_id: DefId) -> bool {
+    debug!("specializes({:?}, {:?})", impl1_def_id, impl2_def_id);
+
     if let Some(r) = tcx.specializes_cache.borrow().check(impl1_def_id, impl2_def_id) {
         return r;
     }
@@ -177,31 +179,24 @@ pub fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     }
 
     // create a parameter environment corresponding to a (skolemized) instantiation of impl1
-    let mut penv = tcx.construct_parameter_environment(DUMMY_SP,
-                                                       impl1_def_id,
-                                                       region::DUMMY_CODE_EXTENT);
+    let penv = tcx.construct_parameter_environment(DUMMY_SP,
+                                                   impl1_def_id,
+                                                   region::DUMMY_CODE_EXTENT);
     let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id)
                              .unwrap()
                              .subst(tcx, &penv.free_substs);
 
-    let result = tcx.infer_ctxt(None, None, Reveal::ExactMatch).enter(|mut infcx| {
-        // Normalize the trait reference, adding any obligations
-        // that arise into the impl1 assumptions.
-        let Normalized { value: impl1_trait_ref, obligations: normalization_obligations } = {
-            let selcx = &mut SelectionContext::new(&infcx);
-            traits::normalize(selcx, ObligationCause::dummy(), &impl1_trait_ref)
-        };
-        penv.caller_bounds.extend(normalization_obligations.into_iter().map(|o| {
-            match tcx.lift_to_global(&o.predicate) {
-                Some(predicate) => predicate,
-                None => {
-                    bug!("specializes: obligation `{:?}` has inference types/regions", o);
+    // Create a infcx, taking the predicates of impl1 as assumptions:
+    let result = tcx.infer_ctxt(None, Some(penv), Reveal::ExactMatch).enter(|infcx| {
+        // Normalize the trait reference. The WF rules ought to ensure
+        // that this always succeeds.
+        let impl1_trait_ref =
+            match traits::fully_normalize(&infcx, ObligationCause::dummy(), &impl1_trait_ref) {
+                Ok(impl1_trait_ref) => impl1_trait_ref,
+                Err(err) => {
+                    bug!("failed to fully normalize {:?}: {:?}", impl1_trait_ref, err);
                 }
-            }
-        }));
-
-        // Install the parameter environment, taking the predicates of impl1 as assumptions:
-        infcx.parameter_environment = penv;
+            };
 
         // Attempt to prove that impl2 applies, given all of the above.
         fulfill_implication(&infcx, impl1_trait_ref, impl2_def_id).is_ok()
diff --git a/src/test/run-pass/issue-37291/auxiliary/lib.rs b/src/test/run-pass/issue-37291/auxiliary/lib.rs
new file mode 100644
index 00000000000..67cdea807be
--- /dev/null
+++ b/src/test/run-pass/issue-37291/auxiliary/lib.rs
@@ -0,0 +1,52 @@
+// Copyright 2016 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.
+
+#![crate_type = "lib"]
+
+use std::ops::Mul;
+
+pub trait A {}
+pub trait B {
+    type AT: A;
+}
+pub trait C {
+    type BT: B;
+}
+
+pub struct AV;
+impl A for AV {}
+
+pub struct BV;
+impl B for BV {
+    type AT = AV;
+}
+
+pub struct CV;
+impl C for CV {
+    type BT = BV;
+}
+
+pub struct WrapperB<T>(pub T);
+pub struct WrapperC<T>(pub T);
+
+impl<C1> Mul<WrapperB<<C1::BT as B>::AT>> for WrapperC<C1>
+    where C1: C
+{
+    type Output = u8;
+    fn mul(self, _: WrapperB<<C1::BT as B>::AT>) -> Self::Output {
+        loop {}
+    }
+}
+impl<C1> Mul<WrapperC<C1>> for WrapperC<C1> {
+    type Output = u8;
+    fn mul(self, _: WrapperC<C1>) -> Self::Output {
+        loop {}
+    }
+}
diff --git a/src/test/run-pass/issue-37291/main.rs b/src/test/run-pass/issue-37291/main.rs
new file mode 100644
index 00000000000..2461f7485f2
--- /dev/null
+++ b/src/test/run-pass/issue-37291/main.rs
@@ -0,0 +1,29 @@
+// Copyright 2016 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.
+
+// aux-build:lib.rs
+
+// Regression test for #37291. The problem was that the starting
+// environment for a specialization check was not including the
+// where-clauses from the impl when attempting to normalize the impl's
+// trait-ref, so things like `<C as Foo>::Item` could not resolve,
+// since the `C: Foo` trait bound was not included in the environment.
+
+extern crate lib;
+
+use lib::{CV, WrapperB, WrapperC};
+
+fn main() {
+    let a = WrapperC(CV);
+    let b = WrapperC(CV);
+    if false {
+        let _ = a * b;
+    }
+}