Skip to content

incr.comp.: Update fingerprint-based auto tests for red/green tracking. #44924

Closed
@michaelwoerister

Description

@michaelwoerister

The incr. comp. system computes fingerprints (hashes) of various things and then uses these fingerprints to check if something has changed in comparison to the previous compilation session. The test cases in src/test/incremental/hashes test, at a fine-grained level, that various changes in the source code lead to changed fingerprints of various intermediate results.
Before red/green change tracking (implemented in #44901) the compiler only computed fingerprints for the inputs of a program (i.e. the HIR) and the things exported to crate metadata. With the new tracking system we also compute hashes for almost all intermediate results, but the test cases in src/test/incremental/hashes do not reflect that yet.

A given item is tested by attaching a #[rustc_clean] attribute to them for expressing the expectation that the item's fingerprint has not changed, or by attaching a #[rustc_dirty] attribute if the fingerprint should have changed. These attributes have two arguments:

  • the label determines which kind of hash we are interested in (i.e. the DepNode), and
  • the cfg argument says in which compilation session this assumption should be tested.

So, for example, if we want to assert that the fingerprint of the optimized MIR of a given function foo has changed between the first and the second compilation session and has not changed between sessions 2 and 3, we can do so as follows:

#[cfg(cfail1)]
fn foo() {
    // ...
}

#[rustc_dirty(label="MirOptimized", cfg="cfail2")]
#[rustc_clean(label="MirOptimized", cfg="cfail3")]
#[cfg(not(cfail1))]
fn foo() {
    // ...
}

Since #45104 we also have a more concise way of expressing these assertions. The following snippet of code tests that all relevant results are clean, except for "MirOptimized". This obviates the need to exhaustively list all DepKinds that should be checked. The testing framework knows which are relevant for the item the #[rustc_clean] or #[rustc_dirty] attribute is attached to.

#[cfg(cfail1)]
fn foo() {
    // ...
}

#[rustc_clean(except="MirOptimized", cfg="cfail2")]
#[rustc_clean(cfg="cfail3")]
#[cfg(not(cfail1))]
fn foo() {
    // ...
}

The #[cfg] attributes attached to the function specify which version gets compiled in which compilation session (see also #36674 for another description of how these tests work). The possible values for the label argument are those DepNode variants that have a single DefId argument.

At the time of writing, there are 134 kinds of dependency nodes and it would be overkill to test fingerprints for all of these. But, depending on the kind of item under test, there are a few key ones that we should verify:

Free-standing Functions and Methods

These represent executable code, so we want to test their MIR:

  • MirValidated
  • MirOptimized

Callers will depend on the signature of these items, so we better test

  • TypeOfItem,
  • GenericsOfItem,
  • PredicatesOfItem, and
  • FnSignature.

And a big part of compilation (that we eventually want to cache) is type inference information:

  • TypeckTables

For methods, we can also check

  • AssociatedItems

which is a bit misnamed and actually describes the ty::AssociatedItem descriptor of the method.

Struct, Enum, and Union Definitions

For these we should at least test

  • TypeOfItem,
  • GenericsOfItem, and
  • PredicatesOfItem.

in addition to Hir and HirBody. Note that changing the type of a
field does not change the type of the struct or enum, but adding/removing
fields or changing a fields name or visibility does.

Struct/Enum/Unions Fields

Fields are kind of separate from their containers, as they can change independently from them. We should at least check

  • TypeOfItem for these.

Trait Definitions

For these we'll want to check

  • TraitDefOfItem,
  • TraitImpls,
  • SpecializationGraph,
  • ObjectSafety,
  • AssociatedItemDefIds,
  • GenericsOfItem, and
  • PredicatesOfItem

(Trait) Impls

For impls we'll want to check

  • ImplTraitRef,
  • AssociatedItemDefIds, and
  • GenericsOfItem.

Associated Items

For associated items (types, constants, and methods) we should check

  • TraitOfItem,
  • AssociatedItems.

Test Files to Update

The existing tests can be found in the src/test/incremental/hashes directory. A description of how the tests were setup initially can be found in issue #36674. The basic testing strategy should stay the same -- the goal here is to add the #[rustc_dirty]/#[rustc_clean] attributes for the labels listed above. The test suite can be executed by running ./x.py test --stage 1 src/test/incremental.

If you come a across an instance where you are not sure if it should be dirty or clean, or the compiler produces a result that's different from your expectation, feel free to leave a comment below or ask on gitter or IRC.

If you want to take on updating a specific test file, leave a comment below and I'll mark it has taken.

Good luck! :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-incr-compArea: Incremental compilationA-testsuiteArea: The testsuite used to check the correctness of rustcC-cleanupCategory: PRs that clean code up or issues documenting cleanup.E-mentorCall for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.WG-incr-compWorking group: Incremental compilation

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions