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

Fixes to mir dataflow #33667

Merged
merged 48 commits into from May 25, 2016

Conversation

Projects
None yet
8 participants
@pnkfelix
Copy link
Member

pnkfelix commented May 16, 2016

Fixes to mir dataflow

This collects a bunch of changes to rustc_borrowck::borrowck::dataflow (which others have pointed out should probably migrate to some crate that isn't tied to the borrow-checker -- but I have not attempted that here, especially since there are competing approaches to dataflow that we should also evaluate).

These changes:

  1. Provide a family of related analyses: MovingOutStatements (which is what the old AST-based dataflo computed), as well as MaybeInitialized, MaybeUninitalized, and DefinitelyInitialized.
    • (The last two are actually inverses of each other; we should pick one and drop the other.)
  2. Fix bugs in the pre-existing analysis implementation, which was untested and thus some obvious bugs went unnoticed, which brings us to the third point:
  3. Add a unit test infrastructure for the MIR dataflow analysis.
    • The tests work by adding a new intrinsic that is able to query the analysis state for a particular expression (technically, a particular L-value).
    • See the examples in compile-fail/mir-dataflow/inits-1.rs and compile-fail/mir-dataflow/uninits-1.rs
    • These tests are only checking the results for MaybeInitialized, MaybeUninitalized, and DefinitelyInitialized; I am not sure if it will be feasible to generalize this testing strategy to the MovingOutStatements dataflow operator.

pnkfelix added some commits Apr 28, 2016

fix bug in `debug!` output from `rustc::middle::dataflow`
(bug was cut/pasted into `rustc_borrowck::bitslice`, so I fixed it
there as well.)
`rustc_mir::pretty` refactoring: break `fn write_fn_intro` into two r…
…outines.

(The crucial thing these changes are working toward (but are not yet
in this commit) is a way to pretty-print MIR without having the
`NodeId` for that MIR in hand.)
`borrowck::mir::gather_moves`: create MovePaths for lvalues even if u…
…nreferenced.

includes misc bug fixes and removal of useless code.
Revised mir-dataflow.
Incorporates many fixes contributed by arielb1.

----

revise borrowck::mir::dataflow code to allow varying domain for bitvectors.

This particular code implements the `BitDenotation` trait for three
analyses:

 * `MovingOutStatements`, which, like `borrowck::move_data`, maps each
   bit-index to a move instruction, and a 1 means "the effect of this
   move reaches this point" (and the assigned l-value, if a scoped
   declaration, is still in scope).

 * `MaybeInitializedLvals`, which maps each bit-index to an l-value.
   A 1 means "there exists a control flow path to this point that
   initializes the associated l-value."

 * `MaybeUninitializedLvals`, which maps each bit-index to an l-value
   A 1 means "there exists a control flow path to this point that
   de-initializes the associated l-value."

----

Revised `graphviz` dataflow-rendering support in `borrowck::mir`.

One big difference is that this code is now parameterized over the
`BitDenotation`, so that it can be used to render dataflow results
independent of how the dataflow bitvectors are interpreted; see where
reference to `MoveOut` is replaced by the type parameter `D`.

----

Factor out routine to query subattributes in `#[rustc_mir(..)]`.

(Later commits build upon this for some unit testing and instrumentation.)

----

thread through a tcx so that I can query types of lvalues as part of analysis.

----

Revised `BitDenotation::Ctxt`, allowing variation beyond `MoveData`.

The main motivation is to ease threading through a `TyCtxt`.

(In hindsight it might have been better to instead attach the `TyCtxt`
to each of the different dataflow implementations, but that would
require e.g. switching away from having a `Default` impl, so I am
leaving that experiment for another time.)
Little unit tests for MIR dataflow analysis.
These use new `rustc_peek` (whose semantics is driven by attribute
attached to fn).
Adding magic `rustc_peek` intrinsic that other code can repurpose to
its own needs based on attributes attached to the function where it
appears.
Add ability to unit-test dataflow results via `rustc_peek` intrinsic.
(The static semantics of `rustc_peek` is derived from attributes
attached to the function being compiled; in this case,
`rustc_peek(&expr)` observes the dataflow state for the l-value
`expr`.)
@rust-highfive

