diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 8a4790c17a4..8f0ccbd4461 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -122,6 +122,7 @@ pub mod middle { pub mod traits; pub mod ty; pub mod ty_fold; + pub mod ty_match; pub mod ty_relate; pub mod ty_walk; pub mod weak_lang_items; diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 8004cc37ccf..169e06a9594 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -38,6 +38,8 @@ use middle::ty::{self, RegionEscape, ToPolyTraitRef, Ty}; use middle::infer; use middle::infer::{InferCtxt, TypeFreshener}; use middle::ty_fold::TypeFoldable; +use middle::ty_match; +use middle::ty_relate::TypeRelation; use std::cell::RefCell; use std::rc::Rc; use syntax::{abi, ast}; @@ -472,7 +474,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { unbound_input_types && (self.intercrate || stack.iter().skip(1).any( - |prev| stack.fresh_trait_ref.def_id() == prev.fresh_trait_ref.def_id())) + |prev| self.match_fresh_trait_refs(&stack.fresh_trait_ref, + &prev.fresh_trait_ref))) { debug!("evaluate_stack({}) --> unbound argument, recursion --> ambiguous", stack.fresh_trait_ref.repr(self.tcx())); @@ -2475,6 +2478,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /////////////////////////////////////////////////////////////////////////// // Miscellany + fn match_fresh_trait_refs(&self, + previous: &ty::PolyTraitRef<'tcx>, + current: &ty::PolyTraitRef<'tcx>) + -> bool + { + let mut matcher = ty_match::Match::new(self.tcx()); + matcher.relate(previous, current).is_ok() + } + fn push_stack<'o,'s:'o>(&mut self, previous_stack: Option<&'s TraitObligationStack<'s, 'tcx>>, obligation: &'o TraitObligation<'tcx>) diff --git a/src/librustc/middle/ty_match.rs b/src/librustc/middle/ty_match.rs new file mode 100644 index 00000000000..bb00fadc39c --- /dev/null +++ b/src/librustc/middle/ty_match.rs @@ -0,0 +1,95 @@ +// 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::ty::{self, Ty}; +use middle::ty_relate::{self, Relate, TypeRelation, RelateResult}; +use util::ppaux::Repr; + +/// A type "A" *matches* "B" if the fresh types in B could be +/// substituted with values so as to make it equal to A. Matching is +/// intended to be used only on freshened types, and it basically +/// indicates if the non-freshened versions of A and B could have been +/// unified. +/// +/// It is only an approximation. If it yields false, unification would +/// definitely fail, but a true result doesn't mean unification would +/// succeed. This is because we don't track the "side-constraints" on +/// type variables, nor do we track if the same freshened type appears +/// more than once. To some extent these approximations could be +/// fixed, given effort. +/// +/// Like subtyping, matching is really a binary relation, so the only +/// important thing about the result is Ok/Err. Also, matching never +/// affects any type variables or unification state. +pub struct Match<'a, 'tcx: 'a> { + tcx: &'a ty::ctxt<'tcx> +} + +impl<'a, 'tcx> Match<'a, 'tcx> { + pub fn new(tcx: &'a ty::ctxt<'tcx>) -> Match<'a, 'tcx> { + Match { tcx: tcx } + } +} + +impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Match<'a, 'tcx> { + fn tag(&self) -> &'static str { "Match" } + fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.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({}, {})", + self.tag(), + a.repr(self.tcx()), + b.repr(self.tcx())); + Ok(a) + } + + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + debug!("{}.tys({}, {})", self.tag(), + a.repr(self.tcx()), b.repr(self.tcx())); + if a == b { return Ok(a); } + + match (&a.sty, &b.sty) { + (_, &ty::ty_infer(ty::FreshTy(_))) | + (_, &ty::ty_infer(ty::FreshIntTy(_))) => { + Ok(a) + } + + (&ty::ty_infer(_), _) | + (_, &ty::ty_infer(_)) => { + Err(ty::terr_sorts(ty_relate::expected_found(self, &a, &b))) + } + + (&ty::ty_err, _) | (_, &ty::ty_err) => { + Ok(self.tcx().types.err) + } + + _ => { + ty_relate::super_relate_tys(self, a, b) + } + } + } + + fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) + -> RelateResult<'tcx, ty::Binder> + where T: Relate<'a,'tcx> + { + Ok(ty::Binder(try!(self.relate(a.skip_binder(), b.skip_binder())))) + } +}