Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

librustc: Implement lifetime elision. #15767

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/libcollections/dlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,14 @@ impl<T> Rawlink<T> {
}

/// Convert the `Rawlink` into an Option value
fn resolve_immut(&self) -> Option<&T> {
unsafe { self.p.to_option() }
fn resolve_immut<'a>(&self) -> Option<&'a T> {
unsafe {
mem::transmute(self.p.to_option())
}
}

/// Convert the `Rawlink` into an Option value
fn resolve(&mut self) -> Option<&mut T> {
fn resolve<'a>(&mut self) -> Option<&'a mut T> {
if self.p.is_null() {
None
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/libgraphviz/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,12 +455,12 @@ impl<'a> LabelText<'a> {
}

/// Puts `prefix` on a line above this label, with a blank line separator.
pub fn prefix_line(self, prefix: LabelText) -> LabelText {
pub fn prefix_line(self, prefix: LabelText) -> LabelText<'static> {
prefix.suffix_line(self)
}

/// Puts `suffix` on a line below this label, with a blank line separator.
pub fn suffix_line(self, suffix: LabelText) -> LabelText {
pub fn suffix_line(self, suffix: LabelText) -> LabelText<'static> {
let prefix = self.pre_escaped_content().into_string();
let suffix = suffix.pre_escaped_content();
EscStr(str::Owned(prefix.append(r"\n\n").append(suffix.as_slice())))
Expand Down
2 changes: 1 addition & 1 deletion src/libgraphviz/maybe_owned_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ impl<'b,T> slice::Vector<T> for MaybeOwnedVector<'b,T> {
}

impl<'a,T> FromIterator<T> for MaybeOwnedVector<'a,T> {
fn from_iter<I:Iterator<T>>(iterator: I) -> MaybeOwnedVector<T> {
fn from_iter<I:Iterator<T>>(iterator: I) -> MaybeOwnedVector<'a,T> {
// If we are building from scratch, might as well build the
// most flexible variant.
Growable(FromIterator::from_iter(iterator))
Expand Down
4 changes: 2 additions & 2 deletions src/libgreen/sched.rs
Original file line number Diff line number Diff line change
Expand Up @@ -959,13 +959,13 @@ impl CleanupJob {
type UnsafeTaskReceiver = raw::Closure;
trait ClosureConverter {
fn from_fn(|&mut Scheduler, Box<GreenTask>|) -> Self;
fn to_fn(self) -> |&mut Scheduler, Box<GreenTask>|;
fn to_fn(self) -> |&mut Scheduler, Box<GreenTask>|:'static ;
}
impl ClosureConverter for UnsafeTaskReceiver {
fn from_fn(f: |&mut Scheduler, Box<GreenTask>|) -> UnsafeTaskReceiver {
unsafe { mem::transmute(f) }
}
fn to_fn(self) -> |&mut Scheduler, Box<GreenTask>| {
fn to_fn(self) -> |&mut Scheduler, Box<GreenTask>|:'static {
unsafe { mem::transmute(self) }
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/trans/basic_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl BasicBlock {
}
}

pub fn pred_iter(self) -> Preds {
pub fn pred_iter(self) -> Preds<'static> {
self.as_value().user_iter()
.filter(|user| user.is_a_terminator_inst())
.map(|user| user.get_parent().unwrap())
Expand Down
52 changes: 52 additions & 0 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4872,3 +4872,55 @@ pub enum ExplicitSelfCategory {
ByBoxExplicitSelfCategory,
}

/// Pushes all the lifetimes in the given type onto the given list. A
/// "lifetime in a type" is a lifetime specified by a reference or a lifetime
/// in a list of type substitutions. This does *not* traverse into nominal
/// types, nor does it resolve fictitious types.
pub fn accumulate_lifetimes_in_type(accumulator: &mut Vec<ty::Region>,
typ: t) {
walk_ty(typ, |typ| {
match get(typ).sty {
ty_rptr(region, _) => accumulator.push(region),
ty_enum(_, ref substs) |
ty_trait(box TyTrait {
substs: ref substs,
..
}) |
ty_struct(_, ref substs) => {
match substs.regions {
subst::ErasedRegions => {}
subst::NonerasedRegions(ref regions) => {
for region in regions.iter() {
accumulator.push(*region)
}
}
}
}
ty_closure(ref closure_ty) => {
match closure_ty.store {
RegionTraitStore(region, _) => accumulator.push(region),
UniqTraitStore => {}
}
}
ty_nil |
ty_bot |
ty_bool |
ty_char |
ty_int(_) |
ty_uint(_) |
ty_float(_) |
ty_box(_) |
ty_uniq(_) |
ty_str |
ty_vec(_, _) |
ty_ptr(_) |
ty_bare_fn(_) |
ty_tup(_) |
ty_param(_) |
ty_infer(_) |
ty_unboxed_closure(_) |
ty_err => {}
}
})
}

94 changes: 72 additions & 22 deletions src/librustc/middle/typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ use middle::lang_items::FnMutTraitLangItem;
use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs};
use middle::ty;
use middle::ty_fold::TypeFolder;
use middle::typeck::rscope::{ExplicitRscope, ImpliedSingleRscope};
use middle::typeck::rscope::RegionScope;
use middle::typeck::{TypeAndSubsts, infer, lookup_def_tcx, rscope};
use middle::typeck;
Expand Down Expand Up @@ -931,31 +932,45 @@ fn ty_of_method_or_bare_fn<AC:AstConv>(
Option<ty::ExplicitSelfCategory>) {
debug!("ty_of_method_or_bare_fn");

// new region names that appear inside of the fn decl are bound to
// that function type
// New region names that appear inside of the arguments of the function
// declaration are bound to that function type.
let rb = rscope::BindingRscope::new(id);

// `implied_output_region` is the region that will be assumed for any
// region parameters in the return type. In accordance with the rules for
// lifetime elision, we can determine it in two ways. First (determined
// here), if self is by-reference, then the implied output region is the
// region of the self parameter.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this comment should have a "Second, ..." clause which states where the other rule is applied.

let mut explicit_self_category_result = None;
let self_ty = opt_self_info.and_then(|self_info| {
// Figure out and record the explicit self category.
let explicit_self_category =
determine_explicit_self_category(this, &rb, &self_info);
explicit_self_category_result = Some(explicit_self_category);
match explicit_self_category {
ty::StaticExplicitSelfCategory => None,
ty::ByValueExplicitSelfCategory => {
Some(self_info.untransformed_self_ty)
}
ty::ByReferenceExplicitSelfCategory(region, mutability) => {
Some(ty::mk_rptr(this.tcx(), region,
ty::mt {ty: self_info.untransformed_self_ty,
mutbl: mutability}))
}
ty::ByBoxExplicitSelfCategory => {
Some(ty::mk_uniq(this.tcx(), self_info.untransformed_self_ty))
let (self_ty, mut implied_output_region) = match opt_self_info {
None => (None, None),
Some(self_info) => {
// Figure out and record the explicit self category.
let explicit_self_category =
determine_explicit_self_category(this, &rb, &self_info);
explicit_self_category_result = Some(explicit_self_category);
match explicit_self_category {
ty::StaticExplicitSelfCategory => (None, None),
ty::ByValueExplicitSelfCategory => {
(Some(self_info.untransformed_self_ty), None)
}
ty::ByReferenceExplicitSelfCategory(region, mutability) => {
(Some(ty::mk_rptr(this.tcx(),
region,
ty::mt {
ty: self_info.untransformed_self_ty,
mutbl: mutability
})),
Some(region))
}
ty::ByBoxExplicitSelfCategory => {
(Some(ty::mk_uniq(this.tcx(),
self_info.untransformed_self_ty)),
None)
}
}
}
});
};

// HACK(eddyb) replace the fake self type in the AST with the actual type.
let input_tys = if self_ty.is_some() {
Expand All @@ -964,12 +979,47 @@ fn ty_of_method_or_bare_fn<AC:AstConv>(
decl.inputs.as_slice()
};
let input_tys = input_tys.iter().map(|a| ty_of_arg(this, &rb, a, None));
let self_and_input_tys: Vec<_> =
self_ty.move_iter().chain(input_tys).collect();

// Second, if there was exactly one lifetime (either a substitution or a
// reference) in the arguments, then any anonymous regions in the output
// have that lifetime.
if implied_output_region.is_none() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, here it is, never mind...

let mut self_and_input_tys_iter = self_and_input_tys.iter();
if self_ty.is_some() {
// Skip the first argument if `self` is present.
drop(self_and_input_tys_iter.next())
}

let self_and_input_tys = self_ty.move_iter().chain(input_tys).collect();
let mut accumulator = Vec::new();
for input_type in self_and_input_tys_iter {
ty::accumulate_lifetimes_in_type(&mut accumulator, *input_type)
}
if accumulator.len() == 1 {
implied_output_region = Some(*accumulator.get(0));
}
}

let output_ty = match decl.output.node {
ast::TyInfer => this.ty_infer(decl.output.span),
_ => ast_ty_to_ty(this, &rb, &*decl.output)
_ => {
match implied_output_region {
Some(implied_output_region) => {
let rb = ImpliedSingleRscope {
region: implied_output_region,
};
ast_ty_to_ty(this, &rb, &*decl.output)
}
None => {
// All regions must be explicitly specified in the output
// if the lifetime elision rules do not apply. This saves
// the user from potentially-confusing errors.
let rb = ExplicitRscope;
ast_ty_to_ty(this, &rb, &*decl.output)
}
}
}
};

(ty::BareFnTy {
Expand Down
16 changes: 15 additions & 1 deletion src/librustc/middle/typeck/rscope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,24 @@ impl RegionScope for BindingRscope {
fn anon_regions(&self,
_: Span,
count: uint)
-> Result<Vec<ty::Region> , ()> {
-> Result<Vec<ty::Region>, ()> {
let idx = self.anon_bindings.get();
self.anon_bindings.set(idx + count);
Ok(Vec::from_fn(count, |i| ty::ReLateBound(self.binder_id,
ty::BrAnon(idx + i))))
}
}

/// A scope in which we generate one specific region. This occurs after the
/// `->` (i.e. in the return type) of function signatures.
pub struct ImpliedSingleRscope {
pub region: ty::Region,
}

impl RegionScope for ImpliedSingleRscope {
fn anon_regions(&self, _: Span, count: uint)
-> Result<Vec<ty::Region>,()> {
Ok(Vec::from_elem(count, self.region.clone()))
}
}

2 changes: 1 addition & 1 deletion src/librustrt/local_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ pub type Map = Vec<Option<(*const u8, TLSValue, uint)>>;
type TLSValue = Box<LocalData + Send>;

// Gets the map from the runtime. Lazily initialises if not done so already.
unsafe fn get_local_map() -> Option<&mut Map> {
unsafe fn get_local_map<'a>() -> Option<&'a mut Map> {
if !Local::exists(None::<Task>) { return None }

let task: *mut Task = Local::unsafe_borrow();
Expand Down
2 changes: 1 addition & 1 deletion src/librustrt/rtio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ impl<'a> Drop for LocalIo<'a> {
impl<'a> LocalIo<'a> {
/// Returns the local I/O: either the local scheduler's I/O services or
/// the native I/O services.
pub fn borrow() -> Option<LocalIo> {
pub fn borrow() -> Option<LocalIo<'a>> {
// FIXME(#11053): bad
//
// This is currently very unsafely implemented. We don't actually
Expand Down
2 changes: 1 addition & 1 deletion src/librustuv/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ mod test {
use super::super::Loop;
use super::super::local_loop;

fn l() -> &mut Loop { &mut local_loop().loop_ }
fn l() -> &'static mut Loop { &mut local_loop().loop_ }

#[test]
fn file_test_full_simple_sync() {
Expand Down
6 changes: 3 additions & 3 deletions src/test/auxiliary/overloaded_autoderef_xc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ struct DerefWithHelper<H, T> {
}

trait Helper<T> {
fn helper_borrow<'a>(&'a self) -> &'a T;
fn helper_borrow(&self) -> &T;
}

impl<T> Helper<T> for Option<T> {
fn helper_borrow<'a>(&'a self) -> &'a T {
fn helper_borrow(&self) -> &T {
self.as_ref().unwrap()
}
}

impl<T, H: Helper<T>> Deref<T> for DerefWithHelper<H, T> {
fn deref<'a>(&'a self) -> &'a T {
fn deref(&self) -> &T {
self.helper.helper_borrow()
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/bench/shootout-k-nucleotide.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ impl Table {
}
}

fn iter<'a>(&'a self) -> Items<'a> {
fn iter(&self) -> Items {
Items { cur: None, items: self.items.iter() }
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/borrowck-borrow-from-temporary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

struct Foo(int);

fn foo() -> &int {
fn foo<'a>() -> &'a int {
let &Foo(ref x) = &Foo(3); //~ ERROR borrowed value does not live long enough
x
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/borrowck-borrow-mut-object-twice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// other `&mut` pointers.

trait Foo {
fn f1<'a>(&'a mut self) -> &'a ();
fn f1(&mut self) -> &();
fn f2(&mut self);
}

Expand Down
Loading