This comment has been minimized.

Copy link
Collaborator

rust-highfive commented May 16, 2016

r? @Aatch

(rust_highfive has picked a reviewer for you, use r? to override)

@pnkfelix

This comment has been minimized.

Copy link
Member Author

pnkfelix commented May 16, 2016

@rust-highfive rust-highfive assigned nikomatsakis and unassigned Aatch May 16, 2016

@pnkfelix pnkfelix referenced this pull request May 16, 2016

Merged

[MIR] non-zeroing drop #33622

/// attached to the function.
///
/// For example, dataflow uses this to inject static assertions so
/// that `rustc_oeek(potentially_uninitialized)` would actually

This comment has been minimized.

@sanxiyn

sanxiyn May 16, 2016

Member

Typo: oeek.

// it will always appear like Lvalues are initialized; e.g. in
// a case like:
//
// <bitset maps var1 to 0>

This comment has been minimized.

@arielb1

arielb1 May 16, 2016

Contributor

What is this talking about? rustc_peek(&var1) should be translated to

        tmp1 = &var0;
        tmp0 = rustc_peek(tmp1) -> bb1;

If it copies the lvalue before taking a reference, that would be totally broken (consider Cell). So it doesn't. Unless something funny is going on here.

This comment has been minimized.

@pnkfelix

pnkfelix May 16, 2016

Author Member

Oh you're right; I didn't update that comment correctly (it was originally authored for a different version of rustc_peek that looked like rustc_peek(var1) (instead of rustc_peek(&var1)), and I erroneously translated its content without thinking about what the actual translation of rustc_peek(&var1) is).

(The original version of rustc_peek had a more fragile API, where one would write rustc_peek(expr) (instead of rustc_peek(&expr)).)


The main point I'm trying to make is this: even with the corrected translation that you wrote:

tmp1 = &var0;
tmp0 = rustc_peek(tmp1) -> bb1;

The normal (naive) dataflow GEN/KILL sets are going to say that tmp1 is initialized. If the semantics of rustc_peek were a naive "look directly at your argument's dataflow bit state", then rustc_peek would query the state of tmp1, which would always indicate that it has been initialized under the naive rules.

So I had written a comment about a work-around I was using for the naive system.


With the new API the story is somewhat related, but the example I gave in the comment isn't helping explain things.

I will update it.

This comment has been minimized.

@arielb1

arielb1 May 16, 2016

Contributor

I guessed. Yeah, you want the state of var0, not of tmp1. I think you could just go to the initialization of tmp1 and use that.

This comment has been minimized.

@nikomatsakis

nikomatsakis May 17, 2016

Contributor

(I also expected to find the definition of tmp1 when @pnkfelix and I discussed in the work week; but this solution works too.)

This comment has been minimized.

@nikomatsakis

nikomatsakis May 17, 2016

Contributor

I guess the reason not to do rustc_peek(&x) is that this code runs before borrowck anyway, so e.g. moves don't matter? never mind, I see you are doing &x

/// you if an l-value *might* be uninitialized at a given point in the
/// control flow. But `MovingOutStatements` also includes the added
/// data of *which* particular statement causing the deinitialization
/// that the borrow checker's error meessage may need to report.

This comment has been minimized.

@nikomatsakis

nikomatsakis May 17, 2016

Contributor

Intention is to compute this lazilly, right? Maybe note that?


These unit tests check the dataflow analysis by embedding calls to a
special `rustc_peek` intrinsic within the code, in tandem with an
attribute `#[rustc_mir(rustc_peek_maybe_init)]` (*). With that

This comment has been minimized.

@nikomatsakis

nikomatsakis May 17, 2016

Contributor

Nit: use (\*) to disable markdown interpretation

@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented May 17, 2016

I gave this a brief review and it seems quite reasonable. I'll try to read a bit more in detail tomorrow though.

pnkfelix added some commits May 17, 2016

`mir::dataflow::sanity_check`: removed hackish `tmp = val` propagatio…
…n code.

(it was an artifact of an earlier design of the `rustc_peek` API, but
its totally unnecessary now.)

pnkfelix added some commits May 24, 2016

`mir::dataflow::sanity_check`: extract an `fn each_block` to simplify…
… presentation.

As a drive-by: unified pair of match arms that flowed to `bug!`, and
replaced `bug!` invocation with a diagnostic `span_err` invocation.
Removed `type Bit` and `fn interpret` items from `trait BitDenotation`.
Also got rid of the `trait HasMoveData`, since I am now just imposing
the constraint that `BitDenotation<Ctxt=MoveData<'tcx>>` where
necessary instead.
threaded a `ty::ParameterEnvironment` for the current node id via the…
… associated Ctxt item.

used this to address a long-standing wart/bug in how filtering-out of
values with type impl'ing `Copy` was done.
@pnkfelix

This comment has been minimized.

Copy link
Member Author

pnkfelix commented May 24, 2016

@arielb1 @nikomatsakis The one thing I'd like someone to take a look at is the new indexed_set abstraction that I am using as a layer atop bitvectors.

My goal was to have a breakdown analogous to Vec<T>/[T] (namely, one is owned, and the other is unsized and can be used with either &[T] or &mut [T]).

My analogous types in indexed_set are OwnIdxSet<T> and IdxSet<T>, where in both cases the phantom type parameter T must implement the trait Idx (also introduced in this module), since the T must be able to be mapped to a usize for the subsequent indexing into the bitvector.

