|
| 1 | +use rustc_ast::{ |
| 2 | + self as ast, GenericArg, GenericBound, GenericParamKind, ItemKind, MetaItem, |
| 3 | + TraitBoundModifiers, |
| 4 | +}; |
| 5 | +use rustc_expand::base::{Annotatable, ExtCtxt}; |
| 6 | +use rustc_span::symbol::{sym, Ident}; |
| 7 | +use rustc_span::Span; |
| 8 | +use thin_vec::{thin_vec, ThinVec}; |
| 9 | + |
| 10 | +macro_rules! path { |
| 11 | + ($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] } |
| 12 | +} |
| 13 | + |
| 14 | +pub fn expand_deriving_smart_ptr( |
| 15 | + cx: &ExtCtxt<'_>, |
| 16 | + span: Span, |
| 17 | + _mitem: &MetaItem, |
| 18 | + item: &Annotatable, |
| 19 | + push: &mut dyn FnMut(Annotatable), |
| 20 | + _is_const: bool, |
| 21 | +) { |
| 22 | + let (name_ident, generics) = match item { |
| 23 | + Annotatable::Item(aitem) => match &aitem.kind { |
| 24 | + ItemKind::Struct(_, g) => (aitem.ident, g), |
| 25 | + // FIXME: Improve error reporting. |
| 26 | + _ => cx.dcx().span_bug(span, "`#[derive(SmartPointer)]` on wrong kind"), |
| 27 | + }, |
| 28 | + _ => cx.dcx().span_bug(span, "`#[derive(SmartPointer)]` on wrong item"), |
| 29 | + }; |
| 30 | + |
| 31 | + // Convert generic parameters (from the struct) into generic args. |
| 32 | + let self_params = generics |
| 33 | + .params |
| 34 | + .iter() |
| 35 | + .map(|p| match p.kind { |
| 36 | + GenericParamKind::Lifetime => GenericArg::Lifetime(cx.lifetime(span, p.ident)), |
| 37 | + GenericParamKind::Type { .. } => GenericArg::Type(cx.ty_ident(span, p.ident)), |
| 38 | + GenericParamKind::Const { .. } => GenericArg::Const(cx.const_ident(span, p.ident)), |
| 39 | + }) |
| 40 | + .collect::<Vec<_>>(); |
| 41 | + |
| 42 | + // Create the type of `self`. |
| 43 | + let path = cx.path_all(span, false, vec![name_ident], self_params.clone()); |
| 44 | + let self_type = cx.ty_path(path); |
| 45 | + |
| 46 | + // Declare helper function that adds implementation blocks. |
| 47 | + // FIXME: Copy attrs from struct? |
| 48 | + let attrs = thin_vec![cx.attr_word(sym::automatically_derived, span),]; |
| 49 | + let mut add_impl_block = |generics, trait_symbol, trait_args| { |
| 50 | + let mut parts = path!(span, core::ops); |
| 51 | + parts.push(Ident::new(trait_symbol, span)); |
| 52 | + let trait_path = cx.path_all(span, true, parts, trait_args); |
| 53 | + let trait_ref = cx.trait_ref(trait_path); |
| 54 | + let item = cx.item( |
| 55 | + span, |
| 56 | + Ident::empty(), |
| 57 | + attrs.clone(), |
| 58 | + ast::ItemKind::Impl(Box::new(ast::Impl { |
| 59 | + unsafety: ast::Unsafe::No, |
| 60 | + polarity: ast::ImplPolarity::Positive, |
| 61 | + defaultness: ast::Defaultness::Final, |
| 62 | + constness: ast::Const::No, |
| 63 | + generics, |
| 64 | + of_trait: Some(trait_ref), |
| 65 | + self_ty: self_type.clone(), |
| 66 | + items: ThinVec::new(), |
| 67 | + })), |
| 68 | + ); |
| 69 | + push(Annotatable::Item(item)); |
| 70 | + }; |
| 71 | + |
| 72 | + // Create unsized `self`, that is, one where the first type arg is replace with `__S`. For |
| 73 | + // example, instead of `MyType<'a, T>`, it will be `MyType<'a, __S>`. |
| 74 | + let s_ty = cx.ty_ident(span, Ident::new(sym::__S, span)); |
| 75 | + let mut alt_self_params = self_params; |
| 76 | + for a in &mut alt_self_params { |
| 77 | + if matches!(*a, GenericArg::Type(_)) { |
| 78 | + *a = GenericArg::Type(s_ty.clone()); |
| 79 | + break; |
| 80 | + } |
| 81 | + } |
| 82 | + let alt_self_type = cx.ty_path(cx.path_all(span, false, vec![name_ident], alt_self_params)); |
| 83 | + |
| 84 | + // Find the first type parameter and add an `Unsize<__S>` bound to it. |
| 85 | + let mut impl_generics = generics.clone(); |
| 86 | + for p in &mut impl_generics.params { |
| 87 | + if matches!(p.kind, ast::GenericParamKind::Type { .. }) { |
| 88 | + let arg = GenericArg::Type(s_ty.clone()); |
| 89 | + let unsize = cx.path_all(span, true, path!(span, core::marker::Unsize), vec![arg]); |
| 90 | + p.bounds.push(cx.trait_bound(unsize, false)); |
| 91 | + break; |
| 92 | + } |
| 93 | + } |
| 94 | + |
| 95 | + // Add the `__S: ?Sized` extra parameter to the impl block. |
| 96 | + let sized = cx.path_global(span, path!(span, core::marker::Sized)); |
| 97 | + let bound = GenericBound::Trait( |
| 98 | + cx.poly_trait_ref(span, sized), |
| 99 | + TraitBoundModifiers { |
| 100 | + polarity: ast::BoundPolarity::Maybe(span), |
| 101 | + constness: ast::BoundConstness::Never, |
| 102 | + asyncness: ast::BoundAsyncness::Normal, |
| 103 | + }, |
| 104 | + ); |
| 105 | + let extra_param = cx.typaram(span, Ident::new(sym::__S, span), vec![bound], None); |
| 106 | + impl_generics.params.push(extra_param); |
| 107 | + |
| 108 | + // Add the impl blocks for `DispatchFromDyn`, `CoerceUnsized`, and `Receiver`. |
| 109 | + let gen_args = vec![GenericArg::Type(alt_self_type.clone())]; |
| 110 | + add_impl_block(impl_generics.clone(), sym::DispatchFromDyn, gen_args.clone()); |
| 111 | + add_impl_block(impl_generics.clone(), sym::CoerceUnsized, gen_args.clone()); |
| 112 | + add_impl_block(generics.clone(), sym::Receiver, Vec::new()); |
| 113 | +} |
0 commit comments