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

RFC: Return by move #3413

Closed
catamorphism opened this issue Sep 7, 2012 · 11 comments
Closed

RFC: Return by move #3413

catamorphism opened this issue Sep 7, 2012 · 11 comments
Labels
A-codegen Area: Code generation A-typesystem Area: The type system C-cleanup Category: PRs that clean code up or issues documenting cleanup.
Milestone

Comments

@catamorphism
Copy link
Contributor

In changing code to prepare for #2633, I'm noticing that most of the "copying a noncopyable" errors when I disable last-use have to do with a return, usually a return of a local variable. I propose that a return x should, by default, mean return (move x) when x is an owned lvalue. This avoids the need to insert lots of explicit moves once last-use is disabled.

An argument against this proposal is that the point of #2633 is to make more things explicit, and this proposal makes more things implicit again. However, I think it's easier to understand that "return is move" when you're returning something you own than it is to understand how last-use works.

@bblum
Copy link
Contributor

bblum commented Sep 7, 2012

To write return by copyin cases where this "might" be confusing, you already have to write out the copy, right? fn foo<T:copy>(x:&T) -> T { return copy *x }?

i can't think of any other conflicts; this sounds fine to me.

@brson
Copy link
Contributor

brson commented Sep 7, 2012

So to return a value we could either write:

fn foo() -> bar {
   move baz
}

or

fn foo() -> bar {
  return baz;
}

This seems ok to me.

@catamorphism
Copy link
Contributor Author

@brson Yes, either would be correct.

@nikomatsakis
Copy link
Contributor

I was considering a similar rule but that was syntactically determined. Something like:

  • If the tail expression of a block is a local variable that is declared inside that block, it is automatically moved
  • If the return expression is a local variable or argument, it is automatically moved

I feel strongly that the rule should be the same for blocks and returns.

The idea of generalizing to an arbitrary owned expression is also plausible, though it means that we won't know if something is a move or not until borrowck. That's probably ok.

@nikomatsakis
Copy link
Contributor

Under my proposal, then,

fn foo() -> baz { EXPR }

would always be entirely equivalent to

fn foo() -> baz { return EXPR; }

and also to:

fn foo() -> baz { { EXPR } }

and so forth.

I think it is important to main this invariant.

One problem I can see, though, is that return { x } would not be equivalent to return x. I guess we can generalize the rule to accommodate this case as well.

@catamorphism
Copy link
Contributor Author

@nikomatsakis Yes, I agree that tail exprs and explicit returns should be handled the same way. I just forgot to mention that.

@graydon
Copy link
Contributor

graydon commented Sep 7, 2012

I'm mostly in agreement (tentatively, not really knowing the complications) with Niko in terms of treating the tail position, in general, as moved-out rather than copied-out. It's not "running a liveness algorithm" to figure out which expression is the tail expression of a block or function: it's a straight syntactic feature, known the moment we parse. Honestly, if you can't figure that out by inspection, you have bigger problems in your code!

(Though I don't understand why return {x} would be different than return x in this proposal)

@ghost ghost assigned catamorphism Sep 7, 2012
@graydon
Copy link
Contributor

graydon commented Sep 7, 2012

Oh. I guess there might be some complication here in the region-inference bit Niko and I were discussing, to lengthen rvalue lifetimes from inside blocks. Though I struggle to come up with an example (especially one borrowck wouldn't catch). Am I barking up a meaningful tree here?

@pcwalton
Copy link
Contributor

pcwalton commented Sep 8, 2012

I guess this is OK.

@nikomatsakis
Copy link
Contributor

@graydon that's not quite what I meant. I just meant that if we are not careful with how we define the rules, then return {x} will not work. For example, my initial thought for how to define the rule was "if the return (resp. tail expression) is a path and the path is a local variable defined in the function block (resp. block), then the value is implicitly moved". But that will not work for return {x} because the return expression is a block, not a path, and the tail expression of {x} is a local variable but not one defined within the block itself.

So the rule has to be more subtle and consider the "destination" of the copy. Something like: "a reference to a local variable is an implicit move if the destination will be out of the variable's scope", but of course we need to define "destination". Essentially it seems like a top-down sort of analysis to me, where each block has a notion of its "destination", with the fn body and any block that appears after a return having a destination of "outside the fn" (in terms of the implementation, perhaps the node id 0). Internal blocks have a destination of the expression that they are a part of. When looking at a block whose tail expression is a local variable, we consider the block's destination id and compare it against the id of the block where the variable was declared. This can be generalized in a fairly straightforward way to arbitrary "owned" expressions, if that seems worth the trouble.

catamorphism added a commit that referenced this issue Sep 10, 2012
I'm continuing to make moves explicit, despite #3413, because that
seems like the most reliable way to make these changes. Once I've made
all the moves explicit, the test for #3413 will be that I can remove them.
@catamorphism
Copy link
Contributor Author

In the meeting today, we agreed not to implement this after all, for simplicity. It's not that bad to have to write explicit moves .We can revisit this if it gets too annoying.

@catamorphism catamorphism removed their assignment Jun 16, 2014
RalfJung pushed a commit to RalfJung/rust that referenced this issue Mar 25, 2024
phase_rustdoc: add a heuristic to make us more certain that this is really rustdoc

Also add anyhow to test-cargo-miri; it has a custom build probe and is widely used so let's make sure the build script does not fail.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-codegen Area: Code generation A-typesystem Area: The type system C-cleanup Category: PRs that clean code up or issues documenting cleanup.
Projects
None yet
Development

No branches or pull requests

6 participants