My problem is that I used a little bit of nasty unsafe code to accomplish this goal. :( (Namely, I did not see a way to turn a &[usize] slice reference into a &IdxSet<T> besides using transmute.)


Maybe the right answer here is to not attempt to provide an unsized IdxSet<T> type, but instead to provide RefIdxSet<'a, T> and MutIdxSet<'a, T>. I briefly considered this but for some reason my initial drafts seemed really unappealing. Still, if either of you wants to insist that this be composed of 100% safe code, I can go back in and do that replacement.

/// "transfer-function" represnting the overall-effect of the
/// block, represented via GEN and KILL sets.
///
/// The statement here is `idx_stmt.1`; `idx_stmt.0` is just

This comment has been minimized.

@pnkfelix

pnkfelix May 24, 2016

Author Member

(note to self: this, like many comments, is out of date and needs to be fixed to reflect changes to the API here.)

// requires a transmute relying on representation guarantees that may
// not hold in the future.

pub struct IdxSet<T: Idx> {

This comment has been minimized.

@nikomatsakis

nikomatsakis May 25, 2016

Contributor

I'd have appreciated a comment explaining what T is.

fn idx(&self) -> usize;
}

pub struct OwnIdxSet<T: Idx> {

This comment has been minimized.

@nikomatsakis

nikomatsakis May 25, 2016

Contributor

I would prefer the name IdxSetBuf, following the precedent of PathBuf and Path

This comment has been minimized.

@pnkfelix

pnkfelix May 25, 2016

Author Member

I'll change the name.

}

pub struct OwnIdxSet<T: Idx> {
_pd: PhantomData<fn(&[T], usize) -> &T>,

This comment has been minimized.

@nikomatsakis

nikomatsakis May 25, 2016

Contributor

it'd be good to explain where this phantomdata comes from, since it's clearly quite deliberate...

This comment has been minimized.

@nikomatsakis

nikomatsakis May 25, 2016

Contributor

In terms of the intuition for phantdomdata, I think of this is basically being a more optimized version of a FnvHashSet<T>, hence I would expect PhantomData<T>, effectively. Not that this matters much in practice. :)

This comment has been minimized.

@pnkfelix

pnkfelix May 25, 2016

Author Member

Oh this PhantomData got things mixed up; you are right that PhantomData<T> would (probably) suffice here, (but I don't think its 100% accurate).

(I had originally attempted to put the actual represented type into the interface, e.g. make a connection from MovePathIndex to MovePath, and then the goal was to have a phantom data that would say Fn(&[MovePath], MovePathIndex) -> Option<&MovePath>. But as I reworked the API and removed references to the referenced data type, I didn't notice that I had mixed up the roles of T (the index type) and the referenced data type (now unnamed here).

I will replace with PhantomData<Fn(&T)> since that more properly reflects the relationship between the IdxSet and the Idx that each is fed.

This comment has been minimized.

@eddyb

eddyb May 25, 2016

Member

IIRC Fn(A) is invariant over A so maybe you want fn(A)?

@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented May 25, 2016

My feeling is that we should land this PR -- it seems like it contains many clear improvements and bug fixes, and it's big enough that we could keep iterating forever with nitpicks. I'd rather land it and then address further problems in follow-up PRs. @arielb1 -- thoughts?

@arielb1

This comment has been minimized.

Copy link
Contributor

arielb1 commented May 25, 2016

I'll go for that

@bors r+

@bors

This comment has been minimized.

Copy link
Contributor

bors commented May 25, 2016

📌 Commit df5c116 has been approved by arielb1

@bors

This comment has been minimized.

Copy link
Contributor

bors commented May 25, 2016

⌛️ Testing commit df5c116 with merge 34fd686...

bors added a commit that referenced this pull request May 25, 2016

Auto merge of #33667 - pnkfelix:fixes-to-mir-dataflow, r=arielb1
Fixes to mir dataflow

Fixes to mir dataflow

This collects a bunch of changes to `rustc_borrowck::borrowck::dataflow` (which others have pointed out should probably migrate to some crate that isn't tied to the borrow-checker -- but I have not attempted that here, especially since there are competing approaches to dataflow that we should also evaluate).

These changes:
 1. Provide a family of related analyses: MovingOutStatements (which is what the old AST-based dataflo computed), as well as MaybeInitialized, MaybeUninitalized, and DefinitelyInitialized.
   * (The last two are actually inverses of each other; we should pick one and drop the other.)
 2. Fix bugs in the pre-existing analysis implementation, which was untested and thus some obvious bugs went unnoticed, which brings us to the third point:
 3. Add a unit test infrastructure for the MIR dataflow analysis.
   * The tests work by adding a new intrinsic that is able to query the analysis state for a particular expression (technically, a particular L-value).
   * See the examples in compile-fail/mir-dataflow/inits-1.rs and compile-fail/mir-dataflow/uninits-1.rs
   * These tests are only checking the results for MaybeInitialized, MaybeUninitalized, and DefinitelyInitialized; I am not sure if it will be feasible to generalize this testing strategy to the MovingOutStatements dataflow operator.

@bors bors merged commit df5c116 into rust-lang:master May 25, 2016

1 check passed

homu Test successful
Details
@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented May 25, 2016

Yay!
On May 25, 2016 6:38 PM, "bors" notifications@github.com wrote:

☀️ Test successful - auto-linux-32-nopt-t
http://buildbot.rust-lang.org/builders/auto-linux-32-nopt-t/builds/9268,
auto-linux-32-opt
http://buildbot.rust-lang.org/builders/auto-linux-32-opt/builds/9305,
auto-linux-32cross-opt
http://buildbot.rust-lang.org/builders/auto-linux-32cross-opt/builds/388,
auto-linux-64-cargotest
http://buildbot.rust-lang.org/builders/auto-linux-64-cargotest/builds/580,
auto-linux-64-cross-armhf
http://buildbot.rust-lang.org/builders/auto-linux-64-cross-armhf/builds/391,
auto-linux-64-cross-armsf
http://buildbot.rust-lang.org/builders/auto-linux-64-cross-armsf/builds/395,
auto-linux-64-cross-freebsd
http://buildbot.rust-lang.org/builders/auto-linux-64-cross-freebsd/builds/394,
auto-linux-64-cross-netbsd
http://buildbot.rust-lang.org/builders/auto-linux-64-cross-netbsd/builds/394,
auto-linux-64-debug-opt
http://buildbot.rust-lang.org/builders/auto-linux-64-debug-opt/builds/2536,
auto-linux-64-nopt-t
http://buildbot.rust-lang.org/builders/auto-linux-64-nopt-t/builds/9243,
auto-linux-64-opt
http://buildbot.rust-lang.org/builders/auto-linux-64-opt/builds/9229,
auto-linux-64-opt-mir
http://buildbot.rust-lang.org/builders/auto-linux-64-opt-mir/builds/815,
auto-linux-64-opt-rustbuild
http://buildbot.rust-lang.org/builders/auto-linux-64-opt-rustbuild/builds/1200,
auto-linux-64-x-android-t
http://buildbot.rust-lang.org/builders/auto-linux-64-x-android-t/builds/9225,
auto-linux-cross-opt
http://buildbot.rust-lang.org/builders/auto-linux-cross-opt/builds/2436,
auto-linux-musl-64-opt
http://buildbot.rust-lang.org/builders/auto-linux-musl-64-opt/builds/4380,
auto-mac-32-opt
http://buildbot.rust-lang.org/builders/auto-mac-32-opt/builds/9285,
auto-mac-64-nopt-t
http://buildbot.rust-lang.org/builders/auto-mac-64-nopt-t/builds/9290,
auto-mac-64-opt
http://buildbot.rust-lang.org/builders/auto-mac-64-opt/builds/9255,
auto-mac-64-opt-rustbuild
http://buildbot.rust-lang.org/builders/auto-mac-64-opt-rustbuild/builds/1209,
auto-mac-cross-ios-opt
http://buildbot.rust-lang.org/builders/auto-mac-cross-ios-opt/builds/393,
auto-win-gnu-32-nopt-t
http://buildbot.rust-lang.org/builders/auto-win-gnu-32-nopt-t/builds/4323,
auto-win-gnu-32-opt
http://buildbot.rust-lang.org/builders/auto-win-gnu-32-opt/builds/4330,
auto-win-gnu-32-opt-rustbuild
http://buildbot.rust-lang.org/builders/auto-win-gnu-32-opt-rustbuild/builds/1218,
auto-win-gnu-64-nopt-t
http://buildbot.rust-lang.org/builders/auto-win-gnu-64-nopt-t/builds/4323,
auto-win-gnu-64-opt
http://buildbot.rust-lang.org/builders/auto-win-gnu-64-opt/builds/4309,
auto-win-msvc-32-cross-opt
http://buildbot.rust-lang.org/builders/auto-win-msvc-32-cross-opt/builds/679,
auto-win-msvc-32-opt
http://buildbot.rust-lang.org/builders/auto-win-msvc-32-opt/builds/3530,
auto-win-msvc-64-cargotest
http://buildbot.rust-lang.org/builders/auto-win-msvc-64-cargotest/builds/583,
auto-win-msvc-64-opt
http://buildbot.rust-lang.org/builders/auto-win-msvc-64-opt/builds/4321,
auto-win-msvc-64-opt-mir
http://buildbot.rust-lang.org/builders/auto-win-msvc-64-opt-mir/builds/822,
auto-win-msvc-64-opt-rustbuild
http://buildbot.rust-lang.org/builders/auto-win-msvc-64-opt-rustbuild/builds/1217


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#33667 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.