Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -548,12 +548,15 @@ fn clean_generic_param_def(
} else {
None
};
let param_ty = ty::ParamTy::for_def(def);
let allow_unsized = !param_ty.to_ty(cx.tcx).is_sized(cx.tcx, cx.typing_env());
(
def.name,
GenericParamDefKind::Type {
bounds: ThinVec::new(), // These are filled in from the where-clauses.
default: default.map(Box::new),
synthetic,
allow_unsized,
},
)
}
Expand Down Expand Up @@ -627,12 +630,38 @@ fn clean_generic_param<'tcx>(
} else {
ThinVec::new()
};

// If this ends up being slow, then optimize it by reading the local bounds
// (from all predicate origins) and check if a bound on `?Sized` is present.
// If there's no `?Sized` bound, then definitely `allow_unsized = false`.
let allow_unsized = {
let parent = cx.tcx.hir_ty_param_owner(param.def_id);
let index = cx
.tcx
.generics_of(parent)
.param_def_id_to_index(cx.tcx, param.def_id.to_def_id());

if let Some(index) = index {
let param_ty = ty::ParamTy::new(index, param.name.ident().name);
!param_ty.to_ty(cx.tcx).is_sized(cx.tcx, cx.typing_env())
} else {
// The type is introduced by a `for<T>` binder.
// This is an unstable, incomplete feature
// gated behind `#![feature(non_lifetime_binders)]`.
//
// When the details of `for<T>` are worked out and
// the feature is closer to stabilization, add appropriate logic here.
false
}
};

(
param.name.ident().name,
GenericParamDefKind::Type {
bounds,
default: default.map(|t| clean_ty(t, cx)).map(Box::new),
synthetic,
allow_unsized,
},
)
}
Expand Down Expand Up @@ -3210,6 +3239,7 @@ fn clean_bound_vars<'tcx>(
bounds: ThinVec::new(),
default: None,
synthetic: false,
allow_unsized: false, // If `for<T>` could support `T: ?Sized`, fix this.
},
})
}
Expand Down
17 changes: 14 additions & 3 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1357,10 +1357,21 @@ impl WherePredicate {

#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub(crate) enum GenericParamDefKind {
Lifetime { outlives: ThinVec<Lifetime> },
Type { bounds: ThinVec<GenericBound>, default: Option<Box<Type>>, synthetic: bool },
Lifetime {
outlives: ThinVec<Lifetime>,
},
Type {
bounds: ThinVec<GenericBound>,
default: Option<Box<Type>>,
synthetic: bool,
allow_unsized: bool,
},
// Option<Box<String>> makes this type smaller than `Option<String>` would.
Const { ty: Box<Type>, default: Option<Box<String>>, synthetic: bool },
Const {
ty: Box<Type>,
default: Option<Box<String>>,
synthetic: bool,
},
}

