Skip to content

Track the "static"-ness of slots in MIR and detect drops in constant initializers. #40036

Closed
@eddyb

Description

@eddyb

Right now we distinguish between "constant initializer" (static, const, promoted fragments) and "runtime" MIR, in that all locals in the former are 'static and drops on them are ignored.
While this seems to work fine for the moment, it can't model, e.g. (mutable) local variables in "constant initializers" and may not be backwards-compatible with a model that can, if we stabilize any extensions.

Problematic example

If we accept rust-lang/rfcs#1817, the current implementation would allow both of these:

const FOO: i32 = (HasDrop {...}, 0).1;
const BAR: &'static i32 = &(HasDrop {...}, 0).1;

If we go by the rest of the language, FOO should drop the tuple after copying out its second field, whereas BAR should borrow the tuple forever, and happen to be pointing at the second tuple (resulting in no drop).
This is important even if we can't run destructors at compile-time, in order to error for FOO but not BAR.

So with the rules inferred above, we could desugar the two constants as follows:

const FOO: i32 = { let tmp = (HasDrop {...}, 0); tmp.1 /* drop(tmp) */ };
static TMP: (HasDrop, i32) = (HasDrop {...}, 0);
const BAR: &'static i32 = &TMP.1;

The distinction between the tmp local slot and the TMP static slot, and the lack of a drop for the latter, is what MIR is missing currently.

Proposed solution

@nikomatsakis and I have mulled this over, and the resulting plan is roughly as follows:

  • use the existing rvalue scope rules that give the two borrowed temporaries in &f(&g()) different scopes (with the inner one living only for the duration of the outer call), with the outermost scope of a constant initializer being special: no destructors run, and its lifetime is 'static
    • this breaks some (unstable) const fn uses, e.g. id(&0) (recovered through rvalue promotion)
  • for 'static scopes, MIR building would use a different kind of "slot" and not emit a drop
    • we might want to repurpose&rename Local to Slot
  • MIR rvalue promotion would create such 'static "slots" for all borrows, e.g.:
// promoted MIR fragment for `&[&42]`
static0 = 42;
tmp0 = &static0;
static1 = [tmp0];
return = &static1;
  • MIR borrowck doesn't require any special treatment, and it should be able to detect malformed MIR
    • e.g. a drop of a 'static slot that was borrowed would overlap with the 'static borrow
  • in miri, only allow pointers into 'static slots to "escape" the constant initializer evaluation
    • eventually we could also allow pointers into allocations that were "leaked", e.g. because their owner was placed in a 'static slot: const S: &'static str = &X.to_string();

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-MIRArea: Mid-level IR (MIR) - https://blog.rust-lang.org/2016/04/19/MIR.htmlA-destructorsArea: Destructors (`Drop`, …)C-feature-requestCategory: A feature request, i.e: not implemented / a PR.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions