|
| 1 | +use std::mem::swap; |
| 2 | + |
| 3 | +use ast::HasAttrs; |
| 4 | +use rustc_ast::{ |
| 5 | +selfas 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 | +pubfnexpand_deriving_smart_ptr( |
| 19 | +cx:&ExtCtxt<'_>, |
| 20 | +span:Span, |
| 21 | +_mitem:&MetaItem, |
| 22 | +item:&Annotatable, |
| 23 | +push:&mutdynFnMut(Annotatable), |
| 24 | +_is_const:bool, |
| 25 | +){ |
| 26 | +let(name_ident, generics) = ifletAnnotatable::Item(aitem) = item |
| 27 | + && letItemKind::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 | +letmut pointee_param = None; |
| 37 | +letmut 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 | +letSome(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 | +letmut add_impl_block = |generics, trait_symbol, trait_args| { |
| 82 | +letmut 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 | +letmut 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 | +letmut 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 | +letmut 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