Merged
Conversation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Tracking issue: #139076
This implements
super letas proposed in #139080, based on the following two equivalence rules.$exprin any context, these are equivalent:& $expr{ super let a = & $expr; a }$expris a temporary (aka rvalue):& $expr{ super let a = $expr; & a }So far, this experiment has a few interesting results:
Interesting result 1
In this snippet:
I originally expected temporary
temp()would be dropped at the end of the statement (;), just like in a regularlet, becausetemp()is not subject to temporary lifetime extension.However, it turns out that that would break the fundamental equivalence rules.
For example, in
the temporary
temp()will be dropped at the;.The first equivalence rule tells us this must be equivalent:
But that means that
temp()must live until the last;(afterg()), not just the first;(afterf()).While this was somewhat surprising to me at first, it does match the exact behavior we need for
pin!(): The following should work. (See also #138718)Here,
temp()lives until the end of the statement. This makes sense from the perspective of the user, as no other;or{}are visible. Whetherpin!()uses a{}block internally or not should be irrelevant.This means that nothing in a
super letstatement will be dropped at the end of that super let statement. It does not even need its own scope.This raises questions that are useful for later on:
Will this make temporaries live too long in cases where
super letis used not in a hidden block in a macro, but as a visible statement in code like the following?Is a
letstatement in a block still the right syntax for this? Considering it has no scope of its own, maybe neither a block nor a statement should be involvedThis leads me to think that instead of
{ super let $pat = $init; $expr }, we might want to consider something likelet $pat = $init in $expror$expr where $pat = $init. Although there are also issues with these, as it isn't obvious anymore if$initshould be subject to temporary lifetime extension. (Do we want bothlet _ = _ in ..andsuper let _ = _ in ..?)Interesting result 2
What about
super let x;without initializer?This works fine with the implementation in this PR:
xis extended to live as long asa.While it matches my expectations, a somewhat interesting thing to realize is that these are not equivalent:
super let x = $expr;super let x; x = $expr;In the first case, all temporaries in $expr will live at least as long as (the result of) the surrounding block.
In the second case, temporaries will be dropped at the end of the assignment statement. (Because the assignment statement itself "is not
super".)This difference in behavior might be confusing, but it might be useful.
One might want to extend the lifetime of a variable without extending all the temporaries in the initializer expression.
On the other hand, that can also be expressed as:
let x = $expr; super let x = x;(w/o temporary lifetime extension), orsuper let x = { $expr };(w/ temporary lifetime extension)So, this raises these questions:
Do we want to accept
super let x;without initializer at all?Does it make sense for statements other than let statements to be "super"? An expression statement also drops temporaries at its
;, so now that we discovered thatsuper letbasically disables that;(see interesting result 1), is there a use to having other statements without their own scope? (I don't think that's ever useful?)Interesting result 3
This works now:
I didn't put in any special cases for
super let else. This is just the behavior that 'naturally' falls out when implementingsuper letwithout thinking of thelet elsecase.super let elsework?Interesting result 4
This 'works':
I didn't put in any special cases for
super letat function scope. I had expected the code to cause an ICE or other weird failure when used at function body scope, because there's no way to let the variable live as long as the result of the function.With the current implementation, the only difference between
letandsuper letat function scope, is thatsuper letdrops all the temporaries in the init expression at the}(of the function) rather than at the;(of the (super) let statement).This raises the question:
super letis used at function scope? Or is this just a quirk and should we explicitly disallowsuper letin a function body? (Probably the latter.)The questions above do not need an answer to land this PR. These questions should be considered when redesigning/rfc'ing/stabilizing the feature.