SmartPointer derive-macro · rust-lang/rust@f1be59f (original) (raw)
``
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:📑: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:📑: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
`+
}
`