|  | 
|  | 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 first type arg is replace 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 first type 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