Rollup merge of #125575 - dingxiangfei2009:derive-smart-ptr, r=davidtwco · rust-lang/rust@ed460d2
1+use std::mem::swap;
2+3+use ast::HasAttrs;
4+use rustc_ast::{
5+self as ast, GenericArg, GenericBound, GenericParamKind, ItemKind, MetaItem,
6+TraitBoundModifiers,
7+};
8+use rustc_expand::base::{Annotatable, ExtCtxt};
9+use rustc_span::symbol::{sym, Ident};
10+use rustc_span::Span;
11+use smallvec::{smallvec, SmallVec};
12+use thin_vec::{thin_vec, ThinVec};
13+14+macro_rules! path {
15+($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] }
16+}
17+18+pub fn expand_deriving_smart_ptr(
19+cx: &ExtCtxt<'_>,
20+span: Span,
21+_mitem: &MetaItem,
22+item: &Annotatable,
23+push: &mut dyn FnMut(Annotatable),
24+_is_const: bool,
25+) {
26+let (name_ident, generics) = if let Annotatable::Item(aitem) = item
27+&& let ItemKind::Struct(_, g) = &aitem.kind
28+{
29+(aitem.ident, g)
30+} else {
31+ cx.dcx().struct_span_err(span, "`SmartPointer` can only be derived on `struct`s").emit();
32+return;
33+};
34+35+// Convert generic parameters (from the struct) into generic args.
36+let mut pointee_param = None;
37+let mut multiple_pointee_diag: SmallVec<[_; 2]> = smallvec![];
38+let self_params = generics
39+.params
40+.iter()
41+.enumerate()
42+.map(|(idx, p)| match p.kind {
43+GenericParamKind::Lifetime => GenericArg::Lifetime(cx.lifetime(p.span(), p.ident)),
44+GenericParamKind::Type { .. } => {
45+if p.attrs().iter().any(|attr| attr.has_name(sym::pointee)) {
46+if pointee_param.is_some() {
47+ multiple_pointee_diag.push(cx.dcx().struct_span_err(
48+ p.span(),
49+"`SmartPointer` can only admit one type as pointee",
50+));
51+} else {
52+ pointee_param = Some(idx);
53+}
54+}
55+GenericArg::Type(cx.ty_ident(p.span(), p.ident))
56+}
57+GenericParamKind::Const { .. } => GenericArg::Const(cx.const_ident(p.span(), p.ident)),
58+})
59+.collect::<Vec<_>>();
60+let Some(pointee_param_idx) = pointee_param else {
61+ cx.dcx().struct_span_err(
62+ span,
63+"At least one generic type should be designated as `#[pointee]` in order to derive `SmartPointer` traits",
64+).emit();
65+return;
66+};
67+if !multiple_pointee_diag.is_empty() {
68+for diag in multiple_pointee_diag {
69+ diag.emit();
70+}
71+return;
72+}
73+74+// Create the type of `self`.
75+let path = cx.path_all(span, false, vec![name_ident], self_params.clone());
76+let self_type = cx.ty_path(path);
77+78+// Declare helper function that adds implementation blocks.
79+// FIXME(dingxiangfei2009): Investigate the set of attributes on target struct to be propagated to impls
80+let attrs = thin_vec![cx.attr_word(sym::automatically_derived, span),];
81+let mut add_impl_block = |generics, trait_symbol, trait_args| {
82+let mut parts = path!(span, core::ops);
83+ parts.push(Ident::new(trait_symbol, span));
84+let trait_path = cx.path_all(span, true, parts, trait_args);
85+let trait_ref = cx.trait_ref(trait_path);
86+let item = cx.item(
87+ span,
88+Ident::empty(),
89+ attrs.clone(),
90+ ast::ItemKind::Impl(Box::new(ast::Impl {
91+safety: ast::Safety::Default,
92+polarity: ast::ImplPolarity::Positive,
93+defaultness: ast::Defaultness::Final,
94+constness: ast::Const::No,
95+ generics,
96+of_trait: Some(trait_ref),
97+self_ty: self_type.clone(),
98+items: ThinVec::new(),
99+})),
100+);
101+push(Annotatable::Item(item));
102+};
103+104+// Create unsized `self`, that is, one where the `#[pointee]` type arg is replaced with `__S`. For
105+// example, instead of `MyType<'a, T>`, it will be `MyType<'a, __S>`.
106+let s_ty = cx.ty_ident(span, Ident::new(sym::__S, span));
107+let mut alt_self_params = self_params;
108+ alt_self_params[pointee_param_idx] = GenericArg::Type(s_ty.clone());
109+let alt_self_type = cx.ty_path(cx.path_all(span, false, vec![name_ident], alt_self_params));
110+111+// Find the `#[pointee]` parameter and add an `Unsize<__S>` bound to it.
112+let mut impl_generics = generics.clone();
113+{
114+let p = &mut impl_generics.params[pointee_param_idx];
115+let arg = GenericArg::Type(s_ty.clone());
116+let unsize = cx.path_all(span, true, path!(span, core::marker::Unsize), vec![arg]);
117+ p.bounds.push(cx.trait_bound(unsize, false));
118+let mut attrs = thin_vec![];
119+swap(&mut p.attrs, &mut attrs);
120+ p.attrs = attrs.into_iter().filter(|attr| !attr.has_name(sym::pointee)).collect();
121+}
122+123+// Add the `__S: ?Sized` extra parameter to the impl block.
124+let sized = cx.path_global(span, path!(span, core::marker::Sized));
125+let bound = GenericBound::Trait(
126+ cx.poly_trait_ref(span, sized),
127+TraitBoundModifiers {
128+polarity: ast::BoundPolarity::Maybe(span),
129+constness: ast::BoundConstness::Never,
130+asyncness: ast::BoundAsyncness::Normal,
131+},
132+);
133+let extra_param = cx.typaram(span, Ident::new(sym::__S, span), vec![bound], None);
134+ impl_generics.params.push(extra_param);
135+136+// Add the impl blocks for `DispatchFromDyn` and `CoerceUnsized`.
137+let gen_args = vec![GenericArg::Type(alt_self_type.clone())];
138+add_impl_block(impl_generics.clone(), sym::DispatchFromDyn, gen_args.clone());
139+add_impl_block(impl_generics.clone(), sym::CoerceUnsized, gen_args.clone());
140+}