Skip to content

Commit

Permalink
Normalize associated types in closure signatures
Browse files Browse the repository at this point in the history
Fixes #25700.
  • Loading branch information
arielb1 authored and Ariel Ben-Yehuda committed Jun 18, 2015
1 parent 56d765d commit 21fd312
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 48 deletions.
23 changes: 18 additions & 5 deletions src/librustc/middle/traits/mod.rs
Expand Up @@ -245,7 +245,7 @@ pub enum Vtable<'tcx, N> {
/// Vtable automatically generated for a closure. The def ID is the ID
/// of the closure expression. This is a `VtableImpl` in spirit, but the
/// impl is generated by the compiler and does not appear in the source.
VtableClosure(ast::DefId, subst::Substs<'tcx>),
VtableClosure(VtableClosureData<'tcx, N>),

/// Same as above, but for a fn pointer type with the given signature.
VtableFnPointer(ty::Ty<'tcx>),
Expand All @@ -268,7 +268,16 @@ pub struct VtableImplData<'tcx, N> {
pub nested: Vec<N>
}

#[derive(Debug,Clone)]
#[derive(Clone, PartialEq, Eq)]
pub struct VtableClosureData<'tcx, N> {
pub closure_def_id: ast::DefId,
pub substs: subst::Substs<'tcx>,
/// Nested obligations. This can be non-empty if the closure
/// signature contains associated types.
pub nested: Vec<N>
}

#[derive(Debug, Clone)]
pub struct VtableDefaultImplData<N> {
pub trait_def_id: ast::DefId,
pub nested: Vec<N>
Expand Down Expand Up @@ -502,8 +511,8 @@ impl<'tcx, N> Vtable<'tcx, N> {
VtableParam(n) => n,
VtableBuiltin(i) => i.nested,
VtableDefaultImpl(d) => d.nested,
VtableObject(_) | VtableFnPointer(..) |
VtableClosure(..) => vec![]
VtableClosure(c) => c.nested,
VtableObject(_) | VtableFnPointer(..) => vec![]
}
}

Expand All @@ -524,7 +533,11 @@ impl<'tcx, N> Vtable<'tcx, N> {
nested: d.nested.into_iter().map(f).collect()
}),
VtableFnPointer(f) => VtableFnPointer(f),
VtableClosure(d, s) => VtableClosure(d, s),
VtableClosure(c) => VtableClosure(VtableClosureData {
closure_def_id: c.closure_def_id,
substs: c.substs,
nested: c.nested.into_iter().map(f).collect()
})
}
}
}
Expand Down
44 changes: 30 additions & 14 deletions src/librustc/middle/traits/project.rs
Expand Up @@ -17,15 +17,15 @@ use super::ObligationCause;
use super::PredicateObligation;
use super::SelectionContext;
use super::SelectionError;
use super::VtableClosureData;
use super::VtableImplData;
use super::util;

use middle::infer;
use middle::subst::{Subst, Substs};
use middle::subst::Subst;
use middle::ty::{self, AsPredicate, ReferencesError, RegionEscape,
HasProjectionTypes, ToPolyTraitRef, Ty};
use middle::ty_fold::{self, TypeFoldable, TypeFolder};
use syntax::ast;
use syntax::parse::token;
use util::common::FN_OUTPUT_NAME;
use util::ppaux::Repr;
Expand Down Expand Up @@ -57,7 +57,7 @@ pub struct MismatchedProjectionTypes<'tcx> {
enum ProjectionTyCandidate<'tcx> {
ParamEnv(ty::PolyProjectionPredicate<'tcx>),
Impl(VtableImplData<'tcx, PredicateObligation<'tcx>>),
Closure(ast::DefId, Substs<'tcx>),
Closure(VtableClosureData<'tcx, PredicateObligation<'tcx>>),
FnPointer(Ty<'tcx>),
}

Expand Down Expand Up @@ -162,11 +162,16 @@ fn consider_unification_despite_ambiguity<'cx,'tcx>(selcx: &mut SelectionContext
self_ty,
&closure_type.sig,
util::TupleArgumentsFlag::No);
// We don't have to normalize the return type here - this is only
// reached for TyClosure: Fn inputs where the closure kind is
// still unknown, which should only occur in typeck where the
// closure type is already normalized.
let (ret_type, _) =
infcx.replace_late_bound_regions_with_fresh_var(
obligation.cause.span,
infer::AssocTypeProjection(obligation.predicate.projection_ty.item_name),
&ty::Binder(ret_type));

debug!("consider_unification_despite_ambiguity: ret_type={:?}",
ret_type.repr(selcx.tcx()));
let origin = infer::RelateOutputImplTypes(obligation.cause.span);
Expand Down Expand Up @@ -686,9 +691,9 @@ fn assemble_candidates_from_impls<'cx,'tcx>(
selcx, obligation, obligation_trait_ref, candidate_set,
data.object_ty);
}
super::VtableClosure(closure_def_id, substs) => {
super::VtableClosure(data) => {
candidate_set.vec.push(
ProjectionTyCandidate::Closure(closure_def_id, substs));
ProjectionTyCandidate::Closure(data));
}
super::VtableFnPointer(fn_type) => {
candidate_set.vec.push(
Expand Down Expand Up @@ -755,8 +760,8 @@ fn confirm_candidate<'cx,'tcx>(
confirm_impl_candidate(selcx, obligation, impl_vtable)
}

ProjectionTyCandidate::Closure(def_id, substs) => {
confirm_closure_candidate(selcx, obligation, def_id, &substs)
ProjectionTyCandidate::Closure(closure_vtable) => {
confirm_closure_candidate(selcx, obligation, closure_vtable)
}

ProjectionTyCandidate::FnPointer(fn_type) => {
Expand All @@ -779,13 +784,24 @@ fn confirm_fn_pointer_candidate<'cx,'tcx>(
fn confirm_closure_candidate<'cx,'tcx>(
selcx: &mut SelectionContext<'cx,'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
closure_def_id: ast::DefId,
substs: &Substs<'tcx>)
vtable: VtableClosureData<'tcx, PredicateObligation<'tcx>>)
-> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
{
let closure_typer = selcx.closure_typer();
let closure_type = closure_typer.closure_type(closure_def_id, substs);
confirm_callable_candidate(selcx, obligation, &closure_type.sig, util::TupleArgumentsFlag::No)
let closure_type = closure_typer.closure_type(vtable.closure_def_id, &vtable.substs);
let Normalized {
value: closure_type,
mut obligations
} = normalize_with_depth(selcx,
obligation.cause.clone(),
obligation.recursion_depth+1,
&closure_type);
let (ty, mut cc_obligations) = confirm_callable_candidate(selcx,
obligation,
&closure_type.sig,
util::TupleArgumentsFlag::No);
obligations.append(&mut cc_obligations);
(ty, obligations)
}

fn confirm_callable_candidate<'cx,'tcx>(
Expand All @@ -797,7 +813,7 @@ fn confirm_callable_candidate<'cx,'tcx>(
{
let tcx = selcx.tcx();

debug!("confirm_closure_candidate({},{})",
debug!("confirm_callable_candidate({},{})",
obligation.repr(tcx),
fn_sig.repr(tcx));

Expand Down Expand Up @@ -921,8 +937,8 @@ impl<'tcx> Repr<'tcx> for ProjectionTyCandidate<'tcx> {
format!("ParamEnv({})", data.repr(tcx)),
ProjectionTyCandidate::Impl(ref data) =>
format!("Impl({})", data.repr(tcx)),
ProjectionTyCandidate::Closure(ref a, ref b) =>
format!("Closure(({},{}))", a.repr(tcx), b.repr(tcx)),
ProjectionTyCandidate::Closure(ref data) =>
format!("Closure({})", data.repr(tcx)),
ProjectionTyCandidate::FnPointer(a) =>
format!("FnPointer(({}))", a.repr(tcx)),
}
Expand Down
72 changes: 53 additions & 19 deletions src/librustc/middle/traits/select.rs
Expand Up @@ -31,7 +31,8 @@ use super::Selection;
use super::SelectionResult;
use super::{VtableBuiltin, VtableImpl, VtableParam, VtableClosure,
VtableFnPointer, VtableObject, VtableDefaultImpl};
use super::{VtableImplData, VtableObjectData, VtableBuiltinData, VtableDefaultImplData};
use super::{VtableImplData, VtableObjectData, VtableBuiltinData,
VtableClosureData, VtableDefaultImplData};
use super::object_safety;
use super::util;

Expand Down Expand Up @@ -355,7 +356,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
};
assert!(!substs.has_escaping_regions());

let closure_trait_ref = self.closure_trait_ref(obligation, closure_def_id, substs);
// It is OK to call the unnormalized variant here - this is only
// reached for TyClosure: Fn inputs where the closure kind is
// still unknown, which should only occur in typeck where the
// closure type is already normalized.
let closure_trait_ref = self.closure_trait_ref_unnormalized(obligation,
closure_def_id,
substs);

match self.confirm_poly_trait_refs(obligation.cause.clone(),
obligation.predicate.to_poly_trait_ref(),
closure_trait_ref) {
Expand Down Expand Up @@ -2001,8 +2009,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}

ClosureCandidate(closure_def_id, substs) => {
try!(self.confirm_closure_candidate(obligation, closure_def_id, &substs));
Ok(VtableClosure(closure_def_id, substs))
let vtable_closure =
try!(self.confirm_closure_candidate(obligation, closure_def_id, &substs));
Ok(VtableClosure(vtable_closure))
}

BuiltinObjectCandidate => {
Expand Down Expand Up @@ -2343,24 +2352,33 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation: &TraitObligation<'tcx>,
closure_def_id: ast::DefId,
substs: &Substs<'tcx>)
-> Result<(),SelectionError<'tcx>>
-> Result<VtableClosureData<'tcx, PredicateObligation<'tcx>>,
SelectionError<'tcx>>
{
debug!("confirm_closure_candidate({},{},{})",
obligation.repr(self.tcx()),
closure_def_id.repr(self.tcx()),
substs.repr(self.tcx()));

let trait_ref = self.closure_trait_ref(obligation,
closure_def_id,
substs);
let Normalized {
value: trait_ref,
obligations
} = self.closure_trait_ref(obligation, closure_def_id, substs);

debug!("confirm_closure_candidate(closure_def_id={}, trait_ref={})",
debug!("confirm_closure_candidate(closure_def_id={}, trait_ref={}, obligations={})",
closure_def_id.repr(self.tcx()),
trait_ref.repr(self.tcx()));
trait_ref.repr(self.tcx()),
obligations.repr(self.tcx()));

try!(self.confirm_poly_trait_refs(obligation.cause.clone(),
obligation.predicate.to_poly_trait_ref(),
trait_ref));

self.confirm_poly_trait_refs(obligation.cause.clone(),
obligation.predicate.to_poly_trait_ref(),
trait_ref)
Ok(VtableClosureData {
closure_def_id: closure_def_id,
substs: substs.clone(),
nested: obligations
})
}

/// In the case of closure types and fn pointers,
Expand Down Expand Up @@ -2819,11 +2837,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}

fn closure_trait_ref(&self,
obligation: &TraitObligation<'tcx>,
closure_def_id: ast::DefId,
substs: &Substs<'tcx>)
-> ty::PolyTraitRef<'tcx>
fn closure_trait_ref_unnormalized(&mut self,
obligation: &TraitObligation<'tcx>,
closure_def_id: ast::DefId,
substs: &Substs<'tcx>)
-> ty::PolyTraitRef<'tcx>
{
let closure_type = self.closure_typer.closure_type(closure_def_id, substs);
let ty::Binder((trait_ref, _)) =
Expand All @@ -2832,7 +2850,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation.predicate.0.self_ty(), // (1)
&closure_type.sig,
util::TupleArgumentsFlag::No);

// (1) Feels icky to skip the binder here, but OTOH we know
// that the self-type is an unboxed closure type and hence is
// in fact unparameterized (or at least does not reference any
Expand All @@ -2842,6 +2859,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ty::Binder(trait_ref)
}

fn closure_trait_ref(&mut self,
obligation: &TraitObligation<'tcx>,
closure_def_id: ast::DefId,
substs: &Substs<'tcx>)
-> Normalized<'tcx, ty::PolyTraitRef<'tcx>>
{
let trait_ref = self.closure_trait_ref_unnormalized(
obligation, closure_def_id, substs);

// A closure signature can contain associated types which
// must be normalized.
normalize_with_depth(self,
obligation.cause.clone(),
obligation.recursion_depth+1,
&trait_ref)
}

/// Returns the obligations that are implied by instantiating an
/// impl or trait. The obligations are substituted and fully
/// normalized. This is used when confirming an impl or default
Expand Down
21 changes: 17 additions & 4 deletions src/librustc/middle/traits/util.rs
Expand Up @@ -308,6 +308,12 @@ impl<'tcx, N> fmt::Debug for VtableImplData<'tcx, N> {
}
}

impl<'tcx, N> fmt::Debug for super::VtableClosureData<'tcx, N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VtableClosure({:?})", self.closure_def_id)
}
}

impl<'tcx> fmt::Debug for super::VtableObjectData<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VtableObject(...)")
Expand Down Expand Up @@ -497,10 +503,8 @@ impl<'tcx, N:Repr<'tcx>> Repr<'tcx> for super::Vtable<'tcx, N> {
super::VtableDefaultImpl(ref t) =>
t.repr(tcx),

super::VtableClosure(ref d, ref s) =>
format!("VtableClosure({},{})",
d.repr(tcx),
s.repr(tcx)),
super::VtableClosure(ref d) =>
d.repr(tcx),

super::VtableFnPointer(ref d) =>
format!("VtableFnPointer({})",
Expand Down Expand Up @@ -529,6 +533,15 @@ impl<'tcx, N:Repr<'tcx>> Repr<'tcx> for super::VtableImplData<'tcx, N> {
}
}

impl<'tcx, N:Repr<'tcx>> Repr<'tcx> for super::VtableClosureData<'tcx, N> {
fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
format!("VtableClosure(closure_def_id={}, substs={}, nested={})",
self.closure_def_id.repr(tcx),
self.substs.repr(tcx),
self.nested.repr(tcx))
}
}

impl<'tcx, N:Repr<'tcx>> Repr<'tcx> for super::VtableBuiltinData<N> {
fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
format!("VtableBuiltin(nested={})",
Expand Down
14 changes: 12 additions & 2 deletions src/librustc/middle/ty_fold.rs
Expand Up @@ -479,6 +479,16 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableImplData<
}
}

impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableClosureData<'tcx, N> {
fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::VtableClosureData<'tcx, N> {
traits::VtableClosureData {
closure_def_id: self.closure_def_id,
substs: self.substs.fold_with(folder),
nested: self.nested.fold_with(folder),
}
}
}

impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableDefaultImplData<N> {
fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::VtableDefaultImplData<N> {
traits::VtableDefaultImplData {
Expand All @@ -501,8 +511,8 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N>
match *self {
traits::VtableImpl(ref v) => traits::VtableImpl(v.fold_with(folder)),
traits::VtableDefaultImpl(ref t) => traits::VtableDefaultImpl(t.fold_with(folder)),
traits::VtableClosure(d, ref s) => {
traits::VtableClosure(d, s.fold_with(folder))
traits::VtableClosure(ref d) => {
traits::VtableClosure(d.fold_with(folder))
}
traits::VtableFnPointer(ref d) => {
traits::VtableFnPointer(d.fold_with(folder))
Expand Down
12 changes: 8 additions & 4 deletions src/librustc_trans/trans/meth.rs
Expand Up @@ -359,13 +359,13 @@ fn trans_monomorphized_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,

Callee { bcx: bcx, data: Fn(llfn) }
}
traits::VtableClosure(closure_def_id, substs) => {
traits::VtableClosure(vtable_closure) => {
// The substitutions should have no type parameters remaining
// after passing through fulfill_obligation
let trait_closure_kind = bcx.tcx().lang_items.fn_trait_kind(trait_id).unwrap();
let llfn = closure::trans_closure_method(bcx.ccx(),
closure_def_id,
substs,
vtable_closure.closure_def_id,
vtable_closure.substs,
MethodCallKey(method_call),
bcx.fcx.param_substs,
trait_closure_kind);
Expand Down Expand Up @@ -716,7 +716,11 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
nested: _ }) => {
emit_vtable_methods(ccx, id, substs, param_substs).into_iter()
}
traits::VtableClosure(closure_def_id, substs) => {
traits::VtableClosure(
traits::VtableClosureData {
closure_def_id,
substs,
nested: _ }) => {
let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_ref.def_id()).unwrap();
let llfn = closure::trans_closure_method(ccx,
closure_def_id,
Expand Down

0 comments on commit 21fd312

Please sign in to comment.