From b7f9d07f4c4f0fdb5e33736814a2f4f7fd1db7bf Mon Sep 17 00:00:00 2001 From: Flavio Percoco Date: Fri, 27 Feb 2015 01:13:31 +0100 Subject: [PATCH] Normalize types before collecting obligations Fixes #22828 Fixes #22629 --- src/librustc/middle/traits/select.rs | 126 ++++++++++++++------------- src/librustc/middle/traits/util.rs | 2 +- src/test/run-pass/issue-22629.rs | 20 +++++ src/test/run-pass/issue-22828.rs | 29 ++++++ 4 files changed, 114 insertions(+), 63 deletions(-) create mode 100644 src/test/run-pass/issue-22629.rs create mode 100644 src/test/run-pass/issue-22828.rs diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 9ec89f55b8f..d8c62780a78 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -1716,6 +1716,60 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + fn collect_predicates_for_types(&mut self, + obligation: &TraitObligation<'tcx>, + trait_def_id: ast::DefId, + types: Vec>) -> Vec> { + + let derived_cause = match self.tcx().lang_items.to_builtin_kind(trait_def_id) { + Some(_) => { + self.derived_cause(obligation, BuiltinDerivedObligation) + }, + None => { + self.derived_cause(obligation, ImplDerivedObligation) + } + }; + + let normalized = project::normalize_with_depth(self, obligation.cause.clone(), + obligation.recursion_depth + 1, + &types); + + let obligations = normalized.value.iter().map(|&nested_ty| { + // the obligation might be higher-ranked, e.g. for<'a> &'a + // int : Copy. In that case, we will wind up with + // late-bound regions in the `nested` vector. So for each + // one we instantiate to a skolemized region, do our work + // to produce something like `&'0 int : Copy`, and then + // re-bind it. This is a bit of busy-work but preserves + // the invariant that we only manipulate free regions, not + // bound ones. + self.infcx.try(|snapshot| { + let (skol_ty, skol_map) = + self.infcx().skolemize_late_bound_regions(&ty::Binder(nested_ty), snapshot); + let skol_predicate = + util::predicate_for_trait_def( + self.tcx(), + derived_cause.clone(), + trait_def_id, + obligation.recursion_depth + 1, + skol_ty); + match skol_predicate { + Ok(skol_predicate) => Ok(self.infcx().plug_leaks(skol_map, snapshot, + &skol_predicate)), + Err(ErrorReported) => Err(ErrorReported) + } + }) + }).collect::>, _>>(); + + match obligations { + Ok(mut obls) => { + obls.push_all(normalized.obligations.as_slice()); + obls + }, + Err(ErrorReported) => Vec::new() + } + } + /////////////////////////////////////////////////////////////////////////// // CONFIRMATION // @@ -1854,38 +1908,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { nested: Vec>) -> VtableBuiltinData> { - let derived_cause = self.derived_cause(obligation, BuiltinDerivedObligation); - let obligations = nested.iter().map(|&bound_ty| { - // the obligation might be higher-ranked, e.g. for<'a> &'a - // int : Copy. In that case, we will wind up with - // late-bound regions in the `nested` vector. So for each - // one we instantiate to a skolemized region, do our work - // to produce something like `&'0 int : Copy`, and then - // re-bind it. This is a bit of busy-work but preserves - // the invariant that we only manipulate free regions, not - // bound ones. - self.infcx.try(|snapshot| { - let (skol_ty, skol_map) = - self.infcx().skolemize_late_bound_regions(&ty::Binder(bound_ty), snapshot); - let skol_predicate = - util::predicate_for_builtin_bound( - self.tcx(), - derived_cause.clone(), - bound, - obligation.recursion_depth + 1, - skol_ty); - match skol_predicate { - Ok(skol_predicate) => Ok(self.infcx().plug_leaks(skol_map, snapshot, - &skol_predicate)), - Err(ErrorReported) => Err(ErrorReported) - } - }) - }).collect::>(); - let obligations = match obligations { - Ok(o) => o, - Err(ErrorReported) => Vec::new(), + let trait_def = match self.tcx().lang_items.from_builtin_kind(bound) { + Ok(def_id) => def_id, + Err(_) => { + self.tcx().sess.bug("builtin trait definition not found"); + } }; + let obligations = self.collect_predicates_for_types(obligation, trait_def, nested); + let obligations = VecPerParamSpace::new(obligations, Vec::new(), Vec::new()); debug!("vtable_builtin_data: obligations={}", @@ -1928,39 +1959,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { nested: Vec>) -> VtableDefaultImplData> { - let derived_cause = self.derived_cause(obligation, ImplDerivedObligation); - let obligations = nested.iter().map(|&nested_ty| { - // the obligation might be higher-ranked, e.g. for<'a> &'a - // int : Copy. In that case, we will wind up with - // late-bound regions in the `nested` vector. So for each - // one we instantiate to a skolemized region, do our work - // to produce something like `&'0 int : Copy`, and then - // re-bind it. This is a bit of busy-work but preserves - // the invariant that we only manipulate free regions, not - // bound ones. - self.infcx.try(|snapshot| { - let (skol_ty, skol_map) = - self.infcx().skolemize_late_bound_regions(&ty::Binder(nested_ty), snapshot); - let skol_predicate = - util::predicate_for_default_trait_impl( - self.tcx(), - derived_cause.clone(), - trait_def_id, - obligation.recursion_depth + 1, - skol_ty); - match skol_predicate { - Ok(skol_predicate) => Ok(self.infcx().plug_leaks(skol_map, snapshot, - &skol_predicate)), - Err(ErrorReported) => Err(ErrorReported) - } - }) - }).collect::>(); - - let mut obligations = match obligations { - Ok(o) => o, - Err(ErrorReported) => Vec::new() - }; + let mut obligations = self.collect_predicates_for_types(obligation, + trait_def_id, + nested); let _: Result<(),()> = self.infcx.try(|snapshot| { let (_, skol_map) = diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs index 9b462e6be60..7bef5f32475 100644 --- a/src/librustc/middle/traits/util.rs +++ b/src/librustc/middle/traits/util.rs @@ -337,7 +337,7 @@ pub fn predicate_for_trait_ref<'tcx>( }) } -pub fn predicate_for_default_trait_impl<'tcx>( +pub fn predicate_for_trait_def<'tcx>( tcx: &ty::ctxt<'tcx>, cause: ObligationCause<'tcx>, trait_def_id: ast::DefId, diff --git a/src/test/run-pass/issue-22629.rs b/src/test/run-pass/issue-22629.rs new file mode 100644 index 00000000000..7bbd85d817f --- /dev/null +++ b/src/test/run-pass/issue-22629.rs @@ -0,0 +1,20 @@ +// Copyright 2015 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. + +// Test transitive analysis for associated types. Collected types +// should be normalized and new obligations generated. + +use std::borrow::{ToOwned, Cow}; + +fn assert_send(_: T) {} + +fn main() { + assert_send(Cow::Borrowed("foo")); +} diff --git a/src/test/run-pass/issue-22828.rs b/src/test/run-pass/issue-22828.rs new file mode 100644 index 00000000000..8ad960b3f1b --- /dev/null +++ b/src/test/run-pass/issue-22828.rs @@ -0,0 +1,29 @@ +// Copyright 2015 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. + +// Test transitive analysis for associated types. Collected types +// should be normalized and new obligations generated. + +trait Foo { + type A; + fn foo(&self) {} +} + +impl Foo for usize { + type A = usize; +} + +struct Bar { inner: T::A } + +fn is_send() {} + +fn main() { + is_send::>(); +}