Skip to content

Commit

Permalink
Allow anonymized return types to be declared in associated types.
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyb committed Aug 3, 2015
1 parent 608ab6b commit 250dc4e
Show file tree
Hide file tree
Showing 8 changed files with 1,089 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/librustc_typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1598,7 +1598,7 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>,
(None, _) => {
span_err!(this.tcx().sess, ast_ty.span, E0439,
"anonymized types are not allowed outside of function and \
impl method return types");
impl method return types and impl associated types");
this.tcx().types.err
}
(Some(_), Err(_)) => this.tcx().types.err,
Expand Down
1 change: 1 addition & 0 deletions src/librustc_typeck/check/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
expr.id,
&fn_sig,
decl,
None,
expr.id,
&*body,
fcx.inh);
Expand Down
77 changes: 62 additions & 15 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,13 @@ pub struct Inherited<'a, 'tcx: 'a> {

deferred_cast_checks: RefCell<Vec<cast::CastCheck<'tcx>>>,

// Anonymized types found in explicit return types and their
// associated fresh inference variable. Writeback resolves these
// variables to get the concrete type, which can be used to
// deanonymize TyAnon, after typeck is done with all functions.
anon_types: RefCell<Vec<(DefId, Ty<'tcx>)>>,
// Anonymized types found in explicit return types, their param
// space of definition (TypeSpace in associated types, FnSpace in
// function return types) and their respective fresh inference
// variable. Writeback resolves these variables to get the concrete
// type, which can be used to deanonymize TyAnon, after typeck is
// done processing all functions.
anon_types: RefCell<Vec<(DefId, ParamSpace, Ty<'tcx>)>>,
}

trait DeferredCallResolution<'tcx> {
Expand Down Expand Up @@ -364,6 +366,7 @@ fn static_inherited_fields<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,

struct CheckItemTypesVisitor<'a, 'tcx: 'a> { ccx: &'a CrateCtxt<'a, 'tcx> }
struct CheckItemBodiesVisitor<'a, 'tcx: 'a> { ccx: &'a CrateCtxt<'a, 'tcx> }
struct CheckAnonTypesVisitor<'a, 'tcx: 'a> { ccx: &'a CrateCtxt<'a, 'tcx> }

impl<'a, 'tcx> Visitor<'tcx> for CheckItemTypesVisitor<'a, 'tcx> {
fn visit_item(&mut self, i: &'tcx ast::Item) {
Expand All @@ -390,6 +393,22 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckItemBodiesVisitor<'a, 'tcx> {
}
}

impl<'a, 'tcx> Visitor<'tcx> for CheckAnonTypesVisitor<'a, 'tcx> {
fn visit_ty(&mut self, ty: &'tcx ast::Ty) {
if let ast::TyAnon(_) = ty.node {
if !self.ccx.tcx.tcache.borrow().contains_key(&local_def(ty.id)) {
span_err!(self.ccx.tcx.sess, ty.span, E0442,
"anonymized types must be used in a return \
type in the same impl");
}
}
visit::walk_ty(self, ty);
}

// Do not recurse into items.
fn visit_item(&mut self, _: &ast::Item) {}
}

pub fn check_item_types(ccx: &CrateCtxt) {
let krate = ccx.tcx.map.krate();
let mut visit = wf::CheckTypeWellFormedVisitor::new(ccx);
Expand Down Expand Up @@ -425,6 +444,7 @@ pub fn check_item_types(ccx: &CrateCtxt) {
}

fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
impl_id: Option<ast::NodeId>,
decl: &'tcx ast::FnDecl,
body: &'tcx ast::Block,
fn_id: ast::NodeId,
Expand All @@ -449,7 +469,7 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
&fn_sig);

let fcx = check_fn(ccx, fn_ty.unsafety, fn_id, &fn_sig,
decl, fn_id, body, &inh);
decl, impl_id, fn_id, body, &inh);

fcx.select_all_obligations_and_apply_defaults();
upvar::closure_analyze_fn(&fcx, fn_id, decl, body);
Expand Down Expand Up @@ -559,6 +579,7 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,
unsafety_id: ast::NodeId,
fn_sig: &ty::FnSig<'tcx>,
decl: &'tcx ast::FnDecl,
impl_id: Option<ast::NodeId>,
fn_id: ast::NodeId,
body: &'tcx ast::Block,
inherited: &'a Inherited<'a, 'tcx>)
Expand Down Expand Up @@ -632,7 +653,7 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,

