未验证 提交 aec65353 编写于 作者: G Guillaume Gomez 提交者: GitHub

Rollup merge of #47833 - Aaron1011:final_auto_trait, r=GuillaumeGomez

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).
......@@ -72,6 +72,10 @@ fn allocate(&mut self,
index
}
pub fn next_id(&self, address_space: DefIndexAddressSpace) -> DefIndex {
DefIndex::from_array_index(self.index_to_key[address_space.index()].len(), address_space)
}
#[inline(always)]
pub fn def_key(&self, index: DefIndex) -> DefKey {
self.index_to_key[index.address_space().index()]
......
......@@ -180,7 +180,7 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
// for each body-id in this map, which will process the
// obligations within. This is expected to be done 'late enough'
// that all type inference variables have been bound and so forth.
region_obligations: RefCell<Vec<(ast::NodeId, RegionObligation<'tcx>)>>,
pub region_obligations: RefCell<Vec<(ast::NodeId, RegionObligation<'tcx>)>>,
}
/// A map returned by `skolemize_late_bound_regions()` indicating the skolemized
......@@ -1555,11 +1555,20 @@ pub fn partially_normalize_associated_types_in<T>(&self,
InferOk { value, obligations }
}
fn borrow_region_constraints(&self) -> RefMut<'_, RegionConstraintCollector<'tcx>> {
pub fn borrow_region_constraints(&self) -> RefMut<'_, RegionConstraintCollector<'tcx>> {
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 changing only
/// its ParamEnv, since FulfillmentContext doesn't use 'probe'
pub fn clear_caches(&self) {
self.selection_cache.clear();
self.evaluation_cache.clear();
self.projection_cache.borrow_mut().clear();
}
}
impl<'a, 'gcx, 'tcx> TypeTrace<'tcx> {
......
......@@ -82,7 +82,7 @@ pub struct RegionConstraintCollector<'tcx> {
/// Describes constraints between the region variables and other
/// regions, as well as other conditions that must be verified, or
/// assumptions that can be made.
#[derive(Debug, Default)]
#[derive(Debug, Default, Clone)]
pub struct RegionConstraintData<'tcx> {
/// Constraints of the form `A <= B`, where either `A` or `B` can
/// be a region variable (or neither, as it happens).
......@@ -142,7 +142,7 @@ pub enum Constraint<'tcx> {
/// outlive `RS`. Therefore verify that `R <= RS[i]` for some
/// `i`. Inference variables may be involved (but this verification
/// step doesn't influence inference).
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Verify<'tcx> {
pub kind: GenericKind<'tcx>,
pub origin: SubregionOrigin<'tcx>,
......@@ -159,7 +159,7 @@ pub enum GenericKind<'tcx> {
/// When we introduce a verification step, we wish to test that a
/// particular region (let's call it `'min`) meets some bound.
/// The bound is described the by the following grammar:
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum VerifyBound<'tcx> {
/// B = exists {R} --> some 'r in {R} must outlive 'min
///
......@@ -288,6 +288,10 @@ pub fn var_origins(&self) -> &VarOrigins {
&self.var_origins
}
pub fn region_constraint_data(&self) -> &RegionConstraintData<'tcx> {
&self.data
}
/// Once all the constraints have been gathered, extract out the final data.
///
/// Not legal during a snapshot.
......
......@@ -32,8 +32,8 @@
pub use self::coherence::{orphan_check, overlapping_impls, OrphanCheckErr, OverlapResult};
pub use self::fulfill::FulfillmentContext;
pub use self::project::MismatchedProjectionTypes;
pub use self::project::{normalize, normalize_projection_type, Normalized};
pub use self::project::{ProjectionCache, ProjectionCacheSnapshot, Reveal};
pub use self::project::{normalize, normalize_projection_type, poly_project_and_unify_type};
pub use self::project::{ProjectionCache, ProjectionCacheSnapshot, Reveal, Normalized};
pub use self::object_safety::ObjectSafetyViolation;
pub use self::object_safety::MethodViolationCode;
pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote};
......
......@@ -1596,6 +1596,10 @@ pub fn new() -> Self {
}
}
pub fn clear(&mut self) {
self.map.clear();
}
pub fn snapshot(&mut self) -> ProjectionCacheSnapshot {
ProjectionCacheSnapshot { snapshot: self.map.snapshot() }
}
......
......@@ -93,6 +93,11 @@ pub struct SelectionContext<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
inferred_obligations: SnapshotVec<InferredObligationsSnapshotVecDelegate<'tcx>>,
intercrate_ambiguity_causes: Option<Vec<IntercrateAmbiguityCause>>,
/// Controls whether or not to filter out negative impls when selecting.
/// This is used in librustdoc to distinguish between the lack of an impl
/// and a negative impl
allow_negative_impls: bool
}
#[derive(Clone, Debug)]
......@@ -424,6 +429,7 @@ pub fn new(infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>) -> SelectionContext<'cx, 'gcx
intercrate: None,
inferred_obligations: SnapshotVec::new(),
intercrate_ambiguity_causes: None,
allow_negative_impls: false,
}
}
......@@ -436,6 +442,20 @@ pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
intercrate: Some(mode),
inferred_obligations: SnapshotVec::new(),
intercrate_ambiguity_causes: None,
allow_negative_impls: false,
}
}
pub fn with_negative(infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
allow_negative_impls: bool) -> SelectionContext<'cx, 'gcx, 'tcx> {
debug!("with_negative({:?})", allow_negative_impls);
SelectionContext {
infcx,
freshener: infcx.freshener(),
intercrate: None,
inferred_obligations: SnapshotVec::new(),
intercrate_ambiguity_causes: None,
allow_negative_impls,
}
}
......@@ -1086,7 +1106,8 @@ fn in_task<OP, R>(&mut self, op: OP) -> (R, DepNodeIndex)
fn filter_negative_impls(&self, candidate: SelectionCandidate<'tcx>)
-> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
if let ImplCandidate(def_id) = candidate {
if self.tcx().impl_polarity(def_id) == hir::ImplPolarity::Negative {
if !self.allow_negative_impls &&
self.tcx().impl_polarity(def_id) == hir::ImplPolarity::Negative {
return Err(Unimplemented)
}
}
......@@ -3337,6 +3358,10 @@ pub fn new() -> SelectionCache<'tcx> {
hashmap: RefCell::new(FxHashMap())
}
}
pub fn clear(&self) {
*self.hashmap.borrow_mut() = FxHashMap()
}
}
impl<'tcx> EvaluationCache<'tcx> {
......@@ -3345,6 +3370,10 @@ pub fn new() -> EvaluationCache<'tcx> {
hashmap: RefCell::new(FxHashMap())
}
}
pub fn clear(&self) {
*self.hashmap.borrow_mut() = FxHashMap()
}
}
impl<'o,'tcx> TraitObligationStack<'o,'tcx> {
......
......@@ -45,6 +45,11 @@ pub fn new() -> Self {
}
}
pub fn clear(&mut self) {
self.map.clear();
self.undo_log.clear();
}
pub fn insert(&mut self, key: K, value: V) -> bool {
match self.map.insert(key.clone(), value) {
None => {
......
此差异已折叠。
......@@ -25,7 +25,7 @@
use html::escape::Escape;
#[derive(Clone, RustcEncodable, RustcDecodable, Debug, PartialEq)]
#[derive(Clone, RustcEncodable, RustcDecodable, Debug, PartialEq, Eq, Hash)]
pub enum Cfg {
/// Accepts all configurations.
True,
......
......@@ -12,8 +12,8 @@
use std::collections::BTreeMap;
use std::io;
use std::iter::once;
use std::rc::Rc;
use std::iter::once;
use syntax::ast;
use rustc::hir;
......@@ -25,7 +25,7 @@
use core::{DocContext, DocAccessLevels};
use doctree;
use clean::{self, GetDefId};
use clean::{self, GetDefId, get_auto_traits_with_def_id};
use super::Clean;
......@@ -50,7 +50,7 @@ pub fn try_inline(cx: &DocContext, def: Def, name: ast::Name)
let inner = match def {
Def::Trait(did) => {
record_extern_fqn(cx, did, clean::TypeKind::Trait);
ret.extend(build_impls(cx, did));
ret.extend(build_impls(cx, did, false));
clean::TraitItem(build_external_trait(cx, did))
}
Def::Fn(did) => {
......@@ -59,27 +59,27 @@ pub fn try_inline(cx: &DocContext, def: Def, name: ast::Name)
}
Def::Struct(did) => {
record_extern_fqn(cx, did, clean::TypeKind::Struct);
ret.extend(build_impls(cx, did));
ret.extend(build_impls(cx, did, true));
clean::StructItem(build_struct(cx, did))
}
Def::Union(did) => {
record_extern_fqn(cx, did, clean::TypeKind::Union);
ret.extend(build_impls(cx, did));
ret.extend(build_impls(cx, did, true));
clean::UnionItem(build_union(cx, did))
}
Def::TyAlias(did) => {
record_extern_fqn(cx, did, clean::TypeKind::Typedef);
ret.extend(build_impls(cx, did));
ret.extend(build_impls(cx, did, false));
clean::TypedefItem(build_type_alias(cx, did), false)
}
Def::Enum(did) => {
record_extern_fqn(cx, did, clean::TypeKind::Enum);
ret.extend(build_impls(cx, did));
ret.extend(build_impls(cx, did, true));
clean::EnumItem(build_enum(cx, did))
}
Def::TyForeign(did) => {
record_extern_fqn(cx, did, clean::TypeKind::Foreign);
ret.extend(build_impls(cx, did));
ret.extend(build_impls(cx, did, false));
clean::ForeignTypeItem
}
// Never inline enum variants but leave them shown as re-exports.
......@@ -125,6 +125,11 @@ pub fn load_attrs(cx: &DocContext, did: DefId) -> clean::Attributes {
/// These names are used later on by HTML rendering to generate things like
/// source links back to the original item.
pub fn record_extern_fqn(cx: &DocContext, did: DefId, kind: clean::TypeKind) {
if did.is_local() {
debug!("record_extern_fqn(did={:?}, kind+{:?}): def_id is local, aborting", did, kind);
return;
}
let crate_name = cx.tcx.crate_name(did.krate).to_string();
let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| {
// extern blocks have an empty name
......@@ -144,6 +149,7 @@ pub fn record_extern_fqn(cx: &DocContext, did: DefId, kind: clean::TypeKind) {
}
pub fn build_external_trait(cx: &DocContext, did: DefId) -> clean::Trait {
let auto_trait = cx.tcx.trait_def(did).has_auto_impl;
let trait_items = cx.tcx.associated_items(did).map(|item| item.clean(cx)).collect();
let predicates = cx.tcx.predicates_of(did);
let generics = (cx.tcx.generics_of(did), &predicates).clean(cx);
......@@ -152,6 +158,7 @@ pub fn build_external_trait(cx: &DocContext, did: DefId) -> clean::Trait {
let is_spotlight = load_attrs(cx, did).has_doc_flag("spotlight");
let is_auto = cx.tcx.trait_is_auto(did);
clean::Trait {
auto: auto_trait,
unsafety: cx.tcx.trait_def(did).unsafety,
generics,
items: trait_items,
......@@ -227,7 +234,7 @@ fn build_type_alias(cx: &DocContext, did: DefId) -> clean::Typedef {
}
}
pub fn build_impls(cx: &DocContext, did: DefId) -> Vec<clean::Item> {
pub fn build_impls(cx: &DocContext, did: DefId, auto_traits: bool) -> Vec<clean::Item> {
let tcx = cx.tcx;
let mut impls = Vec::new();
......@@ -235,6 +242,16 @@ pub fn build_impls(cx: &DocContext, did: DefId) -> Vec<clean::Item> {
build_impl(cx, did, &mut impls);
}
if auto_traits {
let auto_impls = get_auto_traits_with_def_id(cx, did);
let mut renderinfo = cx.renderinfo.borrow_mut();
let new_impls: Vec<clean::Item> = auto_impls.into_iter()
.filter(|i| renderinfo.inlined.insert(i.def_id)).collect();
impls.extend(new_impls);
}
// If this is the first time we've inlined something from another crate, then
// we inline *all* impls from all the crates into this crate. Note that there's
// currently no way for us to filter this based on type, and we likely need
......@@ -347,13 +364,14 @@ pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec<clean::Item>) {
ret.push(clean::Item {
inner: clean::ImplItem(clean::Impl {
unsafety: hir::Unsafety::Normal, // FIXME: this should be decoded
unsafety: hir::Unsafety::Normal,
generics: (tcx.generics_of(did), &predicates).clean(cx),
provided_trait_methods: provided,
trait_,
for_,
generics: (tcx.generics_of(did), &predicates).clean(cx),
items: trait_items,
polarity: Some(polarity.clean(cx)),
synthetic: false,
}),
source: tcx.def_span(did).clean(cx),
name: None,
......
此差异已折叠。
......@@ -11,13 +11,14 @@
use rustc_lint;
use rustc_driver::{self, driver, target_features, abort_on_err};
use rustc::session::{self, config};
use rustc::hir::def_id::DefId;
use rustc::hir::def_id::{DefId, CrateNum};
use rustc::hir::def::Def;
use rustc::middle::cstore::CrateStore;
use rustc::middle::privacy::AccessLevels;
use rustc::ty::{self, TyCtxt, AllArenas};
use rustc::hir::map as hir_map;
use rustc::lint;
use rustc::util::nodemap::FxHashMap;
use rustc::util::nodemap::{FxHashMap, FxHashSet};
use rustc_resolve as resolve;
use rustc_metadata::creader::CrateLoader;
use rustc_metadata::cstore::CStore;
......@@ -48,6 +49,8 @@ pub struct DocContext<'a, 'tcx: 'a, 'rcx: 'a> {
pub resolver: &'a RefCell<resolve::Resolver<'rcx>>,
/// The stack of module NodeIds up till this point
pub mod_ids: RefCell<Vec<NodeId>>,
pub crate_name: Option<String>,
pub cstore: Rc<CrateStore>,
pub populated_all_crate_impls: Cell<bool>,
// Note that external items for which `doc(hidden)` applies to are shown as
// non-reachable while local items aren't. This is because we're reusing
......@@ -65,6 +68,11 @@ pub struct DocContext<'a, 'tcx: 'a, 'rcx: 'a> {
pub ty_substs: RefCell<FxHashMap<Def, clean::Type>>,
/// Table node id of lifetime parameter definition -> substituted lifetime
pub lt_substs: RefCell<FxHashMap<DefId, clean::Lifetime>>,
pub send_trait: Option<DefId>,
pub fake_def_ids: RefCell<FxHashMap<CrateNum, DefId>>,
pub all_fake_def_ids: RefCell<FxHashSet<DefId>>,
/// Maps (type_id, trait_id) -> auto trait impl
pub generated_synthetics: RefCell<FxHashSet<(DefId, DefId)>>
}
impl<'a, 'tcx, 'rcx> DocContext<'a, 'tcx, 'rcx> {
......@@ -107,6 +115,7 @@ pub fn run_core(search_paths: SearchPaths,
triple: Option<String>,
maybe_sysroot: Option<PathBuf>,
allow_warnings: bool,
crate_name: Option<String>,
force_unstable_if_unmarked: bool) -> (clean::Crate, RenderInfo)
{
// Parse, resolve, and typecheck the given crate.
......@@ -230,9 +239,17 @@ pub fn run_core(search_paths: SearchPaths,
.collect()
};
let send_trait = if crate_name == Some("core".to_string()) {
clean::get_trait_def_id(&tcx, &["marker", "Send"], true)
} else {
clean::get_trait_def_id(&tcx, &["core", "marker", "Send"], false)
};
let ctxt = DocContext {
tcx,
resolver: &resolver,
crate_name,
cstore: cstore.clone(),
populated_all_crate_impls: Cell::new(false),
access_levels: RefCell::new(access_levels),
external_traits: Default::default(),
......@@ -240,6 +257,10 @@ pub fn run_core(search_paths: SearchPaths,
ty_substs: Default::default(),
lt_substs: Default::default(),
mod_ids: Default::default(),
send_trait: send_trait,
fake_def_ids: RefCell::new(FxHashMap()),
all_fake_def_ids: RefCell::new(FxHashSet()),
generated_synthetics: RefCell::new(FxHashSet()),
};
debug!("crate: {:?}", tcx.hir.krate());
......
......@@ -210,6 +210,7 @@ pub struct Trait {
pub depr: Option<attr::Deprecation>,
}
#[derive(Debug)]
pub struct Impl {
pub unsafety: hir::Unsafety,
pub polarity: hir::ImplPolarity,
......
......@@ -37,7 +37,7 @@
use std::borrow::Cow;
use std::cell::RefCell;
use std::cmp::Ordering;
use std::collections::{BTreeMap, HashSet};
use std::collections::{BTreeMap, HashSet, VecDeque};
use std::default::Default;
use std::error;
use std::fmt::{self, Display, Formatter, Write as FmtWrite};
......@@ -270,6 +270,18 @@ pub struct Cache {
/// generating explicit hyperlinks to other crates.
pub external_paths: FxHashMap<DefId, (Vec<String>, ItemType)>,
/// Maps local def ids of exported types to fully qualified paths.
/// Unlike 'paths', this mapping ignores any renames that occur
/// due to 'use' statements.
///
/// This map is used when writing out the special 'implementors'
/// javascript file. By using the exact path that the type
/// is declared with, we ensure that each path will be identical
/// to the path used if the corresponding type is inlined. By
/// doing this, we can detect duplicate impls on a trait page, and only display
/// the impl for the inlined type.
pub exact_paths: FxHashMap<DefId, Vec<String>>,
/// This map contains information about all known traits of this crate.
/// Implementations of a crate should inherit the documentation of the
/// parent trait if no extra documentation is specified, and default methods
......@@ -322,6 +334,7 @@ pub struct RenderInfo {
pub inlined: FxHashSet<DefId>,
pub external_paths: ::core::ExternalPaths,
pub external_typarams: FxHashMap<DefId, String>,
pub exact_paths: FxHashMap<DefId, Vec<String>>,
pub deref_trait_did: Option<DefId>,
pub deref_mut_trait_did: Option<DefId>,
pub owned_box_did: Option<DefId>,
......@@ -436,7 +449,9 @@ fn init_ids() -> FxHashMap<String, usize> {
"required-methods",
"provided-methods",
"implementors",
"synthetic-implementors",
"implementors-list",
"synthetic-implementors-list",
"methods",
"deref-methods",
"implementations",
......@@ -556,6 +571,7 @@ pub fn run(mut krate: clean::Crate,
inlined: _,
external_paths,
external_typarams,
exact_paths,
deref_trait_did,
deref_mut_trait_did,
owned_box_did,
......@@ -568,6 +584,7 @@ pub fn run(mut krate: clean::Crate,
let mut cache = Cache {
impls: FxHashMap(),
external_paths,
exact_paths,
paths: FxHashMap(),
implementors: FxHashMap(),
stack: Vec::new(),
......@@ -873,7 +890,10 @@ fn collect(path: &Path, krate: &str,
// should add it.
if !imp.impl_item.def_id.is_local() { continue }
have_impls = true;
write!(implementors, "{},", as_json(&imp.inner_impl().to_string())).unwrap();
write!(implementors, "{{text:{},synthetic:{},types:{}}},",
as_json(&imp.inner_impl().to_string()),
imp.inner_impl().synthetic,
as_json(&collect_paths_for_type(imp.inner_impl().for_.clone()))).unwrap();
}
implementors.push_str("];");
......@@ -1856,8 +1876,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
item: &clean::Item, items: &[clean::Item]) -> fmt::Result {
document(w, cx, item)?;
let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped())
.collect::<Vec<usize>>();
let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::<Vec<usize>>();
// the order of item types in the listing
fn reorder(ty: ItemType) -> u8 {
......@@ -2201,6 +2220,50 @@ fn item_function(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
document(w, cx, it)
}
fn render_implementor(cx: &Context, implementor: &Impl, w: &mut fmt::Formatter,
implementor_dups: &FxHashMap<&str, (DefId, bool)>) -> Result<(), fmt::Error> {
write!(w, "<li>")?;
if let Some(l) = (Item { cx, item: &implementor.impl_item }).src_href() {
write!(w, "<div class='out-of-band'>")?;
write!(w, "<a class='srclink' href='{}' title='{}'>[src]</a>",
l, "goto source code")?;
write!(w, "</div>")?;
}
write!(w, "<code>")?;
// If there's already another implementor that has the same abbridged name, use the
// full path, for example in `std::iter::ExactSizeIterator`
let use_absolute = match implementor.inner_impl().for_ {
clean::ResolvedPath { ref path, is_generic: false, .. } |
clean::BorrowedRef {
type_: box clean::ResolvedPath { ref path, is_generic: false, .. },
..
} => implementor_dups[path.last_name()].1,
_ => false,
};
fmt_impl_for_trait_page(&implementor.inner_impl(), w, use_absolute)?;
for it in &implementor.inner_impl().items {
if let clean::TypedefItem(ref tydef, _) = it.inner {
write!(w, "<span class=\"where fmt-newline\"> ")?;
assoc_type(w, it, &vec![], Some(&tydef.type_), AssocItemLink::Anchor(None))?;
write!(w, ";</span>")?;
}
}
writeln!(w, "</code></li>")?;
Ok(())
}
fn render_impls(cx: &Context, w: &mut fmt::Formatter,
traits: Vec<&&Impl>,
containing_item: &clean::Item) -> Result<(), fmt::Error> {
for i in &traits {
let did = i.trait_did().unwrap();
let assoc_link = AssocItemLink::GotoSource(did, &i.inner_impl().provided_trait_methods);
render_impl(w, cx, i, assoc_link,
RenderMode::Normal, containing_item.stable_since(), true)?;
}
Ok(())
}
fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
t: &clean::Trait) -> fmt::Result {
let mut bounds = String::new();
......@@ -2380,6 +2443,16 @@ fn trait_item(w: &mut fmt::Formatter, cx: &Context, m: &clean::Item, t: &clean::
</h2>
<ul class='item-list' id='implementors-list'>
";
let synthetic_impl_header = "
<h2 id='synthetic-implementors' class='small-section-header'>
Auto implementors<a href='#synthetic-implementors' class='anchor'></a>
</h2>
<ul class='item-list' id='synthetic-implementors-list'>
";
let mut synthetic_types = Vec::new();
if let Some(implementors) = cache.implementors.get(&it.def_id) {
// The DefId is for the first Type found with that name. The bool is
// if any Types with the same name but different DefId have been found.
......@@ -2405,6 +2478,11 @@ fn trait_item(w: &mut fmt::Formatter, cx: &Context, m: &clean::Item, t: &clean::
.partition::<Vec<_>, _>(|i| i.inner_impl().for_.def_id()
.map_or(true, |d| cache.paths.contains_key(&d)));
let (synthetic, concrete) = local.iter()
.partition::<Vec<_>, _>(|i| i.inner_impl().synthetic);
if !foreign.is_empty() {
write!(w, "
<h2 id='foreign-impls' class='small-section-header'>
......@@ -2422,42 +2500,35 @@ fn trait_item(w: &mut fmt::Formatter, cx: &Context, m: &clean::Item, t: &clean::
}
write!(w, "{}", impl_header)?;
for implementor in concrete {
render_implementor(cx, implementor, w, &implementor_dups)?;
}
write!(w, "</ul>")?;
for implementor in local {
write!(w, "<li>")?;
if let Some(l) = (Item { cx, item: &implementor.impl_item }).src_href() {
write!(w, "<div class='out-of-band'>")?;
write!(w, "<a class='srclink' href='{}' title='{}'>[src]</a>",
l, "goto source code")?;
write!(w, "</div>")?;
}
write!(w, "<code>")?;
// If there's already another implementor that has the same abbridged name, use the
// full path, for example in `std::iter::ExactSizeIterator`
let use_absolute = match implementor.inner_impl().for_ {
clean::ResolvedPath { ref path, is_generic: false, .. } |
clean::BorrowedRef {
type_: box clean::ResolvedPath { ref path, is_generic: false, .. },
..
} => implementor_dups[path.last_name()].1,
_ => false,
};
fmt_impl_for_trait_page(&implementor.inner_impl(), w, use_absolute)?;
for it in &implementor.inner_impl().items {
if let clean::TypedefItem(ref tydef, _) = it.inner {
write!(w, "<span class=\"where fmt-newline\"> ")?;
assoc_type(w, it, &vec![], Some(&tydef.type_), AssocItemLink::Anchor(None))?;
write!(w, ";</span>")?;
}
if t.auto {
write!(w, "{}", synthetic_impl_header)?;
for implementor in synthetic {
synthetic_types.extend(
collect_paths_for_type(implementor.inner_impl().for_.clone())
);
render_implementor(cx, implementor, w, &implementor_dups)?;
}
writeln!(w, "</code></li>")?;
write!(w, "</ul>")?;
}
} else {
// even without any implementations to write in, we still want the heading and list, so the
// implementors javascript file pulled in below has somewhere to write the impls into
write!(w, "{}", impl_header)?;
write!(w, "</ul>")?;
if t.auto {
write!(w, "{}", synthetic_impl_header)?;
write!(w, "</ul>")?;
}
}
write!(w, "</ul>")?;
write!(w, r#"<script type="text/javascript">window.inlined_types=new Set({});</script>"#,
as_json(&synthetic_types))?;
write!(w, r#"<script type="text/javascript" async
src="{root_path}/implementors/{path}/{ty}.{name}.js">
</script>"#,
......@@ -3075,17 +3146,28 @@ fn render_assoc_items(w: &mut fmt::Formatter,
}).is_some();
render_deref_methods(w, cx, impl_, containing_item, has_deref_mut)?;
}
let (synthetic, concrete) = traits
.iter()
.partition::<Vec<_>, _>(|t| t.inner_impl().synthetic);
write!(w, "
<h2 id='implementations' class='small-section-header'>
Trait Implementations<a href='#implementations' class='anchor'></a>
</h2>
<div id='implementations-list'>
")?;
for i in &traits {
let did = i.trait_did().unwrap();
let assoc_link = AssocItemLink::GotoSource(did, &i.inner_impl().provided_trait_methods);
render_impl(w, cx, i, assoc_link,
RenderMode::Normal, containing_item.stable_since(), true)?;
}
render_impls(cx, w, concrete, containing_item)?;
write!(w, "</div>")?;
write!(w, "
<h2 id='synthetic-implementations' class='small-section-header'>
Auto Trait Implementations<a href='#synthetic-implementations' class='anchor'></a>
</h2>
<div id='synthetic-implementations-list'>
")?;
render_impls(cx, w, synthetic, containing_item)?;
write!(w, "</div>")?;
}
Ok(())
}
......@@ -3586,32 +3668,48 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
}
}
}
let mut links = HashSet::new();
let ret = v.iter()
.filter_map(|i| {
let is_negative_impl = is_negative_impl(i.inner_impl());
if let Some(ref i) = i.inner_impl().trait_ {
let i_display = format!("{:#}", i);
let out = Escape(&i_display);
let encoded = small_url_encode(&format!("{:#}", i));
let generated = format!("<a href=\"#impl-{}\">{}{}</a>",
encoded,
if is_negative_impl { "!" } else { "" },
out);
if !links.contains(&generated) && links.insert(generated.clone()) {
Some(generated)
let format_impls = |impls: Vec<&Impl>| {
let mut links = HashSet::new();
impls.iter()
.filter_map(|i| {
let is_negative_impl = is_negative_impl(i.inner_impl());
if let Some(ref i) = i.inner_impl().trait_ {
let i_display = format!("{:#}", i);
let out = Escape(&i_display);
let encoded = small_url_encode(&format!("{:#}", i));
let generated = format!("<a href=\"#impl-{}\">{}{}</a>",
encoded,
if is_negative_impl { "!" } else { "" },
out);
if links.insert(generated.clone()) {
Some(generated)
} else {
None
}
} else {
None
}
} else {
None
}
})
.collect::<String>();
if !ret.is_empty() {
})
.collect::<String>()
};
let (synthetic, concrete) = v
.iter()
.partition::<Vec<_>, _>(|i| i.inner_impl().synthetic);
let concrete_format = format_impls(concrete);
let synthetic_format = format_impls(synthetic);
if !concrete_format.is_empty() {
out.push_str("<a class=\"sidebar-title\" href=\"#implementations\">\
Trait Implementations</a>");
out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", ret));
out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", concrete_format));
}
if !synthetic_format.is_empty() {
out.push_str("<a class=\"sidebar-title\" href=\"#synthetic-implementations\">\
Auto Trait Implementations</a>");
out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", synthetic_format));
}
}
}
......@@ -3734,7 +3832,7 @@ fn sidebar_trait(fmt: &mut fmt::Formatter, it: &clean::Item,
if let Some(implementors) = c.implementors.get(&it.def_id) {
let res = implementors.iter()
.filter(|i| i.inner_impl().for_.def_id()
.map_or(false, |d| !c.paths.contains_key(&d)))
.map_or(false, |d| !c.paths.contains_key(&d)))
.filter_map(|i| {
match extract_for_impl_name(&i.impl_item) {
Some((ref name, ref url)) => {
......@@ -3755,6 +3853,10 @@ fn sidebar_trait(fmt: &mut fmt::Formatter, it: &clean::Item,
}
sidebar.push_str("<a class=\"sidebar-title\" href=\"#implementors\">Implementors</a>");
if t.auto {
sidebar.push_str("<a class=\"sidebar-title\" \
href=\"#synthetic-implementors\">Auto Implementors</a>");
}
sidebar.push_str(&sidebar_assoc_items(it));
......@@ -3969,6 +4071,66 @@ fn get_index_type(clean_type: &clean::Type) -> Type {
t
}
/// Returns a list of all paths used in the type.
/// This is used to help deduplicate imported impls
/// for reexported types. If any of the contained
/// types are re-exported, we don't use the corresponding
/// entry from the js file, as inlining will have already
/// picked up the impl
fn collect_paths_for_type(first_ty: clean::Type) -> Vec<String> {
let mut out = Vec::new();
let mut visited = FxHashSet();
let mut work = VecDeque::new();
let cache = cache();
work.push_back(first_ty);
while let Some(ty) = work.pop_front() {
if !visited.insert(ty.clone()) {
continue;
}
match ty {
clean::Type::ResolvedPath { did, .. } => {
let get_extern = || cache.external_paths.get(&did).map(|s| s.0.clone());
let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern);
match fqp {
Some(path) => {
out.push(path.join("::"));
},
_ => {}
};
},
clean::Type::Tuple(tys) => {
work.extend(tys.into_iter());
},
clean::Type::Slice(ty) => {
work.push_back(*ty);
}
clean::Type::Array(ty, _) => {
work.push_back(*ty);
},
clean::Type::Unique(ty) => {
work.push_back(*ty);
},
clean::Type::RawPointer(_, ty) => {
work.push_back(*ty);
},
clean::Type::BorrowedRef { type_, .. } => {
work.push_back(*type_);
},
clean::Type::QPath { self_type, trait_, .. } => {
work.push_back(*self_type);
work.push_back(*trait_);
},
_ => {}
}
};
out
}
fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option<String> {
match *clean_type {
clean::ResolvedPath { ref path, .. } => {
......
......@@ -1563,14 +1563,31 @@
window.initSidebarItems = initSidebarItems;
window.register_implementors = function(imp) {
var list = document.getElementById('implementors-list');
var implementors = document.getElementById('implementors-list');
var synthetic_implementors = document.getElementById('synthetic-implementors-list');
var libs = Object.getOwnPropertyNames(imp);
for (var i = 0; i < libs.length; ++i) {
if (libs[i] === currentCrate) { continue; }
var structs = imp[libs[i]];
struct_loop:
for (var j = 0; j < structs.length; ++j) {
var struct = structs[j];
var list = struct.synthetic ? synthetic_implementors : implementors;
if (struct.synthetic) {
for (var k = 0; k < struct.types.length; k++) {
if (window.inlined_types.has(struct.types[k])) {
continue struct_loop;
}
window.inlined_types.add(struct.types[k]);
}
}
var code = document.createElement('code');
code.innerHTML = structs[j];
code.innerHTML = struct.text;
var x = code.getElementsByTagName('a');
for (var k = 0; k < x.length; k++) {
......
......@@ -24,6 +24,7 @@
#![feature(test)]
#![feature(unicode)]
#![feature(vec_remove_item)]
#![feature(entry_and_modify)]
extern crate arena;
extern crate getopts;
......@@ -549,7 +550,8 @@ fn rust_input<R, F>(cratefile: PathBuf, externs: Externs, matches: &getopts::Mat
let (mut krate, renderinfo) =
core::run_core(paths, cfgs, externs, Input::File(cratefile), triple, maybe_sysroot,
display_warnings, force_unstable_if_unmarked);
display_warnings, crate_name.clone(),
force_unstable_if_unmarked);
info!("finished with rustc");
......
......@@ -24,12 +24,12 @@
use rustc::middle::cstore::{LoadedMacro, CrateStore};
use rustc::middle::privacy::AccessLevel;
use rustc::ty::Visibility;
use rustc::util::nodemap::FxHashSet;
use rustc::util::nodemap::{FxHashSet, FxHashMap};
use rustc::hir;
use core;
use clean::{self, AttributesExt, NestedAttributesExt};
use clean::{self, AttributesExt, NestedAttributesExt, def_id_to_path};
use doctree::*;
// looks to me like the first two of these are actually
......@@ -41,7 +41,7 @@
// framework from syntax?
pub struct RustdocVisitor<'a, 'tcx: 'a, 'rcx: 'a> {
cstore: &'a CrateStore,
pub cstore: &'a CrateStore,
pub module: Module,
pub attrs: hir::HirVec<ast::Attribute>,
pub cx: &'a core::DocContext<'a, 'tcx, 'rcx>,
......@@ -50,6 +50,7 @@ pub struct RustdocVisitor<'a, 'tcx: 'a, 'rcx: 'a> {
/// Is the current module and all of its parents public?
inside_public_path: bool,
reexported_macros: FxHashSet<DefId>,
exact_paths: Option<FxHashMap<DefId, Vec<String>>>,
}
impl<'a, 'tcx, 'rcx> RustdocVisitor<'a, 'tcx, 'rcx> {
......@@ -66,10 +67,21 @@ pub fn new(cstore: &'a CrateStore,
inlining: false,
inside_public_path: true,
reexported_macros: FxHashSet(),
exact_paths: Some(FxHashMap()),
cstore,
}
}
fn store_path(&mut self, did: DefId) {
// We can't use the entry api, as that keeps the mutable borrow of self active
// when we try to use cx
let exact_paths = self.exact_paths.as_mut().unwrap();
if exact_paths.get(&did).is_none() {
let path = def_id_to_path(self.cx, did, self.cx.crate_name.clone());
exact_paths.insert(did, path);
}
}
fn stability(&self, id: ast::NodeId) -> Option<attr::Stability> {
self.cx.tcx.hir.opt_local_def_id(id)
.and_then(|def_id| self.cx.tcx.lookup_stability(def_id)).cloned()
......@@ -94,6 +106,8 @@ pub fn visit(&mut self, krate: &hir::Crate) {
krate.exported_macros.iter().map(|def| self.visit_local_macro(def)).collect();
self.module.macros.extend(macro_exports);
self.module.is_crate = true;
self.cx.renderinfo.borrow_mut().exact_paths = self.exact_paths.take().unwrap();
}
pub fn visit_variant_data(&mut self, item: &hir::Item,
......@@ -371,6 +385,12 @@ pub fn visit_item(&mut self, item: &hir::Item,
renamed: Option<ast::Name>, om: &mut Module) {
debug!("Visiting item {:?}", item);
let name = renamed.unwrap_or(item.name);
if item.vis == hir::Public {
let def_id = self.cx.tcx.hir.local_def_id(item.id);
self.store_path(def_id);
}
match item.node {
hir::ItemForeignMod(ref fm) => {
// If inlining we only want to include public functions.
......
......@@ -11,7 +11,8 @@
// @has issue_33054/impls/struct.Foo.html
// @has - '//code' 'impl Foo'
// @has - '//code' 'impl Bar for Foo'
// @count - '//*[@class="impl"]' 2
// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 1
// @count - '//*[@id="main"]/*[@class="impl"]' 1
// @has issue_33054/impls/bar/trait.Bar.html
// @has - '//code' 'impl Bar for Foo'
// @count - '//*[@class="struct"]' 1
......
......@@ -17,5 +17,5 @@ impl super::Blah for super::What { }
pub trait Blah { }
// @count issue_21474/struct.What.html \
// '//*[@class="impl"]' 1
// '//*[@id="implementations-list"]/*[@class="impl"]' 1
pub struct What;
......@@ -14,12 +14,12 @@ pub trait Bar<T, U> {}
// @has 'foo/struct.Foo1.html'
pub struct Foo1;
// @count - '//*[@class="impl"]' 1
// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 1
// @has - '//*[@class="impl"]' "impl Bar<Foo1, &'static Foo1> for Foo1"
impl Bar<Foo1, &'static Foo1> for Foo1 {}
// @has 'foo/struct.Foo2.html'
pub struct Foo2;
// @count - '//*[@class="impl"]' 1
// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 1
// @has - '//*[@class="impl"]' "impl Bar<&'static Foo2, Foo2> for u8"
impl Bar<&'static Foo2, Foo2> for u8 {}
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// @has basic/struct.Foo.html
// @has - '//code' 'impl<T> Send for Foo<T> where T: Send'
// @has - '//code' 'impl<T> Sync for Foo<T> where T: Sync'
// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 0
// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 2
pub struct Foo<T> {
field: T,
}
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
mod foo {
pub trait MyTrait<'a> {
type MyItem: ?Sized;
}
pub struct Inner<'a, Q, R: ?Sized> {
field: Q,
field3: &'a u8,
my_foo: Foo<Q>,
field2: R,
}
pub struct Outer<'a, T, K: ?Sized> {
my_inner: Inner<'a, T, K>,
}
pub struct Foo<T> {
myfield: T,
}
}
// @has complex/struct.NotOuter.html
// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]/*/code' "impl<'a, T, K: \
// ?Sized> Send for NotOuter<'a, T, K> where K: for<'b> Fn((&'b bool, &'a u8)) \
// -> &'b i8, T: MyTrait<'a>, <T as MyTrait<'a>>::MyItem: Copy, 'a: 'static"
pub use foo::{Foo, Inner as NotInner, MyTrait as NotMyTrait, Outer as NotOuter};
unsafe impl<T> Send for Foo<T>
where
T: NotMyTrait<'static>,
{
}
unsafe impl<'a, Q, R: ?Sized> Send for NotInner<'a, Q, R>
where
Q: NotMyTrait<'a>,
<Q as NotMyTrait<'a>>::MyItem: Copy,
R: for<'b> Fn((&'b bool, &'a u8)) -> &'b i8,
Foo<Q>: Send,
{
}
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub struct Inner<'a, T: 'a> {
field: &'a T,
}
unsafe impl<'a, T> Send for Inner<'a, T>
where
'a: 'static,
T: for<'b> Fn(&'b bool) -> &'a u8,
{}
// @has lifetimes/struct.Foo.html
// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]/*/code' "impl<'c, K> Send \
// for Foo<'c, K> where K: for<'b> Fn(&'b bool) -> &'c u8, 'c: 'static"
//
// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]/*/code' "impl<'c, K> Sync \
// for Foo<'c, K> where K: Sync"
pub struct Foo<'c, K: 'c> {
inner_field: Inner<'c, K>,
}
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// @has manual/struct.Foo.html
// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]/*/code' 'impl<T> Sync for \
// Foo<T> where T: Sync'
//
// @has - '//*[@id="implementations-list"]/*[@class="impl"]/*/code' \
// 'impl<T> Send for Foo<T>'
//
// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 1
// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 1
pub struct Foo<T> {
field: T,
}
unsafe impl<T> Send for Foo<T> {}
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub struct Inner<T: Copy> {
field: *mut T,
}
// @has negative/struct.Outer.html
// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]/*/code' "impl<T> !Send for \
// Outer<T>"
//
// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]/*/code' "impl<T> \
// !Sync for Outer<T>"
pub struct Outer<T: Copy> {
inner_field: Inner<T>,
}
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub struct Inner<T> {
field: T,
}
unsafe impl<T> Send for Inner<T>
where
T: Copy,
{
}
// @has nested/struct.Foo.html
// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]/*/code' 'impl<T> Send for \
// Foo<T> where T: Copy'
//
// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]/*/code' \
// 'impl<T> Sync for Foo<T> where T: Sync'
pub struct Foo<T> {
inner_field: Inner<T>,
}
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub struct Inner<T> {
field: T,
}
unsafe impl<T> Send for Inner<T>
where
T: Copy + Send,
{
}
// @has no_redundancy/struct.Outer.html
// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]/*/code' "impl<T> Send for \
// Outer<T> where T: Copy + Send"
pub struct Outer<T> {
inner_field: Inner<T>,
}
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub struct Inner<'a, T: 'a> {
field: &'a T,
}
trait MyTrait {
type MyItem;
}
trait OtherTrait {}
unsafe impl<'a, T> Send for Inner<'a, T>
where
'a: 'static,
T: MyTrait<MyItem = bool>,
{
}
unsafe impl<'a, T> Sync for Inner<'a, T>
where
'a: 'static,
T: MyTrait,
<T as MyTrait>::MyItem: OtherTrait,
{
}
// @has project/struct.Foo.html
// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]/*/code' "impl<'c, K> Send \
// for Foo<'c, K> where K: MyTrait<MyItem = bool>, 'c: 'static"
//
// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]/*/code' "impl<'c, K> Sync \
// for Foo<'c, K> where K: MyTrait, <K as MyTrait>::MyItem: OtherTrait, 'c: 'static,"
pub struct Foo<'c, K: 'c> {
inner_field: Inner<'c, K>,
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册