From fb45c899a1b4e183056323a9cf20daa2d987af59 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Tue, 3 Jan 2017 07:41:19 +0100 Subject: [PATCH 1/7] 'fn lifetime ascription --- text/0000-fn-lifetime.md | 71 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 text/0000-fn-lifetime.md diff --git a/text/0000-fn-lifetime.md b/text/0000-fn-lifetime.md new file mode 100644 index 00000000000..b19c0fb52cc --- /dev/null +++ b/text/0000-fn-lifetime.md @@ -0,0 +1,71 @@ +- Feature Name: fn lifetime +- Start Date: 2017-01-02 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +Add a `'fn` lifetime that is bound to the scope of the body of the current +innermost function or closure. + +# Motivation +[motivation]: #motivation + +Doing this will enable us to declare that some values live longer than their +containing scope, allowing us to "allocate" recursive data structures (e.g. +linked lists) on the stack, as in: + +```rust +struct Node { + data: T, + next: Option<&'fn Node>, +} + +let mut head : Node = Node { data: 0, None }; +for i in iter { + head = Node { data: i, Some(head) } +} +``` + +# Detailed design +[design]: #detailed-design + +The compiler is extended to recognise the `'fn` lifetime, and bind the region +of the resulting type to the scope of the body of the current item or closure. + +# Drawbacks +[drawbacks]: #drawbacks + +This change makes a lifetime annotation change the behavior of the program +(although in the motivating case, it only changes the program from "fails to +compile" to "works"). The change incurs a considerable implementation cost, +although a more complete "lifetime ascription" feature has been discussed in +various places already (see the [Alternatives](alternatives) section for +further discussion. + +# Alternatives +[alternatives]: #alternatives + +- do nothing. Rust has been working quite well without this feature so far. It +is only of use in very specific situations (on the other hand those situations +are currently not well handled in Rust at all). + +- lifetime ascription – this is the idea to allow labels on each block (instead +of only on loops) and treat the label names as lifetime designators. This is an +awesome teaching device, as it allows us to make lifetimes explicit *in working +code*, where we can now only use comments and pseudocode. Even with lifetime +ascription, this feature is still useful, because ascribing a lifetime to a +`fn` block gets awfully hard to parse, especially for a human. Consider +following example: + +```Rust +fn somewhat_awkward<'a, T>(Foo<'a>) -> Box + 'a 'wha { .. } +``` + +What other designs have been considered? What is the impact of not doing this? + +# Unresolved questions +[unresolved]: #unresolved-questions + +What parts of the design are still TBD? From 4f9b2c1776eb8fe98fbf6f037b71acd12a31aac5 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 7 Jan 2017 12:09:53 -0500 Subject: [PATCH 2/7] Add unresolved question of `break 'fn ..` --- text/0000-fn-lifetime.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/text/0000-fn-lifetime.md b/text/0000-fn-lifetime.md index b19c0fb52cc..14456ec50c6 100644 --- a/text/0000-fn-lifetime.md +++ b/text/0000-fn-lifetime.md @@ -68,4 +68,19 @@ What other designs have been considered? What is the impact of not doing this? # Unresolved questions [unresolved]: #unresolved-questions +- Should we allow `break 'fn ..` with the same semantics as `return ..`. In + [RFC 1624](https://github.com/rust-lang/rfcs/blob/master/text/1624-loop-break-value.md) + we got breaking with a value out of loops, so the value part is already + established. This provides one canonnical syntax for breaking out of a loops + or functions, leaving `return` just a convenience. In a future with full + lifetime ascription, this would further generalize to supporting all labeled + blocks, not just loops and functions. + + A downside is `continue 'a` will still only make sense when `'a` is bound to a + loop, and `break 'a` won't make sense when `'a` is a lifetime parameter. That + means users will need to understand their are three tiers of lifetimes: loop, + block (including `'fn`), and, parameter, where each is less usable than the + last. Today loop lables and lifetimes are disjoint in that neither can be used + where the other is expected, though they do share the same syntax. + What parts of the design are still TBD? From 259ef692be4639b91752d68da664ce30260f5151 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 7 Jan 2017 12:58:20 -0500 Subject: [PATCH 3/7] Put example in function body to make clear 'fn is bound Also fix type error with missing & operator --- text/0000-fn-lifetime.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/text/0000-fn-lifetime.md b/text/0000-fn-lifetime.md index 14456ec50c6..52dab916f8b 100644 --- a/text/0000-fn-lifetime.md +++ b/text/0000-fn-lifetime.md @@ -17,14 +17,16 @@ containing scope, allowing us to "allocate" recursive data structures (e.g. linked lists) on the stack, as in: ```rust -struct Node { - data: T, - next: Option<&'fn Node>, -} - -let mut head : Node = Node { data: 0, None }; -for i in iter { - head = Node { data: i, Some(head) } +fn foo() { + struct Node { + data: T, + next: Option<&'fn Node>, + } + + let mut head : Node = Node { data: 0, &None }; + for i in iter { + head = Node { data: i, Some(head) } + } } ``` From 2949e53b2818c849fae46d37f610a9e2800c338a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 7 Jan 2017 13:07:49 -0500 Subject: [PATCH 4/7] One sentance per line This helps make diffs more readable because there is no longer manual rewrapping every time. I propose this as a general policy in #1811, but absent one right now I think its fine to do this already. --- text/0000-fn-lifetime.md | 62 ++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 40 deletions(-) diff --git a/text/0000-fn-lifetime.md b/text/0000-fn-lifetime.md index 52dab916f8b..d6ca87a8021 100644 --- a/text/0000-fn-lifetime.md +++ b/text/0000-fn-lifetime.md @@ -6,15 +6,12 @@ # Summary [summary]: #summary -Add a `'fn` lifetime that is bound to the scope of the body of the current -innermost function or closure. +Add a `'fn` lifetime that is bound to the scope of the body of the current innermost function or closure. # Motivation [motivation]: #motivation -Doing this will enable us to declare that some values live longer than their -containing scope, allowing us to "allocate" recursive data structures (e.g. -linked lists) on the stack, as in: +Doing this will enable us to declare that some values live longer than their containing scope, allowing us to "allocate" recursive data structures (e.g. linked lists) on the stack, as in: ```rust fn foo() { @@ -33,56 +30,41 @@ fn foo() { # Detailed design [design]: #detailed-design -The compiler is extended to recognise the `'fn` lifetime, and bind the region -of the resulting type to the scope of the body of the current item or closure. +The compiler is extended to recognise the `'fn` lifetime, and bind the region of the resulting type to the scope of the body of the current item or closure. # Drawbacks [drawbacks]: #drawbacks -This change makes a lifetime annotation change the behavior of the program -(although in the motivating case, it only changes the program from "fails to -compile" to "works"). The change incurs a considerable implementation cost, -although a more complete "lifetime ascription" feature has been discussed in -various places already (see the [Alternatives](alternatives) section for -further discussion. +This change makes a lifetime annotation change the behavior of the program (although in the motivating case, it only changes the program from "fails to compile" to "works"). +The change incurs a considerable implementation cost, although a more complete "lifetime ascription" feature has been discussed in various places already (see the [Alternatives](alternatives) section for further discussion. # Alternatives [alternatives]: #alternatives -- do nothing. Rust has been working quite well without this feature so far. It -is only of use in very specific situations (on the other hand those situations -are currently not well handled in Rust at all). +- do nothing. Rust has been working quite well without this feature so far. + It is only of use in very specific situations (on the other hand those situations are currently not well handled in Rust at all). -- lifetime ascription – this is the idea to allow labels on each block (instead -of only on loops) and treat the label names as lifetime designators. This is an -awesome teaching device, as it allows us to make lifetimes explicit *in working -code*, where we can now only use comments and pseudocode. Even with lifetime -ascription, this feature is still useful, because ascribing a lifetime to a -`fn` block gets awfully hard to parse, especially for a human. Consider -following example: +- lifetime ascription – this is the idea to allow labels on each block (instead of only on loops) and treat the label names as lifetime designators. + This is an awesome teaching device, as it allows us to make lifetimes explicit *in working code*, where we can now only use comments and pseudocode. + Even with lifetime ascription, this feature is still useful, because ascribing a lifetime to a `fn` block gets awfully hard to parse, especially for a human. + Consider following example: -```Rust -fn somewhat_awkward<'a, T>(Foo<'a>) -> Box + 'a 'wha { .. } -``` + ```Rust + fn somewhat_awkward<'a, T>(Foo<'a>) -> Box + 'a 'wha { .. } + ``` What other designs have been considered? What is the impact of not doing this? # Unresolved questions [unresolved]: #unresolved-questions -- Should we allow `break 'fn ..` with the same semantics as `return ..`. In - [RFC 1624](https://github.com/rust-lang/rfcs/blob/master/text/1624-loop-break-value.md) - we got breaking with a value out of loops, so the value part is already - established. This provides one canonnical syntax for breaking out of a loops - or functions, leaving `return` just a convenience. In a future with full - lifetime ascription, this would further generalize to supporting all labeled - blocks, not just loops and functions. - - A downside is `continue 'a` will still only make sense when `'a` is bound to a - loop, and `break 'a` won't make sense when `'a` is a lifetime parameter. That - means users will need to understand their are three tiers of lifetimes: loop, - block (including `'fn`), and, parameter, where each is less usable than the - last. Today loop lables and lifetimes are disjoint in that neither can be used - where the other is expected, though they do share the same syntax. +- Should we allow `break 'fn ..` with the same semantics as `return ..`. + In [RFC 1624](https://github.com/rust-lang/rfcs/blob/master/text/1624-loop-break-value.md) we got breaking with a value out of loops, so the value part is already established. + This provides one canonnical syntax for breaking out of a loops or functions, leaving `return` just a convenience. + In a future with full lifetime ascription, this would further generalize to supporting all labeled blocks, not just loops and functions. + + A downside is `continue 'a` will still only make sense when `'a` is bound to a loop, and `break 'a` won't make sense when `'a` is a lifetime parameter. + That means users will need to understand their are three tiers of lifetimes: loop, block (including `'fn`), and, parameter, where each is less usable than the last. + Today loop lables and lifetimes are disjoint in that neither can be used where the other is expected, though they do share the same syntax. What parts of the design are still TBD? From 6b2840581136193d138b782a1cab29f6cab2ecda Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 7 Jan 2017 13:22:54 -0500 Subject: [PATCH 5/7] Remove section descriptions for filled out sections --- text/0000-fn-lifetime.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/text/0000-fn-lifetime.md b/text/0000-fn-lifetime.md index d6ca87a8021..9d5550868db 100644 --- a/text/0000-fn-lifetime.md +++ b/text/0000-fn-lifetime.md @@ -53,8 +53,6 @@ The change incurs a considerable implementation cost, although a more complete " fn somewhat_awkward<'a, T>(Foo<'a>) -> Box + 'a 'wha { .. } ``` -What other designs have been considered? What is the impact of not doing this? - # Unresolved questions [unresolved]: #unresolved-questions @@ -66,5 +64,3 @@ What other designs have been considered? What is the impact of not doing this? A downside is `continue 'a` will still only make sense when `'a` is bound to a loop, and `break 'a` won't make sense when `'a` is a lifetime parameter. That means users will need to understand their are three tiers of lifetimes: loop, block (including `'fn`), and, parameter, where each is less usable than the last. Today loop lables and lifetimes are disjoint in that neither can be used where the other is expected, though they do share the same syntax. - -What parts of the design are still TBD? From 5b4d1205ce22759e6436c6091bfd116fd95f567e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 7 Jan 2017 14:01:36 -0500 Subject: [PATCH 6/7] Rewrite the detailed design section in greater detail, including issues from thread --- text/0000-fn-lifetime.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/text/0000-fn-lifetime.md b/text/0000-fn-lifetime.md index 9d5550868db..90e7d1e3697 100644 --- a/text/0000-fn-lifetime.md +++ b/text/0000-fn-lifetime.md @@ -30,7 +30,26 @@ fn foo() { # Detailed design [design]: #detailed-design -The compiler is extended to recognise the `'fn` lifetime, and bind the region of the resulting type to the scope of the body of the current item or closure. +There are three steps + +1. Bind `'fn` in every closure or function body, representing the lifetime of the body. + This is equivalent to adding an additional `'fn` lifetime parameter to every function that cannot be used in the function's type (argument declaration or where clause), nor manually instantiated by a caller. + Closure literals currently do not allow explicit parameters, but the desugaring would be the same if they did. + Note that the `'fn` lifetimes will shadow each other, whereas we don't currently allow lifetime shadowing. + +2. Allow items inside functions to use lifetimes, including `'fn`, bound by the enclosing functions. + Unclear how this interfacts with current well-formedness rules. + +3. Change axioms of borrowing so it is fine if the lifetime is already entered, as long as the borrow does not outlive it *from* the point of borrowing *after*. For example: + ```rust + fn foo<'a>() { + /* do stuff */ + let a = 0; + let b: &'a i32 = &a; + } + ``` + This is currently prohibited because the function body is entered before a is initialized (and thus able to be borrowed). + Under the new rules, this would be allowed because `a` is uninitialized when the function happens, and the "past" before the borrow begins can be ignored. # Drawbacks [drawbacks]: #drawbacks From 4804d141d20f90ac821e642e1a1f7231cff2a4c0 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Sun, 8 Jan 2017 17:17:10 +0100 Subject: [PATCH 7/7] (hopefully) clear up example, fix label syntax, ask for unsoundness --- text/0000-fn-lifetime.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/text/0000-fn-lifetime.md b/text/0000-fn-lifetime.md index 90e7d1e3697..aaf212eaf04 100644 --- a/text/0000-fn-lifetime.md +++ b/text/0000-fn-lifetime.md @@ -14,15 +14,16 @@ Add a `'fn` lifetime that is bound to the scope of the body of the current inner Doing this will enable us to declare that some values live longer than their containing scope, allowing us to "allocate" recursive data structures (e.g. linked lists) on the stack, as in: ```rust -fn foo() { - struct Node { - data: T, - next: Option<&'fn Node>, - } +struct Node<'a, T> { + data: T, + next: Option<&'a Node>, +} - let mut head : Node = Node { data: 0, &None }; +fn foo() { + // here we ascribe the `'fn` lifetime to `next` + let mut head : Node<'fn, usize> = Node { data: 0, None }; for i in iter { - head = Node { data: i, Some(head) } + head = Node { data: i, Some(&head) } } } ``` @@ -69,7 +70,7 @@ The change incurs a considerable implementation cost, although a more complete " Consider following example: ```Rust - fn somewhat_awkward<'a, T>(Foo<'a>) -> Box + 'a 'wha { .. } + fn somewhat_awkward<'a, T>(Foo<'a>) -> Box + 'a 'wha: { .. } ``` # Unresolved questions @@ -83,3 +84,5 @@ The change incurs a considerable implementation cost, although a more complete " A downside is `continue 'a` will still only make sense when `'a` is bound to a loop, and `break 'a` won't make sense when `'a` is a lifetime parameter. That means users will need to understand their are three tiers of lifetimes: loop, block (including `'fn`), and, parameter, where each is less usable than the last. Today loop lables and lifetimes are disjoint in that neither can be used where the other is expected, though they do share the same syntax. + +- does this allow for any unsound patterns?