From 797e0420e3666b873600038ec4298915fe41db83 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 2 Nov 2016 12:27:11 -0400 Subject: [PATCH 1/3] add #32791 test case --- .../run-pass/issue-37291/auxiliary/lib.rs | 52 +++++++++++++++++++ src/test/run-pass/issue-37291/main.rs | 29 +++++++++++ 2 files changed, 81 insertions(+) create mode 100644 src/test/run-pass/issue-37291/auxiliary/lib.rs create mode 100644 src/test/run-pass/issue-37291/main.rs 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 0000000000000..67cdea807beeb --- /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 or the MIT license +// , 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(pub T); +pub struct WrapperC(pub T); + +impl Mul::AT>> for WrapperC + where C1: C +{ + type Output = u8; + fn mul(self, _: WrapperB<::AT>) -> Self::Output { + loop {} + } +} +impl Mul> for WrapperC { + type Output = u8; + fn mul(self, _: WrapperC) -> 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 0000000000000..2461f7485f26f --- /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 or the MIT license +// , 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 `::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; + } +} From d9bc86032d0aa20db522e04a550deb1034005784 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 2 Nov 2016 11:50:16 -0400 Subject: [PATCH 2/3] normalize trait-ref in context of impl The `specializes()` function was trying to normalize the impl trait in an empty environment. This could lead to inexplicable failures. --- src/librustc/traits/specialize/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index e37425901c8c8..6a27558b0b77e 100644 --- a/src/librustc/traits/specialize/mod.rs +++ b/src/librustc/traits/specialize/mod.rs @@ -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,21 +179,22 @@ 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| { + // Create a infcx, taking the predicates of impl1 as assumptions: + let result = tcx.infer_ctxt(None, Some(penv), 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| { + infcx.parameter_environment.caller_bounds.extend(normalization_obligations.into_iter().map(|o| { match tcx.lift_to_global(&o.predicate) { Some(predicate) => predicate, None => { @@ -200,9 +203,6 @@ pub fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } })); - // 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() }); From b4f910d9004f09620ef5b1aff5d676c1dab7d42f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 2 Nov 2016 18:08:32 -0400 Subject: [PATCH 3/3] just use full-normalization when for the impl trait ref This seems better because I want to avoid the situation where unresolved inference variables make it into the environment. On the other hand, I am not 100% sure that this is correct. My assumption was that the WF check should ensure that this normalization can succeed. But it occurs to me that the WF checks may need to make use of the `specializes` predicate themselves, and hence we may have a kind of cycle here (this is a bigger problem with spec in any case that we need to resolve). On the other hand, this should just cause extra errors I think, so it seems like a safe thing to attempt. Certainly all tests pass. --- src/librustc/traits/specialize/mod.rs | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index 6a27558b0b77e..24cafa7f7253a 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; @@ -187,21 +187,16 @@ pub fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, .subst(tcx, &penv.free_substs); // Create a infcx, taking the predicates of impl1 as assumptions: - let result = tcx.infer_ctxt(None, Some(penv), 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) - }; - infcx.parameter_environment.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); + 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); } - } - })); + }; // Attempt to prove that impl2 applies, given all of the above. fulfill_implication(&infcx, impl1_trait_ref, impl2_def_id).is_ok()