From 4f05ec7d2c487c2e48f8753a4426ac6940d6be9a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 30 Dec 2014 09:55:11 -0500 Subject: [PATCH 01/11] Patch projection to not be so eager to unify type variables. This code is still probably wrong since it fails to incorporate the ambiguity resolution measures that `select` uses. Also, made more complicated by the fact that trait object types do not impl their own traits yet. --- src/librustc/middle/traits/project.rs | 13 ++--- ...iated-types-where-clause-impl-ambiguity.rs | 52 +++++++++++++++++++ 2 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 src/test/run-pass/associated-types-where-clause-impl-ambiguity.rs diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index 435babf168e..ab90f567ea2 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -125,21 +125,14 @@ pub fn project_type<'cx,'tcx>( ambiguous: false, }; - let () = assemble_candidates_from_param_env(selcx, - obligation, - &mut candidates); - let () = assemble_candidates_from_object_type(selcx, obligation, &mut candidates); if candidates.vec.is_empty() { - // FIXME(#20297) -- In `select.rs` there is similar logic that - // gives precedence to where-clauses, but it's a bit more - // fine-grained. I was lazy here and just always give - // precedence to where-clauses or other such sources over - // actually dredging through impls. This logic probably should - // be tightened up. + let () = assemble_candidates_from_param_env(selcx, + obligation, + &mut candidates); let () = try!(assemble_candidates_from_impls(selcx, obligation, diff --git a/src/test/run-pass/associated-types-where-clause-impl-ambiguity.rs b/src/test/run-pass/associated-types-where-clause-impl-ambiguity.rs new file mode 100644 index 00000000000..12018b194ba --- /dev/null +++ b/src/test/run-pass/associated-types-where-clause-impl-ambiguity.rs @@ -0,0 +1,52 @@ +// Copyright 2014 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 how resolving a projection interacts with inference. In this +// case, we were eagerly unifying the type variable for the iterator +// type with `I` from the where clause, ignoring the in-scope `impl` +// for `ByRef`. The right answer was to consider the result ambiguous +// until more type information was available. + +#![feature(associated_types, lang_items, unboxed_closures)] +#![no_implicit_prelude] + +use std::option::Option::{None, Some, mod}; + +trait Iterator { + type Item; + + fn next(&mut self) -> Option; +} + +trait IteratorExt: Iterator { + fn by_ref(&mut self) -> ByRef { + ByRef(self) + } +} + +impl IteratorExt for I where I: Iterator {} + +struct ByRef<'a, I: 'a + Iterator>(&'a mut I); + +impl<'a, A, I> Iterator for ByRef<'a, I> where I: Iterator { + type Item = A; + + fn next(&mut self) -> Option< ::Item > { + self.0.next() + } +} + +fn is_iterator_of>(_: &I) {} + +fn test>(mut it: I) { + is_iterator_of::(&it.by_ref()); +} + +fn main() { } From 23eec0c955c3573c0132f81a40ea7c210995a862 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 30 Dec 2014 09:57:03 -0500 Subject: [PATCH 02/11] Incorporate fix from japaric for cross-crate ICE --- src/librustc/metadata/encoder.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index db29d0111f4..b3b04a66452 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -1402,6 +1402,7 @@ fn encode_info_for_item(ecx: &EncodeContext, } ty::TypeTraitItem(associated_type) => { encode_name(rbml_w, associated_type.name); + encode_def_id(rbml_w, associated_type.def_id); let elem = ast_map::PathName(associated_type.name); encode_path(rbml_w, From 90252b8ddb439a538765ef85532f4caa029b5e8e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 30 Dec 2014 11:19:51 -0500 Subject: [PATCH 03/11] Add ignore pretty. --- .../run-pass/associated-types-where-clause-impl-ambiguity.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/run-pass/associated-types-where-clause-impl-ambiguity.rs b/src/test/run-pass/associated-types-where-clause-impl-ambiguity.rs index 12018b194ba..062d37556ec 100644 --- a/src/test/run-pass/associated-types-where-clause-impl-ambiguity.rs +++ b/src/test/run-pass/associated-types-where-clause-impl-ambiguity.rs @@ -14,6 +14,8 @@ // for `ByRef`. The right answer was to consider the result ambiguous // until more type information was available. +// ignore-pretty -- FIXME(#17362) pretty prints with `<<` which lexes wrong + #![feature(associated_types, lang_items, unboxed_closures)] #![no_implicit_prelude] From 0aa7ba9f5e9882c63a8f4bf4397ba8cd558b33ab Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 30 Dec 2014 14:49:13 -0500 Subject: [PATCH 04/11] Normalize bounds also in the UFCS cases (and get more systematic about it) --- src/librustc/middle/traits/project.rs | 2 + src/librustc/middle/ty.rs | 47 ++++++++++++++++++- src/librustc_typeck/check/method/mod.rs | 2 +- src/librustc_typeck/check/method/probe.rs | 1 + src/librustc_typeck/check/mod.rs | 43 +++++++++-------- src/librustc_typeck/check/wf.rs | 13 +++-- ...sociated-types-normalize-in-bounds-ufcs.rs | 41 ++++++++++++++++ 7 files changed, 123 insertions(+), 26 deletions(-) create mode 100644 src/test/run-pass/associated-types-normalize-in-bounds-ufcs.rs diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index ab90f567ea2..0fa171523f4 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -363,6 +363,8 @@ fn confirm_candidate<'cx,'tcx>( break; } + // TODO we need the impl_vtable items here + match impl_ty { Some(ty) => ty, None => { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index ab39c761a38..351485d74e8 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -6385,7 +6385,7 @@ pub fn construct_parameter_environment<'tcx>( } fn push_types_from_defs<'tcx>(tcx: &ty::ctxt<'tcx>, - types: &mut subst::VecPerParamSpace>, + types: &mut VecPerParamSpace>, defs: &[TypeParameterDef<'tcx>]) { for def in defs.iter() { debug!("construct_parameter_environment(): push_types_from_defs: def={}", @@ -6915,12 +6915,49 @@ impl<'tcx> RegionEscape for Ty<'tcx> { } } +impl<'tcx,T:RegionEscape> RegionEscape for VecPerParamSpace { + fn has_regions_escaping_depth(&self, depth: u32) -> bool { + self.iter_enumerated().any(|(space, _, t)| { + if space == subst::FnSpace { + t.has_regions_escaping_depth(depth+1) + } else { + t.has_regions_escaping_depth(depth) + } + }) + } +} + +impl<'tcx> RegionEscape for TypeScheme<'tcx> { + fn has_regions_escaping_depth(&self, depth: u32) -> bool { + self.ty.has_regions_escaping_depth(depth) || + self.generics.has_regions_escaping_depth(depth) + } +} + impl RegionEscape for Region { fn has_regions_escaping_depth(&self, depth: u32) -> bool { self.escapes_depth(depth) } } +impl<'tcx> RegionEscape for Generics<'tcx> { + fn has_regions_escaping_depth(&self, depth: u32) -> bool { + self.predicates.has_regions_escaping_depth(depth) + } +} + +impl<'tcx> RegionEscape for Predicate<'tcx> { + fn has_regions_escaping_depth(&self, depth: u32) -> bool { + match *self { + Predicate::Trait(ref data) => data.has_regions_escaping_depth(depth), + Predicate::Equate(ref data) => data.has_regions_escaping_depth(depth), + Predicate::RegionOutlives(ref data) => data.has_regions_escaping_depth(depth), + Predicate::TypeOutlives(ref data) => data.has_regions_escaping_depth(depth), + Predicate::Projection(ref data) => data.has_regions_escaping_depth(depth), + } + } +} + impl<'tcx> RegionEscape for TraitRef<'tcx> { fn has_regions_escaping_depth(&self, depth: u32) -> bool { self.substs.types.iter().any(|t| t.has_regions_escaping_depth(depth)) || @@ -6988,9 +7025,15 @@ pub trait HasProjectionTypes { fn has_projection_types(&self) -> bool; } +impl<'tcx,T:HasProjectionTypes> HasProjectionTypes for VecPerParamSpace { + fn has_projection_types(&self) -> bool { + self.iter().any(|p| p.has_projection_types()) + } +} + impl<'tcx> HasProjectionTypes for ty::GenericBounds<'tcx> { fn has_projection_types(&self) -> bool { - self.predicates.iter().any(|p| p.has_projection_types()) + self.predicates.has_projection_types() } } diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 1971be11760..4e6593deddd 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -216,7 +216,7 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>, // // Note that as the method comes from a trait, it should not have // any late-bound regions appearing in its bounds. - let method_bounds = method_ty.generics.to_bounds(fcx.tcx(), trait_ref.substs); + let method_bounds = fcx.instantiate_bounds(span, trait_ref.substs, &method_ty.generics); assert!(!method_bounds.has_escaping_regions()); fcx.add_obligations_for_parameters( traits::ObligationCause::misc(span, fcx.body_id), diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 1a9e124521e..610a5c0b246 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -768,6 +768,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { // Check whether the impl imposes obligations we have to worry about. let impl_generics = ty::lookup_item_type(self.tcx(), impl_def_id).generics; let impl_bounds = impl_generics.to_bounds(self.tcx(), substs); + // TODO assoc type normalization here? // Erase any late-bound regions bound in the impl // which appear in the bounds. diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 8069d00dda8..e2691a778ac 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -93,7 +93,7 @@ use middle::subst::{mod, Subst, Substs, VecPerParamSpace, ParamSpace}; use middle::traits; use middle::ty::{FnSig, VariantInfo, TypeScheme}; use middle::ty::{Disr, ParamTy, ParameterEnvironment}; -use middle::ty::{mod, HasProjectionTypes, Ty}; +use middle::ty::{mod, HasProjectionTypes, RegionEscape, Ty}; use middle::ty::liberate_late_bound_regions; use middle::ty::{MethodCall, MethodCallee, MethodMap, ObjectCastMap}; use middle::ty_fold::{TypeFolder, TypeFoldable}; @@ -1741,6 +1741,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { result } + /// As `instantiate_type_scheme`, but for the bounds found in a + /// generic type scheme. + fn instantiate_bounds(&self, + span: Span, + substs: &Substs<'tcx>, + generics: &ty::Generics<'tcx>) + -> ty::GenericBounds<'tcx> + { + ty::GenericBounds { + predicates: self.instantiate_type_scheme(span, substs, &generics.predicates) + } + } + + fn normalize_associated_types_in(&self, span: Span, value: &T) -> T where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes { @@ -1852,7 +1866,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, &type_scheme.generics); let bounds = - type_scheme.generics.to_bounds(self.tcx(), &substs); + self.instantiate_bounds(span, &substs, &type_scheme.generics); self.add_obligations_for_parameters( traits::ObligationCause::new( span, @@ -4455,7 +4469,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, if let Some(did) = did { let polytype = ty::lookup_item_type(tcx, did); let substs = Substs::new_type(vec![idx_type], vec![]); - let bounds = polytype.generics.to_bounds(tcx, &substs); + let bounds = fcx.instantiate_bounds(expr.span, &substs, &polytype.generics); fcx.add_obligations_for_parameters( traits::ObligationCause::new(expr.span, fcx.body_id, @@ -5270,31 +5284,20 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } // The things we are substituting into the type should not contain - // escaping late-bound regions. + // escaping late-bound regions, and nor should the base type scheme. assert!(!substs.has_regions_escaping_depth(0)); + assert!(!type_scheme.has_escaping_regions()); - // In the case of static items taken from impls, there may be - // late-bound regions associated with the impl (not declared on - // the fn itself). Those should be replaced with fresh variables - // now. These can appear either on the type being referenced, or - // on the associated bounds. - let bounds = type_scheme.generics.to_bounds(fcx.tcx(), &substs); - let (ty_late_bound, bounds) = - fcx.infcx().replace_late_bound_regions_with_fresh_var( - span, - infer::FnCall, - &ty::Binder((type_scheme.ty, bounds))).0; - - debug!("after late-bounds have been replaced: ty_late_bound={}", ty_late_bound.repr(fcx.tcx())); - debug!("after late-bounds have been replaced: bounds={}", bounds.repr(fcx.tcx())); - + // Add all the obligations that are required, substituting and + // normalized appropriately. + let bounds = fcx.instantiate_bounds(span, &substs, &type_scheme.generics); fcx.add_obligations_for_parameters( traits::ObligationCause::new(span, fcx.body_id, traits::ItemObligation(def.def_id())), &bounds); // Substitute the values for the type parameters into the type of // the referenced item. - let ty_substituted = fcx.instantiate_type_scheme(span, &substs, &ty_late_bound); + let ty_substituted = fcx.instantiate_type_scheme(span, &substs, &type_scheme.ty); fcx.write_ty(node_id, ty_substituted); fcx.write_substs(node_id, ty::ItemSubsts { substs: substs }); diff --git a/src/librustc_typeck/check/wf.rs b/src/librustc_typeck/check/wf.rs index 2a3f528809c..2d9b243e00e 100644 --- a/src/librustc_typeck/check/wf.rs +++ b/src/librustc_typeck/check/wf.rs @@ -269,7 +269,14 @@ impl<'cx,'tcx> BoundsChecker<'cx,'tcx> { pub fn check_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>) { let trait_def = ty::lookup_trait_def(self.fcx.tcx(), trait_ref.def_id); - let bounds = trait_def.generics.to_bounds(self.tcx(), trait_ref.substs); + // TODO uncommented this line causes failures because the impl + // obligations are not registered when we do a projection, and + // in this case it's those obligations that make the link + // between the normalized type ($1) and the result + // + // let bounds = self.fcx.instantiate_bounds(self.span, trait_ref.substs, &trait_def.generics); + + let bounds = trait_def.generics.to_bounds(self.fcx.tcx(), trait_ref.substs); self.fcx.add_obligations_for_parameters( traits::ObligationCause::new( self.span, @@ -319,13 +326,14 @@ impl<'cx,'tcx> TypeFolder<'tcx> for BoundsChecker<'cx,'tcx> { ty::ty_struct(type_id, substs) | ty::ty_enum(type_id, substs) => { let type_scheme = ty::lookup_item_type(self.fcx.tcx(), type_id); + let bounds = self.fcx.instantiate_bounds(self.span, substs, &type_scheme.generics); if self.binding_count == 0 { self.fcx.add_obligations_for_parameters( traits::ObligationCause::new(self.span, self.fcx.body_id, traits::ItemObligation(type_id)), - &type_scheme.generics.to_bounds(self.tcx(), substs)); + &bounds); } else { // There are two circumstances in which we ignore // region obligations. @@ -349,7 +357,6 @@ impl<'cx,'tcx> TypeFolder<'tcx> for BoundsChecker<'cx,'tcx> { // // (I believe we should do the same for traits, but // that will require an RFC. -nmatsakis) - let bounds = type_scheme.generics.to_bounds(self.tcx(), substs); let bounds = filter_to_trait_obligations(bounds); self.fcx.add_obligations_for_parameters( traits::ObligationCause::new(self.span, diff --git a/src/test/run-pass/associated-types-normalize-in-bounds-ufcs.rs b/src/test/run-pass/associated-types-normalize-in-bounds-ufcs.rs new file mode 100644 index 00000000000..0fd47720421 --- /dev/null +++ b/src/test/run-pass/associated-types-normalize-in-bounds-ufcs.rs @@ -0,0 +1,41 @@ +// Copyright 2014 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 that we normalize associated types that appear in bounds; if +// we didn't, the call to `self.split2()` fails to type check. + +#![feature(associated_types)] + +struct Splits<'a, T, P>; +struct SplitsN; + +trait SliceExt2 for Sized? { + type Item; + + fn split2<'a, P>(&'a self, pred: P) -> Splits<'a, Self::Item, P> + where P: FnMut(&Self::Item) -> bool; + fn splitn2<'a, P>(&'a self, n: uint, pred: P) -> SplitsN> + where P: FnMut(&Self::Item) -> bool; +} + +impl SliceExt2 for [T] { + type Item = T; + + fn split2

(&self, pred: P) -> Splits where P: FnMut(&T) -> bool { + loop {} + } + + fn splitn2

(&self, n: uint, pred: P) -> SplitsN> where P: FnMut(&T) -> bool { + SliceExt2::split2(self, pred); + loop {} + } +} + +fn main() { } From 6cb425d964637da3ffa99cac902bf8fe696baf08 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 30 Dec 2014 17:42:02 -0500 Subject: [PATCH 05/11] Rework normalization so that it works recursively, since the types extracted from an impl are potentially in need of normalization. This also lays groundwork for further cleanup in other areas by disconnecting normalization from the fulfillment context. --- src/librustc/middle/traits/error_reporting.rs | 136 +++---- src/librustc/middle/traits/fulfill.rs | 135 ++----- src/librustc/middle/traits/mod.rs | 14 +- src/librustc/middle/traits/project.rs | 367 ++++++++++++++---- src/librustc_trans/trans/common.rs | 2 +- src/librustc_trans/trans/monomorphize.rs | 64 +-- src/librustc_typeck/check/assoc.rs | 89 +---- src/librustc_typeck/check/mod.rs | 25 +- .../compile-fail/associated-types-path-2.rs | 33 +- ...ated-types-project-from-hrtb-in-fn-body.rs | 3 +- .../associated-types-unconstrained.rs | 28 ++ ...traits-multidispatch-convert-ambig-dest.rs | 2 +- .../associated-types-impl-redirect.rs | 58 +++ 13 files changed, 578 insertions(+), 378 deletions(-) create mode 100644 src/test/compile-fail/associated-types-unconstrained.rs create mode 100644 src/test/run-pass/associated-types-impl-redirect.rs diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs index a3d92c698a2..a0413701abc 100644 --- a/src/librustc/middle/traits/error_reporting.rs +++ b/src/librustc/middle/traits/error_reporting.rs @@ -77,13 +77,7 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, "overflow evaluating the requirement `{}`", predicate.user_string(infcx.tcx)).as_slice()); - let current_limit = infcx.tcx.sess.recursion_limit.get(); - let suggested_limit = current_limit * 2; - infcx.tcx.sess.span_note( - obligation.cause.span, - format!( - "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate", - suggested_limit)[]); + suggest_new_overflow_limit(infcx, obligation.cause.span); note_obligation_cause(infcx, obligation); } @@ -165,73 +159,76 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, // ambiguous impls. The latter *ought* to be a // coherence violation, so we don't report it here. - let trait_ref = match obligation.predicate { - ty::Predicate::Trait(ref trait_predicate) => { - infcx.resolve_type_vars_if_possible( - &trait_predicate.to_poly_trait_ref()) - } - _ => { - infcx.tcx.sess.span_bug( - obligation.cause.span, - format!("ambiguity from something other than a trait: {}", - obligation.predicate.repr(infcx.tcx)).as_slice()); - } - }; + let predicate = infcx.resolve_type_vars_if_possible(&obligation.predicate); - let self_ty = trait_ref.self_ty(); - - debug!("maybe_report_ambiguity(trait_ref={}, self_ty={}, obligation={})", - trait_ref.repr(infcx.tcx), - self_ty.repr(infcx.tcx), + debug!("maybe_report_ambiguity(predicate={}, obligation={})", + predicate.repr(infcx.tcx), obligation.repr(infcx.tcx)); - let all_types = &trait_ref.substs().types; - if all_types.iter().any(|&t| ty::type_is_error(t)) { - } else if all_types.iter().any(|&t| ty::type_needs_infer(t)) { - // This is kind of a hack: it frequently happens that some earlier - // error prevents types from being fully inferred, and then we get - // a bunch of uninteresting errors saying something like " doesn't implement Sized". It may even be true that we - // could just skip over all checks where the self-ty is an - // inference variable, but I was afraid that there might be an - // inference variable created, registered as an obligation, and - // then never forced by writeback, and hence by skipping here we'd - // be ignoring the fact that we don't KNOW the type works - // out. Though even that would probably be harmless, given that - // we're only talking about builtin traits, which are known to be - // inhabited. But in any case I just threw in this check for - // has_errors() to be sure that compilation isn't happening - // anyway. In that case, why inundate the user. - if !infcx.tcx.sess.has_errors() { - if infcx.tcx.lang_items.sized_trait() - .map_or(false, |sized_id| sized_id == trait_ref.def_id()) { - infcx.tcx.sess.span_err( + + match predicate { + ty::Predicate::Trait(ref data) => { + let trait_ref = data.to_poly_trait_ref(); + let self_ty = trait_ref.self_ty(); + let all_types = &trait_ref.substs().types; + if all_types.iter().any(|&t| ty::type_is_error(t)) { + } else if all_types.iter().any(|&t| ty::type_needs_infer(t)) { + // This is kind of a hack: it frequently happens that some earlier + // error prevents types from being fully inferred, and then we get + // a bunch of uninteresting errors saying something like " doesn't implement Sized". It may even be true that we + // could just skip over all checks where the self-ty is an + // inference variable, but I was afraid that there might be an + // inference variable created, registered as an obligation, and + // then never forced by writeback, and hence by skipping here we'd + // be ignoring the fact that we don't KNOW the type works + // out. Though even that would probably be harmless, given that + // we're only talking about builtin traits, which are known to be + // inhabited. But in any case I just threw in this check for + // has_errors() to be sure that compilation isn't happening + // anyway. In that case, why inundate the user. + if !infcx.tcx.sess.has_errors() { + if + infcx.tcx.lang_items.sized_trait() + .map_or(false, |sized_id| sized_id == trait_ref.def_id()) + { + infcx.tcx.sess.span_err( + obligation.cause.span, + format!( + "unable to infer enough type information about `{}`; \ + type annotations required", + self_ty.user_string(infcx.tcx)).as_slice()); + } else { + infcx.tcx.sess.span_err( + obligation.cause.span, + format!( + "type annotations required: cannot resolve `{}`", + predicate.user_string(infcx.tcx)).as_slice()); + note_obligation_cause(infcx, obligation); + } + } + } else if !infcx.tcx.sess.has_errors() { + // Ambiguity. Coherence should have reported an error. + infcx.tcx.sess.span_bug( obligation.cause.span, format!( - "unable to infer enough type information about `{}`; type annotations \ - required", - self_ty.user_string(infcx.tcx)).as_slice()); - } else { - infcx.tcx.sess.span_err( - obligation.cause.span, - format!( - "unable to infer enough type information to \ - locate the impl of the trait `{}` for \ - the type `{}`; type annotations required", + "coherence failed to report ambiguity: \ + cannot locate the impl of the trait `{}` for \ + the type `{}`", trait_ref.user_string(infcx.tcx), self_ty.user_string(infcx.tcx)).as_slice()); + } + } + + _ => { + if !infcx.tcx.sess.has_errors() { + infcx.tcx.sess.span_err( + obligation.cause.span, + format!( + "type annotations required: cannot resolve `{}`", + predicate.user_string(infcx.tcx)).as_slice()); note_obligation_cause(infcx, obligation); } } - } else if !infcx.tcx.sess.has_errors() { - // Ambiguity. Coherence should have reported an error. - infcx.tcx.sess.span_bug( - obligation.cause.span, - format!( - "coherence failed to report ambiguity: \ - cannot locate the impl of the trait `{}` for \ - the type `{}`", - trait_ref.user_string(infcx.tcx), - self_ty.user_string(infcx.tcx)).as_slice()); } } @@ -335,3 +332,12 @@ fn note_obligation_cause_code<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, } } +pub fn suggest_new_overflow_limit(infcx: &InferCtxt, span: Span) { + let current_limit = infcx.tcx.sess.recursion_limit.get(); + let suggested_limit = current_limit * 2; + infcx.tcx.sess.span_note( + span, + format!( + "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate", + suggested_limit)[]); +} diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs index 7ec221fcfa9..c85baccd633 100644 --- a/src/librustc/middle/traits/fulfill.rs +++ b/src/librustc/middle/traits/fulfill.rs @@ -8,9 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use middle::infer::{mod, InferCtxt}; +use middle::infer::{InferCtxt}; use middle::mem_categorization::Typer; -use middle::ty::{mod, AsPredicate, RegionEscape, Ty, ToPolyTraitRef}; +use middle::ty::{mod, RegionEscape, Ty}; use std::collections::HashSet; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::default::Default; @@ -23,7 +23,6 @@ use super::CodeAmbiguity; use super::CodeProjectionError; use super::CodeSelectionError; use super::FulfillmentError; -use super::Obligation; use super::ObligationCause; use super::PredicateObligation; use super::project; @@ -110,6 +109,8 @@ impl<'tcx> FulfillmentContext<'tcx> { /// `projection_ty` again. pub fn normalize_projection_type<'a>(&mut self, infcx: &InferCtxt<'a,'tcx>, + param_env: &ty::ParameterEnvironment<'tcx>, + typer: &Typer<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, cause: ObligationCause<'tcx>) -> Ty<'tcx> @@ -121,18 +122,16 @@ impl<'tcx> FulfillmentContext<'tcx> { // FIXME(#20304) -- cache - let ty_var = infcx.next_ty_var(); - let projection = - ty::Binder(ty::ProjectionPredicate { - projection_ty: projection_ty, - ty: ty_var - }); - let obligation = Obligation::new(cause, projection.as_predicate()); - self.register_predicate(infcx, obligation); + let mut selcx = SelectionContext::new(infcx, param_env, typer); + let normalized = project::normalize_projection_type(&mut selcx, projection_ty, cause, 0); - debug!("normalize_associated_type: result={}", ty_var.repr(infcx.tcx)); + for obligation in normalized.obligations.into_iter() { + self.register_predicate_obligation(infcx, obligation); + } - ty_var + debug!("normalize_associated_type: result={}", normalized.value.repr(infcx.tcx)); + + normalized.value } pub fn register_builtin_bound<'a>(&mut self, @@ -143,7 +142,7 @@ impl<'tcx> FulfillmentContext<'tcx> { { match predicate_for_builtin_bound(infcx.tcx, cause, builtin_bound, 0, ty) { Ok(predicate) => { - self.register_predicate(infcx, predicate); + self.register_predicate_obligation(infcx, predicate); } Err(ErrorReported) => { } } @@ -158,10 +157,14 @@ impl<'tcx> FulfillmentContext<'tcx> { register_region_obligation(infcx.tcx, t_a, r_b, cause, &mut self.region_obligations); } - pub fn register_predicate<'a>(&mut self, - infcx: &InferCtxt<'a,'tcx>, - obligation: PredicateObligation<'tcx>) + pub fn register_predicate_obligation<'a>(&mut self, + infcx: &InferCtxt<'a,'tcx>, + obligation: PredicateObligation<'tcx>) { + // this helps to reduce duplicate errors, as well as making + // debug output much nicer to read and so on. + let obligation = infcx.resolve_type_vars_if_possible(&obligation); + if !self.duplicate_set.insert(obligation.predicate.clone()) { debug!("register_predicate({}) -- already seen, skip", obligation.repr(infcx.tcx)); return; @@ -290,7 +293,7 @@ impl<'tcx> FulfillmentContext<'tcx> { // Now go through all the successful ones, // registering any nested obligations for the future. for new_obligation in new_obligations.into_iter() { - self.register_predicate(selcx.infcx(), new_obligation); + self.register_predicate_obligation(selcx.infcx(), new_obligation); } } @@ -398,104 +401,18 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>, project_obligation.repr(tcx), result.repr(tcx)); match result { - Ok(()) => { + Ok(Some(obligations)) => { + new_obligations.extend(obligations.into_iter()); true } - Err(project::ProjectionError::TooManyCandidates) => { - // Without more type information, we can't say much. + Ok(None) => { false } - Err(project::ProjectionError::NoCandidate) => { - // This means that we have a type like `::name = U` but we couldn't find any more - // information. This could just be that we're in a - // function like: - // - // fn foo(...) - // - // in which case this is not an error. But it - // might also mean we're in a situation where we - // don't actually know that `T : Trait` holds, - // which would be weird (e.g., if `T` was not a - // parameter type but a normal type, like `int`). - // - // So what we do is to (1) add a requirement that - // `T : Trait` (just in case) and (2) try to unify - // `U` with `::name`. - - if !ty::binds_late_bound_regions(selcx.tcx(), data) { - // Check that `T : Trait` holds. - let trait_ref = data.to_poly_trait_ref(); - new_obligations.push(obligation.with(trait_ref.as_predicate())); - - // Fallback to `::name`. If this - // fails, then the output must be at least - // somewhat constrained, and we cannot verify - // that constraint, so yield an error. - let ty_projection = ty::mk_projection(tcx, - trait_ref.0.clone(), - data.0.projection_ty.item_name); - - debug!("process_predicate: falling back to projection {}", - ty_projection.repr(selcx.tcx())); - - match infer::mk_eqty(selcx.infcx(), - true, - infer::EquatePredicate(obligation.cause.span), - ty_projection, - data.0.ty) { - Ok(()) => { } - Err(_) => { - debug!("process_predicate: fallback failed to unify; error"); - errors.push( - FulfillmentError::new( - obligation.clone(), - CodeSelectionError(Unimplemented))); - } - } - - true - } else { - // If we have something like - // - // for<'a> as Trait>::name == &'a int - // - // there is no "canonical form" for us to - // make, so just report the lack of candidates - // as an error. - - debug!("process_predicate: can't fallback, higher-ranked"); - errors.push( - FulfillmentError::new( - obligation.clone(), - CodeSelectionError(Unimplemented))); - - true - } - } - Err(project::ProjectionError::MismatchedTypes(e)) => { + Err(err) => { errors.push( FulfillmentError::new( obligation.clone(), - CodeProjectionError(e))); - true - } - Err(project::ProjectionError::TraitSelectionError(_)) => { - // There was an error matching `T : Trait` (which - // is a pre-requisite for `::Name` - // being valid). We could just report the error - // now, but that tends to lead to double error - // reports for the user (one for the obligation `T - // : Trait`, typically incurred somewhere else, - // and one from here). Instead, we'll create the - // `T : Trait` obligation and add THAT as a - // requirement. This will (eventually) trigger the - // same error, but it will also wind up flagged as - // a duplicate if another requirement that `T : - // Trait` arises from somewhere else. - let trait_predicate = data.to_poly_trait_ref(); - let trait_obligation = obligation.with(trait_predicate.as_predicate()); - new_obligations.push(trait_obligation); + CodeProjectionError(err))); true } } diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index d4fa0c98ad5..b10dfa5b718 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -27,8 +27,8 @@ use util::ppaux::Repr; pub use self::error_reporting::report_fulfillment_errors; pub use self::fulfill::{FulfillmentContext, RegionObligation}; pub use self::project::MismatchedProjectionTypes; -pub use self::project::project_type; -pub use self::project::ProjectionResult; +pub use self::project::normalize; +pub use self::project::Normalized; pub use self::select::SelectionContext; pub use self::select::SelectionCache; pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch}; @@ -320,6 +320,16 @@ impl<'tcx,O> Obligation<'tcx,O> { predicate: trait_ref } } + fn with_depth(cause: ObligationCause<'tcx>, + recursion_depth: uint, + trait_ref: O) + -> Obligation<'tcx, O> + { + Obligation { cause: cause, + recursion_depth: recursion_depth, + predicate: trait_ref } + } + pub fn misc(span: Span, body_id: ast::NodeId, trait_ref: O) -> Obligation<'tcx, O> { Obligation::new(ObligationCause::misc(span, body_id), trait_ref) } diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index 0fa171523f4..28a826b859b 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -12,6 +12,8 @@ use super::elaborate_predicates; use super::Obligation; +use super::ObligationCause; +use super::Overflow; use super::PredicateObligation; use super::SelectionContext; use super::SelectionError; @@ -19,7 +21,8 @@ use super::VtableImplData; use middle::infer; use middle::subst::Subst; -use middle::ty::{mod, AsPredicate, ToPolyTraitRef, Ty}; +use middle::ty::{mod, AsPredicate, RegionEscape, HasProjectionTypes, ToPolyTraitRef, Ty}; +use middle::ty_fold::{mod, TypeFoldable, TypeFolder}; use util::ppaux::Repr; pub type PolyProjectionObligation<'tcx> = @@ -31,21 +34,11 @@ pub type ProjectionObligation<'tcx> = pub type ProjectionTyObligation<'tcx> = Obligation<'tcx, ty::ProjectionTy<'tcx>>; -/// When attempting to resolve `::Name == U`... -pub enum ProjectionError<'tcx> { - /// ...we could not find any helpful information on what `Name` - /// might be. This could occur, for example, if there is a where - /// clause `T : TraitRef` but not `T : TraitRef`. When - /// normalizing, this case is where we opt to normalize back to - /// the projection type `::Name`. - NoCandidate, - +/// When attempting to resolve `::Name` ... +pub enum ProjectionTyError<'tcx> { /// ...we found multiple sources of information and couldn't resolve the ambiguity. TooManyCandidates, - /// ...`` ws resolved to some type `V` that failed to unify with `U` - MismatchedTypes(MismatchedProjectionTypes<'tcx>), - /// ...an error occurred matching `T : TraitRef` TraitSelectionError(SelectionError<'tcx>), } @@ -55,8 +48,6 @@ pub struct MismatchedProjectionTypes<'tcx> { pub err: ty::type_err<'tcx> } -pub type ProjectionResult<'tcx, T> = Result>; - enum ProjectionTyCandidate<'tcx> { ParamEnv(ty::PolyProjectionPredicate<'tcx>), Impl(VtableImplData<'tcx, PredicateObligation<'tcx>>), @@ -70,24 +61,43 @@ struct ProjectionTyCandidateSet<'tcx> { pub fn poly_project_and_unify_type<'cx,'tcx>( selcx: &mut SelectionContext<'cx,'tcx>, obligation: &PolyProjectionObligation<'tcx>) - -> ProjectionResult<'tcx, ()> + -> Result>>, MismatchedProjectionTypes<'tcx>> { debug!("poly_project(obligation={})", obligation.repr(selcx.tcx())); let infcx = selcx.infcx(); - - infcx.try(|snapshot| { + let result = infcx.try(|snapshot| { let (skol_predicate, skol_map) = infcx.skolemize_late_bound_regions(&obligation.predicate, snapshot); let skol_obligation = obligation.with(skol_predicate); - let () = try!(project_and_unify_type(selcx, &skol_obligation)); - match infcx.leak_check(&skol_map, snapshot) { - Ok(()) => Ok(()), - Err(e) => Err(ProjectionError::MismatchedTypes(MismatchedProjectionTypes{err: e})), + match project_and_unify_type(selcx, &skol_obligation) { + Ok(Some(obligations)) => { + match infcx.leak_check(&skol_map, snapshot) { + Ok(()) => Ok(infcx.plug_leaks(skol_map, snapshot, &obligations)), + Err(e) => Err(Some(MismatchedProjectionTypes { err: e })), + } + } + Ok(None) => { + // Signal ambiguity using Err just so that infcx.try() + // rolls back the snapshot. We adapt below. + Err(None) + } + Err(e) => { + Err(Some(e)) + } } - }) + }); + + // Above, we use Err(None) to signal ambiguity so that the + // snapshot will be rolled back. But here, we want to translate to + // Ok(None). Kind of weird. + match result { + Ok(obligations) => Ok(Some(obligations)), + Err(None) => Ok(None), + Err(Some(e)) => Err(e), + } } /// Compute result of projecting an associated type and unify it with @@ -95,48 +105,281 @@ pub fn poly_project_and_unify_type<'cx,'tcx>( pub fn project_and_unify_type<'cx,'tcx>( selcx: &mut SelectionContext<'cx,'tcx>, obligation: &ProjectionObligation<'tcx>) - -> ProjectionResult<'tcx, ()> + -> Result>>, MismatchedProjectionTypes<'tcx>> { debug!("project_and_unify(obligation={})", obligation.repr(selcx.tcx())); - let ty_obligation = obligation.with(obligation.predicate.projection_ty.clone()); - let projected_ty = try!(project_type(selcx, &ty_obligation)); + let Normalized { value: normalized_ty, obligations } = + match opt_normalize_projection_type(selcx, + obligation.predicate.projection_ty.clone(), + obligation.cause.clone(), + obligation.recursion_depth) { + Some(n) => n, + None => { return Ok(None); } + }; + + debug!("project_and_unify_type: normalized_ty={} obligations={}", + normalized_ty.repr(selcx.tcx()), + obligations.repr(selcx.tcx())); + let infcx = selcx.infcx(); let origin = infer::RelateOutputImplTypes(obligation.cause.span); - debug!("project_and_unify_type: projected_ty = {}", projected_ty.repr(selcx.tcx())); - match infer::mk_eqty(infcx, true, origin, projected_ty, obligation.predicate.ty) { - Ok(()) => Ok(()), - Err(e) => Err(ProjectionError::MismatchedTypes(MismatchedProjectionTypes{err: e})), + match infer::mk_eqty(infcx, true, origin, normalized_ty, obligation.predicate.ty) { + Ok(()) => Ok(Some(obligations)), + Err(err) => Err(MismatchedProjectionTypes { err: err }), } } +pub fn normalize<'a,'b,'tcx,T>(selcx: &'a mut SelectionContext<'b,'tcx>, + cause: ObligationCause<'tcx>, + value: &T) + -> Normalized<'tcx, T> + where T : TypeFoldable<'tcx> + HasProjectionTypes + Clone +{ + let mut normalizer = AssociatedTypeNormalizer::new(selcx, cause, 0); + let result = normalizer.fold(value); + Normalized { + value: result, + obligations: normalizer.obligations, + } +} + +struct AssociatedTypeNormalizer<'a,'b:'a,'tcx:'b> { + selcx: &'a mut SelectionContext<'b,'tcx>, + cause: ObligationCause<'tcx>, + obligations: Vec>, + depth: uint, +} + +impl<'a,'b,'tcx> AssociatedTypeNormalizer<'a,'b,'tcx> { + fn new(selcx: &'a mut SelectionContext<'b,'tcx>, + cause: ObligationCause<'tcx>, + depth: uint) + -> AssociatedTypeNormalizer<'a,'b,'tcx> + { + AssociatedTypeNormalizer { + selcx: selcx, + cause: cause, + obligations: vec!(), + depth: depth, + } + } + + fn fold + HasProjectionTypes + Clone>(&mut self, value: &T) -> T { + let value = self.selcx.infcx().resolve_type_vars_if_possible(value); + + if !value.has_projection_types() { + value.clone() + } else { + value.fold_with(self) + } + } +} + +impl<'a,'b,'tcx> TypeFolder<'tcx> for AssociatedTypeNormalizer<'a,'b,'tcx> { + fn tcx(&self) -> &ty::ctxt<'tcx> { + self.selcx.tcx() + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + // We don't want to normalize associated types that occur inside of region + // binders, because they may contain bound regions, and we can't cope with that. + // + // Example: + // + // for<'a> fn(>::A) + // + // Instead of normalizing `>::A` here, we'll + // normalize it when we instantiate those bound regions (which + // should occur eventually). + + match ty.sty { + ty::ty_projection(ref data) if !data.has_escaping_regions() => { // (*) + + // (*) This is kind of hacky -- we need to be able to + // handle normalization within binders because + // otherwise we wind up a need to normalize when doing + // trait matching (since you can have a trait + // obligation like `for<'a> T::B : Fn(&'a int)`), but + // we can't normalize with bound regions in scope. So + // far now we just ignore binders but only normalize + // if all bound regions are gone (and then we still + // have to renormalize whenever we instantiate a + // binder). It would be better to normalize in a + // binding-aware fashion. + + let Normalized { value: ty, obligations } = + normalize_projection_type(self.selcx, + data.clone(), + self.cause.clone(), + self.depth); + self.obligations.extend(obligations.into_iter()); + ty + } + _ => { + ty_fold::super_fold_ty(self, ty) + } + } + } +} + +pub struct Normalized<'tcx,T> { + pub value: T, + pub obligations: Vec>, +} + +pub type NormalizedTy<'tcx> = Normalized<'tcx, Ty<'tcx>>; + +pub fn normalize_projection_type<'a,'b,'tcx>( + selcx: &'a mut SelectionContext<'b,'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: uint) + -> NormalizedTy<'tcx> +{ + opt_normalize_projection_type(selcx, projection_ty.clone(), cause.clone(), depth) + .unwrap_or_else(move || { + // if we bottom out in ambiguity, create a type variable + // and a deferred predicate to resolve this when more type + // information is available. + + let ty_var = selcx.infcx().next_ty_var(); + let projection = ty::Binder(ty::ProjectionPredicate { + projection_ty: projection_ty, + ty: ty_var + }); + let obligation = Obligation::with_depth(cause, depth+1, projection.as_predicate()); + Normalized { + value: ty_var, + obligations: vec!(obligation) + } + }) +} + +fn opt_normalize_projection_type<'a,'b,'tcx>( + selcx: &'a mut SelectionContext<'b,'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: uint) + -> Option> +{ + debug!("normalize_projection_type(\ + projection_ty={}, \ + depth={})", + projection_ty.repr(selcx.tcx()), + depth); + + let obligation = Obligation::with_depth(cause.clone(), depth, projection_ty.clone()); + match project_type(selcx, &obligation) { + Ok(ProjectedTy::Progress(projected_ty, mut obligations)) => { + // if projection succeeded, then what we get out of this + // is also non-normalized (consider: it was derived from + // an impl, where-clause etc) and hence we must + // re-normalize it + + debug!("normalize_projection_type: projected_ty={} depth={}", + projected_ty.repr(selcx.tcx()), + depth); + + if ty::type_has_projection(projected_ty) { + let tcx = selcx.tcx(); + let mut normalizer = AssociatedTypeNormalizer::new(selcx, cause, depth); + let normalized_ty = normalizer.fold(&projected_ty); + + debug!("normalize_projection_type: normalized_ty={} depth={}", + normalized_ty.repr(tcx), + depth); + + obligations.extend(normalizer.obligations.into_iter()); + Some(Normalized { + value: normalized_ty, + obligations: obligations, + }) + } else { + Some(Normalized { + value: projected_ty, + obligations: obligations, + }) + } + } + Ok(ProjectedTy::NoProgress(projected_ty)) => { + Some(Normalized { + value: projected_ty, + obligations: vec!() + }) + } + Err(ProjectionTyError::TooManyCandidates) => { + None + } + Err(ProjectionTyError::TraitSelectionError(_)) => { + // if we got an error processing the `T as Trait` part, + // just return `ty::err` but add the obligation `T : + // Trait`, which when processed will cause the error to be + // reported later + + Some(normalize_to_error(selcx, projection_ty, cause, depth)) + } + } +} + +/// in various error cases, we just set ty_err and return an obligation +/// that, when fulfiled, will lead to an error +fn normalize_to_error<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: uint) + -> NormalizedTy<'tcx> +{ + let trait_ref = projection_ty.trait_ref.to_poly_trait_ref(); + let trait_obligation = Obligation { cause: cause, + recursion_depth: depth, + predicate: trait_ref.as_predicate() }; + Normalized { + value: selcx.tcx().types.err, + obligations: vec!(trait_obligation) + } +} + +enum ProjectedTy<'tcx> { + Progress(Ty<'tcx>, Vec>), + NoProgress(Ty<'tcx>), +} + /// Compute the result of a projection type (if we can). -pub fn project_type<'cx,'tcx>( +fn project_type<'cx,'tcx>( selcx: &mut SelectionContext<'cx,'tcx>, obligation: &ProjectionTyObligation<'tcx>) - -> ProjectionResult<'tcx, Ty<'tcx>> + -> Result, ProjectionTyError<'tcx>> { debug!("project(obligation={})", obligation.repr(selcx.tcx())); + let recursion_limit = selcx.tcx().sess.recursion_limit.get(); + if obligation.recursion_depth >= recursion_limit { + debug!("project: overflow!"); + return Err(ProjectionTyError::TraitSelectionError(Overflow)); + } + let mut candidates = ProjectionTyCandidateSet { vec: Vec::new(), ambiguous: false, }; - let () = assemble_candidates_from_object_type(selcx, - obligation, - &mut candidates); + assemble_candidates_from_object_type(selcx, + obligation, + &mut candidates); if candidates.vec.is_empty() { - let () = assemble_candidates_from_param_env(selcx, - obligation, - &mut candidates); + assemble_candidates_from_param_env(selcx, + obligation, + &mut candidates); - let () = try!(assemble_candidates_from_impls(selcx, - obligation, - &mut candidates)); + if let Err(e) = assemble_candidates_from_impls(selcx, + obligation, + &mut candidates) { + return Err(ProjectionTyError::TraitSelectionError(e)); + } } debug!("{} candidates, ambiguous={}", @@ -146,15 +389,18 @@ pub fn project_type<'cx,'tcx>( // We probably need some winnowing logic similar to select here. if candidates.ambiguous || candidates.vec.len() > 1 { - return Err(ProjectionError::TooManyCandidates); + return Err(ProjectionTyError::TooManyCandidates); } match candidates.vec.pop() { Some(candidate) => { - Ok(try!(confirm_candidate(selcx, obligation, candidate))) + let (ty, obligations) = confirm_candidate(selcx, obligation, candidate); + Ok(ProjectedTy::Progress(ty, obligations)) } None => { - Err(ProjectionError::NoCandidate) + Ok(ProjectedTy::NoProgress(ty::mk_projection(selcx.tcx(), + obligation.predicate.trait_ref.clone(), + obligation.predicate.item_name))) } } } @@ -230,9 +476,9 @@ fn assemble_candidates_from_object_type<'cx,'tcx>( fn assemble_candidates_from_impls<'cx,'tcx>( selcx: &mut SelectionContext<'cx,'tcx>, - obligation: &ProjectionTyObligation<'tcx>, + obligation: &ProjectionTyObligation<'tcx>, candidate_set: &mut ProjectionTyCandidateSet<'tcx>) - -> ProjectionResult<'tcx, ()> + -> Result<(), SelectionError<'tcx>> { // If we are resolving `>::Item == Type`, // start out by selecting the predicate `T as TraitRef<...>`: @@ -249,7 +495,7 @@ fn assemble_candidates_from_impls<'cx,'tcx>( Err(e) => { debug!("assemble_candidates_from_impls: selection error {}", e.repr(selcx.tcx())); - return Err(ProjectionError::TraitSelectionError(e)); + return Err(e); } }; @@ -301,9 +547,9 @@ fn assemble_candidates_from_impls<'cx,'tcx>( fn confirm_candidate<'cx,'tcx>( selcx: &mut SelectionContext<'cx,'tcx>, - obligation: &ProjectionTyObligation<'tcx>, + obligation: &ProjectionTyObligation<'tcx>, candidate: ProjectionTyCandidate<'tcx>) - -> ProjectionResult<'tcx, Ty<'tcx>> + -> (Ty<'tcx>, Vec>) { let infcx = selcx.infcx(); @@ -311,7 +557,7 @@ fn confirm_candidate<'cx,'tcx>( candidate.repr(infcx.tcx), obligation.repr(infcx.tcx)); - let projected_ty = match candidate { + match candidate { ProjectionTyCandidate::ParamEnv(poly_projection) => { let projection = infcx.replace_late_bound_regions_with_fresh_var( @@ -338,7 +584,7 @@ fn confirm_candidate<'cx,'tcx>( } } - projection.ty + (projection.ty, vec!()) } ProjectionTyCandidate::Impl(impl_vtable) => { @@ -363,10 +609,8 @@ fn confirm_candidate<'cx,'tcx>( break; } - // TODO we need the impl_vtable items here - match impl_ty { - Some(ty) => ty, + Some(ty) => (ty, impl_vtable.nested.to_vec()), None => { selcx.tcx().sess.span_bug( obligation.cause.span, @@ -376,21 +620,15 @@ fn confirm_candidate<'cx,'tcx>( } } } - }; - - Ok(projected_ty) + } } -impl<'tcx> Repr<'tcx> for ProjectionError<'tcx> { +impl<'tcx> Repr<'tcx> for ProjectionTyError<'tcx> { fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String { match *self { - ProjectionError::NoCandidate => + ProjectionTyError::TooManyCandidates => format!("NoCandidate"), - ProjectionError::TooManyCandidates => - format!("NoCandidate"), - ProjectionError::MismatchedTypes(ref m) => - format!("MismatchedTypes({})", m.repr(tcx)), - ProjectionError::TraitSelectionError(ref e) => + ProjectionTyError::TraitSelectionError(ref e) => format!("TraitSelectionError({})", e.repr(tcx)), } } @@ -406,4 +644,3 @@ impl<'tcx> Repr<'tcx> for ProjectionTyCandidate<'tcx> { } } } - diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 06819aac5bc..1b00a77fe51 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -960,7 +960,7 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // iterating early. let mut fulfill_cx = traits::FulfillmentContext::new(); let vtable = selection.map_move_nested(|predicate| { - fulfill_cx.register_predicate(&infcx, predicate); + fulfill_cx.register_predicate_obligation(&infcx, predicate); }); match fulfill_cx.select_all_or_error(&infcx, ¶m_env, tcx) { Ok(()) => { } diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs index 3b7043e4f40..c693c6ea428 100644 --- a/src/librustc_trans/trans/monomorphize.rs +++ b/src/librustc_trans/trans/monomorphize.rs @@ -16,7 +16,7 @@ use middle::infer; use middle::subst; use middle::subst::{Subst, Substs}; use middle::traits; -use middle::ty_fold::{mod, TypeFolder, TypeFoldable}; +use middle::ty_fold::{TypeFolder, TypeFoldable}; use trans::base::{set_llvm_fn_attrs, set_inline_hint}; use trans::base::{trans_enum_variant, push_ctxt, get_item_val}; use trans::base::{trans_fn, decl_internal_rust_fn}; @@ -32,7 +32,6 @@ use syntax::ast_map; use syntax::ast_util::{local_def, PostExpansionMethod}; use syntax::attr; use std::hash::{sip, Hash}; -use std::rc::Rc; pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_id: ast::DefId, @@ -310,13 +309,13 @@ pub fn apply_param_substs<'tcx,T>(tcx: &ty::ctxt<'tcx>, /// monomorphization, we know that only concrete types are involved, /// and hence we can be sure that all associated types will be /// completely normalized away. -pub fn normalize_associated_type<'tcx,T>(tcx: &ty::ctxt<'tcx>, t: &T) -> T +pub fn normalize_associated_type<'tcx,T>(tcx: &ty::ctxt<'tcx>, value: &T) -> T where T : TypeFoldable<'tcx> + Repr<'tcx> + HasProjectionTypes + Clone { - debug!("normalize_associated_type(t={})", t.repr(tcx)); + debug!("normalize_associated_type(t={})", value.repr(tcx)); - if !t.has_projection_types() { - return t.clone(); + if !value.has_projection_types() { + return value.clone(); } // FIXME(#20304) -- cache @@ -324,52 +323,15 @@ pub fn normalize_associated_type<'tcx,T>(tcx: &ty::ctxt<'tcx>, t: &T) -> T let infcx = infer::new_infer_ctxt(tcx); let param_env = ty::empty_parameter_environment(); let mut selcx = traits::SelectionContext::new(&infcx, ¶m_env, tcx); - let mut normalizer = AssociatedTypeNormalizer { selcx: &mut selcx }; - let result = t.fold_with(&mut normalizer); + let cause = traits::ObligationCause::dummy(); + let traits::Normalized { value: result, obligations } = + traits::normalize(&mut selcx, cause, value); - debug!("normalize_associated_type: t={} result={}", - t.repr(tcx), - result.repr(tcx)); + debug!("normalize_associated_type: result={} obligations={}", + result.repr(tcx), + obligations.repr(tcx)); + + assert_eq!(obligations.len(), 0); // TODO not good enough result } - -struct AssociatedTypeNormalizer<'a,'tcx:'a> { - selcx: &'a mut traits::SelectionContext<'a,'tcx>, -} - -impl<'a,'tcx> TypeFolder<'tcx> for AssociatedTypeNormalizer<'a,'tcx> { - fn tcx(&self) -> &ty::ctxt<'tcx> { self.selcx.tcx() } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.sty { - ty::ty_projection(ref data) => { - debug!("ty_projection({})", data.repr(self.tcx())); - - let tcx = self.selcx.tcx(); - let substs = data.trait_ref.substs.clone().erase_regions(); - let substs = self.tcx().mk_substs(substs); - assert!(substs.types.iter().all(|&t| (!ty::type_has_params(t) && - !ty::type_has_self(t)))); - let trait_ref = Rc::new(ty::TraitRef::new(data.trait_ref.def_id, substs)); - let projection_ty = ty::ProjectionTy { trait_ref: trait_ref.clone(), - item_name: data.item_name }; - let obligation = traits::Obligation::new(traits::ObligationCause::dummy(), - projection_ty); - match traits::project_type(self.selcx, &obligation) { - Ok(ty) => ty, - Err(errors) => { - tcx.sess.bug( - format!("Encountered error(s) `{}` selecting `{}` during trans", - errors.repr(tcx), - trait_ref.repr(tcx)).as_slice()); - } - } - } - - _ => { - ty_fold::super_fold_ty(self, ty) - } - } - } -} diff --git a/src/librustc_typeck/check/assoc.rs b/src/librustc_typeck/check/assoc.rs index 081959a4efa..98081e28f2f 100644 --- a/src/librustc_typeck/check/assoc.rs +++ b/src/librustc_typeck/check/assoc.rs @@ -9,85 +9,34 @@ // except according to those terms. use middle::infer::InferCtxt; -use middle::traits::{ObligationCause, ObligationCauseCode, FulfillmentContext}; -use middle::ty::{mod, RegionEscape, HasProjectionTypes, Ty}; -use middle::ty_fold::{mod, TypeFoldable, TypeFolder}; +use middle::mem_categorization as mc; +use middle::traits::{mod, FulfillmentContext, Normalized, MiscObligation, + SelectionContext, ObligationCause}; +use middle::ty::{mod, HasProjectionTypes}; +use middle::ty_fold::{TypeFoldable, TypeFolder}; use syntax::ast; use syntax::codemap::Span; +use util::ppaux::Repr; pub fn normalize_associated_types_in<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>, + param_env: &ty::ParameterEnvironment<'tcx>, + typer: &(mc::Typer<'tcx>+'a), fulfillment_cx: &mut FulfillmentContext<'tcx>, span: Span, body_id: ast::NodeId, value: &T) -> T - where T : TypeFoldable<'tcx> + HasProjectionTypes + Clone + where T : TypeFoldable<'tcx> + HasProjectionTypes + Clone + Repr<'tcx> { - let value = infcx.resolve_type_vars_if_possible(value); - - if !value.has_projection_types() { - return value.clone(); - } - - let mut normalizer = AssociatedTypeNormalizer { span: span, - body_id: body_id, - infcx: infcx, - fulfillment_cx: fulfillment_cx }; - value.fold_with(&mut normalizer) -} - -struct AssociatedTypeNormalizer<'a,'tcx:'a> { - infcx: &'a InferCtxt<'a, 'tcx>, - fulfillment_cx: &'a mut FulfillmentContext<'tcx>, - span: Span, - body_id: ast::NodeId, -} - -impl<'a,'tcx> TypeFolder<'tcx> for AssociatedTypeNormalizer<'a,'tcx> { - fn tcx(&self) -> &ty::ctxt<'tcx> { - self.infcx.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - // We don't want to normalize associated types that occur inside of region - // binders, because they may contain bound regions, and we can't cope with that. - // - // Example: - // - // for<'a> fn(>::A) - // - // Instead of normalizing `>::A` here, we'll - // normalize it when we instantiate those bound regions (which - // should occur eventually). - - match ty.sty { - ty::ty_projection(ref data) if !data.has_escaping_regions() => { // (*) - - // (*) This is kind of hacky -- we need to be able to - // handle normalization within binders because - // otherwise we wind up a need to normalize when doing - // trait matching (since you can have a trait - // obligation like `for<'a> T::B : Fn(&'a int)`), but - // we can't normalize with bound regions in scope. So - // far now we just ignore binders but only normalize - // if all bound regions are gone (and then we still - // have to renormalize whenever we instantiate a - // binder). It would be better to normalize in a - // binding-aware fashion. - - let cause = - ObligationCause::new( - self.span, - self.body_id, - ObligationCauseCode::MiscObligation); - self.fulfillment_cx - .normalize_projection_type(self.infcx, - data.clone(), - cause) - } - _ => { - ty_fold::super_fold_ty(self, ty) - } - } + debug!("normalize_associated_types_in(value={})", value.repr(infcx.tcx)); + let mut selcx = SelectionContext::new(infcx, param_env, typer); + let cause = ObligationCause::new(span, body_id, MiscObligation); + let Normalized { value: result, obligations } = traits::normalize(&mut selcx, cause, value); + debug!("normalize_associated_types_in: result={} predicates={}", + result.repr(infcx.tcx), + obligations.repr(infcx.tcx)); + for obligation in obligations.into_iter() { + fulfillment_cx.register_predicate_obligation(infcx, obligation); } + result } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index e2691a778ac..12cebabc997 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -350,11 +350,18 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> { } } - fn normalize_associated_types_in(&self, span: Span, body_id: ast::NodeId, value: &T) -> T - where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes + fn normalize_associated_types_in(&self, + typer: &mc::Typer<'tcx>, + span: Span, + body_id: ast::NodeId, + value: &T) + -> T + where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes + Repr<'tcx> { let mut fulfillment_cx = self.fulfillment_cx.borrow_mut(); assoc::normalize_associated_types_in(&self.infcx, + &self.param_env, + typer, &mut *fulfillment_cx, span, body_id, value) @@ -438,7 +445,7 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let fn_sig = liberate_late_bound_regions(ccx.tcx, CodeExtent::from_node_id(body.id), &fn_sig); let fn_sig = - inh.normalize_associated_types_in(body.span, body.id, &fn_sig); + inh.normalize_associated_types_in(ccx.tcx, body.span, body.id, &fn_sig); let fcx = check_fn(ccx, fn_ty.unsafety, id, &fn_sig, decl, id, body, &inh); @@ -1190,6 +1197,8 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, impl_sig.subst(tcx, impl_to_skol_substs); let impl_sig = assoc::normalize_associated_types_in(&infcx, + &impl_param_env, + infcx.tcx, &mut fulfillment_cx, impl_m_span, impl_m_body_id, @@ -1209,6 +1218,8 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, trait_sig.subst(tcx, &trait_to_skol_substs); let trait_sig = assoc::normalize_associated_types_in(&infcx, + &impl_param_env, + infcx.tcx, &mut fulfillment_cx, impl_m_span, impl_m_body_id, @@ -1756,9 +1767,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn normalize_associated_types_in(&self, span: Span, value: &T) -> T - where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes + where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes + Repr<'tcx> { - self.inh.normalize_associated_types_in(span, self.body_id, value) + self.inh.normalize_associated_types_in(self, span, self.body_id, value) } fn normalize_associated_type(&self, @@ -1773,6 +1784,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.inh.fulfillment_cx .borrow_mut() .normalize_projection_type(self.infcx(), + &self.inh.param_env, + self, ty::ProjectionTy { trait_ref: trait_ref, item_name: item_name, @@ -1943,7 +1956,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.inh.fulfillment_cx .borrow_mut() - .register_predicate(self.infcx(), obligation); + .register_predicate_obligation(self.infcx(), obligation); } pub fn to_ty(&self, ast_t: &ast::Ty) -> Ty<'tcx> { diff --git a/src/test/compile-fail/associated-types-path-2.rs b/src/test/compile-fail/associated-types-path-2.rs index 486d3d38c60..989214d81da 100644 --- a/src/test/compile-fail/associated-types-path-2.rs +++ b/src/test/compile-fail/associated-types-path-2.rs @@ -25,11 +25,30 @@ pub fn f2(a: T) -> T::A { panic!(); } -pub fn main() { - f1(2i, 4i); //~ ERROR expected uint, found int - f1(2i, 4u); - f1(2u, 4u); //~ ERROR the trait `Foo` is not implemented - f1(2u, 4i); //~ ERROR the trait `Foo` is not implemented - - let _: int = f2(2i); //~ERROR expected `int`, found `uint` +pub fn f1_int_int() { + f1(2i, 4i); + //~^ ERROR expected uint, found int } + +pub fn f1_int_uint() { + f1(2i, 4u); +} + +pub fn f1_uint_uint() { + f1(2u, 4u); + //~^ ERROR the trait `Foo` is not implemented + //~| ERROR the trait `Foo` is not implemented +} + +pub fn f1_uint_int() { + f1(2u, 4i); + //~^ ERROR the trait `Foo` is not implemented + //~| ERROR the trait `Foo` is not implemented +} + +pub fn f2_int() { + let _: int = f2(2i); + //~^ ERROR expected `int`, found `uint` +} + +pub fn main() { } diff --git a/src/test/compile-fail/associated-types-project-from-hrtb-in-fn-body.rs b/src/test/compile-fail/associated-types-project-from-hrtb-in-fn-body.rs index 8cdca50d9b6..0e13efdebc9 100644 --- a/src/test/compile-fail/associated-types-project-from-hrtb-in-fn-body.rs +++ b/src/test/compile-fail/associated-types-project-from-hrtb-in-fn-body.rs @@ -29,9 +29,10 @@ fn bar<'a, 'b, I : for<'x> Foo<&'x int>>( x: >::A, y: >::A, cond: bool) -{ //~ ERROR cannot infer +{ // x and y here have two distinct lifetimes: let z: I::A = if cond { x } else { y }; + //~^ ERROR cannot infer } pub fn main() {} diff --git a/src/test/compile-fail/associated-types-unconstrained.rs b/src/test/compile-fail/associated-types-unconstrained.rs new file mode 100644 index 00000000000..02e11218806 --- /dev/null +++ b/src/test/compile-fail/associated-types-unconstrained.rs @@ -0,0 +1,28 @@ +// Copyright 2014 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. + +// Check that an associated type cannot be bound in an expression path. + +#![feature(associated_types)] + +trait Foo { + type A; + fn bar() -> int; +} + +impl Foo for int { + type A = uint; + fn bar() -> int { 42 } +} + +pub fn main() { + let x: int = Foo::bar(); + //~^ ERROR type annotations required +} diff --git a/src/test/compile-fail/traits-multidispatch-convert-ambig-dest.rs b/src/test/compile-fail/traits-multidispatch-convert-ambig-dest.rs index 9ceae41d1a4..3c461fd5b4b 100644 --- a/src/test/compile-fail/traits-multidispatch-convert-ambig-dest.rs +++ b/src/test/compile-fail/traits-multidispatch-convert-ambig-dest.rs @@ -33,7 +33,7 @@ where T : Convert } fn a() { - test(22_i32, 44); //~ ERROR unable to infer + test(22_i32, 44); //~ ERROR type annotations required } fn main() {} diff --git a/src/test/run-pass/associated-types-impl-redirect.rs b/src/test/run-pass/associated-types-impl-redirect.rs new file mode 100644 index 00000000000..a28cf346336 --- /dev/null +++ b/src/test/run-pass/associated-types-impl-redirect.rs @@ -0,0 +1,58 @@ +// Copyright 2014 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 how resolving a projection interacts with inference. In this +// case, we were eagerly unifying the type variable for the iterator +// type with `I` from the where clause, ignoring the in-scope `impl` +// for `ByRef`. The right answer was to consider the result ambiguous +// until more type information was available. + +// ignore-pretty -- FIXME(#17362) + +#![feature(associated_types, lang_items, unboxed_closures)] +#![no_implicit_prelude] + +use std::option::Option::{None, Some, mod}; + +trait Iterator { + type Item; + + fn next(&mut self) -> Option; +} + +trait IteratorExt: Iterator { + fn by_ref(&mut self) -> ByRef { + ByRef(self) + } +} + +impl IteratorExt for I where I: Iterator {} + +struct ByRef<'a, I: 'a + Iterator>(&'a mut I); + +impl<'a, I: Iterator> Iterator for ByRef<'a, I> { + type Item = I::Item; + + fn next(&mut self) -> Option< ::Item > { + self.0.next() + } +} + +fn is_iterator_of>(_: &I) {} + +fn test>(mut it: I) { + is_iterator_of::(&it.by_ref()); +} + +fn test2, I2: Iterator>(mut it: I2) { + is_iterator_of::(&it) +} + +fn main() { } From 9675488ef9480365c2d26e23bfd649128037880f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 31 Dec 2014 12:38:53 -0500 Subject: [PATCH 06/11] Add tests for two random issues that seem to be fixed on this branch. Fixes #20346. Fixes #20371. --- .../associated-types-issue-20346.rs | 46 +++++++++++++++++++ .../run-pass/associated-types-issue-20371.rs | 17 +++++++ 2 files changed, 63 insertions(+) create mode 100644 src/test/compile-fail/associated-types-issue-20346.rs create mode 100644 src/test/run-pass/associated-types-issue-20371.rs diff --git a/src/test/compile-fail/associated-types-issue-20346.rs b/src/test/compile-fail/associated-types-issue-20346.rs new file mode 100644 index 00000000000..255b8a25a4c --- /dev/null +++ b/src/test/compile-fail/associated-types-issue-20346.rs @@ -0,0 +1,46 @@ +// Copyright 2014 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 that we reliably check the value of the associated type. + +#![crate_type = "lib"] +#![feature(associated_types)] +#![no_implicit_prelude] + +use std::option::Option::{None, Some, mod}; +use std::vec::Vec; + +trait Iterator { + type Item; + + fn next(&mut self) -> Option; +} + +fn is_iterator_of>(_: &I) {} + +struct Adapter { + iter: I, + found_none: bool, +} + +impl Iterator for Adapter where I: Iterator> { + type Item = T; + + fn next(&mut self) -> Option { + loop {} + } +} + +fn test_adapter>>(it: I) { + is_iterator_of::, _>(&it); // Sanity check + let adapter = Adapter { iter: it, found_none: false }; + is_iterator_of::(&adapter); // OK + is_iterator_of::, _>(&adapter); //~ ERROR type mismatch +} diff --git a/src/test/run-pass/associated-types-issue-20371.rs b/src/test/run-pass/associated-types-issue-20371.rs new file mode 100644 index 00000000000..a6fb2b9e2ea --- /dev/null +++ b/src/test/run-pass/associated-types-issue-20371.rs @@ -0,0 +1,17 @@ +// Copyright 2014 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 that we are able to have an impl that defines an associated type +// before the actual trait. + +#![feature(associated_types)] +impl X for f64 { type Y = int; } +trait X {type Y; } +fn main() {} From 67dab2af81ba64023c526dc96315863a9f7e9e40 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 31 Dec 2014 13:49:49 -0500 Subject: [PATCH 07/11] Include projection bounds in superpredicates. Fixes #19451. Fixes #20345. --- src/librustc/middle/ty.rs | 17 ++++++---- ...d-types-projection-bound-in-supertraits.rs | 33 +++++++++++++++++++ 2 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 src/test/run-pass/associated-types-projection-bound-in-supertraits.rs diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 351485d74e8..dab24f6a163 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -5449,8 +5449,15 @@ pub fn predicates_for_trait_ref<'tcx>(tcx: &ctxt<'tcx>, .map(|poly_trait_ref| ty::Binder(poly_trait_ref.0.subst(tcx, trait_ref.substs()))) .collect(); - debug!("bounds_for_trait_ref: trait_bounds={}", - trait_bounds.repr(tcx)); + let projection_bounds: Vec<_> = + trait_def.bounds.projection_bounds + .iter() + .map(|poly_proj| ty::Binder(poly_proj.0.subst(tcx, trait_ref.substs()))) + .collect(); + + debug!("bounds_for_trait_ref: trait_bounds={} projection_bounds={}", + trait_bounds.repr(tcx), + projection_bounds.repr(tcx)); // The region bounds and builtin bounds do not currently introduce // binders so we can just substitute in a straightforward way here. @@ -5463,11 +5470,7 @@ pub fn predicates_for_trait_ref<'tcx>(tcx: &ctxt<'tcx>, trait_bounds: trait_bounds, region_bounds: region_bounds, builtin_bounds: builtin_bounds, - - // FIXME(#19451) -- if a trait has a bound like `trait Foo : - // Bar`, we should probably be returning that, but this - // code here will just ignore it. - projection_bounds: Vec::new(), + projection_bounds: projection_bounds, }; predicates(tcx, trait_ref.self_ty(), &bounds) diff --git a/src/test/run-pass/associated-types-projection-bound-in-supertraits.rs b/src/test/run-pass/associated-types-projection-bound-in-supertraits.rs new file mode 100644 index 00000000000..c7ffb56cc83 --- /dev/null +++ b/src/test/run-pass/associated-types-projection-bound-in-supertraits.rs @@ -0,0 +1,33 @@ +// Copyright 2014 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 that we correctly handle projection bounds appearing in the +// supertrait list (and in conjunction with overloaded operators). In +// this case, the `Result=Self` binding in the supertrait listing of +// `Int` was being ignored. + +#![feature(associated_types)] + +trait Not { + type Result; + + fn not(self) -> Self::Result; +} + +trait Int: Not { + fn count_ones(self) -> uint; + fn count_zeros(self) -> uint { + // neither works + let x: Self = self.not(); + 0 + } +} + +fn main() { } From 0a2d531b94f4c49ecc0b190b1feb438e27c3e882 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 31 Dec 2014 14:42:06 -0500 Subject: [PATCH 08/11] Teach trans to drain fulfillment context. japaric encountered problems due to this but we were not able to isolate a smaller test case. --- src/librustc_trans/trans/common.rs | 47 +++++++++++++++--------- src/librustc_trans/trans/monomorphize.rs | 7 +++- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 1b00a77fe51..7c2585becea 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -954,28 +954,47 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // Currently, we use a fulfillment context to completely resolve // all nested obligations. This is because they can inform the - // inference of the impl's type parameters. However, in principle, - // we only need to do this until the impl's type parameters are - // fully bound. It could be a slight optimization to stop - // iterating early. + // inference of the impl's type parameters. let mut fulfill_cx = traits::FulfillmentContext::new(); let vtable = selection.map_move_nested(|predicate| { fulfill_cx.register_predicate_obligation(&infcx, predicate); }); - match fulfill_cx.select_all_or_error(&infcx, ¶m_env, tcx) { + let vtable = drain_fulfillment_cx(span, &infcx, ¶m_env, &mut fulfill_cx, &vtable); + + info!("Cache miss: {}", trait_ref.repr(ccx.tcx())); + ccx.trait_cache().borrow_mut().insert(trait_ref, + vtable.clone()); + + vtable +} + +pub fn drain_fulfillment_cx<'a,'tcx,T>(span: Span, + infcx: &infer::InferCtxt<'a,'tcx>, + param_env: &ty::ParameterEnvironment<'tcx>, + fulfill_cx: &mut traits::FulfillmentContext<'tcx>, + result: &T) + -> T + where T : TypeFoldable<'tcx> + Repr<'tcx> +{ + debug!("drain_fulfillment_cx(result={})", + result.repr(infcx.tcx)); + + // In principle, we only need to do this so long as `result` + // contains unbound type parameters. It could be a slight + // optimization to stop iterating early. + match fulfill_cx.select_all_or_error(infcx, param_env, infcx.tcx) { Ok(()) => { } Err(errors) => { if errors.iter().all(|e| e.is_overflow()) { // See Ok(None) case above. - ccx.sess().span_fatal( + infcx.tcx.sess.span_fatal( span, "reached the recursion limit during monomorphization"); } else { - tcx.sess.span_bug( + infcx.tcx.sess.span_bug( span, - format!("Encountered errors `{}` fulfilling `{}` during trans", - errors.repr(tcx), - trait_ref.repr(tcx))[]); + format!("Encountered errors `{}` fulfilling during trans", + errors.repr(infcx.tcx))[]); } } } @@ -985,13 +1004,7 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // sort of overkill because we do not expect there to be any // unbound type variables, hence no `TyFresh` types should ever be // inserted. - let vtable = vtable.fold_with(&mut infcx.freshener()); - - info!("Cache miss: {}", trait_ref.repr(ccx.tcx())); - ccx.trait_cache().borrow_mut().insert(trait_ref, - vtable.clone()); - - vtable + result.fold_with(&mut infcx.freshener()) } // Key used to lookup values supplied for type parameters in an expr. diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs index c693c6ea428..fdcb232e125 100644 --- a/src/librustc_trans/trans/monomorphize.rs +++ b/src/librustc_trans/trans/monomorphize.rs @@ -31,6 +31,7 @@ use syntax::ast; use syntax::ast_map; use syntax::ast_util::{local_def, PostExpansionMethod}; use syntax::attr; +use syntax::codemap::DUMMY_SP; use std::hash::{sip, Hash}; pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, @@ -331,7 +332,11 @@ pub fn normalize_associated_type<'tcx,T>(tcx: &ty::ctxt<'tcx>, value: &T) -> T result.repr(tcx), obligations.repr(tcx)); - assert_eq!(obligations.len(), 0); // TODO not good enough + let mut fulfill_cx = traits::FulfillmentContext::new(); + for obligation in obligations.into_iter() { + fulfill_cx.register_predicate_obligation(&infcx, obligation); + } + let result = drain_fulfillment_cx(DUMMY_SP, &infcx, ¶m_env, &mut fulfill_cx, &result); result } From cadd4335b4150f94b573eb3c87235d653cfba6d2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 31 Dec 2014 15:05:38 -0500 Subject: [PATCH 09/11] Fix whitespace. --- .../associated-types-projection-bound-in-supertraits.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/run-pass/associated-types-projection-bound-in-supertraits.rs b/src/test/run-pass/associated-types-projection-bound-in-supertraits.rs index c7ffb56cc83..83686d92a7c 100644 --- a/src/test/run-pass/associated-types-projection-bound-in-supertraits.rs +++ b/src/test/run-pass/associated-types-projection-bound-in-supertraits.rs @@ -26,7 +26,7 @@ trait Int: Not { fn count_zeros(self) -> uint { // neither works let x: Self = self.not(); - 0 + 0 } } From 7ae1c6bc269cd0fddb45590a91d77e39b47a4965 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 31 Dec 2014 16:02:58 -0500 Subject: [PATCH 10/11] Remove a TODO now that we handle normalization-derived bounds properly. --- src/librustc_typeck/check/wf.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/librustc_typeck/check/wf.rs b/src/librustc_typeck/check/wf.rs index 2d9b243e00e..410697b0aca 100644 --- a/src/librustc_typeck/check/wf.rs +++ b/src/librustc_typeck/check/wf.rs @@ -269,14 +269,8 @@ impl<'cx,'tcx> BoundsChecker<'cx,'tcx> { pub fn check_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>) { let trait_def = ty::lookup_trait_def(self.fcx.tcx(), trait_ref.def_id); - // TODO uncommented this line causes failures because the impl - // obligations are not registered when we do a projection, and - // in this case it's those obligations that make the link - // between the normalized type ($1) and the result - // - // let bounds = self.fcx.instantiate_bounds(self.span, trait_ref.substs, &trait_def.generics); + let bounds = self.fcx.instantiate_bounds(self.span, trait_ref.substs, &trait_def.generics); - let bounds = trait_def.generics.to_bounds(self.fcx.tcx(), trait_ref.substs); self.fcx.add_obligations_for_parameters( traits::ObligationCause::new( self.span, From 004a567de3d885e72d4053aeb91798749685a030 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 31 Dec 2014 16:23:49 -0500 Subject: [PATCH 11/11] Convert TODO to FIXME for now --- src/librustc_typeck/check/method/probe.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 610a5c0b246..f00e3e2d452 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -768,7 +768,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { // Check whether the impl imposes obligations we have to worry about. let impl_generics = ty::lookup_item_type(self.tcx(), impl_def_id).generics; let impl_bounds = impl_generics.to_bounds(self.tcx(), substs); - // TODO assoc type normalization here? + // FIXME(#20378) assoc type normalization here? // Erase any late-bound regions bound in the impl // which appear in the bounds.