about summary refs log tree commit diff
path: root/compiler/rustc_builtin_macros
diff options
context:
space:
mode:
authorXiangfei Ding <dingxiangfei2009@protonmail.ch>2024-05-26 17:57:13 +0800
committerDing Xiang Fei <dingxiangfei2009@protonmail.ch>2024-06-24 03:03:34 +0800
commitf1be59fa72b06c4a28acc59a0f0dff1484db0778 (patch)
tree2abe2b107ae2a78d206e33c0fed974ea5e5a291b /compiler/rustc_builtin_macros
parentaabbf84b45a5e7b868c33e959d7e5cc985097d19 (diff)
downloadrust-f1be59fa72b06c4a28acc59a0f0dff1484db0778.tar.gz
rust-f1be59fa72b06c4a28acc59a0f0dff1484db0778.zip
SmartPointer derive-macro
Co-authored-by: Wedson Almeida Filho <walmeida@microsoft.com>
Diffstat (limited to 'compiler/rustc_builtin_macros')
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/mod.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs140
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs1
3 files changed, 142 insertions, 0 deletions
diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs
index e3a93ae13e4..32936ac183d 100644
--- a/compiler/rustc_builtin_macros/src/deriving/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs
@@ -27,6 +27,7 @@ pub(crate) mod decodable;
 pub(crate) mod default;
 pub(crate) mod encodable;
 pub(crate) mod hash;
+pub(crate) mod smart_ptr;
 
 #[path = "cmp/eq.rs"]
 pub(crate) mod eq;
