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+

}