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

Last use doesn't consider closure bodies properly #1399

Closed
nikomatsakis opened this issue Dec 31, 2011 · 2 comments
Closed

Last use doesn't consider closure bodies properly #1399

nikomatsakis opened this issue Dec 31, 2011 · 2 comments
Labels
I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics.

Comments

@nikomatsakis
Copy link
Contributor

The following test case will crash (once fixed, something similar should be added to the test suite):

fn invoke_several_times(f: lambda()) {
    f();
    f();
    f();
}

fn bycopy<K>(+k: K) { log(error,k); }
fn byref<K>(&&k: K) { log(error,k); }

fn testfn(+k: ~int) {
    bycopy(k); // <-- gets incorrectly optimized into a move
    invoke_several_times {||
        byref(k);
    }
    // bycopy(k); <-- (this would be a last use)
}

fn main() {
    testfn(~22);
}

The reason is that the bycopy(k) is considered a last use of k as uses via upvar are not considered by the last use analysis. I think we should probably just disable any 'last use' reasoning about variables that escape into a closure of any kind. After all, the closure could be stored and invoked any number of times, so it's hard to reason about what a last use is in that context.

The only place we could try to get smart is around blocks (i.e., closures with proto block), as they cannot escape and so can be tracked more precisely: in the example above, we know that the block passed to invoke_several_times() won't leak, so a final call to bycopy(k) could safely be considered a last use (as indicated in the example).

As an aside, there is some code in lastuse.rs that seems to try to consider blocks and closures carefully, but so I could not quite figure out what constraints it was trying to enforce. It didn't seem right to me, but I might be missing something. The code analyzes blocks twice, presumably to account for the possibility of the block being invoked multiple times: but seeing as upvars are ignored, I don't think this serves its intended purpose. In addition, it may harm the precision of the analysis for non-upvars within the block? There is also some special reasoning that delays the visiting of closure arguments to a function until after the rest of the function has been analyzed, though I'm not quite sure why. Perhaps to simulate the possible execution order? Removing this special case made no difference in any of my tests.

@marijnh
Copy link
Contributor

marijnh commented Jan 2, 2012

I think we should probably just disable any 'last use' reasoning about variables that escape into a closure of any kind. After all, the closure could be stored and invoked any number of times, so it's hard to reason about what a last use is in that context.

Unless your changes were more radical than I understood them, only copying closures can be stored anywhere. A 'block' closure an only be invoked (0-N times) by the call that it is passed to, and behaves much like a loop.

The bug you see probably results from the block being classified incorrectly. I'll debug.

@marijnh marijnh closed this as completed in bd6646e Jan 2, 2012
@nikomatsakis
Copy link
Contributor Author

Yes, of course, I forgot that lambdas etc copy the variables at the point of creation, so it's a moot point for those sorts of closures.

bjorn3 added a commit to bjorn3/rust that referenced this issue Oct 24, 2023
celinval added a commit to celinval/rust-dev that referenced this issue Jun 4, 2024
* Update toolchain to nightly-2022-07-19

This update required the following changes:
  - Add support to ProjectionElem::OpaqueCast
  - Add support to Rvalue::CopyForDeref
  - Add support to ConstValue::ZST
  - Rename debugging_opts to unstable_opts
  - Change to mem::uninit/zeroed validity checks
  - Change vecdeque harness due to std capacity check

Co-authored-by: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics.
Projects
None yet
Development

No branches or pull requests

2 participants