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

Automatically make "boring" types noop-traversable #117620

Conversation

eggyal
Copy link
Contributor

@eggyal eggyal commented Nov 5, 2023

This is a reincarnation of #108214 (or at least, what that PR ultimately became). It may be easier to review by commit, albeit there are quite a few of them.

Terminology

I refer to:

  • folds and visits as "traversals"
  • the types that are folded or visited as "traversables"
  • the folders and visitors as "traversers"
  • traversables on which traversers can act as "interesting" (there are currently only five: binders, types, consts, regions and predicates) and all others as "boring" (traversals of which are no-ops)
  • the TypeFoldable and TypeVisitable traits (and only these traits) as the "traversable traits"

Content

This PR:

  • introduces an auto-trait, rustc_type_ir::BoringTraversable, that is then "unimplemented" on the interesting types (and thereby remains implemented only on, but on all, boring types);
  • introduces a macro, rustc_type_ir::noop_traversal_if_boring that uses auto-deref specialisation to:
    • perform a no-op traversal if the type is boring (thereby obviating all need for most boring types to implement the traversable traits—and indeed such implementations are removed) or
    • delegate to the relevant traversable trait implementation otherwise;
  • replaces the traversable traits' derive macros' distinct helper attributes (introduced in Use derive attributes for uninteresting traversals #108040) with a unified #[skip_traversal] helper attribute that requires a justifying reason to be provided (this does mean that the different types of traversal can no longer be independently skipped, but nowhere is that currently required or ever expected to be);
    • the derive macros by default refuse to be implemented on boring types as specialisation usually negates any need—but this can be overridden with a #[skip_traversal(despite_boring_because = "<reason>")] attribute;
  • updates those derive macros to not only to use the above specialisation macro but also (if the traversable is not parameterised by a 'tcx lifetime) generates implementations that are generic over the interner
  • uses those derive macros in place of remaining usages of the TrivialTypeTraversal macros and some explicit implementations; and
  • a few other minor relevant refactorings.

Commentary

One limitation of auto-deref specialisation is that it cannot work for unconstrained generic types. Therefore, absent real specialisation, implementations of the traversable traits must constrain their generics:

  • by default, the derive macros constrain every generic field type to implementors of the relevant traversable trait—however this can be modified on a field (or variant) basis with a #[skip_traversal(because_boring)] attribute so that the types of such fields must instead implement the BoringTraversable trait (and the field is not traversed)
    • the constraints are applied to the field types rather than the generic type parameters to achieve a "perfect derive" that not only avoids having to propagate #[skip_traversal] annotations into wrapping types but also works with associated types and other esoteric situations—however this could result in trait solver cycles if interesting generic field types are one day involved in recursive type definitions;
    • for exceptionally rare cases where traversal of an item/variant/field should be a no-op despite it being (potentially, if generic) interesting, it can be annotated with the explicit and deliberately cumbersome #[skip_traversal(despite_potential_miscompilation_because = "<reason>")] (which produces a no-op without adding any constraint).
  • (the few remaining) explicit implementations of the traversable traits over generic types are similarly constrained
    • indeed, for generic tuples all element types must implement the trait—however, since (most) boring types no longer do, this unfortunately means that the implementations are not applicable to tuples with boring elements and hence some such tuples have been replaced with more concrete newtypes that have appropriate constraints: 14b85cb is a particularly hefty example of this (where tuples containing Span are replaced with Spanned<T> instead).

r? @lcnr
cc @oli-obk

@rustbot label A-query-system T-compiler WG-trait-system-refactor

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. WG-trait-system-refactor The Rustc Trait System Refactor Initiative labels Nov 5, 2023
@rustbot
Copy link
Collaborator

rustbot commented Nov 5, 2023

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

The Miri subtree was changed

cc @rust-lang/miri

Some changes occurred in abstract_const.rs

cc @BoxyUwU

These commits modify compiler targets.
(See the Target Tier Policy.)

Some changes occurred to the core trait solver

cc @rust-lang/initiative-trait-system-refactor

Some changes occurred to the CTFE / Miri engine

cc @rust-lang/miri

Some changes occurred in engine.rs, potentially modifying the public API of ObligationCtxt.

cc @lcnr, @compiler-errors

This PR changes MIR

cc @oli-obk, @RalfJung, @JakobDegen, @davidtwco, @celinval, @vakaras

Some changes occurred to the CTFE / Miri engine

cc @rust-lang/miri

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

These commits modify the Cargo.lock file. Unintentional changes to Cargo.lock can be introduced when switching branches and rebasing PRs.

If this was unintentional then you should revert the changes before this PR is merged.
Otherwise, you can ignore this comment.

@bors
Copy link
Contributor

bors commented Nov 6, 2023

☔ The latest upstream changes (presumably #117578) made this pull request unmergeable. Please resolve the merge conflicts.

@lcnr
Copy link
Contributor

lcnr commented Nov 6, 2023

will review either during the weekend or next week, until then

@bors try @rust-timer queue

@rust-timer
Copy link
Collaborator

Awaiting bors try build completion.

@rustbot label: +S-waiting-on-perf

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Nov 6, 2023
@bors
Copy link
Contributor

bors commented Nov 6, 2023

🔒 Merge conflict

This pull request and the master branch diverged in a way that cannot be automatically merged. Please rebase on top of the latest master branch, and let the reviewer approve again.

How do I rebase?

Assuming self is your fork and upstream is this repository, you can resolve the conflict following these steps:

  1. git checkout automatically_make_boring_types_noop-traversable (switch to your branch)
  2. git fetch upstream master (retrieve the latest master)
  3. git rebase upstream/master -p (rebase on top of it)
  4. Follow the on-screen instruction to resolve conflicts (check git status if you got lost).
  5. git push self automatically_make_boring_types_noop-traversable --force-with-lease (update this PR)

You may also read Git Rebasing to Resolve Conflicts by Drew Blessing for a short tutorial.

Please avoid the "Resolve conflicts" button on GitHub. It uses git merge instead of git rebase which makes the PR commit history more difficult to read.

Sometimes step 4 will complete without asking for resolution. This is usually due to difference between how Cargo.lock conflict is handled during merge and rebase. This is normal, and you should still perform step 5 to update this PR.

Error message
Auto-merging compiler/rustc_type_ir/src/predicate_kind.rs
CONFLICT (content): Merge conflict in compiler/rustc_type_ir/src/predicate_kind.rs
Auto-merging compiler/rustc_type_ir/src/lib.rs
Auto-merging compiler/rustc_type_ir/src/const_kind.rs
Auto-merging compiler/rustc_type_ir/src/canonical.rs
CONFLICT (content): Merge conflict in compiler/rustc_type_ir/src/canonical.rs
Auto-merging compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
Removing compiler/rustc_macros/src/type_visitable.rs
Removing compiler/rustc_macros/src/type_foldable.rs
Auto-merging Cargo.lock
Automatic merge failed; fix conflicts and then commit the result.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Nov 8, 2023
[Auto-deref specialisation] is introduced to type library traversals,
the public API of which is via the `TriviallyTraverses` trait and the
`noop_if_trivially_traversable` macro.  Traversals invoked via the macro
will then be no-ops if the interner "trivially traverses" the type being
traversed *without requiring that type to implement the relevant
traversable trait*.

A further trait, `rustc_middle::ty::TriviallyTraversable`, is then
auto-implemented for types that do not contain anything that may be of
interest to traversers.  A generic implementation of the former trait is
then provided for the `TyCtxt` interner to indicate that it "trivially
traverses" all such `TriviallyTraversable` types.  This indirection is
necessary because auto-traits are unstable, whereas the type library is
intended to be stabilised.

Finally, the traversable derive macros are updated to utilise this
specialisation machinery, thereby avoiding any need for trivially
traversable fields (of types for which the traits are derived) to
implement the traversable traits.

[Auto-deref specialisation]: http://lukaskalbertodt.github.io/2019/12/05/generalized-autoref-based-specialization.html
BindingForm has been a no-op traversable ever since it was first added
in cac6126, but it was modified in 0193d1f to (optionally) contain a
Place without its implementations being updated to fold/visit such.

By using the derive macro to implement the visitable traits, we ensure
that all contained types of interest are folded/visited.
Ever since folding was first added in c5754f3, obligations have not
folded or visited their causes the ObligationCauseCode potentially
containing types that may be of interest to a folder or visitor.

By using the derive macro to implement the visitable traits, we ensure
that all contained types of interest are folded/visited.
Rather than being concerned with internals of ExternalConstraintsData
and PredefinedOpaquesData, we derive traversable impls for those types
to which we then delegate.
@eggyal eggyal force-pushed the automatically_make_boring_types_noop-traversable branch from a416685 to 419cc6d Compare November 8, 2023 21:25
@eggyal eggyal closed this Nov 8, 2023
@eggyal eggyal deleted the automatically_make_boring_types_noop-traversable branch November 8, 2023 21:25
@eggyal
Copy link
Contributor Author

eggyal commented Nov 8, 2023

meh. Github fail. Will open another.

@rust-log-analyzer
Copy link
Collaborator

The job mingw-check failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)
GITHUB_ACTION=__run_7
GITHUB_ACTIONS=true
GITHUB_ACTION_REF=
GITHUB_ACTION_REPOSITORY=
GITHUB_ACTOR=eggyal
GITHUB_API_URL=https://api.github.com
GITHUB_BASE_REF=master
GITHUB_ENV=/home/runner/work/_temp/_runner_file_commands/set_env_958c1bce-4ea3-4295-a32d-941bc884d540
GITHUB_EVENT_NAME=pull_request
GITHUB_EVENT_NAME=pull_request
GITHUB_EVENT_PATH=/home/runner/work/_temp/_github_workflow/event.json
GITHUB_GRAPHQL_URL=https://api.github.com/graphql
GITHUB_HEAD_REF=automatically_make_boring_types_noop-traversable
GITHUB_JOB=pr
GITHUB_PATH=/home/runner/work/_temp/_runner_file_commands/add_path_958c1bce-4ea3-4295-a32d-941bc884d540
GITHUB_REF=refs/pull/117620/merge
GITHUB_REF_NAME=117620/merge
GITHUB_REF_PROTECTED=false
---
GITHUB_SERVER_URL=https://github.com
GITHUB_SHA=3a51552a659030050396ad774ef84b61dda10e4c
GITHUB_STATE=/home/runner/work/_temp/_runner_file_commands/save_state_958c1bce-4ea3-4295-a32d-941bc884d540
GITHUB_STEP_SUMMARY=/home/runner/work/_temp/_runner_file_commands/step_summary_958c1bce-4ea3-4295-a32d-941bc884d540
GITHUB_TRIGGERING_ACTOR=eggyal
GITHUB_WORKFLOW_REF=rust-lang/rust/.github/workflows/ci.yml@refs/pull/117620/merge
GITHUB_WORKFLOW_SHA=3a51552a659030050396ad774ef84b61dda10e4c
GITHUB_WORKSPACE=/home/runner/work/rust/rust
GOROOT_1_19_X64=/opt/hostedtoolcache/go/1.19.13/x64
---
 Documenting rustc_lexer v0.0.0 (/checkout/compiler/rustc_lexer)
error: unclosed HTML tag `B`
  --> compiler/rustc_type_ir/src/interner.rs:76:19
   |
76 | /// * Self::Binder<B> for any type B
   |
   = note: `-D rustdoc::invalid-html-tags` implied by `-D warnings`
   = note: `-D rustdoc::invalid-html-tags` implied by `-D warnings`
   = help: to override `-D warnings` add `#[allow(rustdoc::invalid_html_tags)]`
   |
   |
76 | /// * `Self::Binder<B>` for any type B

error: could not document `rustc_type_ir`

Caused by:
Caused by:
  process didn't exit successfully: `/checkout/obj/build/bootstrap/debug/rustdoc --edition=2021 --crate-type lib --crate-name rustc_type_ir compiler/rustc_type_ir/src/lib.rs --target x86_64-unknown-linux-gnu -o /checkout/obj/build/x86_64-unknown-linux-gnu/stage0-rustc/x86_64-unknown-linux-gnu/doc -Zunstable-options --check-cfg 'values(feature)' --check-cfg 'names()' --check-cfg 'values()' --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat -C metadata=63b8e436a98fbcbc -L dependency=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-rustc/x86_64-unknown-linux-gnu/release/deps -L dependency=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-rustc/release/deps --extern bitflags=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-rustc/x86_64-unknown-linux-gnu/release/deps/libbitflags-94e2fae040b288a7.rmeta --extern derivative=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-rustc/release/deps/libderivative-b66331e40f3838a8.so --extern rustc_data_structures=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-rustc/x86_64-unknown-linux-gnu/release/deps/librustc_data_structures-db56e38ca7666c80.rmeta --extern rustc_index=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-rustc/x86_64-unknown-linux-gnu/release/deps/librustc_index-a137eaafbbe62977.rmeta --extern rustc_macros=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-rustc/release/deps/librustc_macros-a3009b08c66bb671.so --extern rustc_serialize=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-rustc/x86_64-unknown-linux-gnu/release/deps/librustc_serialize-045b105c01167fd7.rmeta --extern smallvec=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-rustc/x86_64-unknown-linux-gnu/release/deps/libsmallvec-720d9ea4f4342760.rmeta --extern-html-root-url 'bitflags=https://docs.rs/bitflags/1.3.2/' --extern-html-root-url 'derivative=https://docs.rs/derivative/2.2.0/' --extern-html-root-url 'smallvec=https://docs.rs/smallvec/1.11.0/' -Zunstable-options --cfg=bootstrap --cfg=windows_raw_dylib -Csymbol-mangling-version=v0 -Zunstable-options '--check-cfg=values(bootstrap)' '--check-cfg=values(parallel_compiler)' '--check-cfg=values(no_btreemap_remove_entry)' '--check-cfg=values(crossbeam_loom)' '--check-cfg=values(span_locations)' '--check-cfg=values(rustix_use_libc)' '--check-cfg=values(emulate_second_only_system)' '--check-cfg=values(windows_raw_dylib)' --document-private-items --document-hidden-items -Dwarnings '-Wrustdoc::invalid_codeblock_attributes' --crate-version '1.75.0-nightly (3a51552a6 2023-11-08)' --document-private-items '-Arustdoc::private-intra-doc-links' --enable-index-page -Zunstable-options -Znormalize-docs --show-type-layout --generate-link-to-definition '-Zcrate-attr=warn(rust_2018_idioms)' --cfg=parallel_compiler --extern-html-root-url 'ena=https://docs.rs/ena/latest/'` (exit status: 1)
Build completed unsuccessfully in 0:02:04
  local time: Wed Nov  8 21:41:47 UTC 2023
  network time: Wed, 08 Nov 2023 21:41:47 GMT
##[error]Process completed with exit code 1.

bors added a commit to rust-lang-ci/rust that referenced this pull request Nov 9, 2023
…pes_noop-traversable, r=<try>

Automatically make trivial types noop-traversable

This is a second reincarnation of rust-lang#108214 (or at least, what that PR ultimately became), the previous reincarnation in rust-lang#117620 having been closed accidentally.  It may be easier to review by commit, albeit there are quite a few of them.

## Terminology

I refer to:
* folds and visits as "traversals"
* the types that are folded or visited as "traversables"
* the folders and visitors as "traversers"
* traversables on which traversers can act as "interesting" (there are currently only five: binders, types, consts, regions and predicates) and all others as "trivial" (traversals of which are no-ops)
* the `TypeFoldable` and `TypeVisitable` traits (and only these traits) as the "traversable traits"

## Content

This PR:

* introduces a macro, `rustc_type_ir::noop_if_trivially_traversable` that uses [auto-deref specialisation](http://lukaskalbertodt.github.io/2019/12/05/generalized-autoref-based-specialization.html) to:
    * perform a no-op traversal on values of type `T` if the interner implements `rustc_type_ir::TriviallyTraverses<T>` (thereby obviating all need for most trivial types to implement the traversable traits—and indeed such implementations are removed) or
    * delegate to the relevant traversable trait implementation on `T` otherwise;
* introduces an auto-trait, `rustc_middle::ty::TriviallyTraversable`, that is then "unimplemented" on the interesting types (and thereby remains implemented only on, but on all, trivial types);
* implements `rustc_type_ir::TriviallyTraverses<T>` on the `TyCtxt<'tcx>` interner for all `T: rustc_middle::ty::TriviallyTraversable` (thus ensuring that, for that interner at least, trivial types do not require implementations of the traversable traits);
* updates the traversable traits' derive macros to:
    * skip fields that reference neither any generic type parameters nor, if present, the `'tcx` lifetime parameter
    * use the above specialisation macro when traversing fields
    * if the traversable is not parameterised by a `'tcx` lifetime, generate implementations that are generic over the interner;
* replaces those derive macros' distinct helper attributes (introduced in rust-lang#108040) with a unified `#[skip_traversal]` helper attribute that requires a justifying reason to be provided (this does mean that the different types of traversal can no longer be independently skipped, but nowhere is that currently required or ever expected to be);
    * the derive macros by default refuse to be implemented on trivial types as specialisation usually negates any need—but this can be overridden with a `#[skip_traversal(impl_despite_trivial_because = "<reason>")]` attribute;
* uses those derive macros in place of remaining usages of the `TrivialTypeTraversal` macros and some explicit implementations; and
* a few other minor relevant refactorings.

## Commentary

One limitation of auto-deref specialisation is that it cannot work for unconstrained generic types.  Therefore, absent real specialisation, implementations of the traversable traits must constrain their generics:
* by default, the derive macros constrain every field type `T` that references one or more generic type parameters to implementors of the relevant traversable trait—however this can be modified on a field (or variant) basis with a `#[skip_traversal(because_trivial)]` attribute so that instead the interner is constrained to implement `TriviallyTraverses<T>` (and the field is not traversed)
    * the constraints are applied to the field types rather than the generic type parameters to achieve a "[perfect derive](https://smallcultfollowing.com/babysteps/blog/2022/04/12/implied-bounds-and-perfect-derive/)" that not only avoids having to propagate `#[skip_traversal]` annotations into wrapping types but also works with associated types and other esoteric situations—however this could result in trait solver cycles if interesting generic field types are one day involved in recursive type definitions;
    * for exceptionally rare cases where traversal of an item/variant/field should be a no-op despite it being (potentially, if generic) interesting, it can be annotated with the explicit and deliberately cumbersome `#[skip_traversal(despite_potential_miscompilation_because = "<reason>")]` (which produces a no-op without adding any constraint).
* (the few remaining) explicit implementations of the traversable traits over generic types are similarly constrained
    * indeed, for generic tuples *all element types* must implement the trait—however, since (most) trivial types no longer do, this unfortunately means that the implementations are not applicable to tuples with trivial elements and hence some such tuples have been replaced with more concrete newtypes that have appropriate constraints: c5e9447 is a particularly hefty example of this (where tuples containing `Span` are replaced with `Spanned<T>` instead).

r? `@lcnr`
cc `@oli-obk`

`@rustbot` label A-query-system T-compiler WG-trait-system-refactor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. S-waiting-on-perf Status: Waiting on a perf run to be completed. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. WG-trait-system-refactor The Rustc Trait System Refactor Initiative
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants