From 37d6ca9e3ea18c1610d9fd3546644ba105e2aa4b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 15 Nov 2013 17:04:01 -0500 Subject: [PATCH 1/2] Make trait lifetime parameters early bound in static fn type. This is related to #5121. Fixes #10391. --- src/librustc/middle/typeck/check/vtable.rs | 1 - src/librustc/middle/typeck/collect.rs | 30 ++++++++++---- ...egions-early-bound-lifetime-in-assoc-fn.rs | 39 +++++++++++++++++++ 3 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 src/test/run-pass/regions-early-bound-lifetime-in-assoc-fn.rs diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index 996157df2e299..0224d1d4f0b04 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -94,7 +94,6 @@ fn lookup_vtables(vcx: &VtableContext, location_info, type_param_defs.repr(vcx.tcx()), substs.repr(vcx.tcx())); - let _i = indenter(); // We do this backwards for reasons discussed above. assert_eq!(substs.tps.len(), type_param_defs.len()); diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 36ed9f94fb71a..4aab9759dff21 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -236,13 +236,18 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, trait_ty_generics: &ty::Generics) { // If declaration is // - // trait<'a,'b,'c,A,B,C> { + // trait Trait<'a,'b,'c,A,B,C> { // fn foo<'d,'e,'f,D,E,F>(...) -> Self; // } // // and we will create a function like // - // fn foo<'a,'b,'c,'d,'e,'f,A',B',C',D',E',F',G'>(...) -> D' {} + // fn foo<'a,'b,'c, // First the lifetime params from trait + // 'd,'e,'f, // Then lifetime params from `foo()` + // A',B',C', // Then type params from trait + // D':Trait<'a,'b,'c,A',B',C'>, // Then this sucker + // E',F',G' // Then type params from `foo()`, offset by 1 + // >(...) -> D' {} // // Note that `Self` is replaced with an explicit type // parameter D' that is sandwiched in between the trait params @@ -251,6 +256,11 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, // parameters are mapped from D, E, F to E', F', and G'). The // choice of this ordering is somewhat arbitrary. // + // Note also that the bound for `D'` is `Trait<'a,'b,'c,A',B',C'>`. + // This implies that the lifetime parameters that were inherited + // from the trait (i.e., `'a`, `'b`, and `'c`) all must be early + // bound, since they appear in a trait bound. + // // Also, this system is rather a hack that should be replaced // with a more uniform treatment of Self (which is partly // underway). @@ -280,13 +290,17 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, }; // Convert the regions 'a, 'b, 'c defined on the trait into - // bound regions on the fn. - let rps_from_trait = trait_ty_generics.region_param_defs.iter().map(|d| { - ty::ReLateBound(m.fty.sig.binder_id, - ty::BrNamed(d.def_id, d.ident)) - }).collect(); + // bound regions on the fn. Note that because these appear in the + // bound for `Self` they must be early bound. + let new_early_region_param_defs = trait_ty_generics.region_param_defs; + let rps_from_trait = + trait_ty_generics.region_param_defs.iter(). + enumerate(). + map(|(index,d)| ty::ReEarlyBound(d.def_id.node, index, d.ident)). + collect(); // build up the substitution from + // 'a,'b,'c => 'a,'b,'c // A,B,C => A',B',C' // Self => D' // D,E,F => E',F',G' @@ -336,7 +350,7 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, ty_param_bounds_and_ty { generics: ty::Generics { type_param_defs: @new_type_param_defs, - region_param_defs: @[], // fn items + region_param_defs: new_early_region_param_defs }, ty: ty }); diff --git a/src/test/run-pass/regions-early-bound-lifetime-in-assoc-fn.rs b/src/test/run-pass/regions-early-bound-lifetime-in-assoc-fn.rs new file mode 100644 index 0000000000000..8dddbfad981e6 --- /dev/null +++ b/src/test/run-pass/regions-early-bound-lifetime-in-assoc-fn.rs @@ -0,0 +1,39 @@ +// Copyright 2013 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 compile calls to associated fns like +// `decode()` where the bound on the `Self` parameter references a +// lifetime parameter of the trait. This example indicates why trait +// lifetime parameters must be early bound in the type of the +// associated item. + +pub enum Value<'v> { + A(&'v str), + B, +} + +pub trait Decoder<'v> { + fn read(&mut self) -> Value<'v>; +} + +pub trait Decodable<'v, D: Decoder<'v>> { + fn decode(d: &mut D) -> Self; +} + +impl<'v, D: Decoder<'v>> Decodable<'v, D> for () { + fn decode(d: &mut D) -> () { + match d.read() { + A(*) => (), + B => Decodable::decode(d), + } + } +} + +fn main() { } From b2d11dcf8241776a5ba36604c7a3aaa432c301ae Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 18 Nov 2013 18:42:32 -0500 Subject: [PATCH 2/2] Distinguish early- from late-bound lifetime parameters on the basis of whether they appear in a trait bound. Fixes #5121. --- src/librustc/middle/resolve_lifetime.rs | 203 ++++++++++++++---- src/librustc/middle/ty.rs | 35 ++- src/librustc/middle/typeck/check/method.rs | 31 +-- src/librustc/middle/typeck/check/mod.rs | 3 +- src/librustc/middle/typeck/check/vtable.rs | 1 + src/librustc/middle/typeck/collect.rs | 91 ++++---- .../regions-early-bound-error-method.rs | 35 +++ .../compile-fail/regions-early-bound-error.rs | 33 +++ ...egions-early-bound-used-in-bound-method.rs | 37 ++++ .../regions-early-bound-used-in-bound.rs | 35 +++ .../regions-early-bound-used-in-type-param.rs | 35 +++ 11 files changed, 446 insertions(+), 93 deletions(-) create mode 100644 src/test/compile-fail/regions-early-bound-error-method.rs create mode 100644 src/test/compile-fail/regions-early-bound-error.rs create mode 100644 src/test/run-pass/regions-early-bound-used-in-bound-method.rs create mode 100644 src/test/run-pass/regions-early-bound-used-in-bound.rs create mode 100644 src/test/run-pass/regions-early-bound-used-in-type-param.rs diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index b21720f2e30a0..bb6bf89d1e3a5 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -21,6 +21,7 @@ use driver::session; use std::hashmap::HashMap; use syntax::ast; use syntax::codemap::Span; +use syntax::opt_vec; use syntax::opt_vec::OptVec; use syntax::parse::token::special_idents; use syntax::print::pprust::{lifetime_to_str}; @@ -37,12 +38,14 @@ struct LifetimeContext { } enum ScopeChain<'self> { - ItemScope(&'self OptVec), - FnScope(ast::NodeId, &'self OptVec, &'self ScopeChain<'self>), - BlockScope(ast::NodeId, &'self ScopeChain<'self>), + EarlyScope(uint, &'self OptVec, Scope<'self>), + LateScope(ast::NodeId, &'self OptVec, Scope<'self>), + BlockScope(ast::NodeId, Scope<'self>), RootScope } +type Scope<'self> = &'self ScopeChain<'self>; + pub fn crate(sess: session::Session, crate: &ast::Crate) -> @mut NamedRegionMap { @@ -55,10 +58,10 @@ pub fn crate(sess: session::Session, ctxt.named_region_map } -impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext { +impl<'self> Visitor> for LifetimeContext { fn visit_item(&mut self, item: @ast::item, - _: &'self ScopeChain<'self>) { + _: Scope<'self>) { let scope = match item.node { ast::item_fn(*) | // fn lifetimes get added in visit_fn below ast::item_mod(*) | @@ -73,7 +76,7 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext { ast::item_impl(ref generics, _, _, _) | ast::item_trait(ref generics, _, _) => { self.check_lifetime_names(&generics.lifetimes); - ItemScope(&generics.lifetimes) + EarlyScope(0, &generics.lifetimes, &RootScope) } }; debug!("entering scope {:?}", scope); @@ -87,33 +90,32 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext { b: &ast::Block, s: Span, n: ast::NodeId, - scope: &'self ScopeChain<'self>) { + scope: Scope<'self>) { match *fk { visit::fk_item_fn(_, generics, _, _) | visit::fk_method(_, generics, _) => { - let scope1 = FnScope(n, &generics.lifetimes, scope); - self.check_lifetime_names(&generics.lifetimes); - debug!("pushing fn scope id={} due to item/method", n); - visit::walk_fn(self, fk, fd, b, s, n, &scope1); - debug!("popping fn scope id={} due to item/method", n); + self.visit_fn_decl( + n, generics, scope, + |this, scope1| visit::walk_fn(this, fk, fd, b, + s, n, scope1)) } visit::fk_anon(*) | visit::fk_fn_block(*) => { - visit::walk_fn(self, fk, fd, b, s, n, scope); + visit::walk_fn(self, fk, fd, b, s, n, scope) } } } fn visit_ty(&mut self, ty: &ast::Ty, - scope: &'self ScopeChain<'self>) { + scope: Scope<'self>) { match ty.node { ast::ty_closure(@ast::TyClosure { lifetimes: ref lifetimes, _ }) | ast::ty_bare_fn(@ast::TyBareFn { lifetimes: ref lifetimes, _ }) => { - let scope1 = FnScope(ty.id, lifetimes, scope); + let scope1 = LateScope(ty.id, lifetimes, scope); self.check_lifetime_names(lifetimes); - debug!("pushing fn scope id={} due to type", ty.id); + debug!("pushing fn ty scope id={} due to type", ty.id); visit::walk_ty(self, ty, &scope1); - debug!("popping fn scope id={} due to type", ty.id); + debug!("popping fn ty scope id={} due to type", ty.id); } _ => { visit::walk_ty(self, ty, scope); @@ -123,17 +125,15 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext { fn visit_ty_method(&mut self, m: &ast::TypeMethod, - scope: &'self ScopeChain<'self>) { - let scope1 = FnScope(m.id, &m.generics.lifetimes, scope); - self.check_lifetime_names(&m.generics.lifetimes); - debug!("pushing fn scope id={} due to ty_method", m.id); - visit::walk_ty_method(self, m, &scope1); - debug!("popping fn scope id={} due to ty_method", m.id); + scope: Scope<'self>) { + self.visit_fn_decl( + m.id, &m.generics, scope, + |this, scope1| visit::walk_ty_method(this, m, scope1)) } fn visit_block(&mut self, b: &ast::Block, - scope: &'self ScopeChain<'self>) { + scope: Scope<'self>) { let scope1 = BlockScope(b.id, scope); debug!("pushing block scope {}", b.id); visit::walk_block(self, b, &scope1); @@ -142,7 +142,7 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext { fn visit_lifetime_ref(&mut self, lifetime_ref: &ast::Lifetime, - scope: &'self ScopeChain<'self>) { + scope: Scope<'self>) { if lifetime_ref.ident == special_idents::statik { self.insert_lifetime(lifetime_ref, ast::DefStaticRegion); return; @@ -151,7 +151,93 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext { } } +impl<'self> ScopeChain<'self> { + fn count_early_params(&self) -> uint { + /*! + * Counts the number of early parameters that are in scope. + * Used when checking methods so that we assign the early + * lifetime parameters declared on the method indices that + * come after those from the type (e.g., if there is something + * like `impl<'a> Foo { ... fn bar<'b>(...) }` then `'a` would + * have index 0 and `'b` would have index 1. + */ + + match *self { + RootScope => 0, + EarlyScope(base, lifetimes, _) => base + lifetimes.len(), + LateScope(_, _, s) => s.count_early_params(), + BlockScope(_, _) => 0, + } + } +} + impl LifetimeContext { + fn visit_fn_decl(&mut self, + n: ast::NodeId, + generics: &ast::Generics, + scope: Scope, + walk: |&mut LifetimeContext, Scope|) { + /*! + * Handles visiting fns and methods. These are a bit + * complicated because we must distinguish early- vs late-bound + * lifetime parameters. We do this by checking which lifetimes + * appear within type bounds; those are early bound lifetimes, + * and the rest are late bound. + * + * For example: + * + * fn foo<'a,'b,'c,T:Trait<'b>>(...) + * + * Here `'a` and `'c` are late bound but `'b` is early + * bound. Note that early- and late-bound lifetimes may be + * interspersed together. + * + * If early bound lifetimes are present, we separate them into + * their own list (and likewise for late bound). They will be + * numbered sequentially, starting from the lowest index that + * is already in scope (for a fn item, that will be 0, but for + * a method it might not be). Late bound lifetimes are + * resolved by name and associated with a binder id (`n`), so + * the ordering is not important there. + */ + + self.check_lifetime_names(&generics.lifetimes); + + let early_count = scope.count_early_params(); + let referenced_idents = + free_lifetimes(&generics.ty_params); + debug!("pushing fn scope id={} due to item/method \ + referenced_idents={:?} \ + early_count={}", + n, + referenced_idents.map(|&i| self.sess.str_of(i)), + early_count); + if referenced_idents.is_empty() { + let scope1 = LateScope(n, &generics.lifetimes, scope); + + walk(self, &scope1) + } else { + let early: OptVec = + generics.lifetimes.iter() + .filter(|l| referenced_idents.iter().any(|i| i == &l.ident)) + .map(|l| *l) + .collect(); + let scope1 = EarlyScope(early_count, &early, scope); + debug!("early names = {:?}", + early.map(|l| self.sess.str_of(l.ident))); + + let late: OptVec = + generics.lifetimes.iter() + .filter(|l| !referenced_idents.iter().any(|i| i == &l.ident)) + .map(|l| *l) + .collect(); + let scope2 = LateScope(n, &late, &scope1); + + walk(self, &scope2) + } + debug!("popping fn scope id={} due to item/method", n); + } + fn resolve_lifetime_ref(&self, lifetime_ref: &ast::Lifetime, scope: &ScopeChain) { @@ -173,23 +259,25 @@ impl LifetimeContext { break; } - ItemScope(lifetimes) => { + EarlyScope(base, lifetimes, s) => { match search_lifetimes(lifetimes, lifetime_ref) { - Some((index, decl_id)) => { + Some((offset, decl_id)) => { + let index = base + offset; let def = ast::DefEarlyBoundRegion(index, decl_id); self.insert_lifetime(lifetime_ref, def); return; } None => { - break; + depth += 1; + scope = s; } } } - FnScope(id, lifetimes, s) => { + LateScope(binder_id, lifetimes, s) => { match search_lifetimes(lifetimes, lifetime_ref) { Some((_index, decl_id)) => { - let def = ast::DefLateBoundRegion(id, depth, decl_id); + let def = ast::DefLateBoundRegion(binder_id, depth, decl_id); self.insert_lifetime(lifetime_ref, def); return; } @@ -227,12 +315,7 @@ impl LifetimeContext { break; } - ItemScope(lifetimes) => { - search_result = search_lifetimes(lifetimes, lifetime_ref); - break; - } - - FnScope(_, lifetimes, s) => { + EarlyScope(_, lifetimes, s) | LateScope(_, lifetimes, s) => { search_result = search_lifetimes(lifetimes, lifetime_ref); if search_result.is_some() { break; @@ -319,3 +402,51 @@ fn search_lifetimes(lifetimes: &OptVec, } return None; } + +/////////////////////////////////////////////////////////////////////////// + +pub fn early_bound_lifetimes<'a>(generics: &'a ast::Generics) -> OptVec { + let referenced_idents = free_lifetimes(&generics.ty_params); + if referenced_idents.is_empty() { + return opt_vec::Empty; + } + + generics.lifetimes.iter() + .filter(|l| referenced_idents.iter().any(|i| i == &l.ident)) + .map(|l| *l) + .collect() +} + +pub fn free_lifetimes(ty_params: &OptVec) -> OptVec { + /*! + * Gathers up and returns the names of any lifetimes that appear + * free in `ty_params`. Of course, right now, all lifetimes appear + * free, since we don't have any binders in type parameter + * declarations, but I just to be forwards compatible for future + * extensions with my terminology. =) + */ + + let mut collector = FreeLifetimeCollector { names: opt_vec::Empty }; + for ty_param in ty_params.iter() { + visit::walk_ty_param_bounds(&mut collector, &ty_param.bounds, ()); + } + return collector.names; + + struct FreeLifetimeCollector { + names: OptVec, + } + + impl Visitor<()> for FreeLifetimeCollector { + fn visit_ty(&mut self, t:&ast::Ty, _:()) { + // for some weird reason visitor doesn't descend into + // types by default + visit::walk_ty(self, t, ()); + } + + fn visit_lifetime_ref(&mut self, + lifetime_ref: &ast::Lifetime, + _: ()) { + self.names.push(lifetime_ref.ident); + } + } +} diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index bb92ececcbf6f..40c6ac3452bef 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -882,7 +882,8 @@ pub struct Generics { /// List of type parameters declared on the item. type_param_defs: @~[TypeParameterDef], - /// List of region parameters declared on the item. + /// List of region parameters declared on the item. In the + /// case of a fn or method, only includes *early-bound* lifetimes. region_param_defs: @[RegionParameterDef], } @@ -4739,6 +4740,7 @@ pub fn construct_parameter_environment( item_type_params: &[TypeParameterDef], method_type_params: &[TypeParameterDef], item_region_params: &[RegionParameterDef], + method_region_params: &[RegionParameterDef], free_id: ast::NodeId) -> ParameterEnvironment { @@ -4766,11 +4768,23 @@ pub fn construct_parameter_environment( }); // map bound 'a => free 'a - let region_params = item_region_params.iter(). - map(|r| ty::ReFree(ty::FreeRegion { - scope_id: free_id, - bound_region: ty::BrNamed(r.def_id, r.ident)})). - collect(); + let region_params = { + fn push_region_params(accum: OptVec, + free_id: ast::NodeId, + region_params: &[RegionParameterDef]) + -> OptVec { + let mut accum = accum; + for r in region_params.iter() { + accum.push( + ty::ReFree(ty::FreeRegion { + scope_id: free_id, + bound_region: ty::BrNamed(r.def_id, r.ident)})); + } + accum + } + let t = push_region_params(opt_vec::Empty, free_id, item_region_params); + push_region_params(t, free_id, method_region_params) + }; let free_substs = substs { self_ty: self_ty, @@ -4792,6 +4806,15 @@ pub fn construct_parameter_environment( } }); + debug!("construct_parameter_environment: free_id={} \ + free_substs={} \ + self_param_bound={} \ + type_param_bounds={}", + free_id, + free_substs.repr(tcx), + self_bound_substd.repr(tcx), + type_param_bounds_substd.repr(tcx)); + ty::ParameterEnvironment { free_substs: free_substs, self_param_bound: self_bound_substd, diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 870b29882fd6e..1778003491481 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -395,7 +395,7 @@ impl<'self> LookupContext<'self> { substs: &ty::substs) { debug!("push_inherent_candidates_from_object(did={}, substs={})", self.did_to_str(did), - substs_to_str(self.tcx(), substs)); + substs.repr(self.tcx())); let _indenter = indenter(); // It is illegal to invoke a method on a trait instance that @@ -507,7 +507,8 @@ impl<'self> LookupContext<'self> { let cand = mk_cand(bound_trait_ref, method, pos, this_bound_idx); - debug!("pushing inherent candidate for param: {:?}", cand); + debug!("pushing inherent candidate for param: {}", + cand.repr(self.tcx())); self.inherent_candidates.push(cand); } None => { @@ -877,8 +878,9 @@ impl<'self> LookupContext<'self> { let mut j = i + 1; while j < candidates.len() { let candidate_b = &candidates[j]; - debug!("attempting to merge {:?} and {:?}", - candidate_a, candidate_b); + debug!("attempting to merge {} and {}", + candidate_a.repr(self.tcx()), + candidate_b.repr(self.tcx())); let candidates_same = match (&candidate_a.origin, &candidate_b.origin) { (&method_param(ref p1), &method_param(ref p2)) => { @@ -919,7 +921,7 @@ impl<'self> LookupContext<'self> { debug!("confirm_candidate(expr={}, candidate={}, fty={})", self.expr.repr(tcx), - self.cand_to_str(candidate), + candidate.repr(self.tcx()), self.ty_to_str(fty)); self.enforce_object_limitations(fty, candidate); @@ -1171,7 +1173,7 @@ impl<'self> LookupContext<'self> { // candidate method's `self_ty`. fn is_relevant(&self, rcvr_ty: ty::t, candidate: &Candidate) -> bool { debug!("is_relevant(rcvr_ty={}, candidate={})", - self.ty_to_str(rcvr_ty), self.cand_to_str(candidate)); + self.ty_to_str(rcvr_ty), candidate.repr(self.tcx())); return match candidate.method_ty.explicit_self { sty_static => { @@ -1332,13 +1334,6 @@ impl<'self> LookupContext<'self> { self.fcx.infcx().ty_to_str(t) } - fn cand_to_str(&self, cand: &Candidate) -> ~str { - format!("Candidate(rcvr_ty={}, rcvr_substs={}, origin={:?})", - cand.rcvr_match_condition.repr(self.tcx()), - ty::substs_to_str(self.tcx(), &cand.rcvr_substs), - cand.origin) - } - fn did_to_str(&self, did: DefId) -> ~str { ty::item_path_str(self.tcx(), did) } @@ -1367,3 +1362,13 @@ impl Repr for RcvrMatchCondition { } } } + +impl Repr for Candidate { + fn repr(&self, tcx: ty::ctxt) -> ~str { + format!("Candidate(rcvr_ty={}, rcvr_substs={}, origin={:?})", + self.rcvr_match_condition.repr(tcx), + self.rcvr_substs.repr(tcx), + self.origin) + } +} + diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index f567a880db527..49b5698eb77d7 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -583,12 +583,12 @@ pub fn check_item(ccx: @mut CrateCtxt, it: @ast::item) { ast::item_fn(ref decl, _, _, _, ref body) => { let fn_tpt = ty::lookup_item_type(ccx.tcx, ast_util::local_def(it.id)); - // FIXME(#5121) -- won't work for lifetimes that appear in type bounds let param_env = ty::construct_parameter_environment( ccx.tcx, None, *fn_tpt.generics.type_param_defs, [], + fn_tpt.generics.region_param_defs, [], body.id); @@ -700,6 +700,7 @@ fn check_method_body(ccx: @mut CrateCtxt, *item_generics.type_param_defs, *method_generics.type_param_defs, item_generics.region_param_defs, + method_generics.region_param_defs, method.body.id); // Compute the self type and fty from point of view of inside fn diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index 0224d1d4f0b04..34268127a54cc 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -732,6 +732,7 @@ pub fn resolve_impl(ccx: @mut CrateCtxt, *impl_generics.type_param_defs, [], impl_generics.region_param_defs, + [], impl_item.id); let impl_trait_ref = @impl_trait_ref.subst(ccx.tcx, ¶m_env.free_substs); diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 4aab9759dff21..436f1ce20a5b6 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -32,6 +32,7 @@ are represented as `ty_param()` instances. use metadata::csearch; +use middle::resolve_lifetime; use middle::ty::{ImplContainer, MethodContainer, TraitContainer, substs}; use middle::ty::{ty_param_bounds_and_ty}; use middle::ty; @@ -43,6 +44,7 @@ use middle::typeck::rscope::*; use middle::typeck::{CrateCtxt, lookup_def_tcx, no_params, write_ty_to_tcx}; use util::ppaux; +use std::at_vec; use std::vec; use syntax::abi::AbiSet; use syntax::ast::{RegionTyParamBound, TraitTyParamBound}; @@ -156,7 +158,7 @@ pub fn get_enum_variant_types(ccx: &CrateCtxt, ast::struct_variant_kind(struct_def) => { let tpt = ty_param_bounds_and_ty { - generics: ty_generics(ccx, generics, 0), + generics: ty_generics_for_type(ccx, generics), ty: enum_ty }; @@ -169,7 +171,7 @@ pub fn get_enum_variant_types(ccx: &CrateCtxt, }; let tpt = ty_param_bounds_and_ty { - generics: ty_generics(ccx, generics, 0), + generics: ty_generics_for_type(ccx, generics), ty: result_ty }; tcx.tcache.insert(local_def(variant.node.id), tpt); @@ -187,7 +189,7 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, _ }, _) => { let trait_ty_generics = - ty_generics(ccx, generics, 0); + ty_generics_for_type(ccx, generics); // For each method, construct a suitable ty::Method and // store it into the `tcx.methods` table: @@ -371,10 +373,12 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, astconv::ty_of_method(this, *m_id, *m_purity, trait_self_ty, *m_explicit_self, m_decl); let num_trait_type_params = trait_generics.type_param_defs.len(); + let ty_generics = + ty_generics_for_fn_or_method(this, m_generics, + num_trait_type_params); ty::Method::new( *m_ident, - // FIXME(#5121) -- distinguish early vs late lifetime params - ty_generics(this, m_generics, num_trait_type_params), + ty_generics, transformed_self_ty, fty, m_explicit_self.node, @@ -449,7 +453,8 @@ fn convert_methods(ccx: &CrateCtxt, let tcx = ccx.tcx; for m in ms.iter() { let num_rcvr_ty_params = rcvr_ty_generics.type_param_defs.len(); - let m_ty_generics = ty_generics(ccx, &m.generics, num_rcvr_ty_params); + let m_ty_generics = + ty_generics_for_fn_or_method(ccx, &m.generics, num_rcvr_ty_params); let mty = @ty_of_method(ccx, container, *m, @@ -471,7 +476,9 @@ fn convert_methods(ccx: &CrateCtxt, type_param_defs: @vec::append( (*rcvr_ty_generics.type_param_defs).clone(), *m_ty_generics.type_param_defs), - region_param_defs: rcvr_ty_generics.region_param_defs, + region_param_defs: at_vec::append( + rcvr_ty_generics.region_param_defs, + m_ty_generics.region_param_defs), }, ty: fty }); @@ -496,20 +503,18 @@ fn convert_methods(ccx: &CrateCtxt, // { fn foo(); }` is public, but private in `priv impl { fn // foo(); }`). let method_vis = m.vis.inherit_from(rcvr_visibility); - let num_rcvr_type_params = rcvr_generics.ty_params.len(); - ty::Method::new( - m.ident, - // FIXME(#5121) -- distinguish early vs late lifetime params - ty_generics(ccx, &m.generics, num_rcvr_type_params), - transformed_self_ty, - fty, - m.explicit_self.node, - method_vis, - local_def(m.id), - container, - None - ) + let m_ty_generics = + ty_generics_for_fn_or_method(ccx, &m.generics, num_rcvr_type_params); + ty::Method::new(m.ident, + m_ty_generics, + transformed_self_ty, + fty, + m.explicit_self.node, + method_vis, + local_def(m.id), + container, + None) } } @@ -543,7 +548,7 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::item) { generics); } ast::item_impl(ref generics, ref opt_trait_ref, ref selfty, ref ms) => { - let i_ty_generics = ty_generics(ccx, generics, 0); + let i_ty_generics = ty_generics_for_type(ccx, generics); let selfty = ccx.to_ty(&ExplicitRscope, selfty); write_ty_to_tcx(tcx, it.id, selfty); tcx.tcache.insert(local_def(it.id), @@ -741,7 +746,7 @@ pub fn trait_def_of_item(ccx: &CrateCtxt, it: &ast::item) -> @ty::TraitDef { match it.node { ast::item_trait(ref generics, ref supertraits, _) => { let self_ty = ty::mk_self(tcx, def_id); - let ty_generics = ty_generics(ccx, generics, 0); + let ty_generics = ty_generics_for_type(ccx, generics); let substs = mk_item_substs(ccx, &ty_generics, Some(self_ty)); let bounds = ensure_supertraits(ccx, it.id, it.span, *supertraits); let trait_ref = @ty::TraitRef {def_id: def_id, @@ -776,17 +781,14 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: &ast::item) return tpt; } ast::item_fn(ref decl, purity, abi, ref generics, _) => { - let ty_generics = ty_generics(ccx, generics, 0); + let ty_generics = ty_generics_for_fn_or_method(ccx, generics, 0); let tofd = astconv::ty_of_bare_fn(ccx, it.id, purity, abi, decl); let tpt = ty_param_bounds_and_ty { - generics: ty::Generics { - type_param_defs: ty_generics.type_param_defs, - region_param_defs: @[], - }, + generics: ty_generics, ty: ty::mk_bare_fn(ccx.tcx, tofd) }; debug!("type of {} (id {}) is {}", @@ -805,7 +807,7 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: &ast::item) let tpt = { let ty = ccx.to_ty(&ExplicitRscope, t); ty_param_bounds_and_ty { - generics: ty_generics(ccx, generics, 0), + generics: ty_generics_for_type(ccx, generics), ty: ty } }; @@ -815,7 +817,7 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: &ast::item) } ast::item_enum(_, ref generics) => { // Create a new generic polytype. - let ty_generics = ty_generics(ccx, generics, 0); + let ty_generics = ty_generics_for_type(ccx, generics); let substs = mk_item_substs(ccx, &ty_generics, None); let t = ty::mk_enum(tcx, local_def(it.id), substs); let tpt = ty_param_bounds_and_ty { @@ -831,7 +833,7 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: &ast::item) format!("Invoked ty_of_item on trait")); } ast::item_struct(_, ref generics) => { - let ty_generics = ty_generics(ccx, generics, 0); + let ty_generics = ty_generics_for_type(ccx, generics); let substs = mk_item_substs(ccx, &ty_generics, None); let t = ty::mk_struct(tcx, local_def(it.id), substs); let tpt = ty_param_bounds_and_ty { @@ -871,19 +873,34 @@ pub fn ty_of_foreign_item(ccx: &CrateCtxt, } } +pub fn ty_generics_for_type(ccx: &CrateCtxt, + generics: &ast::Generics) + -> ty::Generics { + ty_generics(ccx, &generics.lifetimes, &generics.ty_params, 0) +} + +pub fn ty_generics_for_fn_or_method(ccx: &CrateCtxt, + generics: &ast::Generics, + base_index: uint) + -> ty::Generics { + let early_lifetimes = resolve_lifetime::early_bound_lifetimes(generics); + ty_generics(ccx, &early_lifetimes, &generics.ty_params, base_index) +} + pub fn ty_generics(ccx: &CrateCtxt, - generics: &ast::Generics, - base_index: uint) -> ty::Generics { + lifetimes: &OptVec, + ty_params: &OptVec, + base_ty_index: uint) -> ty::Generics { return ty::Generics { - region_param_defs: generics.lifetimes.iter().map(|l| { + region_param_defs: lifetimes.iter().map(|l| { ty::RegionParameterDef { ident: l.ident, def_id: local_def(l.id) } }).collect(), - type_param_defs: @generics.ty_params.mapi_to_vec(|offset, param| { + type_param_defs: @ty_params.mapi_to_vec(|offset, param| { match ccx.tcx.ty_param_defs.find(¶m.id) { Some(&def) => def, None => { - let param_ty = ty::param_ty {idx: base_index + offset, + let param_ty = ty::param_ty {idx: base_ty_index + offset, def_id: local_def(param.id)}; let bounds = @compute_bounds(ccx, param_ty, ¶m.bounds); let def = ty::TypeParameterDef { @@ -945,8 +962,8 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt, def_id: ast::DefId, ast_generics: &ast::Generics, abis: AbiSet) - -> ty::ty_param_bounds_and_ty { - let ty_generics = ty_generics(ccx, ast_generics, 0); + -> ty::ty_param_bounds_and_ty { + let ty_generics = ty_generics_for_fn_or_method(ccx, ast_generics, 0); let rb = BindingRscope::new(def_id.node); let input_tys = decl.inputs.map(|a| ty_of_arg(ccx, &rb, a, None) ); let output_ty = ast_ty_to_ty(ccx, &rb, &decl.output); diff --git a/src/test/compile-fail/regions-early-bound-error-method.rs b/src/test/compile-fail/regions-early-bound-error-method.rs new file mode 100644 index 0000000000000..21b32aa028cb7 --- /dev/null +++ b/src/test/compile-fail/regions-early-bound-error-method.rs @@ -0,0 +1,35 @@ +// 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. + +// Tests that you can use a fn lifetime parameter as part of +// the value for a type parameter in a bound. + +trait GetRef<'a> { + fn get(&self) -> &'a int; +} + +struct Box<'a> { + t: &'a int +} + +impl<'a> GetRef<'a> for Box<'a> { + fn get(&self) -> &'a int { + self.t + } +} + +impl<'a> Box<'a> { + fn or<'b,G:GetRef<'b>>(&self, g2: G) -> &'a int { + g2.get() + } +} + +fn main() { +} diff --git a/src/test/compile-fail/regions-early-bound-error.rs b/src/test/compile-fail/regions-early-bound-error.rs new file mode 100644 index 0000000000000..9cff4849cbeb2 --- /dev/null +++ b/src/test/compile-fail/regions-early-bound-error.rs @@ -0,0 +1,33 @@ +// 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. + +// Tests that you can use a fn lifetime parameter as part of +// the value for a type parameter in a bound. + +trait GetRef<'a, T> { + fn get(&self) -> &'a T; +} + +struct Box<'a, T> { + t: &'a T +} + +impl<'a,T:Clone> GetRef<'a,T> for Box<'a,T> { + fn get(&self) -> &'a T { + self.t + } +} + +fn get<'a,'b,G:GetRef<'a, int>>(g1: G, b: &'b int) -> &'b int { + g1.get() //~ ERROR lifetime mismatch +} + +fn main() { +} diff --git a/src/test/run-pass/regions-early-bound-used-in-bound-method.rs b/src/test/run-pass/regions-early-bound-used-in-bound-method.rs new file mode 100644 index 0000000000000..cc95594bcd745 --- /dev/null +++ b/src/test/run-pass/regions-early-bound-used-in-bound-method.rs @@ -0,0 +1,37 @@ +// 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. + +// Tests that you can use a fn lifetime parameter as part of +// the value for a type parameter in a bound. + +trait GetRef<'a> { + fn get(&self) -> &'a int; +} + +struct Box<'a> { + t: &'a int +} + +impl<'a> GetRef<'a> for Box<'a> { + fn get(&self) -> &'a int { + self.t + } +} + +impl<'a> Box<'a> { + fn add<'b,G:GetRef<'b>>(&self, g2: G) -> int { + *self.t + *g2.get() + } +} + +fn main() { + let b1 = Box { t: &3 }; + assert_eq!(b1.add(b1), 6); +} diff --git a/src/test/run-pass/regions-early-bound-used-in-bound.rs b/src/test/run-pass/regions-early-bound-used-in-bound.rs new file mode 100644 index 0000000000000..69ff5c7e83cd6 --- /dev/null +++ b/src/test/run-pass/regions-early-bound-used-in-bound.rs @@ -0,0 +1,35 @@ +// 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. + +// Tests that you can use a fn lifetime parameter as part of +// the value for a type parameter in a bound. + +trait GetRef<'a, T> { + fn get(&self) -> &'a T; +} + +struct Box<'a, T> { + t: &'a T +} + +impl<'a,T:Clone> GetRef<'a,T> for Box<'a,T> { + fn get(&self) -> &'a T { + self.t + } +} + +fn add<'a,G:GetRef<'a, int>>(g1: G, g2: G) -> int { + *g1.get() + *g2.get() +} + +fn main() { + let b1 = Box { t: &3 }; + assert_eq!(add(b1, b1), 6); +} diff --git a/src/test/run-pass/regions-early-bound-used-in-type-param.rs b/src/test/run-pass/regions-early-bound-used-in-type-param.rs new file mode 100644 index 0000000000000..3f582e9b6e8d5 --- /dev/null +++ b/src/test/run-pass/regions-early-bound-used-in-type-param.rs @@ -0,0 +1,35 @@ +// 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. + +// Tests that you can use a fn lifetime parameter as part of +// the value for a type parameter in a bound. + +trait Get { + fn get(&self) -> T; +} + +struct Box { + t: T +} + +impl Get for Box { + fn get(&self) -> T { + self.t.clone() + } +} + +fn add<'a,G:Get<&'a int>>(g1: G, g2: G) -> int { + *g1.get() + *g2.get() +} + +fn main() { + let b1 = Box { t: &3 }; + assert_eq!(add(b1, b1), 6); +}