impl GenericParamDefKind {
Expand Down
3 changes: 2 additions & 1 deletion src/librustdoc/json/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,10 +458,11 @@ impl FromClean<clean::GenericParamDefKind> for GenericParamDefKind {
Lifetime { outlives } => {
GenericParamDefKind::Lifetime { outlives: outlives.into_json(renderer) }
}
Type { bounds, default, synthetic } => GenericParamDefKind::Type {
Type { bounds, default, synthetic, allow_unsized } => GenericParamDefKind::Type {
bounds: bounds.into_json(renderer),
default: default.into_json(renderer),
is_synthetic: *synthetic,
allow_unsized: *allow_unsized,
},
Const { ty, default, synthetic: _ } => GenericParamDefKind::Const {
type_: ty.into_json(renderer),
Expand Down
9 changes: 7 additions & 2 deletions src/rustdoc-json-types/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc
// will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line
// are deliberately not in a doc comment, because they need not be in public docs.)
//
// Latest feature: Add Attribute::MacroUse
pub const FORMAT_VERSION: u32 = 55;
// Latest feature: Add `allow_unsized` field to `GenericParamDefKind::Type`
pub const FORMAT_VERSION: u32 = 56;

/// The root of the emitted JSON blob.
///
Expand Down Expand Up @@ -977,6 +977,11 @@ pub enum GenericParamDefKind {
/// is bound by `Trait`) is synthetic, because it was not originally in
/// the Rust source text.
is_synthetic: bool,
/// Whether this type parameter can be instantiated with an unsized type.
///
/// This is `true` if the parameter has a `?Sized` bound without any
/// additional bounds that imply `Sized`.
allow_unsized: bool,
},

/// Denotes a constant parameter.
Expand Down
7 changes: 6 additions & 1 deletion src/tools/jsondoclint/src/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,12 @@ impl<'a> Validator<'a> {
fn check_generic_param_def(&mut self, gpd: &'a GenericParamDef) {
match &gpd.kind {
rustdoc_json_types::GenericParamDefKind::Lifetime { outlives: _ } => {}
rustdoc_json_types::GenericParamDefKind::Type { bounds, default, is_synthetic: _ } => {
rustdoc_json_types::GenericParamDefKind::Type {
bounds,
default,
is_synthetic: _,
allow_unsized: _,
} => {
bounds.iter().for_each(|b| self.check_generic_bound(b));
if let Some(ty) = default {
self.check_type(ty);
Expand Down
139 changes: 139 additions & 0 deletions tests/rustdoc-json/allow_unsized.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#![crate_name = "allow_unsized"]

use std::fmt::Debug;
use std::marker::PhantomData;

pub trait CustomSized: Sized {}
impl CustomSized for u8 {}

// Generic functions
//@ is "$.index[?(@.name=='func_custom')].inner.function.generics.params[0].kind.type.allow_unsized" false
pub fn func_custom<T: ?Sized + CustomSized>() {}
Copy link
Member

Choose a reason for hiding this comment

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

This should test that we don't remove the ?Sized bound from the bounds list, even though it ends up not mattering.


//@ is "$.index[?(@.name=='func_custom')].inner.function.generics.params[0].kind.type.allow_unsized" false
pub fn func_custom_where_denies<T: ?Sized>()
where
T: CustomSized,
{
}

//@ is "$.index[?(@.name=='func_custom')].inner.function.generics.params[0].kind.type.allow_unsized" false
pub fn func_custom_where_allows<T: CustomSized>()
where
T: ?Sized,
{
}

//@ is "$.index[?(@.name=='func_custom')].inner.function.generics.params[0].kind.type.allow_unsized" false
pub fn func_custom_where_both<T>()
where
T: ?Sized + CustomSized,
{
}

//@ is "$.index[?(@.name=='func_unsized')].inner.function.generics.params[0].kind.type.allow_unsized" true
pub fn func_unsized<T: ?Sized>() {}

//@ is "$.index[?(@.name=='func_clone')].inner.function.generics.params[0].kind.type.allow_unsized" false
pub fn func_clone<T: ?Sized + Clone>() {}

//@ is "$.index[?(@.name=='func_debug')].inner.function.generics.params[0].kind.type.allow_unsized" true
pub fn func_debug<T: ?Sized + Debug>() {}

// Generic structs
//@ is "$.index[?(@.name=='StructCustom')].inner.struct.generics.params[0].kind.type.allow_unsized" false
pub struct StructCustom<T: ?Sized + CustomSized>(PhantomData<T>);

//@ is "$.index[?(@.name=='StructUnsized')].inner.struct.generics.params[0].kind.type.allow_unsized" true
pub struct StructUnsized<T: ?Sized>(PhantomData<T>);

//@ is "$.index[?(@.name=='StructClone')].inner.struct.generics.params[0].kind.type.allow_unsized" false
pub struct StructClone<T: ?Sized + Clone>(PhantomData<T>);

//@ is "$.index[?(@.name=='StructDebug')].inner.struct.generics.params[0].kind.type.allow_unsized" true
pub struct StructDebug<T: ?Sized + Debug>(PhantomData<T>);

// Struct with `?Sized` bound, and impl blocks that add additional bounds
//@ is "$.index[?(@.name=='Wrapper')].inner.struct.generics.params[0].kind.type.allow_unsized" true
pub struct Wrapper<T: ?Sized>(PhantomData<T>);

//@ is "$.index[?(@.docs=='impl custom')].inner.impl.generics.params[0].kind.type.allow_unsized" false
/// impl custom
impl<T: ?Sized + CustomSized> Wrapper<T> {
pub fn impl_custom() {}
}

//@ is "$.index[?(@.docs=='impl clone')].inner.impl.generics.params[0].kind.type.allow_unsized" false
/// impl clone
impl<T: ?Sized + Clone> Wrapper<T> {
pub fn impl_clone() {}
}

//@ is "$.index[?(@.docs=='impl debug')].inner.impl.generics.params[0].kind.type.allow_unsized" true
/// impl debug
impl<T: ?Sized + Debug> Wrapper<T> {
pub fn impl_debug() {}
}

impl<T: ?Sized> Wrapper<T> {
//@ is "$.index[?(@.name=='assoc_custom')].inner.function.generics.params[0].kind.type.allow_unsized" false
pub fn assoc_custom<U: ?Sized + CustomSized>(&self) {}

//@ is "$.index[?(@.name=='assoc_unsized')].inner.function.generics.params[0].kind.type.allow_unsized" true
pub fn assoc_unsized<U: ?Sized>(&self) {}

//@ is "$.index[?(@.name=='assoc_clone')].inner.function.generics.params[0].kind.type.allow_unsized" false
pub fn assoc_clone<U: ?Sized + Clone>(&self) {}

//@ is "$.index[?(@.name=='assoc_debug')].inner.function.generics.params[0].kind.type.allow_unsized" true
pub fn assoc_debug<U: ?Sized + Debug>(&self) {}
}

// Traits with generic parameters
//@ is "$.index[?(@.name=='TraitCustom')].inner.trait.generics.params[0].kind.type.allow_unsized" false
pub trait TraitCustom<T: ?Sized + CustomSized> {}

//@ is "$.index[?(@.name=='TraitUnsized')].inner.trait.generics.params[0].kind.type.allow_unsized" true
pub trait TraitUnsized<T: ?Sized> {}

//@ is "$.index[?(@.name=='TraitClone')].inner.trait.generics.params[0].kind.type.allow_unsized" false
pub trait TraitClone<T: ?Sized + Clone> {}

//@ is "$.index[?(@.name=='TraitDebug')].inner.trait.generics.params[0].kind.type.allow_unsized" true
pub trait TraitDebug<T: ?Sized + Debug> {}

pub trait TraitMethods {
//@ is "$.index[?(@.name=='method_custom')].inner.function.generics.params[0].kind.type.allow_unsized" false
fn method_custom<T: ?Sized + CustomSized>();

//@ is "$.index[?(@.name=='method_unsized')].inner.function.generics.params[0].kind.type.allow_unsized" true
fn method_unsized<T: ?Sized>();

//@ is "$.index[?(@.name=='method_clone')].inner.function.generics.params[0].kind.type.allow_unsized" false
fn method_clone<T: ?Sized + Clone>();

//@ is "$.index[?(@.name=='method_debug')].inner.function.generics.params[0].kind.type.allow_unsized" true
fn method_debug<T: ?Sized + Debug>();
}

// `where` clauses on trait functions, which only affect `T` for that function
//@ is "$.index[?(@.name=='OuterDebug')].inner.trait.generics.params[0].kind.type.allow_unsized" true
pub trait OuterDebug<T: ?Sized> {
fn foo()
where
T: Debug;
}

//@ is "$.index[?(@.name=='OuterClone')].inner.trait.generics.params[0].kind.type.allow_unsized" true
pub trait OuterClone<T: ?Sized> {
fn foo()
where
T: Clone;
}

// Synthetic generic parameters
//@ is "$.index[?(@.name=='synth_clone')].inner.function.generics.params[0].kind.type.allow_unsized" false
pub fn synth_clone(_: impl Clone + ?Sized) {}

//@ is "$.index[?(@.name=='synth_debug')].inner.function.generics.params[0].kind.type.allow_unsized" true
pub fn synth_debug(_: impl Debug + ?Sized) {}
2 changes: 1 addition & 1 deletion tests/rustdoc-json/fns/generic_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub fn impl_trait(f: impl Foo) {}

//@ count "$.index[?(@.name=='where_clase')].inner.function.generics.params[*]" 3
//@ is "$.index[?(@.name=='where_clase')].inner.function.generics.params[0].name" '"F"'
//@ is "$.index[?(@.name=='where_clase')].inner.function.generics.params[0].kind" '{"type": {"bounds": [], "default": null, "is_synthetic": false}}'
//@ is "$.index[?(@.name=='where_clase')].inner.function.generics.params[0].kind" '{"type": {"bounds": [], "default": null, "is_synthetic": false, "allow_unsized": false}}'
//@ count "$.index[?(@.name=='where_clase')].inner.function.sig.inputs[*]" 3
//@ is "$.index[?(@.name=='where_clase')].inner.function.sig.inputs[0][0]" '"f"'
//@ is "$.index[?(@.name=='where_clase')].inner.function.sig.inputs[0][1].generic" '"F"'
Expand Down
2 changes: 1 addition & 1 deletion tests/rustdoc-json/non_lifetime_binders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub struct Wrapper<T_>(std::marker::PhantomData<T_>);
//@ is "$.index[?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[0].name" \"\'a\"
//@ is "$.index[?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[0].kind" '{ "lifetime": { "outlives": [] } }'
//@ is "$.index[?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[1].name" \"T\"
//@ is "$.index[?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[1].kind" '{ "type": { "bounds": [], "default": null, "is_synthetic": false } }'
//@ is "$.index[?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[1].kind" '{ "type": { "bounds": [], "default": null, "is_synthetic": false, "allow_unsized": false } }'
pub fn foo()
where
for<'a, T> &'a Wrapper<T>: Trait,
Expand Down
Loading