diff --git a/src/librustc/middle/infer/error_reporting.rs b/src/librustc/middle/infer/error_reporting.rs index 111872f1784cc..1b0a09684cfc8 100644 --- a/src/librustc/middle/infer/error_reporting.rs +++ b/src/librustc/middle/infer/error_reporting.rs @@ -655,6 +655,18 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> { let terr = TypeError::RegionsDoesNotOutlive(sup, sub); self.report_and_explain_type_error(trace, &terr); } + infer::TraitMatch(span) => { + // TODO oh dear god the terribleness + self.tcx.sess.span_err(span, "lifetime mismatch during trait processing"); + self.tcx.note_and_explain_region( + "...the impl or where clause references the lifetime", + sub, + "..."); + self.tcx.note_and_explain_region( + "...but the lifetime is required", + sup, + ""); + } infer::Reborrow(span) => { span_err!(self.tcx.sess, span, E0312, "lifetime of reference outlines \ @@ -1611,12 +1623,9 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> { infer::ExprAssignable(_) => { "expression is assignable" } - infer::RelateTraitRefs(_) => { + infer::TraitMatchTypes(_) => { "traits are compatible" } - infer::RelateSelfType(_) => { - "self type matches impl self type" - } infer::RelateOutputImplTypes(_) => { "trait type parameters matches those \ specified on the impl" @@ -1656,6 +1665,11 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> { } } } + infer::TraitMatch(span) => { + self.tcx.sess.span_note( + span, + "...so that the required trait is satisfied"); + } infer::Reborrow(span) => { self.tcx.sess.span_note( span, diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs index d61029a2d2dc3..75d447eefc99a 100644 --- a/src/librustc/middle/infer/mod.rs +++ b/src/librustc/middle/infer/mod.rs @@ -125,11 +125,8 @@ pub enum TypeOrigin { // FIXME(eddyb) #11161 is the original Expr required? ExprAssignable(Span), - // Relating trait refs when resolving vtables - RelateTraitRefs(Span), - - // Relating self types when resolving vtables - RelateSelfType(Span), + // Relating types in an impl + TraitMatchTypes(Span), // Relating trait type parameters to those found in impl etc RelateOutputImplTypes(Span), @@ -154,10 +151,9 @@ impl TypeOrigin { fn as_str(&self) -> &'static str { match self { &TypeOrigin::Misc(_) | - &TypeOrigin::RelateSelfType(_) | + &TypeOrigin::TraitMatchTypes(_) | &TypeOrigin::RelateOutputImplTypes(_) | &TypeOrigin::ExprAssignable(_) => "mismatched types", - &TypeOrigin::RelateTraitRefs(_) => "mismatched traits", &TypeOrigin::MethodCompatCheck(_) => "method not compatible with trait", &TypeOrigin::MatchExpressionArm(_, _, source) => match source { hir::MatchSource::IfLetDesugar{..} => "`if let` arms have incompatible types", @@ -205,6 +201,9 @@ pub enum SubregionOrigin<'tcx> { // error. RFC1214Subregion(Rc>), + // Arose as part of trait matching + TraitMatch(Span), + // Arose from a subtyping relation Subtype(TypeTrace<'tcx>), @@ -842,6 +841,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.region_vars.add_given(sub, sup); } + pub fn equate_regions(&self, + origin: SubregionOrigin<'tcx>, + a: ty::Region, + b: ty::Region) + { + self.region_vars.make_eqregion(origin, a, b) + } + pub fn sub_types(&self, a_is_expected: bool, origin: TypeOrigin, @@ -1546,8 +1553,7 @@ impl TypeOrigin { MethodCompatCheck(span) => span, ExprAssignable(span) => span, Misc(span) => span, - RelateTraitRefs(span) => span, - RelateSelfType(span) => span, + TraitMatchTypes(span) => span, RelateOutputImplTypes(span) => span, MatchExpressionArm(match_span, _, _) => match_span, IfExpression(span) => span, @@ -1563,6 +1569,7 @@ impl<'tcx> SubregionOrigin<'tcx> { match *self { RFC1214Subregion(ref a) => a.span(), Subtype(ref a) => a.span(), + TraitMatch(a) => a, InfStackClosure(a) => a, InvokeClosure(a) => a, DerefPointer(a) => a, diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index c44891de0a055..a5415200b48cf 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -385,6 +385,10 @@ impl VecPerParamSpace { &self.get_slice(space)[index] } + pub fn get_mut(&mut self, space: ParamSpace, index: usize) -> &mut T { + &mut self.get_mut_slice(space)[index] + } + pub fn iter<'a>(&'a self) -> Iter<'a,T> { self.content.iter() } diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index b78b48ac1d582..e44aa1f82a90b 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -67,6 +67,7 @@ mod project; mod object_safety; mod select; mod structural_impls; +mod ty_match; mod ty_recur; mod util; diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 0f9402c213770..0587ddf82f9b7 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -29,6 +29,7 @@ use super::TraitNotObjectSafe; use super::RFC1214Warning; use super::Selection; use super::SelectionResult; +use super::ty_match; use super::ty_recur; use super::{VtableBuiltin, VtableImpl, VtableParam, VtableClosure, VtableFnPointer, VtableObject, VtableDefaultImpl}; @@ -2675,12 +2676,29 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { snapshot); let skol_obligation_trait_ref = skol_obligation.trait_ref; - let impl_substs = util::fresh_type_vars_for_impl(self.infcx, - obligation.cause.span, - impl_def_id); + let impl_generics = self.tcx().lookup_item_type(impl_def_id).generics; + let impl_substs = + match ty_match::perform_match(self.infcx, + &impl_generics, + obligation.cause.span, + &impl_trait_ref, + &skol_obligation_trait_ref) { + Ok(substs) => substs, + Err(e) => { + debug!("match_impl: perform_match failed due to `{}`", e); + return Err(()); + } + }; + + // TODO Match computation creates a set of substitions, but + // doesn't guarantee that all necessary relations are + // established, particularly with regard to + // projections. Therefore, we also do the subtyping check here + // a second time. Obviously inefficient and suboptimal. + // + // TODO question: is the leak check sufficient now? - let impl_trait_ref = impl_trait_ref.subst(self.tcx(), - &impl_substs); + let impl_trait_ref = impl_trait_ref.subst(self.tcx(), &impl_substs); let impl_trait_ref = project::normalize_with_depth(self, diff --git a/src/librustc/middle/traits/ty_match.rs b/src/librustc/middle/traits/ty_match.rs new file mode 100644 index 0000000000000..fb2d23992ab2d --- /dev/null +++ b/src/librustc/middle/traits/ty_match.rs @@ -0,0 +1,285 @@ +// Copyright 2012 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. + +use middle::infer; +use middle::subst::{Substs, VecPerParamSpace}; +use middle::ty::{self, Ty}; +use middle::ty::error::TypeError; +use middle::ty::relate::{self, Relate, TypeRelation, RelateResult}; +use std::fmt::Debug; +use syntax::codemap::Span; +use util::nodemap::FnvHashMap; + +pub fn perform_match<'a,'tcx,T>(infcx: &infer::InferCtxt<'a, 'tcx>, + generics: &ty::Generics<'tcx>, + span: Span, + template: &T, + value: &T) + -> RelateResult<'tcx, Substs<'tcx>> + where T: Relate<'a, 'tcx> + Debug +{ + debug!("perform_match(template={:?}, value={:?}, generics={:?}, span={:?}", + template, value, generics, span); + + let mut matcher = Match::new(infcx, generics, span); + try!(matcher.relate(template, value)); + Ok(matcher.into_substs()) +} + +struct Match<'mtch, 'a:'mtch, 'tcx: 'a> { + infcx: &'mtch infer::InferCtxt<'a, 'tcx>, + generics: &'mtch ty::Generics<'tcx>, + types: VecPerParamSpace>>, + regions: VecPerParamSpace>, + span: Span, +} + +impl<'mtch, 'a, 'tcx> Match<'mtch, 'a, 'tcx> { + fn new(infcx: &'mtch infer::InferCtxt<'a, 'tcx>, + generics: &'mtch ty::Generics<'tcx>, + span: Span) + -> Match<'mtch, 'a, 'tcx> { + let types = generics.types.map(|_| None); + let regions = generics.regions.map(|_| None); + Match { infcx: infcx, generics: generics, types: types, regions: regions, span: span } + } + + /// When we are matching a higher-ranked type, we replace the bound regions within + /// with skolemized variants. + fn search_for_skolemized_region(&self, skol_map: &infer::SkolemizationMap) + -> RelateResult<'tcx, ()> + { + debug!("search_for_skolemized_region(skol_map={:?})", skol_map); + + let inverse_skol_map: FnvHashMap<_,_> = + skol_map.iter() + .map(|(br, r)| (r, br)) + .collect(); + + let regions_from_regions = self.regions.iter() + .flat_map(|opt_r| opt_r.iter()) + .cloned(); + let regions_from_types = self.types.iter() + .flat_map(|opt_t| opt_t.iter()) + .flat_map(|t| t.walk()) // (*) + .flat_map(|t| t.regions()); + let regions = regions_from_regions.chain(regions_from_types); + + // (*) skips over binders, but that's ok, because the regions we are searching for + // would appear free in the subst + + for r in regions { + debug!("search_for_skolemized_region: r={:?}", r); + if let Some(&&br) = inverse_skol_map.get(&r) { + return Err(TypeError::RegionsInsufficientlyPolymorphic(br, r)); + } + } + + Ok(()) + } + + fn into_substs(self) -> Substs<'tcx> { + debug!("into_substs(self.types={:?}, self.regions={:?})", + self.types, self.regions); + + // There are sometimes types that are unconstrained due + // in cases like: + // + // impl Foo for Bar where I: Trait { ... } + // + // In those cases, just make a fresh type variable. + let types = + self.types + .map_enumerated(|(space, index, opt_ty)| { + opt_ty.unwrap_or_else(|| { + let def = self.generics.types.get(space, index); + + // TODO -- deal with defaults. Hmm. + assert!(def.default.is_none()); + + self.infcx.next_ty_var_with_default(None) + }) + }); + + // For regions, we allow impls where there are region parameters + // that are not used in the trait reference somewhere, which is + // really kind of sad. Anyway, if we didn't find a value, then + // just create a new region variable. + let regions = + self.regions + .map_enumerated(|(space, index, opt_region)| { + opt_region.unwrap_or_else(|| { + let def = self.generics.regions.get(space, index); + self.infcx.next_region_var(infer::EarlyBoundRegion(self.span, def.name)) + }) + }); + + Substs::new(types, regions) + } +} + +impl<'mtch, 'a, 'tcx> TypeRelation<'a, 'tcx> for Match<'mtch, 'a, 'tcx> { + fn tag(&self) -> &'static str { "Match" } + fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.infcx.tcx } + fn a_is_expected(&self) -> bool { true } // irrelevant + + fn relate_with_variance>(&mut self, + _: ty::Variance, + a: &T, + b: &T) + -> RelateResult<'tcx, T> + { + self.relate(a, b) + } + + fn regions(&mut self, a: ty::Region, b: ty::Region) -> RelateResult<'tcx, ty::Region> { + debug!("regions(a={:?}, b={:?})", a, b); + + // if LHS is a type, assign substitution + let a_prime = match a { + ty::ReEarlyBound(data) => { + let slot = self.regions.get_mut(data.space, data.index as usize); + *slot = Some(b); // see (*) below + return Ok(b); + } + + ty::ReVar(_) => { + self.infcx.tcx.sess.span_bug( + self.span, + &format!("encountered inference region {:?} in LHS of match", a)); + } + + _ => a, + }; + debug!("regions: a_prime={:?}", a_prime); + + let origin = infer::SubregionOrigin::TraitMatch(self.span); + self.infcx.equate_regions(origin, a_prime, b); + Ok(b) + } + + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + debug!("tys(a={:?}, b={:?})", a, b); + + // remove any type variables in RHS that we can + let b = self.infcx.shallow_resolve(b); + debug!("tys: b={:?}", b); + + // if LHS was a type variable, either use RHS as its value or, if it + // already has a value, unify old value with RHS; else just unify + let a_prime = match a.sty { + ty::TyParam(param_ty) => { + let slot = self.types.get_mut(param_ty.space, param_ty.idx as usize); + *slot = Some(b); // see (*) below + return Ok(b); + } + + ty::TyProjection(_) => { + // If LHS is a projection, then unifying the result + // doesn't tell us anything about the inputs per se. + // Moreover, any parameters in the inputs must appear + // elsewhere in the impl or else the impl is + // underconstrained, so we can just skip this + // case. Note that we do have to come back once subst + // is known to doublecheck that the result of the + // projection; for notes on this, see (*) below. + return Ok(b); + } + + ty::TyInfer(_) => { + self.infcx.tcx.sess.span_bug( + self.span, + &format!("encountered inference variable {:?} in LHS of match", a)); + } + + _ => a, + }; + debug!("tys: a_prime={:?}", a_prime); + + match b.sty { + ty::TyInfer(_) => { + // If RHS is an inference variable, we can't learn + // much from unifying it, and in fact we have to be + // careful because `a` may contain early bound regions + // and parameters, so we can't unify with `b` unless + // we apply SOME sort of substitution. For more + // details, see (*) below. + } + _ => { + try!(relate::super_relate_tys(self, a_prime, b)); + } + } + + Ok(b) + } + + fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) + -> RelateResult<'tcx, ty::Binder> + where T: Relate<'a,'tcx> + { + debug!("binders(a={:?}, b={:?})", a, b); + + // TODO This is kind of hacky. But we want to check that the + // `a` and `b` are *equally* polymorphic, and the primitives + // we currently have implemented are targeted at subtyping. We + // should integrate a new equality check, but for now we'll + // just do the check in both directions, as equate.rs does. + + try!(self.infcx.commit_if_ok(|snapshot| { + let (a_prime, _) = + self.infcx.replace_late_bound_regions_with_fresh_var( + self.span, infer::HigherRankedType, a); + + let (b_prime, skol_map) = + self.infcx.skolemize_late_bound_regions(b, snapshot); + + try!(self.relate(&a_prime, &b_prime)); + + // check whether any of the skolemization regions appear + // in the substitution we've been building up; if so, + // that's a leak + try!(self.search_for_skolemized_region(&skol_map)); + + // also search the inference graph + self.infcx.leak_check(&skol_map, snapshot) + })); + + try!(self.infcx.commit_if_ok(|snapshot| { + let (a_prime, skol_map) = + self.infcx.skolemize_late_bound_regions(a, snapshot); + + let (b_prime, _) = + self.infcx.replace_late_bound_regions_with_fresh_var( + self.span, infer::HigherRankedType, b); + + try!(self.relate(&a_prime, &b_prime)); + + // no need to re-search the substitution we're building + // up, because reversing role of A and B should not cause + // any new entries to appear since the last time we + // checked + debug_assert!(self.search_for_skolemized_region(&skol_map).is_ok()); + + self.infcx.leak_check(&skol_map, snapshot) + })); + + Ok(b.clone()) + } +} + +// (*) In general in this code, we are just trying to find the best +// values we can for regions and types. Even if this operation +// succeeds, there is no guarantee that the types we found are +// sufficient to permit unification; therefore this operation should +// be followed up by doing the substitution with the returned types +// and then doing the unification. Forcing this second step is +// inefficient but simplifies the logic here, since when we encounter +// multiple constraints relating to the same thing, we don't have to +// record them all, we can just keep track of one of them. diff --git a/src/librustc/middle/ty/sty.rs b/src/librustc/middle/ty/sty.rs index 37ed716eaa0b8..82498c7dcccd9 100644 --- a/src/librustc/middle/ty/sty.rs +++ b/src/librustc/middle/ty/sty.rs @@ -1190,13 +1190,24 @@ impl<'tcx> TyS<'tcx> { } TyEnum(_, substs) | TyStruct(_, substs) => { - substs.regions().as_slice().to_vec() + match substs.regions { + subst::ErasedRegions => vec![], + subst::NonerasedRegions(ref regions) => regions.as_slice().to_vec(), + } } TyClosure(_, ref substs) => { - substs.func_substs.regions().as_slice().to_vec() + let func_regions = match substs.func_substs.regions { + subst::ErasedRegions => &[][..], + subst::NonerasedRegions(ref regions) => regions.as_slice(), + }; + // TODO incomplete! + func_regions.to_vec() } TyProjection(ref data) => { - data.trait_ref.substs.regions().as_slice().to_vec() + match data.trait_ref.substs.regions { + subst::ErasedRegions => vec![], + subst::NonerasedRegions(ref regions) => regions.as_slice().to_vec(), + } } TyBareFn(..) | TyBool |