check_block_with_expected(&fcx, body, match ret_ty {
ty::FnConverging(result_type) => {
ExpectHasType(fcx.instantiate_anon_types(result_type))
ExpectHasType(fcx.instantiate_anon_types(result_type, fn_id, impl_id))
}
ty::FnDiverging => NoExpectation
});
Expand Down Expand Up @@ -725,7 +746,7 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) {
ast::ItemFn(ref decl, _, _, _, _, ref body) => {
let fn_pty = ccx.tcx.lookup_item_type(ast_util::local_def(it.id));
let param_env = ParameterEnvironment::for_item(ccx.tcx, it.id);
check_bare_fn(ccx, &**decl, &**body, it.id, it.span, fn_pty.ty, param_env);
check_bare_fn(ccx, None, &**decl, &**body, it.id, it.span, fn_pty.ty, param_env);
}
ast::ItemImpl(_, _, _, _, _, ref impl_items) => {
debug!("ItemImpl {} with id {}", it.ident, it.id);
Expand All @@ -738,15 +759,23 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) {
check_const(ccx, impl_item.span, &*expr, impl_item.id)
}
ast::MethodImplItem(ref sig, ref body) => {
check_method_body(ccx, &impl_pty.generics, sig, body,
impl_item.id, impl_item.span);
check_method_body(ccx, Some(it.id), &impl_pty.generics, sig,
body, impl_item.id, impl_item.span);
}
ast::TypeImplItem(_) |
ast::MacImplItem(_) => {
// Nothing to do here.
}
}
}

// Check that if we had anonymized types in an associated type,
// they were assigned by one of the methods in this impl.
for impl_item in impl_items {
if let ast::TypeImplItem(ref ty) = impl_item.node {
CheckAnonTypesVisitor { ccx: ccx }.visit_ty(ty);
}
}
}
ast::ItemTrait(_, _, _, ref trait_items) => {
let trait_def = ccx.tcx.lookup_trait_def(local_def(it.id));
Expand All @@ -758,8 +787,8 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) {
ast::MethodTraitItem(ref sig, Some(ref body)) => {
check_trait_fn_not_const(ccx, trait_item.span, sig.constness);

check_method_body(ccx, &trait_def.generics, sig, body,
trait_item.id, trait_item.span);
check_method_body(ccx, None, &trait_def.generics, sig,
body, trait_item.id, trait_item.span);
}
ast::MethodTraitItem(ref sig, None) => {
check_trait_fn_not_const(ccx, trait_item.span, sig.constness);
Expand Down Expand Up @@ -842,6 +871,7 @@ fn check_trait_on_unimplemented<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
/// * `self_bound`: bound for the `Self` type parameter, if any
/// * `method`: the method definition
fn check_method_body<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
impl_id: Option<ast::NodeId>,
item_generics: &ty::Generics<'tcx>,
sig: &'tcx ast::MethodSig,
body: &'tcx ast::Block,
Expand All @@ -853,7 +883,7 @@ fn check_method_body<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
let fty = ccx.tcx.node_id_to_type(id);
debug!("check_method_body: fty={:?}", fty);

check_bare_fn(ccx, &sig.decl, body, id, span, fty, param_env);
check_bare_fn(ccx, impl_id, &sig.decl, body, id, span, fty, param_env);
}

fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
Expand Down Expand Up @@ -1363,15 +1393,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

/// Replace all anonymized types with fresh inference variables
/// and record them for writeback.
fn instantiate_anon_types(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
fn instantiate_anon_types(&self, ty: Ty<'tcx>, fn_id: ast::NodeId,
impl_id: Option<ast::NodeId>) -> Ty<'tcx> {
let tcx = self.tcx();
let mut anon_types = self.inh.anon_types.borrow_mut();
ty.fold_with(&mut BottomUpFolder {
tcx: tcx,
fldop: |ty| {
if let ty::TyAnon(def_id, _, ref data) = ty.sty {
// Do not instantiate an `impl Trait` type unless it was
// defined in the same function's return type or in an
// associated type of the impl, in the case of methods.
if def_id.krate != ast::LOCAL_CRATE {
return ty;
}

let parent = tcx.map.get_parent(def_id.node);
let param_space = if parent == fn_id {
subst::FnSpace
} else if Some(tcx.map.get_parent(parent)) == impl_id {
subst::TypeSpace
} else {
return ty;
};

let ty_var = self.infcx().next_ty_var();
anon_types.push((def_id, ty_var));
anon_types.push((def_id, param_space, ty_var));

let span = tcx.map.def_id_span(def_id, codemap::DUMMY_SP);
let cause = traits::ObligationCause::new(span, self.body_id,
Expand Down
40 changes: 32 additions & 8 deletions src/librustc_typeck/check/writeback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
use self::ResolveReason::*;

use astconv::AstConv;
use check::FnCtxt;
use check::{FnCtxt, demand};
use middle::pat_util;
use middle::subst::{FnSpace, TypeSpace};
use middle::ty::{self, Ty, MethodCall, MethodCallee};
use middle::ty_fold::{TypeFolder,TypeFoldable};
use middle::infer;
Expand Down Expand Up @@ -246,12 +247,35 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
return
}

for &(def_id, concrete_ty) in &*self.fcx.inh.anon_types.borrow() {
let concrete_ty = self.resolve(&concrete_ty, ResolvingAnonTy(def_id));
self.fcx.tcx().tcache.borrow_mut().insert(def_id, ty::TypeScheme {
ty: concrete_ty,
generics: ty::Generics::empty()
});
for &(def_id, param_space, concrete_ty) in &*self.fcx.inh.anon_types.borrow() {
let reason = ResolvingAnonTy(def_id);
let concrete_ty = self.resolve(&concrete_ty, reason);
let old_ty = self.fcx.tcx().tcache.borrow_mut().get(&def_id).as_ref().map(|t| t.ty);

if let Some(old_ty) = old_ty {
// Ensure all anonymized type assignments agree with eachother.
demand::eqtype(self.fcx, reason.span(self.fcx.tcx()), old_ty, concrete_ty);
} else {
// Do not let method type parameters escape into anonymized types
// defined inside an associated type, which is only parametrized
// on the impl type parameters.
if param_space == TypeSpace {
for ty in concrete_ty.walk() {
if let ty::TyParam(ty::ParamTy { space: FnSpace, .. }) = ty.sty {
let span = reason.span(self.fcx.tcx());
span_err!(self.fcx.tcx().sess, span, E0441,
"method type parameter `{}` cannot be used in an \
anonymized type defined in an associated type",
ty);
}
}
}

self.fcx.tcx().tcache.borrow_mut().insert(def_id, ty::TypeScheme {
ty: concrete_ty,
generics: ty::Generics::empty()
});
}
}
}

Expand Down Expand Up @@ -441,7 +465,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {

ResolvingAnonTy(_) => {
let span = self.reason.span(self.tcx);
span_err!(self.tcx.sess, span, E0399,
span_err!(self.tcx.sess, span, E0440,
"cannot determine a concrete type for this anonymized type")
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -916,7 +916,9 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) {
"associated types are not allowed in inherent impls");
}

let typ = ccx.icx(&ty_predicates).to_ty(&ExplicitRscope, ty);
let anon_scope = Some(AnonTypeScope::new(&ty_generics));
let rscope = MaybeWithAnonTypes::new(ExplicitRscope, anon_scope);
let typ = ccx.icx(&ty_predicates).to_ty(&rscope, ty);

convert_associated_type(ccx, ImplContainer(local_def(it.id)),
impl_item.ident, impl_item.id, impl_item.vis,
Expand Down
7 changes: 5 additions & 2 deletions src/librustc_typeck/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2474,6 +2474,9 @@ register_diagnostics! {
// type `{}` was overridden
E0436, // functional record update requires a struct
E0439, // anonymized types are not allowed outside of function and
// impl method return types
E0440 // cannot determine a concrete type for this anonymized type
// impl method return types and impl associated types
E0440, // cannot determine a concrete type for this anonymized type
E0441, // method type parameter `{}` cannot be used in an anonymized type
// defined in an associated type
E0442 // anonymized types must be used in a return type in the same impl
}
Loading

0 comments on commit 250dc4e

Please sign in to comment.