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

Generate documentation for auto-trait impls #47833

Merged
merged 11 commits into from
Feb 22, 2018

Conversation

Aaron1011
Copy link
Member

A new section is added to both both struct and trait doc pages.

On struct/enum pages, a new 'Auto Trait Implementations' section displays any synthetic implementations for auto traits. Currently, this is only done for Send and Sync.

Auto trait implementations for Cloned

On trait pages, a new 'Auto Implementors' section displays all types which automatically implement the trait. Effectively, this is a list of all public types in the standard library.

Auto trait implementors for Send

Synthesized impls for a particular auto trait ('synthetic impls') take generic bounds into account. For example, a type

struct Foo<T>(T)

will have 'impl Send for Foo where T: Send' generated for it.

Manual implementations of auto traits are also taken into account. If we have
the following types:

struct Foo<T>(T)
struct Wrapper<T>(Foo<T>)
unsafe impl<T> Send for Wrapper<T>' // pretend that Wrapper<T> makes this sound somehow

Then Wrapper will have the following impl generated:

impl<T> Send for Wrapper<T>

reflecting the fact that 'T: Send' need not hold for 'Wrapper: Send' to hold

Lifetimes, HRTBS, and projections (e.g. '::Item') are taken into account by synthetic impls:

A ridiculous demonstration type

However, if a type can never implement a particular auto trait (e.g. struct MyStruct<T>(*const T)), then a negative impl will be generated (in this case, impl<T> !Send for MyStruct<T>)

All of this means that a user should be able to copy-paste a syntheticimpl into their code, without any observable changes in behavior (assuming the rest of the program remains unchanged).

@rust-highfive
Copy link
Collaborator

Some changes occurred in HTML/CSS.

cc @GuillaumeGomez

@rust-highfive
Copy link
Collaborator

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @GuillaumeGomez (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

@Aaron1011
Copy link
Member Author

Fixes #17606

@kennytm kennytm added A-docs Area: documentation for any part of the project, including the compiler, standard library, and tools S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 29, 2018
@GuillaumeGomez
Copy link
Member

Thanks a lot for this PR! Considering its size, it'll take a bit of time to get reviewed so don't worry.

@bors
Copy link
Contributor

bors commented Feb 1, 2018

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

@GuillaumeGomez
Copy link
Member

Sorry, forgot about it and I won't have have time before next week (FOSDEM). If anyone from @rust-lang/docs team could take a look as well, it'd be nice!

@QuietMisdreavus
Copy link
Member

[00:06:59] error[E0308]: mismatched types
[00:06:59]    --> librustc/traits/select.rs:457:42
[00:06:59]     |
[00:06:59] 457 |             intercrate_ambiguity_causes: Vec::new(),
[00:06:59]     |                                          ^^^^^^^^^^
[00:06:59]     |                                          |
[00:06:59]     |                                          expected enum `std::option::Option`, found struct `std::vec::Vec`
[00:06:59]     |                                          help: try using a variant of the expected type: `Some(<Vec>::new())`
[00:06:59]     |
[00:06:59]     = note: expected type `std::option::Option<std::vec::Vec<traits::select::IntercrateAmbiguityCause>>`
[00:06:59]                found type `std::vec::Vec<_>`
[00:06:59]     = help: here are some functions which might fulfill your needs:
[00:06:59]             - .pop()
[00:06:59] 
[00:07:10] error: aborting due to previous error
[00:07:10] 
[00:07:10] error: Could not compile `rustc`.

(I'm starting to read through this, but i might not get all the way through in one sitting >_>)

@Aaron1011
Copy link
Member Author

@QuietMisdreavus: Oops, looks like I didn't fix the merge conflict properly.

@pietroalbini
Copy link
Member

@QuietMisdreavus looks like the author addressed that error?

@GuillaumeGomez
Copy link
Member

We're still reviewing. It's taking some time considering the amount of code.

RefMut::map(
self.region_constraints.borrow_mut(),
|c| c.as_mut().expect("region constraints already solved"))
}

/// Clears the selection, evaluation, and projection cachesThis is useful when
/// repeatedly attemping to select an Obligation while chagning only
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"chagning"

intercrate: None,
inferred_obligations: SnapshotVec::new(),
intercrate_ambiguity_causes: None,
allow_negative_impls
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comma at the end of this line.


// Due to the way projections are handled by SelectionContext, we need to run
// evaluate_predicates twice: once on the original param env, and once on the result of
// the first evaluate_predicates call
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it's a sentence, please add a dot at the end.

// We fix the first assumption by manually clearing out all of the InferCtxt's caches
// in between calls to SelectionContext.select. This allows us to keep all of the
// intermediate types we create bound to the 'tcx lifetime, rather than needing to lift
// them between calls
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing dot.

infcx.freshen(p)
}

fn evaluate_nested_obligations<
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function declaration is quite difficult to read. Could you put generics on the same line (if it fits) please?

}
}

// This is very simiiar to handle_lifetimes. Instead of matching ty::Region's to ty::Region's,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"simiiar"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw:

match ty::Region's to ty::Region's

Is this repetition wanted or not?

// with determining if a given set up constraints/predicates *are* met, given some
// starting conditions (e.g. user-provided code). For this reason, it's easier
// to perform the calculations we need on our own, rather than trying to make
// existing inference/solver code do what we want
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing dot.

}

// This method calculates two things: Lifetime constraints of the form 'a: 'b,
// and region constraints of the form ReVar: 'a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing dot.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me, writing either of the form ReVar: 'a. or of the form ReVar: 'a .makes it look as though the . is part of the previous expression, instead of being a normal punctuation mark.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Just ignore this comment then.

// all intermediate RegionVids. At the end, all constraints should
// be between Regions (aka region variables). This gives us the information
// we need to create the Generics
//
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this empty comment line?

// Our goal is to 'flatten' the list of constraints by eliminating
// all intermediate RegionVids. At the end, all constraints should
// be between Regions (aka region variables). This gives us the information
// we need to create the Generics
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing dot.


for smaller in deps.smaller.iter() {
for larger in deps.larger.iter() {
match (smaller, larger) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the feeling that this whole match could be greatly simplified. The only "unique" case is for RegionTarget::Region, otherwise they all perform the same operation. Could this code be rewritten (by writing a function which takes one argument and you apply the operation on it)? The match will then become something like this:

match (smaller, larger) {
    (&RegionTarget::Region(r1), &RegionTarget::Region(r2)) => {
        // the same as now
    }
    _ => the_function_call(),
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this would remove much duplication - the behavior is different if the smaller region a RegionTarget::Region vs a RegionTarget::RegionVid

ty_to_fn: FxHashMap<Type, (Option<PolyTrait>, Option<Type>)>,
lifetime_to_bounds: FxHashMap<Lifetime, FxHashSet<Lifetime>>,
) -> Vec<WherePredicate> {
let final_predicates = ty_to_bounds
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This let binding is useless, just return the operations you're performing on ty_to_bounds

}

// This is an ugly hack, but it's the simplest way to handle synthetic impls without greatly
// refactorying either librustdoc or librustc. In particular, allowing new DefIds to be
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"refactorying"

@@ -120,11 +120,17 @@ pub fn load_attrs(cx: &DocContext, did: DefId) -> clean::Attributes {
cx.tcx.get_attrs(did).clean(cx)
}


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this new empty line?

@@ -249,6 +267,7 @@ pub fn build_impls(cx: &DocContext, did: DefId) -> Vec<clean::Item> {

cx.populated_all_crate_impls.set(true);


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this new line?

items: trait_items,
polarity: Some(polarity.clean(cx)),
synthetic: false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comma at the end of this line.

@Aaron1011
Copy link
Member Author

@GuillaumeGomez: I messed up the submodule ref when I rebased. It should be fixed now.

@GuillaumeGomez
Copy link
Member

Ok, let's give it another try and see.

@bors: r+

@bors
Copy link
Contributor

bors commented Feb 19, 2018

📌 Commit 8788179 has been approved by GuillaumeGomez

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Feb 19, 2018
@Manishearth
Copy link
Member

[01:31:02] failures:
[01:31:02] 
[01:31:02] ---- [rustdoc] rustdoc/synthetic_auto/complex.rs stdout ----
[01:31:02] 	
[01:31:02] error: htmldocck failed!
[01:31:02] status: exit code: 1
[01:31:02] command: "/usr/bin/python2.7" "/checkout/src/etc/htmldocck.py" "/checkout/obj/build/i686-unknown-linux-gnu/test/rustdoc/synthetic_auto/complex.stage2-i686-unknown-linux-gnu" "/checkout/src/test/rustdoc/synthetic_auto/complex.rs"
[01:31:02] stdout:
[01:31:02] ------------------------------------------
[01:31:02] 
[01:31:02] ------------------------------------------
[01:31:02] stderr:
[01:31:02] ------------------------------------------
[01:31:02] 33: @has check failed
[01:31:02] 	`XPATH PATTERN` did not match
[01:31:02] thread 'main' panicked at 'Some tests failed', tools/compiletest/src/main.rs:476:22
[01:31:02] 	// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]/*/code' "impl<'a, T, K: ?Sized> Send for NotOuter<'a, T, K> where 'a: 'static, K: for<'b> Fn((&'b bool, &'a u8)) -> &'b i8, <T as MyTrait<'a>>::MyItem: Copy, T: MyTrait<'a>"
[01:31:02] 
[01:31:02] Encountered 1 errors
[01:31:02] 
[01:31:02] ------------------------------------------
[01:31:02] 
[01:31:02] thread '[rustdoc] rustdoc/synthetic_auto/complex.rs' panicked at 'explicit panic', tools/compiletest/src/runtest.rs:2892:9
[01:31:02] 
[01:31:02] 
[01:31:02] failures:
[01:31:02]     [rustdoc] rustdoc/synthetic_auto/complex.rs
[01:31:02] 
[01:31:02] test result: �[31mFAILED�(B�[m. 210 passed; 1 failed; 1 ignored; 0 measured; 0 filtered out
[01:31:02] 
[01:31:02] 
[01:31:02] 
[01:31:02] command did not execute successfully: "/checkout/obj/build/i686-unknown-linux-gnu/stage0-tools-bin/compiletest" "--compile-lib-path" "/checkout/obj/build/i686-unknown-linux-gnu/stage2/lib" "--run-lib-path" "/checkout/obj/build/i686-unknown-linux-gnu/stage2/lib/rustlib/i686-unknown-linux-gnu/lib" "--rustc-path" "/checkout/obj/build/i686-unknown-linux-gnu/stage2/bin/rustc" "--rustdoc-path" "/checkout/obj/build/i686-unknown-linux-gnu/stage2/bin/rustdoc" "--src-base" "/checkout/src/test/rustdoc" "--build-base" "/checkout/obj/build/i686-unknown-linux-gnu/test/rustdoc" "--stage-id" "stage2-i686-unknown-linux-gnu" "--mode" "rustdoc" "--target" "i686-unknown-linux-gnu" "--host" "i686-unknown-linux-gnu" "--llvm-filecheck" "/checkout/obj/build/i686-unknown-linux-gnu/llvm/build/bin/FileCheck" "--host-rustcflags" "-Crpath -Zmiri -Zunstable-options " "--target-rustcflags" "-Crpath -Zmiri -Zunstable-options  -Lnative=/checkout/obj/build/i686-unknown-linux-gnu/native/rust-test-helpers" "--docck-python" "/usr/bin/python2.7" "--lldb-python" "/usr/bin/python2.7" "--gdb" "/usr/bin/gdb" "--llvm-version" "6.0.0\n" "--cc" "" "--cxx" "" "--cflags" "" "--llvm-components" "" "--llvm-cxxflags" "" "--adb-path" "adb" "--adb-test-dir" "/data/tmp/work" "--android-cross-path" "" "--color" "always"
[01:31:02] expected success, got: exit code: 101
[01:31:02] 
[01:31:02] 
[01:31:02] failed to run: /checkout/obj/build/bootstrap/debug/bootstrap test
[01:31:02] Build completed unsuccessfully in 0:20:42
[01:31:02] make: *** [check] Error 1
[01:31:02] Makefile:52: recipe for target 'check' failed


@Aaron1011
Copy link
Member Author

Aaron1011 commented Feb 19, 2018

This is bizarre:

From the x86_64-gnu-nopt job for the rollup:

[01:09:58] test [rustdoc] rustdoc/synthetic_auto/complex.rs ... �[32mok�(B�[m

From the i686-gnu-nopt job:

[01:30:53] test [rustdoc] rustdoc/synthetic_auto/complex.rs ... �[31mFAILED�(B�[m

Apparently, it actually is the platform that causes the issue. I'll try to compile a 32-bit build of Rust locally, and see if I can reproduce it.

EDIT: Link to the build in question: https://travis-ci.org/rust-lang/rust/builds/343477652?utm_source=github_status&utm_medium=notification

@Aaron1011
Copy link
Member Author

It turns out that my code is creating the final Generics struct directly from an FxHashMap - so the iteration order isn't guaranteed to be consistent.

I believe the simplest solution is to sort the final WherePredicates into some defined order. This will help keep the docs visually consistent between rustdoc runs, and reduce the need for more complex matching in tests.

This removes the implicit dependency on the iteration
order of FxHashMap
@Aaron1011
Copy link
Member Author

@GuillaumeGomez: The issue should be resolved now - the failing tests now pass for me on both i686 and x86_64.

@GuillaumeGomez
Copy link
Member

Ah perfect! I was about to suggest to have a vec alongside an HashMap (and HashMap entries would be references to the  vec ones) but this solution seems better. Thanks!

@bors: r+

@bors
Copy link
Contributor

bors commented Feb 20, 2018

📌 Commit 44d07df has been approved by GuillaumeGomez

GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this pull request Feb 21, 2018
…aumeGomez

Generate documentation for auto-trait impls

A new section is added to both both struct and trait doc pages.

On struct/enum pages, a new 'Auto Trait Implementations' section displays any synthetic implementations for auto traits. Currently, this is only done for Send and Sync.

![Auto trait implementations for Cloned](https://i.imgur.com/XtTV6IJ.png)

On trait pages, a new 'Auto Implementors' section displays all types which automatically implement the trait. Effectively, this is a list of all public types in the standard library.

![Auto trait implementors for Send](https://i.imgur.com/3GRBpTy.png)

Synthesized impls for a particular auto trait ('synthetic impls') take generic bounds into account. For example, a type
```rust
struct Foo<T>(T)
```
 will have 'impl<T> Send for Foo<T> where T: Send' generated for it.

Manual implementations of auto traits are also taken into account. If we have
the following types:

```rust
struct Foo<T>(T)
struct Wrapper<T>(Foo<T>)
unsafe impl<T> Send for Wrapper<T>' // pretend that Wrapper<T> makes this sound somehow
```

Then Wrapper will have the following impl generated:
```rust
impl<T> Send for Wrapper<T>
```
reflecting the fact that 'T: Send' need not hold for 'Wrapper<T>: Send' to hold

Lifetimes, HRTBS, and projections (e.g. '<T as Iterator>::Item') are taken into account by synthetic impls:

![A ridiculous demonstration type](https://i.imgur.com/TkZMWuN.png)

However, if a type can *never* implement a particular auto trait (e.g. `struct MyStruct<T>(*const T)`), then a negative impl will be generated (in this case, `impl<T> !Send for MyStruct<T>`)

All of this means that a user should be able to copy-paste a syntheticimpl into their code, without any observable changes in behavior (assuming the rest of the program remains unchanged).
bors added a commit that referenced this pull request Feb 22, 2018
Rollup of 12 pull requests

- Successful merges: #47379, #47833, #48106, #48198, #48314, #48325, #48335, #48352, #48354, #48360, #48382, #48397
- Failed merges:
@bors bors merged commit 44d07df into rust-lang:master Feb 22, 2018
@Aaron1011 Aaron1011 deleted the final_auto_trait branch February 22, 2018 20:19
bors added a commit that referenced this pull request May 9, 2018
Refactor auto trait handling in librustdoc to be accessible from librustc.

These commits transfer some of the functionality introduced in #47833 to librustc with the intention of making the tools to work with auto traits accessible to third-party code, for example [rust-semverver](https://github.com/rust-lang-nursery/rust-semverver).

Some rough edges remain, and I'm certain some of the FIXMEs introduced will need some discussion, most notably the fairly ugly overall approach to pull out the core logic into librustc, which was previously fairly tightly coupled with various bits and bobs from librustdoc.

cc @Aaron1011
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-docs Area: documentation for any part of the project, including the compiler, standard library, and tools S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants