Skip to content

Commit c1707dd

Browse files
Auto merge of #143559 - obi1kenobi:pg/allow_unsized, r=<try>
[rustdoc-json] Show whether `?Sized` parameters are actually `Sized`
2 parents e8a792d + 85e32ef commit c1707dd

File tree

8 files changed

+200
-9
lines changed

8 files changed

+200
-9
lines changed

src/librustdoc/clean/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,12 +548,15 @@ fn clean_generic_param_def(
548548
} else {
549549
None
550550
};
551+
let param_ty = ty::ParamTy::for_def(def);
552+
let allow_unsized = !param_ty.to_ty(cx.tcx).is_sized(cx.tcx, cx.typing_env());
551553
(
552554
def.name,
553555
GenericParamDefKind::Type {
554556
bounds: ThinVec::new(), // These are filled in from the where-clauses.
555557
default: default.map(Box::new),
556558
synthetic,
559+
allow_unsized,
557560
},
558561
)
559562
}
@@ -627,12 +630,38 @@ fn clean_generic_param<'tcx>(
627630
} else {
628631
ThinVec::new()
629632
};
633+
634+
// If this ends up being slow, then optimize it by reading the local bounds
635+
// (from all predicate origins) and check if a bound on `?Sized` is present.
636+
// If there's no `?Sized` bound, then definitely `allow_unsized = false`.
637+
let allow_unsized = {
638+
let parent = cx.tcx.hir_ty_param_owner(param.def_id);
639+
let index = cx
640+
.tcx
641+
.generics_of(parent)
642+
.param_def_id_to_index(cx.tcx, param.def_id.to_def_id());
643+
644+
if let Some(index) = index {
645+
let param_ty = ty::ParamTy::new(index, param.name.ident().name);
646+
!param_ty.to_ty(cx.tcx).is_sized(cx.tcx, cx.typing_env())
647+
} else {
648+
// The type is introduced by a `for<T>` binder.
649+
// This is an unstable, incomplete feature
650+
// gated behind `#![feature(non_lifetime_binders)]`.
651+
//
652+
// When the details of `for<T>` are worked out and
653+
// the feature is closer to stabilization, add appropriate logic here.
654+
false
655+
}
656+
};
657+
630658
(
631659
param.name.ident().name,
632660
GenericParamDefKind::Type {
633661
bounds,
634662
default: default.map(|t| clean_ty(t, cx)).map(Box::new),
635663
synthetic,
664+
allow_unsized,
636665
},
637666
)
638667
}
@@ -3210,6 +3239,7 @@ fn clean_bound_vars<'tcx>(
32103239
bounds: ThinVec::new(),
32113240
default: None,
32123241
synthetic: false,
3242+
allow_unsized: false, // If `for<T>` could support `T: ?Sized`, fix this.
32133243
},
32143244
})
32153245
}

src/librustdoc/clean/types.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,10 +1357,21 @@ impl WherePredicate {
13571357

13581358
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
13591359
pub(crate) enum GenericParamDefKind {
1360-
Lifetime { outlives: ThinVec<Lifetime> },
1361-
Type { bounds: ThinVec<GenericBound>, default: Option<Box<Type>>, synthetic: bool },
1360+
Lifetime {
1361+
outlives: ThinVec<Lifetime>,
1362+
},
1363+
Type {
1364+
bounds: ThinVec<GenericBound>,
1365+
default: Option<Box<Type>>,
1366+
synthetic: bool,
1367+
allow_unsized: bool,
1368+
},
13621369
// Option<Box<String>> makes this type smaller than `Option<String>` would.
1363-
Const { ty: Box<Type>, default: Option<Box<String>>, synthetic: bool },
1370+
Const {
1371+
ty: Box<Type>,
1372+
default: Option<Box<String>>,
1373+
synthetic: bool,
1374+
},
13641375
}
13651376

13661377
impl GenericParamDefKind {

src/librustdoc/json/conversions.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,10 +458,11 @@ impl FromClean<clean::GenericParamDefKind> for GenericParamDefKind {
458458
Lifetime { outlives } => {
459459
GenericParamDefKind::Lifetime { outlives: outlives.into_json(renderer) }
460460
}
461-
Type { bounds, default, synthetic } => GenericParamDefKind::Type {
461+
Type { bounds, default, synthetic, allow_unsized } => GenericParamDefKind::Type {
462462
bounds: bounds.into_json(renderer),
463463
default: default.into_json(renderer),
464464
is_synthetic: *synthetic,
465+
allow_unsized: *allow_unsized,
465466
},
466467
Const { ty, default, synthetic: _ } => GenericParamDefKind::Const {
467468
type_: ty.into_json(renderer),

src/rustdoc-json-types/lib.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc
3737
// will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line
3838
// are deliberately not in a doc comment, because they need not be in public docs.)
3939
//
40-
// Latest feature: Add Attribute::MacroUse
41-
pub const FORMAT_VERSION: u32 = 55;
40+
// Latest feature: Add `allow_unsized` field to `GenericParamDefKind::Type`
41+
pub const FORMAT_VERSION: u32 = 56;
4242

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

982987
/// Denotes a constant parameter.

src/tools/jsondoclint/src/validator.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,12 @@ impl<'a> Validator<'a> {
332332
fn check_generic_param_def(&mut self, gpd: &'a GenericParamDef) {
333333
match &gpd.kind {
334334
rustdoc_json_types::GenericParamDefKind::Lifetime { outlives: _ } => {}
335-
rustdoc_json_types::GenericParamDefKind::Type { bounds, default, is_synthetic: _ } => {
335+
rustdoc_json_types::GenericParamDefKind::Type {
336+
bounds,
337+
default,
338+
is_synthetic: _,
339+
allow_unsized: _,
340+
} => {
336341
bounds.iter().for_each(|b| self.check_generic_bound(b));
337342
if let Some(ty) = default {
338343
self.check_type(ty);

tests/rustdoc-json/allow_unsized.rs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
#![crate_name = "allow_unsized"]
2+
3+
use std::fmt::Debug;
4+
use std::marker::PhantomData;
5+
6+
pub trait CustomSized: Sized {}
7+
impl CustomSized for u8 {}
8+
9+
// Generic functions
10+
//@ is "$.index[?(@.name=='func_custom')].inner.function.generics.params[0].kind.type.allow_unsized" false
11+
pub fn func_custom<T: ?Sized + CustomSized>() {}
12+
13+
//@ is "$.index[?(@.name=='func_custom')].inner.function.generics.params[0].kind.type.allow_unsized" false
14+
pub fn func_custom_where_denies<T: ?Sized>()
15+
where
16+
T: CustomSized,
17+
{
18+
}
19+
20+
//@ is "$.index[?(@.name=='func_custom')].inner.function.generics.params[0].kind.type.allow_unsized" false
21+
pub fn func_custom_where_allows<T: CustomSized>()
22+
where
23+
T: ?Sized,
24+
{
25+
}
26+
27+
//@ is "$.index[?(@.name=='func_custom')].inner.function.generics.params[0].kind.type.allow_unsized" false
28+
pub fn func_custom_where_both<T>()
29+
where
30+
T: ?Sized + CustomSized,
31+
{
32+
}
33+
34+
//@ is "$.index[?(@.name=='func_unsized')].inner.function.generics.params[0].kind.type.allow_unsized" true
35+
pub fn func_unsized<T: ?Sized>() {}
36+
37+
//@ is "$.index[?(@.name=='func_clone')].inner.function.generics.params[0].kind.type.allow_unsized" false
38+
pub fn func_clone<T: ?Sized + Clone>() {}
39+
40+
//@ is "$.index[?(@.name=='func_debug')].inner.function.generics.params[0].kind.type.allow_unsized" true
41+
pub fn func_debug<T: ?Sized + Debug>() {}
42+
43+
// Generic structs
44+
//@ is "$.index[?(@.name=='StructCustom')].inner.struct.generics.params[0].kind.type.allow_unsized" false
45+
pub struct StructCustom<T: ?Sized + CustomSized>(PhantomData<T>);
46+
47+
//@ is "$.index[?(@.name=='StructUnsized')].inner.struct.generics.params[0].kind.type.allow_unsized" true
48+
pub struct StructUnsized<T: ?Sized>(PhantomData<T>);
49+
50+
//@ is "$.index[?(@.name=='StructClone')].inner.struct.generics.params[0].kind.type.allow_unsized" false
51+
pub struct StructClone<T: ?Sized + Clone>(PhantomData<T>);
52+
53+
//@ is "$.index[?(@.name=='StructDebug')].inner.struct.generics.params[0].kind.type.allow_unsized" true
54+
pub struct StructDebug<T: ?Sized + Debug>(PhantomData<T>);
55+
56+
// Struct with `?Sized` bound, and impl blocks that add additional bounds
57+
//@ is "$.index[?(@.name=='Wrapper')].inner.struct.generics.params[0].kind.type.allow_unsized" true
58+
pub struct Wrapper<T: ?Sized>(PhantomData<T>);
59+
60+
//@ is "$.index[?(@.docs=='impl custom')].inner.impl.generics.params[0].kind.type.allow_unsized" false
61+
/// impl custom
62+
impl<T: ?Sized + CustomSized> Wrapper<T> {
63+
pub fn impl_custom() {}
64+
}
65+
66+
//@ is "$.index[?(@.docs=='impl clone')].inner.impl.generics.params[0].kind.type.allow_unsized" false
67+
/// impl clone
68+
impl<T: ?Sized + Clone> Wrapper<T> {
69+
pub fn impl_clone() {}
70+
}
71+
72+
//@ is "$.index[?(@.docs=='impl debug')].inner.impl.generics.params[0].kind.type.allow_unsized" true
73+
/// impl debug
74+
impl<T: ?Sized + Debug> Wrapper<T> {
75+
pub fn impl_debug() {}
76+
}
77+
78+
impl<T: ?Sized> Wrapper<T> {
79+
//@ is "$.index[?(@.name=='assoc_custom')].inner.function.generics.params[0].kind.type.allow_unsized" false
80+
pub fn assoc_custom<U: ?Sized + CustomSized>(&self) {}
81+
82+
//@ is "$.index[?(@.name=='assoc_unsized')].inner.function.generics.params[0].kind.type.allow_unsized" true
83+
pub fn assoc_unsized<U: ?Sized>(&self) {}
84+
85+
//@ is "$.index[?(@.name=='assoc_clone')].inner.function.generics.params[0].kind.type.allow_unsized" false
86+
pub fn assoc_clone<U: ?Sized + Clone>(&self) {}
87+
88+
//@ is "$.index[?(@.name=='assoc_debug')].inner.function.generics.params[0].kind.type.allow_unsized" true
89+
pub fn assoc_debug<U: ?Sized + Debug>(&self) {}
90+
}
91+
92+
// Traits with generic parameters
93+
//@ is "$.index[?(@.name=='TraitCustom')].inner.trait.generics.params[0].kind.type.allow_unsized" false
94+
pub trait TraitCustom<T: ?Sized + CustomSized> {}
95+
96+
//@ is "$.index[?(@.name=='TraitUnsized')].inner.trait.generics.params[0].kind.type.allow_unsized" true
97+
pub trait TraitUnsized<T: ?Sized> {}
98+
99+
//@ is "$.index[?(@.name=='TraitClone')].inner.trait.generics.params[0].kind.type.allow_unsized" false
100+
pub trait TraitClone<T: ?Sized + Clone> {}
101+
102+
//@ is "$.index[?(@.name=='TraitDebug')].inner.trait.generics.params[0].kind.type.allow_unsized" true
103+
pub trait TraitDebug<T: ?Sized + Debug> {}
104+
105+
pub trait TraitMethods {
106+
//@ is "$.index[?(@.name=='method_custom')].inner.function.generics.params[0].kind.type.allow_unsized" false
107+
fn method_custom<T: ?Sized + CustomSized>();
108+
109+
//@ is "$.index[?(@.name=='method_unsized')].inner.function.generics.params[0].kind.type.allow_unsized" true
110+
fn method_unsized<T: ?Sized>();
111+
112+
//@ is "$.index[?(@.name=='method_clone')].inner.function.generics.params[0].kind.type.allow_unsized" false
113+
fn method_clone<T: ?Sized + Clone>();
114+
115+
//@ is "$.index[?(@.name=='method_debug')].inner.function.generics.params[0].kind.type.allow_unsized" true
116+
fn method_debug<T: ?Sized + Debug>();
117+
}
118+
119+
// `where` clauses on trait functions, which only affect `T` for that function
120+
//@ is "$.index[?(@.name=='OuterDebug')].inner.trait.generics.params[0].kind.type.allow_unsized" true
121+
pub trait OuterDebug<T: ?Sized> {
122+
fn foo()
123+
where
124+
T: Debug;
125+
}
126+
127+
//@ is "$.index[?(@.name=='OuterClone')].inner.trait.generics.params[0].kind.type.allow_unsized" true
128+
pub trait OuterClone<T: ?Sized> {
129+
fn foo()
130+
where
131+
T: Clone;
132+
}
133+
134+
// Synthetic generic parameters
135+
//@ is "$.index[?(@.name=='synth_clone')].inner.function.generics.params[0].kind.type.allow_unsized" false
136+
pub fn synth_clone(_: impl Clone + ?Sized) {}
137+
138+
//@ is "$.index[?(@.name=='synth_debug')].inner.function.generics.params[0].kind.type.allow_unsized" true
139+
pub fn synth_debug(_: impl Debug + ?Sized) {}

tests/rustdoc-json/fns/generic_args.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub fn impl_trait(f: impl Foo) {}
2727

2828
//@ count "$.index[?(@.name=='where_clase')].inner.function.generics.params[*]" 3
2929
//@ is "$.index[?(@.name=='where_clase')].inner.function.generics.params[0].name" '"F"'
30-
//@ is "$.index[?(@.name=='where_clase')].inner.function.generics.params[0].kind" '{"type": {"bounds": [], "default": null, "is_synthetic": false}}'
30+
//@ is "$.index[?(@.name=='where_clase')].inner.function.generics.params[0].kind" '{"type": {"bounds": [], "default": null, "is_synthetic": false, "allow_unsized": false}}'
3131
//@ count "$.index[?(@.name=='where_clase')].inner.function.sig.inputs[*]" 3
3232
//@ is "$.index[?(@.name=='where_clase')].inner.function.sig.inputs[0][0]" '"f"'
3333
//@ is "$.index[?(@.name=='where_clase')].inner.function.sig.inputs[0][1].generic" '"F"'

tests/rustdoc-json/non_lifetime_binders.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub struct Wrapper<T_>(std::marker::PhantomData<T_>);
99
//@ is "$.index[?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[0].name" \"\'a\"
1010
//@ is "$.index[?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[0].kind" '{ "lifetime": { "outlives": [] } }'
1111
//@ is "$.index[?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[1].name" \"T\"
12-
//@ is "$.index[?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[1].kind" '{ "type": { "bounds": [], "default": null, "is_synthetic": false } }'
12+
//@ 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 } }'
1313
pub fn foo()
1414
where
1515
for<'a, T> &'a Wrapper<T>: Trait,

0 commit comments

Comments
 (0)