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

Use the recorded types in MIR to determine generator auto-trait implementations #65782

Closed
wants to merge 15 commits into from

Commits on Dec 7, 2019

  1. Use the recorded types in MIR to determine generator auto-trait imple…

    …mentations
    
    When we construct a generator type, we build a `ty::GeneratorWitness`
    from a list of types that may live across a suspend point. These types are
    used to determine the 'constitutent types' for a generator when
    selecting an auto-trait predicate. Any types appearing in the
    GeneratorWitness are required to implement the auto trait (e.g. `Send`
    or `Sync`).
    
    This analysis
    is based on the HIR - as a result, it is unable to take liveness of
    variables into account. This often results in unnecessary bounds being
    computing (e.g requiring that a `Rc` be `Sync` even if it is dropped
    before a suspend point), making generators and `async fn`s much less
    ergonomic to write.
    
    This commit uses the generator MIR to determine the actual 'constituent
    types' of a generator. Specifically, a type in the generator witness is
    considered to be a 'constituent type' of the witness if it appears in
    the `field_tys` of the computed `GeneratorLayout`. Any type which is
    stored across an suspend point must be a constituent type (since it could
    be used again after a suspend), while any type that is not stored across
    a suspend point cannot possible be a constituent type (since it is
    impossible for it to be used again).
    
    By re-using the existing generator layout computation logic, we get some
    nice properties for free:
    * Types which are dead before the suspend point are not considered
    constituent types
    * Types without Drop impls not considered constituent types if their
    scope extends across an await point (assuming that they are never used
    after an await point).
    
    Note that this only affects `ty::GeneratorWitness`, *not*
    `ty::Generator` itself. Upvars (captured types from the parent scope)
    are considered to be constituent types of the base `ty::Generator`, not
    the inner `ty::GeneratorWitness`. This means that upvars are always
    considered constituent types - this is because by defintion, they always
    live across the first implicit suspend point.
    
    -------
    
    Implementation:
    
    The most significant part of this PR is the introduction of a new
    'delayed generator witness mode' to `TraitEngine`. As @nikomatsakis
    pointed out, attmepting to compute generator MIR during type-checking
    results in the following cycle:
    
    1. We attempt to type-check a generator's parent function
    2. During type checking of the parent function, we record a
       predicate of the form `<generator>: AutoTrait`
    3. We add this predicate to a `TraitEngine`, and attempt to fulfill it.
    4. When we atempt to select the predicate, we attempt to compute the MIR
       for `<generator>`
    5. The MIR query attempts to compute `type_of(generator_def_id)`, which
       results in us attempting to type-check the generator's parent function.
    
    To break this cycle, we defer processing of all auto-trait predicates
    involving `ty::GeneratorWitness`. These predicates are recorded in the
    `TypeckTables` for the parent function. During MIR type-checking of the
    parent function, we actually attempt to fulfill these predicates,
    reporting any errors that occur.
    
    The rest of the PR is mostly fallout from this change:
    
    * `ty::GeneratorWitness` now stores the `DefId` of its generator. This
    allows us to retrieve the MIR for the generator when `SelectionContext`
    processes a predicate involving a `ty::GeneratorWitness`
    * Since we now store `PredicateObligations` in `TypeckTables`, several
    different types have now become `RustcEncodable`/`RustcDecodable`. These
    are purely mechanical changes (adding new `#[derives]`), with one
    exception - a new `SpecializedDecoder` impl for `List<Predicate>`.
    This was essentially identical to other `SpecializedDecoder` imps, but it
    would be good to have someone check it over.
    * When we delay processing of a `Predicate`, we move it from one
    `InferCtxt` to another. This requires us to prevent any inference
    variables from leaking out from the first `InferCtxt` - if used in
    another `InferCtxt`, they will either be non-existent or refer to the
    the wrong variable. Fortunately, the predicate itself has no region
    variables - the `ty::GeneratorWitness` has only late-bound regions,
    while auto-traits have no generic parameters whatsoever.
    
    However, we still need to deal with the `ObligationCause` stored by the
    `PredicateObligation`. An `ObligationCause` (or a nested cause) may have
    any number of region variables stored inside it (e.g. from stored
    types). Luckily, `ObligationCause` is only used for error reporting, so
    we can safely erase all regions variables from it, without affecting the
    actual processing of the obligation.
    
    To accomplish this, I took the somewhat unusual approach of implementing
    `TypeFoldable` for `ObligationCause`, but did *not* change the `TypeFoldable`
    implementation of `Obligation` to fold its contained
    `ObligationCause. Other than this one odd case, all other callers of
    `TypeFoldable` have no interest in folding an `ObligationCause`. As a
    result, we explicitly fold the `ObligationCause` when computing our
    deferred generator witness predicates. Since `ObligationCause` is only
    used for displaying error messages, the worst that can happen is that a
    slightly odd error message is displayed to a user.
    
    With this change, several tests now have fewer errors than they did
    previously, due to the improved generator analysis. Unfortunately, this
    does not resolve issue rust-lang#64960. The MIR generator transformation stores
    format temporaries in the generator, due to the fact that the `format!`
    macro takes a reference to them. As a result, they are still considered
    constituent types of the `GeneratorWitness`, and are still required to
    implement `Send` and `Sync.
    
    * I've changed the pretty-printing of `ty::GeneratorWitness` to print
    out its generator DefId, as well as the word `witness`. This makes
    debugging issues related to the computation of constituent types much
    simpler.
    
    As a final note, this PR contains one unrelated change - all generators
    now implement `Freeze` unconditionally. I've opened a separate PR
    containing this change - however, it's necessary to allow this branch to
    compile without cycle errors. I've left it in this PR to make it easy to
    test out this branch on actual code. Assuming that it is accepted, this
    PR will be rebased against `master` when it is merged. Otherwise, I'll
    need to figure out a different approach to generator
    const-qualification.
    Aaron1011 committed Dec 7, 2019
    Configuration menu
    Copy the full SHA
    9303810 View commit details
    Browse the repository at this point in the history
  2. Configuration menu
    Copy the full SHA
    78159e1 View commit details
    Browse the repository at this point in the history
  3. Make generators !Freeze

    Aaron1011 committed Dec 7, 2019
    Configuration menu
    Copy the full SHA
    46e2d2c View commit details
    Browse the repository at this point in the history
  4. Fix rebase fallout

    Aaron1011 committed Dec 7, 2019
    Configuration menu
    Copy the full SHA
    5ce4306 View commit details
    Browse the repository at this point in the history
  5. Update stderr files

    Aaron1011 committed Dec 7, 2019
    Configuration menu
    Copy the full SHA
    6840bc4 View commit details
    Browse the repository at this point in the history
  6. Configuration menu
    Copy the full SHA
    bea0a42 View commit details
    Browse the repository at this point in the history
  7. Configuration menu
    Copy the full SHA
    2b2986d View commit details
    Browse the repository at this point in the history
  8. Configuration menu
    Copy the full SHA
    e4684c7 View commit details
    Browse the repository at this point in the history
  9. Some cleanup

    Aaron1011 committed Dec 7, 2019
    Configuration menu
    Copy the full SHA
    97116eb View commit details
    Browse the repository at this point in the history
  10. Update tests

    Aaron1011 committed Dec 7, 2019
    Configuration menu
    Copy the full SHA
    b3e6f8d View commit details
    Browse the repository at this point in the history
  11. Add check-pass test

    Aaron1011 committed Dec 7, 2019
    Configuration menu
    Copy the full SHA
    0c92b83 View commit details
    Browse the repository at this point in the history
  12. Apply nits

    Co-Authored-By: Ralf Jung <post@ralfj.de>
    Aaron1011 and RalfJung committed Dec 7, 2019
    Configuration menu
    Copy the full SHA
    b891e49 View commit details
    Browse the repository at this point in the history
  13. Configuration menu
    Copy the full SHA
    87fc30d View commit details
    Browse the repository at this point in the history
  14. Fix missed nit

    Co-Authored-By: Ralf Jung <post@ralfj.de>
    Aaron1011 and RalfJung committed Dec 7, 2019
    Configuration menu
    Copy the full SHA
    8f58a71 View commit details
    Browse the repository at this point in the history
  15. Configuration menu
    Copy the full SHA
    71c9dd5 View commit details
    Browse the repository at this point in the history