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 structs").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

`+

}

`