|
| 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 | +} |
0 commit comments