diff --git a/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
new file mode 100644
index 00000000000..ea054a7e355
--- /dev/null
+++ b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
@@ -0,0 +1,140 @@
+use std::mem::swap;
+
+use ast::HasAttrs;
+use rustc_ast::{
+    self as ast, GenericArg, GenericBound, GenericParamKind, ItemKind, MetaItem,
+    TraitBoundModifiers,
+};
+use rustc_expand::base::{Annotatable, ExtCtxt};
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::Span;
+use smallvec::{smallvec, SmallVec};
+use thin_vec::{thin_vec, ThinVec};
+
+macro_rules! path {
+    ($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] }
+}
+
+pub fn expand_deriving_smart_ptr(
+    cx: &ExtCtxt<'_>,
+    span: Span,
+    _mitem: &MetaItem,
+    item: &Annotatable,
+    push: &mut dyn FnMut(Annotatable),
+    _is_const: bool,
+) {
+    let (name_ident, generics) = if let Annotatable::Item(aitem) = item
+        && let ItemKind::Struct(_, g) = &aitem.kind
+    {
+        (aitem.ident, g)
+    } else {
+        cx.dcx().struct_span_err(span, "`SmartPointer` can only be derived on `struct`s").emit();
+        return;
+    };
+
+    // Convert generic parameters (from the struct) into generic args.
+    let mut pointee_param = None;
+    let mut multiple_pointee_diag: SmallVec<[_; 2]> = smallvec![];
+    let self_params = generics
+        .params
+        .iter()
+        .enumerate()
+        .map(|(idx, p)| match p.kind {
+            GenericParamKind::Lifetime => GenericArg::Lifetime(cx.lifetime(p.span(), p.ident)),
+            GenericParamKind::Type { .. } => {
+                if p.attrs().iter().any(|attr| attr.has_name(sym::pointee)) {
+                    if pointee_param.is_some() {
+                        multiple_pointee_diag.push(cx.dcx().struct_span_err(
+                            p.span(),
+                            "`SmartPointer` can only admit one type as pointee",
+                        ));
+                    } else {
+                        pointee_param = Some(idx);
+                    }
+                }
+                GenericArg::Type(cx.ty_ident(p.span(), p.ident))
+            }
+            GenericParamKind::Const { .. } => GenericArg::Const(cx.const_ident(p.span(), p.ident)),
+        })
+        .collect::<Vec<_>>();
+    let Some(pointee_param_idx) = pointee_param else {
+        cx.dcx().struct_span_err(
+            span,
+            "At least one generic type should be designated as `#[pointee]` in order to derive `SmartPointer` traits",
+        ).emit();
+        return;
+    };
+    if !multiple_pointee_diag.is_empty() {
+        for diag in multiple_pointee_diag {
+            diag.emit();
+        }
+        return;
+    }
+
+    // Create the type of `self`.
+    let path = cx.path_all(span, false, vec![name_ident], self_params.clone());
+    let self_type = cx.ty_path(path);
+
+    // Declare helper function that adds implementation blocks.
+    // FIXME(dingxiangfei2009): Investigate the set of attributes on target struct to be propagated to impls
+    let attrs = thin_vec![cx.attr_word(sym::automatically_derived, span),];
+    let mut add_impl_block = |generics, trait_symbol, trait_args| {
+        let mut parts = path!(span, core::ops);
+        parts.push(Ident::new(trait_symbol, span));
+        let trait_path = cx.path_all(span, true, parts, trait_args);
+        let trait_ref = cx.trait_ref(trait_path);
+        let item = cx.item(
+            span,
+            Ident::empty(),
+            attrs.clone(),
+            ast::ItemKind::Impl(Box::new(ast::Impl {
+                safety: ast::Safety::Default,
+                polarity: ast::ImplPolarity::Positive,
+                defaultness: ast::Defaultness::Final,
+                constness: ast::Const::No,
+                generics,
+                of_trait: Some(trait_ref),
+                self_ty: self_type.clone(),
+                items: ThinVec::new(),
+            })),
+        );
+        push(Annotatable::Item(item));
+    };
+
+    // Create unsized `self`, that is, one where the `#[pointee]` type arg is replaced with `__S`. For
+    // example, instead of `MyType<'a, T>`, it will be `MyType<'a, __S>`.
+    let s_ty = cx.ty_ident(span, Ident::new(sym::__S, span));
+    let mut alt_self_params = self_params;
+    alt_self_params[pointee_param_idx] = GenericArg::Type(s_ty.clone());
+    let alt_self_type = cx.ty_path(cx.path_all(span, false, vec![name_ident], alt_self_params));
+
+    // Find the `#[pointee]` parameter and add an `Unsize<__S>` bound to it.
+    let mut impl_generics = generics.clone();
+    {
+        let p = &mut impl_generics.params[pointee_param_idx];
+        let arg = GenericArg::Type(s_ty.clone());
+        let unsize = cx.path_all(span, true, path!(span, core::marker::Unsize), vec![arg]);
+        p.bounds.push(cx.trait_bound(unsize, false));
+        let mut attrs = thin_vec![];
+        swap(&mut p.attrs, &mut attrs);
+        p.attrs = attrs.into_iter().filter(|attr| !attr.has_name(sym::pointee)).collect();
+    }
+
+    // Add the `__S: ?Sized` extra parameter to the impl block.
+    let sized = cx.path_global(span, path!(span, core::marker::Sized));
+    let bound = GenericBound::Trait(
+        cx.poly_trait_ref(span, sized),
+        TraitBoundModifiers {
+            polarity: ast::BoundPolarity::Maybe(span),
+            constness: ast::BoundConstness::Never,
+            asyncness: ast::BoundAsyncness::Normal,
+        },
+    );
+    let extra_param = cx.typaram(span, Ident::new(sym::__S, span), vec![bound], None);
+    impl_generics.params.push(extra_param);
+
+    // Add the impl blocks for `DispatchFromDyn` and `CoerceUnsized`.
+    let gen_args = vec![GenericArg::Type(alt_self_type.clone())];
+    add_impl_block(impl_generics.clone(), sym::DispatchFromDyn, gen_args.clone());
+    add_impl_block(impl_generics.clone(), sym::CoerceUnsized, gen_args.clone());
+}
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index 35b0f43d8af..8ac59605bc1 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -127,6 +127,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
         PartialOrd: partial_ord::expand_deriving_partial_ord,
         RustcDecodable: decodable::expand_deriving_rustc_decodable,
         RustcEncodable: encodable::expand_deriving_rustc_encodable,
+        SmartPointer: smart_ptr::expand_deriving_smart_ptr,
     }
 
     let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote);