Auto merge of #107637 - fmease:rustdoc-reelide-x-crate-def-tr-obj-lt-bnds, r=notriddle,cgillot,GuillaumeGomez
rustdoc: re-elide cross-crate default trait-object lifetime bounds Hide trait-object lifetime bounds (re-exported from an external crate) if they coincide with [their default](https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes). Partially addresses #44306. Follow-up to #103885. [Zulip discussion](https://rust-lang.zulipchat.com/#narrow/stream/266220-rustdoc/topic/clean_middle_ty.3A.20I.20need.20to.20add.20a.20parameter/near/307143097). Most notably, if `std` exported something from `core` containing a type like `Box<dyn Fn()>`, then it would now be rendered as `Box<dyn Fn(), Global>` instead of `Box<dyn Fn() + 'static, Global>` (hiding `+ 'static` as it is the default in this case). Showing `Global` here is a separate issue, #80379, which is on my agenda. Note that I am not really fond of the fact that I had to add a parameter to such a widely used function (30+ call sites) to address such a niche bug. CC `@GuillaumeGomez` Requesting a review from a compiler contributor or team member as recommended on Zulip. r? compiler --- `@rustbot` label T-compiler T-rustdoc A-cross-crate-reexports
This commit is contained in:
commit
7820972f86
@ -124,7 +124,7 @@ where
|
||||
unsafety: hir::Unsafety::Normal,
|
||||
generics: new_generics,
|
||||
trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, ThinVec::new())),
|
||||
for_: clean_middle_ty(ty::Binder::dummy(ty), self.cx, None),
|
||||
for_: clean_middle_ty(ty::Binder::dummy(ty), self.cx, None, None),
|
||||
items: Vec::new(),
|
||||
polarity,
|
||||
kind: ImplKind::Auto,
|
||||
|
@ -107,7 +107,12 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
|
||||
ty::Binder::dummy(trait_ref.subst_identity()),
|
||||
ThinVec::new(),
|
||||
)),
|
||||
for_: clean_middle_ty(ty::Binder::dummy(ty.subst_identity()), cx, None),
|
||||
for_: clean_middle_ty(
|
||||
ty::Binder::dummy(ty.subst_identity()),
|
||||
cx,
|
||||
None,
|
||||
None,
|
||||
),
|
||||
items: cx
|
||||
.tcx
|
||||
.associated_items(impl_def_id)
|
||||
@ -119,6 +124,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
|
||||
ty::Binder::dummy(trait_ref.subst_identity().self_ty()),
|
||||
cx,
|
||||
None,
|
||||
None,
|
||||
))),
|
||||
}))),
|
||||
cfg: None,
|
||||
|
@ -278,8 +278,12 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union {
|
||||
|
||||
fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box<clean::Typedef> {
|
||||
let predicates = cx.tcx.explicit_predicates_of(did);
|
||||
let type_ =
|
||||
clean_middle_ty(ty::Binder::dummy(cx.tcx.type_of(did).subst_identity()), cx, Some(did));
|
||||
let type_ = clean_middle_ty(
|
||||
ty::Binder::dummy(cx.tcx.type_of(did).subst_identity()),
|
||||
cx,
|
||||
Some(did),
|
||||
None,
|
||||
);
|
||||
|
||||
Box::new(clean::Typedef {
|
||||
type_,
|
||||
@ -386,9 +390,12 @@ pub(crate) fn build_impl(
|
||||
|
||||
let for_ = match &impl_item {
|
||||
Some(impl_) => clean_ty(impl_.self_ty, cx),
|
||||
None => {
|
||||
clean_middle_ty(ty::Binder::dummy(tcx.type_of(did).subst_identity()), cx, Some(did))
|
||||
}
|
||||
None => clean_middle_ty(
|
||||
ty::Binder::dummy(tcx.type_of(did).subst_identity()),
|
||||
cx,
|
||||
Some(did),
|
||||
None,
|
||||
),
|
||||
};
|
||||
|
||||
// Only inline impl if the implementing type is
|
||||
@ -630,6 +637,7 @@ fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant {
|
||||
ty::Binder::dummy(cx.tcx.type_of(def_id).subst_identity()),
|
||||
cx,
|
||||
Some(def_id),
|
||||
None,
|
||||
),
|
||||
kind: clean::ConstantKind::Extern { def_id },
|
||||
}
|
||||
@ -641,6 +649,7 @@ fn build_static(cx: &mut DocContext<'_>, did: DefId, mutable: bool) -> clean::St
|
||||
ty::Binder::dummy(cx.tcx.type_of(did).subst_identity()),
|
||||
cx,
|
||||
Some(did),
|
||||
None,
|
||||
),
|
||||
mutability: if mutable { Mutability::Mut } else { Mutability::Not },
|
||||
expr: None,
|
||||
|
@ -31,6 +31,7 @@ use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::hygiene::{AstPass, MacroKind};
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::{self, ExpnKind};
|
||||
use rustc_trait_selection::traits::wf::object_region_bounds;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::hash_map::Entry;
|
||||
@ -253,6 +254,7 @@ pub(crate) fn clean_const<'tcx>(constant: &hir::ConstArg, cx: &mut DocContext<'t
|
||||
ty::Binder::dummy(cx.tcx.type_of(def_id).subst_identity()),
|
||||
cx,
|
||||
Some(def_id),
|
||||
None,
|
||||
),
|
||||
kind: ConstantKind::Anonymous { body: constant.value.body },
|
||||
}
|
||||
@ -264,7 +266,7 @@ pub(crate) fn clean_middle_const<'tcx>(
|
||||
) -> Constant {
|
||||
// FIXME: instead of storing the stringified expression, store `self` directly instead.
|
||||
Constant {
|
||||
type_: clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None),
|
||||
type_: clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None, None),
|
||||
kind: ConstantKind::TyConst { expr: constant.skip_binder().to_string().into() },
|
||||
}
|
||||
}
|
||||
@ -370,7 +372,7 @@ fn clean_poly_trait_predicate<'tcx>(
|
||||
|
||||
let poly_trait_ref = pred.map_bound(|pred| pred.trait_ref);
|
||||
Some(WherePredicate::BoundPredicate {
|
||||
ty: clean_middle_ty(poly_trait_ref.self_ty(), cx, None),
|
||||
ty: clean_middle_ty(poly_trait_ref.self_ty(), cx, None, None),
|
||||
bounds: vec![clean_poly_trait_ref_with_bindings(cx, poly_trait_ref, ThinVec::new())],
|
||||
bound_params: Vec::new(),
|
||||
})
|
||||
@ -396,7 +398,7 @@ fn clean_type_outlives_predicate<'tcx>(
|
||||
let ty::OutlivesPredicate(ty, lt) = pred.skip_binder();
|
||||
|
||||
Some(WherePredicate::BoundPredicate {
|
||||
ty: clean_middle_ty(pred.rebind(ty), cx, None),
|
||||
ty: clean_middle_ty(pred.rebind(ty), cx, None, None),
|
||||
bounds: vec![GenericBound::Outlives(
|
||||
clean_middle_region(lt).expect("failed to clean lifetimes"),
|
||||
)],
|
||||
@ -409,7 +411,7 @@ fn clean_middle_term<'tcx>(
|
||||
cx: &mut DocContext<'tcx>,
|
||||
) -> Term {
|
||||
match term.skip_binder().unpack() {
|
||||
ty::TermKind::Ty(ty) => Term::Type(clean_middle_ty(term.rebind(ty), cx, None)),
|
||||
ty::TermKind::Ty(ty) => Term::Type(clean_middle_ty(term.rebind(ty), cx, None, None)),
|
||||
ty::TermKind::Const(c) => Term::Constant(clean_middle_const(term.rebind(c), cx)),
|
||||
}
|
||||
}
|
||||
@ -462,7 +464,7 @@ fn clean_projection<'tcx>(
|
||||
|
||||
let trait_ =
|
||||
clean_trait_ref_with_bindings(cx, ty.map_bound(|ty| ty.trait_ref(cx.tcx)), ThinVec::new());
|
||||
let self_type = clean_middle_ty(ty.map_bound(|ty| ty.self_ty()), cx, None);
|
||||
let self_type = clean_middle_ty(ty.map_bound(|ty| ty.self_ty()), cx, None, None);
|
||||
let self_def_id = if let Some(def_id) = def_id {
|
||||
cx.tcx.opt_parent(def_id).or(Some(def_id))
|
||||
} else {
|
||||
@ -493,8 +495,13 @@ fn projection_to_path_segment<'tcx>(
|
||||
PathSegment {
|
||||
name: item.name,
|
||||
args: GenericArgs::AngleBracketed {
|
||||
args: substs_to_args(cx, ty.map_bound(|ty| &ty.substs[generics.parent_count..]), false)
|
||||
.into(),
|
||||
args: substs_to_args(
|
||||
cx,
|
||||
ty.map_bound(|ty| &ty.substs[generics.parent_count..]),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
bindings: Default::default(),
|
||||
},
|
||||
}
|
||||
@ -514,6 +521,7 @@ fn clean_generic_param_def<'tcx>(
|
||||
ty::Binder::dummy(cx.tcx.type_of(def.def_id).subst_identity()),
|
||||
cx,
|
||||
Some(def.def_id),
|
||||
None,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
@ -540,6 +548,7 @@ fn clean_generic_param_def<'tcx>(
|
||||
),
|
||||
cx,
|
||||
Some(def.def_id),
|
||||
None,
|
||||
)),
|
||||
default: match has_default {
|
||||
true => Some(Box::new(
|
||||
@ -871,7 +880,7 @@ fn clean_ty_generics<'tcx>(
|
||||
let crate::core::ImplTraitParam::ParamIndex(idx) = param else { unreachable!() };
|
||||
if let Some(proj) = impl_trait_proj.remove(&idx) {
|
||||
for (trait_did, name, rhs, bound_params) in proj {
|
||||
let rhs = clean_middle_ty(rhs, cx, None);
|
||||
let rhs = clean_middle_ty(rhs, cx, None, None);
|
||||
simplify::merge_bounds(
|
||||
cx,
|
||||
&mut bounds,
|
||||
@ -1126,7 +1135,7 @@ fn clean_fn_decl_from_did_and_sig<'tcx>(
|
||||
|
||||
// We assume all empty tuples are default return type. This theoretically can discard `-> ()`,
|
||||
// but shouldn't change any code meaning.
|
||||
let output = clean_middle_ty(sig.output(), cx, None);
|
||||
let output = clean_middle_ty(sig.output(), cx, None, None);
|
||||
|
||||
FnDecl {
|
||||
output,
|
||||
@ -1136,7 +1145,7 @@ fn clean_fn_decl_from_did_and_sig<'tcx>(
|
||||
.inputs()
|
||||
.iter()
|
||||
.map(|t| Argument {
|
||||
type_: clean_middle_ty(t.map_bound(|t| *t), cx, None),
|
||||
type_: clean_middle_ty(t.map_bound(|t| *t), cx, None, None),
|
||||
name: names
|
||||
.next()
|
||||
.map(|i| i.name)
|
||||
@ -1190,8 +1199,12 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
|
||||
hir::TraitItemKind::Type(bounds, Some(default)) => {
|
||||
let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
|
||||
let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect();
|
||||
let item_type =
|
||||
clean_middle_ty(ty::Binder::dummy(hir_ty_to_ty(cx.tcx, default)), cx, None);
|
||||
let item_type = clean_middle_ty(
|
||||
ty::Binder::dummy(hir_ty_to_ty(cx.tcx, default)),
|
||||
cx,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
AssocTypeItem(
|
||||
Box::new(Typedef {
|
||||
type_: clean_ty(default, cx),
|
||||
@ -1230,8 +1243,12 @@ pub(crate) fn clean_impl_item<'tcx>(
|
||||
hir::ImplItemKind::Type(hir_ty) => {
|
||||
let type_ = clean_ty(hir_ty, cx);
|
||||
let generics = clean_generics(impl_.generics, cx);
|
||||
let item_type =
|
||||
clean_middle_ty(ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), cx, None);
|
||||
let item_type = clean_middle_ty(
|
||||
ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)),
|
||||
cx,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
AssocTypeItem(
|
||||
Box::new(Typedef { type_, generics, item_type: Some(item_type) }),
|
||||
Vec::new(),
|
||||
@ -1254,6 +1271,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
|
||||
ty::Binder::dummy(tcx.type_of(assoc_item.def_id).subst_identity()),
|
||||
cx,
|
||||
Some(assoc_item.def_id),
|
||||
None,
|
||||
);
|
||||
|
||||
let provided = match assoc_item.container {
|
||||
@ -1447,6 +1465,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
|
||||
ty::Binder::dummy(tcx.type_of(assoc_item.def_id).subst_identity()),
|
||||
cx,
|
||||
Some(assoc_item.def_id),
|
||||
None,
|
||||
),
|
||||
generics,
|
||||
// FIXME: should we obtain the Type from HIR and pass it on here?
|
||||
@ -1465,6 +1484,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
|
||||
ty::Binder::dummy(tcx.type_of(assoc_item.def_id).subst_identity()),
|
||||
cx,
|
||||
Some(assoc_item.def_id),
|
||||
None,
|
||||
),
|
||||
generics: Generics {
|
||||
params: ThinVec::new(),
|
||||
@ -1510,7 +1530,7 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type
|
||||
if !ty.has_escaping_bound_vars()
|
||||
&& let Some(normalized_value) = normalize(cx, ty::Binder::dummy(ty))
|
||||
{
|
||||
return clean_middle_ty(normalized_value, cx, None);
|
||||
return clean_middle_ty(normalized_value, cx, None, None);
|
||||
}
|
||||
|
||||
let trait_segments = &p.segments[..p.segments.len() - 1];
|
||||
@ -1738,11 +1758,140 @@ fn normalize<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
fn clean_trait_object_lifetime_bound<'tcx>(
|
||||
region: ty::Region<'tcx>,
|
||||
container: Option<ContainerTy<'tcx>>,
|
||||
preds: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> Option<Lifetime> {
|
||||
if can_elide_trait_object_lifetime_bound(region, container, preds, tcx) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Since there is a semantic difference between an implicitly elided (i.e. "defaulted") object
|
||||
// lifetime and an explicitly elided object lifetime (`'_`), we intentionally don't hide the
|
||||
// latter contrary to `clean_middle_region`.
|
||||
match *region {
|
||||
ty::ReStatic => Some(Lifetime::statik()),
|
||||
ty::ReEarlyBound(region) if region.name != kw::Empty => Some(Lifetime(region.name)),
|
||||
ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name), .. })
|
||||
if name != kw::Empty =>
|
||||
{
|
||||
Some(Lifetime(name))
|
||||
}
|
||||
ty::ReEarlyBound(_)
|
||||
| ty::ReLateBound(..)
|
||||
| ty::ReFree(_)
|
||||
| ty::ReVar(_)
|
||||
| ty::RePlaceholder(_)
|
||||
| ty::ReErased
|
||||
| ty::ReError(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn can_elide_trait_object_lifetime_bound<'tcx>(
|
||||
region: ty::Region<'tcx>,
|
||||
container: Option<ContainerTy<'tcx>>,
|
||||
preds: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> bool {
|
||||
// Below we quote extracts from https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes
|
||||
|
||||
// > If the trait object is used as a type argument of a generic type then the containing type is
|
||||
// > first used to try to infer a bound.
|
||||
let default = container
|
||||
.map_or(ObjectLifetimeDefault::Empty, |container| container.object_lifetime_default(tcx));
|
||||
|
||||
// > If there is a unique bound from the containing type then that is the default
|
||||
// If there is a default object lifetime and the given region is lexically equal to it, elide it.
|
||||
match default {
|
||||
ObjectLifetimeDefault::Static => return *region == ty::ReStatic,
|
||||
// FIXME(fmease): Don't compare lexically but respect de Bruijn indices etc. to handle shadowing correctly.
|
||||
ObjectLifetimeDefault::Arg(default) => return region.get_name() == default.get_name(),
|
||||
// > If there is more than one bound from the containing type then an explicit bound must be specified
|
||||
// Due to ambiguity there is no default trait-object lifetime and thus elision is impossible.
|
||||
// Don't elide the lifetime.
|
||||
ObjectLifetimeDefault::Ambiguous => return false,
|
||||
// There is no meaningful bound. Further processing is needed...
|
||||
ObjectLifetimeDefault::Empty => {}
|
||||
}
|
||||
|
||||
// > If neither of those rules apply, then the bounds on the trait are used:
|
||||
match *object_region_bounds(tcx, preds) {
|
||||
// > If the trait has no lifetime bounds, then the lifetime is inferred in expressions
|
||||
// > and is 'static outside of expressions.
|
||||
// FIXME: If we are in an expression context (i.e. fn bodies and const exprs) then the default is
|
||||
// `'_` and not `'static`. Only if we are in a non-expression one, the default is `'static`.
|
||||
// Note however that at the time of this writing it should be fine to disregard this subtlety
|
||||
// as we neither render const exprs faithfully anyway (hiding them in some places or using `_` instead)
|
||||
// nor show the contents of fn bodies.
|
||||
[] => *region == ty::ReStatic,
|
||||
// > If the trait is defined with a single lifetime bound then that bound is used.
|
||||
// > If 'static is used for any lifetime bound then 'static is used.
|
||||
// FIXME(fmease): Don't compare lexically but respect de Bruijn indices etc. to handle shadowing correctly.
|
||||
[object_region] => object_region.get_name() == region.get_name(),
|
||||
// There are several distinct trait regions and none are `'static`.
|
||||
// Due to ambiguity there is no default trait-object lifetime and thus elision is impossible.
|
||||
// Don't elide the lifetime.
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ContainerTy<'tcx> {
|
||||
Ref(ty::Region<'tcx>),
|
||||
Regular { ty: DefId, substs: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>, arg: usize },
|
||||
}
|
||||
|
||||
impl<'tcx> ContainerTy<'tcx> {
|
||||
fn object_lifetime_default(self, tcx: TyCtxt<'tcx>) -> ObjectLifetimeDefault<'tcx> {
|
||||
match self {
|
||||
Self::Ref(region) => ObjectLifetimeDefault::Arg(region),
|
||||
Self::Regular { ty: container, substs, arg: index } => {
|
||||
let (DefKind::Struct
|
||||
| DefKind::Union
|
||||
| DefKind::Enum
|
||||
| DefKind::TyAlias
|
||||
| DefKind::Trait
|
||||
| DefKind::AssocTy
|
||||
| DefKind::Variant) = tcx.def_kind(container)
|
||||
else {
|
||||
return ObjectLifetimeDefault::Empty;
|
||||
};
|
||||
|
||||
let generics = tcx.generics_of(container);
|
||||
let param = generics.params[index].def_id;
|
||||
let default = tcx.object_lifetime_default(param);
|
||||
|
||||
match default {
|
||||
rbv::ObjectLifetimeDefault::Param(lifetime) => {
|
||||
let index = generics.param_def_id_to_index[&lifetime];
|
||||
let arg = substs.skip_binder()[index as usize].expect_region();
|
||||
ObjectLifetimeDefault::Arg(arg)
|
||||
}
|
||||
rbv::ObjectLifetimeDefault::Empty => ObjectLifetimeDefault::Empty,
|
||||
rbv::ObjectLifetimeDefault::Static => ObjectLifetimeDefault::Static,
|
||||
rbv::ObjectLifetimeDefault::Ambiguous => ObjectLifetimeDefault::Ambiguous,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) enum ObjectLifetimeDefault<'tcx> {
|
||||
Empty,
|
||||
Static,
|
||||
Ambiguous,
|
||||
Arg(ty::Region<'tcx>),
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(cx), ret)]
|
||||
pub(crate) fn clean_middle_ty<'tcx>(
|
||||
bound_ty: ty::Binder<'tcx, Ty<'tcx>>,
|
||||
cx: &mut DocContext<'tcx>,
|
||||
parent_def_id: Option<DefId>,
|
||||
container: Option<ContainerTy<'tcx>>,
|
||||
) -> Type {
|
||||
let bound_ty = normalize(cx, bound_ty).unwrap_or(bound_ty);
|
||||
match *bound_ty.skip_binder().kind() {
|
||||
@ -1753,19 +1902,24 @@ pub(crate) fn clean_middle_ty<'tcx>(
|
||||
ty::Uint(uint_ty) => Primitive(uint_ty.into()),
|
||||
ty::Float(float_ty) => Primitive(float_ty.into()),
|
||||
ty::Str => Primitive(PrimitiveType::Str),
|
||||
ty::Slice(ty) => Slice(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None))),
|
||||
ty::Slice(ty) => Slice(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None))),
|
||||
ty::Array(ty, mut n) => {
|
||||
n = n.eval(cx.tcx, ty::ParamEnv::reveal_all());
|
||||
let n = print_const(cx, n);
|
||||
Array(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None)), n.into())
|
||||
Array(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), n.into())
|
||||
}
|
||||
ty::RawPtr(mt) => {
|
||||
RawPointer(mt.mutbl, Box::new(clean_middle_ty(bound_ty.rebind(mt.ty), cx, None)))
|
||||
RawPointer(mt.mutbl, Box::new(clean_middle_ty(bound_ty.rebind(mt.ty), cx, None, None)))
|
||||
}
|
||||
ty::Ref(r, ty, mutbl) => BorrowedRef {
|
||||
lifetime: clean_middle_region(r),
|
||||
mutability: mutbl,
|
||||
type_: Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None)),
|
||||
type_: Box::new(clean_middle_ty(
|
||||
bound_ty.rebind(ty),
|
||||
cx,
|
||||
None,
|
||||
Some(ContainerTy::Ref(r)),
|
||||
)),
|
||||
},
|
||||
ty::FnDef(..) | ty::FnPtr(_) => {
|
||||
// FIXME: should we merge the outer and inner binders somehow?
|
||||
@ -1817,10 +1971,8 @@ pub(crate) fn clean_middle_ty<'tcx>(
|
||||
|
||||
inline::record_extern_fqn(cx, did, ItemType::Trait);
|
||||
|
||||
// FIXME(fmease): Hide the trait-object lifetime bound if it coincides with its default
|
||||
// to partially address #44306. Follow the rules outlined at
|
||||
// https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes
|
||||
let lifetime = clean_middle_region(*reg);
|
||||
let lifetime = clean_trait_object_lifetime_bound(*reg, container, obj, cx.tcx);
|
||||
|
||||
let mut bounds = dids
|
||||
.map(|did| {
|
||||
let empty = ty::Binder::dummy(InternalSubsts::empty());
|
||||
@ -1869,7 +2021,7 @@ pub(crate) fn clean_middle_ty<'tcx>(
|
||||
DynTrait(bounds, lifetime)
|
||||
}
|
||||
ty::Tuple(t) => {
|
||||
Tuple(t.iter().map(|t| clean_middle_ty(bound_ty.rebind(t), cx, None)).collect())
|
||||
Tuple(t.iter().map(|t| clean_middle_ty(bound_ty.rebind(t), cx, None, None)).collect())
|
||||
}
|
||||
|
||||
ty::Alias(ty::Projection, ref data) => {
|
||||
@ -1878,7 +2030,7 @@ pub(crate) fn clean_middle_ty<'tcx>(
|
||||
|
||||
ty::Alias(ty::Inherent, alias_ty) => {
|
||||
let alias_ty = bound_ty.rebind(alias_ty);
|
||||
let self_type = clean_middle_ty(alias_ty.map_bound(|ty| ty.self_ty()), cx, None);
|
||||
let self_type = clean_middle_ty(alias_ty.map_bound(|ty| ty.self_ty()), cx, None, None);
|
||||
|
||||
Type::QPath(Box::new(QPathData {
|
||||
assoc: PathSegment {
|
||||
@ -1888,6 +2040,7 @@ pub(crate) fn clean_middle_ty<'tcx>(
|
||||
cx,
|
||||
alias_ty.map_bound(|ty| ty.substs.as_slice()),
|
||||
true,
|
||||
None,
|
||||
)
|
||||
.into(),
|
||||
bindings: Default::default(),
|
||||
@ -2023,6 +2176,7 @@ pub(crate) fn clean_middle_field<'tcx>(field: &ty::FieldDef, cx: &mut DocContext
|
||||
ty::Binder::dummy(cx.tcx.type_of(field.did).subst_identity()),
|
||||
cx,
|
||||
Some(field.did),
|
||||
None,
|
||||
),
|
||||
cx,
|
||||
)
|
||||
@ -2314,7 +2468,12 @@ fn clean_maybe_renamed_item<'tcx>(
|
||||
ItemKind::TyAlias(hir_ty, generics) => {
|
||||
*cx.current_type_aliases.entry(def_id).or_insert(0) += 1;
|
||||
let rustdoc_ty = clean_ty(hir_ty, cx);
|
||||
let ty = clean_middle_ty(ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), cx, None);
|
||||
let ty = clean_middle_ty(
|
||||
ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)),
|
||||
cx,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let generics = clean_generics(generics, cx);
|
||||
if let Some(count) = cx.current_type_aliases.get_mut(&def_id) {
|
||||
*count -= 1;
|
||||
@ -2418,6 +2577,7 @@ fn clean_impl<'tcx>(
|
||||
ty::Binder::dummy(tcx.type_of(def_id).subst_identity()),
|
||||
cx,
|
||||
Some(def_id.to_def_id()),
|
||||
None,
|
||||
)),
|
||||
_ => None,
|
||||
});
|
||||
|
@ -73,8 +73,9 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
|
||||
|
||||
pub(crate) fn substs_to_args<'tcx>(
|
||||
cx: &mut DocContext<'tcx>,
|
||||
substs: ty::Binder<'tcx, &[ty::subst::GenericArg<'tcx>]>,
|
||||
substs: ty::Binder<'tcx, &'tcx [ty::subst::GenericArg<'tcx>]>,
|
||||
mut skip_first: bool,
|
||||
container: Option<DefId>,
|
||||
) -> Vec<GenericArg> {
|
||||
let mut ret_val =
|
||||
Vec::with_capacity(substs.skip_binder().len().saturating_sub(if skip_first {
|
||||
@ -82,19 +83,29 @@ pub(crate) fn substs_to_args<'tcx>(
|
||||
} else {
|
||||
0
|
||||
}));
|
||||
ret_val.extend(substs.iter().filter_map(|kind| match kind.skip_binder().unpack() {
|
||||
GenericArgKind::Lifetime(lt) => {
|
||||
Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided())))
|
||||
}
|
||||
GenericArgKind::Type(_) if skip_first => {
|
||||
skip_first = false;
|
||||
None
|
||||
}
|
||||
GenericArgKind::Type(ty) => {
|
||||
Some(GenericArg::Type(clean_middle_ty(kind.rebind(ty), cx, None)))
|
||||
}
|
||||
GenericArgKind::Const(ct) => {
|
||||
Some(GenericArg::Const(Box::new(clean_middle_const(kind.rebind(ct), cx))))
|
||||
|
||||
ret_val.extend(substs.iter().enumerate().filter_map(|(index, kind)| {
|
||||
match kind.skip_binder().unpack() {
|
||||
GenericArgKind::Lifetime(lt) => {
|
||||
Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided())))
|
||||
}
|
||||
GenericArgKind::Type(_) if skip_first => {
|
||||
skip_first = false;
|
||||
None
|
||||
}
|
||||
GenericArgKind::Type(ty) => Some(GenericArg::Type(clean_middle_ty(
|
||||
kind.rebind(ty),
|
||||
cx,
|
||||
None,
|
||||
container.map(|container| crate::clean::ContainerTy::Regular {
|
||||
ty: container,
|
||||
substs,
|
||||
arg: index,
|
||||
}),
|
||||
))),
|
||||
GenericArgKind::Const(ct) => {
|
||||
Some(GenericArg::Const(Box::new(clean_middle_const(kind.rebind(ct), cx))))
|
||||
}
|
||||
}
|
||||
}));
|
||||
ret_val
|
||||
@ -107,7 +118,7 @@ fn external_generic_args<'tcx>(
|
||||
bindings: ThinVec<TypeBinding>,
|
||||
substs: ty::Binder<'tcx, SubstsRef<'tcx>>,
|
||||
) -> GenericArgs {
|
||||
let args = substs_to_args(cx, substs.map_bound(|substs| &substs[..]), has_self);
|
||||
let args = substs_to_args(cx, substs.map_bound(|substs| &substs[..]), has_self, Some(did));
|
||||
|
||||
if cx.tcx.fn_trait_kind_from_def_id(did).is_some() {
|
||||
let ty = substs
|
||||
@ -118,7 +129,7 @@ fn external_generic_args<'tcx>(
|
||||
let inputs =
|
||||
// The trait's first substitution is the one after self, if there is one.
|
||||
match ty.skip_binder().kind() {
|
||||
ty::Tuple(tys) => tys.iter().map(|t| clean_middle_ty(ty.rebind(t), cx, None)).collect::<Vec<_>>().into(),
|
||||
ty::Tuple(tys) => tys.iter().map(|t| clean_middle_ty(ty.rebind(t), cx, None, None)).collect::<Vec<_>>().into(),
|
||||
_ => return GenericArgs::AngleBracketed { args: args.into(), bindings },
|
||||
};
|
||||
let output = bindings.into_iter().next().and_then(|binding| match binding.kind {
|
||||
|
@ -46,7 +46,6 @@ pub fn f(_: &(ToString + 'static)) {}
|
||||
impl Bar {
|
||||
// @has assoc_consts/struct.Bar.html '//*[@id="associatedconstant.F"]' \
|
||||
// "const F: fn(_: &(dyn ToString + 'static))"
|
||||
// FIXME(fmease): Hide default lifetime, render "const F: fn(_: &dyn ToString)"
|
||||
pub const F: fn(_: &(ToString + 'static)) = f;
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
// ignore-tidy-linelength
|
||||
|
||||
pub type Ty0 = dyn for<'any> FnOnce(&'any str) -> bool;
|
||||
|
||||
pub type Ty1<'obj> = dyn std::fmt::Display + 'obj;
|
||||
@ -6,12 +8,60 @@ pub type Ty2 = dyn for<'a, 'r> Container<'r, Item<'a, 'static> = ()>;
|
||||
|
||||
pub type Ty3<'s> = &'s dyn ToString;
|
||||
|
||||
pub fn func0(_: &(dyn Fn() + '_)) {}
|
||||
|
||||
pub fn func1<'func>(_: &(dyn Fn() + 'func)) {}
|
||||
|
||||
pub trait Container<'r> {
|
||||
type Item<'a, 'ctx>;
|
||||
}
|
||||
|
||||
pub trait Shape<'a> {}
|
||||
// Trait-object types inside of a container type that has lifetime bounds ("wrapped").
|
||||
|
||||
pub fn late_bound_wrapped_elided(_: &(dyn Fn() + '_)) {}
|
||||
pub fn late_bound_wrapped_late0<'f>(_: &mut (dyn Fn() + 'f)) {}
|
||||
pub fn late_bound_wrapped_defaulted0<'f>(_: &'f mut dyn Fn()) {}
|
||||
pub type EarlyBoundWrappedDefaulted0<'x> = std::cell::Ref<'x, dyn Trait>;
|
||||
pub type EarlyBoundWrappedDefaulted1<'x> = &'x dyn Trait;
|
||||
pub type EarlyBoundWrappedEarly<'x, 'y> = std::cell::Ref<'x, dyn Trait + 'y>;
|
||||
pub type EarlyBoundWrappedStatic<'x> = std::cell::Ref<'x, dyn Trait + 'static>;
|
||||
pub fn late_bound_wrapped_defaulted1<'l>(_: std::cell::Ref<'l, dyn Trait>) {}
|
||||
pub fn late_bound_wrapped_late1<'l, 'm>(_: std::cell::Ref<'l, dyn Trait + 'm>) {}
|
||||
pub fn late_bound_wrapped_early<'e, 'l>(_: std::cell::Ref<'l, dyn Trait + 'e>) where 'e: {} // `'e` is early-bound
|
||||
pub fn elided_bound_wrapped_defaulted(_: std::cell::Ref<'_, dyn Trait>) {}
|
||||
pub type StaticBoundWrappedDefaulted0 = std::cell::Ref<'static, dyn Trait>;
|
||||
pub type StaticBoundWrappedDefaulted1 = &'static dyn Trait;
|
||||
pub type AmbiguousBoundWrappedEarly0<'r, 's> = AmbiguousBoundWrapper<'s, 'r, dyn Trait + 's>;
|
||||
pub type AmbiguousBoundWrappedEarly1<'r, 's> = AmbiguousBoundWrapper<'s, 'r, dyn Trait + 'r>;
|
||||
pub type AmbiguousBoundWrappedStatic<'q> = AmbiguousBoundWrapper<'q, 'q, dyn Trait + 'static>;
|
||||
|
||||
// Trait-object types inside of a container type that doesn't have lifetime bounds ("wrapped").
|
||||
|
||||
pub type NoBoundsWrappedDefaulted = Box<dyn Trait>;
|
||||
pub type NoBoundsWrappedEarly<'e> = Box<dyn Trait + 'e>;
|
||||
pub fn no_bounds_wrapped_late<'l>(_: Box<dyn Trait + 'l>) {}
|
||||
pub fn no_bounds_wrapped_elided(_: Box<dyn Trait + '_>) {}
|
||||
|
||||
// Trait-object types outside of a container (“bare”).
|
||||
|
||||
pub type BareNoBoundsDefaulted = dyn Trait;
|
||||
pub type BareNoBoundsEarly<'p> = dyn Trait + 'p;
|
||||
pub type BareEarlyBoundDefaulted0<'u> = dyn EarlyBoundTrait0<'u>;
|
||||
pub type BareEarlyBoundDefaulted1 = dyn for<'any> EarlyBoundTrait0<'any>;
|
||||
pub type BareEarlyBoundDefaulted2<'w> = dyn EarlyBoundTrait1<'static, 'w>;
|
||||
pub type BareEarlyBoundEarly<'i, 'j> = dyn EarlyBoundTrait0<'i> + 'j;
|
||||
pub type BareEarlyBoundStatic<'i> = dyn EarlyBoundTrait0<'i> + 'static;
|
||||
pub type BareStaticBoundDefaulted = dyn StaticBoundTrait;
|
||||
pub type BareHigherRankedBoundDefaulted0 = dyn HigherRankedBoundTrait0;
|
||||
pub type BareHigherRankedBoundDefaulted1<'r> = dyn HigherRankedBoundTrait1<'r>;
|
||||
pub type BareAmbiguousBoundEarly0<'m, 'n> = dyn AmbiguousBoundTrait<'m, 'n> + 'm;
|
||||
pub type BareAmbiguousBoundEarly1<'m, 'n> = dyn AmbiguousBoundTrait<'m, 'n> + 'n;
|
||||
pub type BareAmbiguousBoundStatic<'o> = dyn AmbiguousBoundTrait<'o, 'o> + 'static;
|
||||
|
||||
// Trait and container definitions.
|
||||
|
||||
pub trait Trait {} // no bounds
|
||||
pub trait EarlyBoundTrait0<'b>: 'b {}
|
||||
pub trait EarlyBoundTrait1<'unused, 'c>: 'c {}
|
||||
pub trait StaticBoundTrait: 'static {}
|
||||
pub trait HigherRankedBoundTrait0 where for<'a> Self: 'a {}
|
||||
pub trait HigherRankedBoundTrait1<'e> where for<'l> Self: 'e + 'l {}
|
||||
pub trait AmbiguousBoundTrait<'a, 'b>: 'a + 'b {}
|
||||
|
||||
pub struct AmbiguousBoundWrapper<'a, 'b, T: ?Sized + 'a + 'b>(&'a T, &'b T);
|
||||
|
@ -1,31 +1,130 @@
|
||||
#![crate_name = "user"]
|
||||
|
||||
// In each test case, we include the trailing semicolon to ensure that nothing extra comes
|
||||
// after the type like an unwanted outlives-bound.
|
||||
|
||||
// aux-crate:dyn_trait=dyn_trait.rs
|
||||
// edition:2021
|
||||
|
||||
// @has user/type.Ty0.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn for<'any> FnOnce(&'any str) -> bool + 'static"
|
||||
// FIXME(fmease): Hide default lifetime bound `'static`
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn for<'any> FnOnce(&'any str) -> bool;"
|
||||
pub use dyn_trait::Ty0;
|
||||
|
||||
// @has user/type.Ty1.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn Display + 'obj"
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn Display + 'obj;"
|
||||
pub use dyn_trait::Ty1;
|
||||
|
||||
// @has user/type.Ty2.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn for<'a, 'r> Container<'r, Item<'a, 'static> = ()>"
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn for<'a, 'r> Container<'r, Item<'a, 'static> = ()>;"
|
||||
pub use dyn_trait::Ty2;
|
||||
|
||||
// @has user/type.Ty3.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "&'s (dyn ToString + 's)"
|
||||
// FIXME(fmease): Hide default lifetime bound, render "&'s dyn ToString"
|
||||
// @has - '//*[@class="rust item-decl"]//code' "&'s dyn ToString;"
|
||||
pub use dyn_trait::Ty3;
|
||||
|
||||
// @has user/fn.func0.html
|
||||
// @has - '//pre[@class="rust item-decl"]' "func0(_: &dyn Fn())"
|
||||
// FIXME(fmease): Show placeholder-lifetime bound, render "func0(_: &(dyn Fn() + '_))"
|
||||
pub use dyn_trait::func0;
|
||||
// Below we check if we correctly elide trait-object lifetime bounds if they coincide with their
|
||||
// default (known as "object lifetime default" or "default trait object lifetime").
|
||||
|
||||
// @has user/fn.func1.html
|
||||
// @has - '//pre[@class="rust item-decl"]' "func1<'func>(_: &(dyn Fn() + 'func))"
|
||||
pub use dyn_trait::func1;
|
||||
// @has user/fn.lbwel.html
|
||||
// @has - '//pre[@class="rust item-decl"]' "lbwel(_: &dyn Fn())"
|
||||
pub use dyn_trait::late_bound_wrapped_elided as lbwel;
|
||||
// @has user/fn.lbwl0.html
|
||||
// has - '//pre[@class="rust item-decl"]' "lbwl0<'f>(_: &mut (dyn Fn() + 'f))"
|
||||
pub use dyn_trait::late_bound_wrapped_late0 as lbwl0;
|
||||
// @has user/fn.lbwd0.html
|
||||
// has - '//pre[@class="rust item-decl"]' "lbwd0<'f>(_: &'f mut dyn Fn())"
|
||||
pub use dyn_trait::late_bound_wrapped_defaulted0 as lbwd0;
|
||||
// @has user/type.EarlyBoundWrappedDefaulted0.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "Ref<'x, dyn Trait>;"
|
||||
pub use dyn_trait::EarlyBoundWrappedDefaulted0;
|
||||
// @has user/type.EarlyBoundWrappedDefaulted1.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "&'x dyn Trait;"
|
||||
pub use dyn_trait::EarlyBoundWrappedDefaulted1;
|
||||
// @has user/type.EarlyBoundWrappedEarly.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "Ref<'x, dyn Trait + 'y>"
|
||||
pub use dyn_trait::EarlyBoundWrappedEarly;
|
||||
// @has user/type.EarlyBoundWrappedStatic.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "Ref<'x, dyn Trait + 'static>"
|
||||
pub use dyn_trait::EarlyBoundWrappedStatic;
|
||||
// @has user/fn.lbwd1.html
|
||||
// @has - '//pre[@class="rust item-decl"]' "lbwd1<'l>(_: Ref<'l, dyn Trait>)"
|
||||
pub use dyn_trait::late_bound_wrapped_defaulted1 as lbwd1;
|
||||
// @has user/fn.lbwl1.html
|
||||
// @has - '//pre[@class="rust item-decl"]' "lbwl1<'l, 'm>(_: Ref<'l, dyn Trait + 'm>)"
|
||||
pub use dyn_trait::late_bound_wrapped_late1 as lbwl1;
|
||||
// @has user/fn.lbwe.html
|
||||
// @has - '//pre[@class="rust item-decl"]' "lbwe<'e, 'l>(_: Ref<'l, dyn Trait + 'e>)"
|
||||
pub use dyn_trait::late_bound_wrapped_early as lbwe;
|
||||
// @has user/fn.ebwd.html
|
||||
// @has - '//pre[@class="rust item-decl"]' "ebwd(_: Ref<'_, dyn Trait>)"
|
||||
pub use dyn_trait::elided_bound_wrapped_defaulted as ebwd;
|
||||
// @has user/type.StaticBoundWrappedDefaulted0.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "Ref<'static, dyn Trait>;"
|
||||
pub use dyn_trait::StaticBoundWrappedDefaulted0;
|
||||
// @has user/type.StaticBoundWrappedDefaulted1.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "&'static dyn Trait;"
|
||||
pub use dyn_trait::StaticBoundWrappedDefaulted1;
|
||||
// @has user/type.AmbiguousBoundWrappedEarly0.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "AmbiguousBoundWrapper<'s, 'r, dyn Trait + 's>;"
|
||||
pub use dyn_trait::AmbiguousBoundWrappedEarly0;
|
||||
// @has user/type.AmbiguousBoundWrappedEarly1.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "AmbiguousBoundWrapper<'s, 'r, dyn Trait + 'r>;"
|
||||
pub use dyn_trait::AmbiguousBoundWrappedEarly1;
|
||||
// @has user/type.AmbiguousBoundWrappedStatic.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "AmbiguousBoundWrapper<'q, 'q, dyn Trait + 'static>;"
|
||||
pub use dyn_trait::AmbiguousBoundWrappedStatic;
|
||||
|
||||
// @has user/type.NoBoundsWrappedDefaulted.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "Box<dyn Trait, Global>;"
|
||||
pub use dyn_trait::NoBoundsWrappedDefaulted;
|
||||
// @has user/type.NoBoundsWrappedEarly.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "Box<dyn Trait + 'e, Global>;"
|
||||
pub use dyn_trait::NoBoundsWrappedEarly;
|
||||
// @has user/fn.nbwl.html
|
||||
// @has - '//pre[@class="rust item-decl"]' "nbwl<'l>(_: Box<dyn Trait + 'l, Global>)"
|
||||
pub use dyn_trait::no_bounds_wrapped_late as nbwl;
|
||||
// @has user/fn.nbwel.html
|
||||
// @has - '//pre[@class="rust item-decl"]' "nbwel(_: Box<dyn Trait + '_, Global>)"
|
||||
// NB: It might seem counterintuitive to display the explicitly elided lifetime `'_` here instead of
|
||||
// eliding it but this behavior is correct: The default is `'static` here which != `'_`.
|
||||
pub use dyn_trait::no_bounds_wrapped_elided as nbwel;
|
||||
|
||||
// @has user/type.BareNoBoundsDefaulted.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn Trait;"
|
||||
pub use dyn_trait::BareNoBoundsDefaulted;
|
||||
// @has user/type.BareNoBoundsEarly.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn Trait + 'p;"
|
||||
pub use dyn_trait::BareNoBoundsEarly;
|
||||
// @has user/type.BareEarlyBoundDefaulted0.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn EarlyBoundTrait0<'u>;"
|
||||
pub use dyn_trait::BareEarlyBoundDefaulted0;
|
||||
// @has user/type.BareEarlyBoundDefaulted1.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn for<'any> EarlyBoundTrait0<'any>;"
|
||||
pub use dyn_trait::BareEarlyBoundDefaulted1;
|
||||
// @has user/type.BareEarlyBoundDefaulted2.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn EarlyBoundTrait1<'static, 'w>;"
|
||||
pub use dyn_trait::BareEarlyBoundDefaulted2;
|
||||
// @has user/type.BareEarlyBoundEarly.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn EarlyBoundTrait0<'i> + 'j;"
|
||||
pub use dyn_trait::BareEarlyBoundEarly;
|
||||
// @has user/type.BareEarlyBoundStatic.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn EarlyBoundTrait0<'i> + 'static;"
|
||||
pub use dyn_trait::BareEarlyBoundStatic;
|
||||
// @has user/type.BareStaticBoundDefaulted.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn StaticBoundTrait;"
|
||||
pub use dyn_trait::BareStaticBoundDefaulted;
|
||||
// @has user/type.BareHigherRankedBoundDefaulted0.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn HigherRankedBoundTrait0;"
|
||||
pub use dyn_trait::BareHigherRankedBoundDefaulted0;
|
||||
// @has user/type.BareHigherRankedBoundDefaulted1.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn HigherRankedBoundTrait1<'r>;"
|
||||
pub use dyn_trait::BareHigherRankedBoundDefaulted1;
|
||||
// @has user/type.BareAmbiguousBoundEarly0.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn AmbiguousBoundTrait<'m, 'n> + 'm;"
|
||||
pub use dyn_trait::BareAmbiguousBoundEarly0;
|
||||
// @has user/type.BareAmbiguousBoundEarly1.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn AmbiguousBoundTrait<'m, 'n> + 'n;"
|
||||
pub use dyn_trait::BareAmbiguousBoundEarly1;
|
||||
// @has user/type.BareAmbiguousBoundStatic.html
|
||||
// @has - '//*[@class="rust item-decl"]//code' "dyn AmbiguousBoundTrait<'o, 'o> + 'static;"
|
||||
pub use dyn_trait::BareAmbiguousBoundStatic;
|
||||
|
Loading…
x
Reference in New Issue
Block a user