about summary refs log tree commit diff
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
parentaabbf84b45a5e7b868c33e959d7e5cc985097d19 (diff)
downloadrust-f1be59fa72b06c4a28acc59a0f0dff1484db0778.tar.gz
rust-f1be59fa72b06c4a28acc59a0f0dff1484db0778.zip
SmartPointer derive-macro
Co-authored-by: Wedson Almeida Filho <walmeida@microsoft.com>
-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
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs6
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs8
-rw-r--r--library/core/src/marker.rs9
-rw-r--r--tests/ui/deriving/deriving-smart-pointer.rs55
-rw-r--r--tests/ui/feature-gates/feature-gate-derive-smart-pointer.rs9
-rw-r--r--tests/ui/feature-gates/feature-gate-derive-smart-pointer.stderr33
10 files changed, 264 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);
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 9e2756f07ed..25fec91c817 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -575,6 +575,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         EncodeCrossCrate::No, coroutines, experimental!(coroutines)
     ),
 
+    // `#[pointee]` attribute to designate the pointee type in SmartPointer derive-macro
+    gated!(
+        pointee, Normal, template!(Word), ErrorFollowing,
+        EncodeCrossCrate::No, derive_smart_pointer, experimental!(pointee)
+    ),
+
     // ==========================================================================
     // Internal attributes: Stability, deprecation, and unsafe:
     // ==========================================================================
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index fbd67657e3b..4abeaf4bd46 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -436,6 +436,8 @@ declare_features! (
     (unstable, deprecated_suggestion, "1.61.0", Some(94785)),
     /// Allows deref patterns.
     (incomplete, deref_patterns, "1.79.0", Some(87121)),
+    /// Allows deriving `SmartPointer` traits
+    (unstable, derive_smart_pointer, "1.79.0", Some(123430)),
     /// Controls errors in trait implementations.
     (unstable, do_not_recommend, "1.67.0", Some(51992)),
     /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 8d8f4927e99..88229bdcc42 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -174,6 +174,7 @@ symbols! {
         Center,
         Cleanup,
         Clone,
+        CoerceUnsized,
         Command,
         ConstParamTy,
         Context,
@@ -189,6 +190,7 @@ symbols! {
         DiagMessage,
         Diagnostic,
         DirBuilder,
+        DispatchFromDyn,
         Display,
         DoubleEndedIterator,
         Duration,
@@ -299,8 +301,10 @@ symbols! {
         Saturating,
         Send,
         SeqCst,
+        Sized,
         SliceIndex,
         SliceIter,
+        SmartPointer,
         Some,
         SpanCtxt,
         String,
@@ -323,6 +327,7 @@ symbols! {
         TyCtxt,
         TyKind,
         Unknown,
+        Unsize,
         Upvars,
         Vec,
         VecDeque,
@@ -707,6 +712,7 @@ symbols! {
         derive,
         derive_const,
         derive_default_enum,
+        derive_smart_pointer,
         destruct,
         destructuring_assignment,
         diagnostic,
@@ -1315,6 +1321,7 @@ symbols! {
         on,
         on_unimplemented,
         opaque,
+        ops,
         opt_out_copy,
         optimize,
         optimize_attribute,
@@ -1389,6 +1396,7 @@ symbols! {
         plugin,
         plugin_registrar,
         plugins,
+        pointee,
         pointee_trait,
         pointer,
         pointer_like,
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index 2e8be7bde4b..0fedb8835d1 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -1018,3 +1018,12 @@ pub trait FnPtr: Copy + Clone {
     #[lang = "fn_ptr_addr"]
     fn addr(self) -> *const ();
 }
+
+/// Derive macro generating impls of traits related to smart pointers.
+#[cfg(not(bootstrap))]
+#[rustc_builtin_macro]
+#[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)]
+#[unstable(feature = "derive_smart_pointer", issue = "123430")]
+pub macro SmartPointer($item:item) {
+    /* compiler built-in */
+}
diff --git a/tests/ui/deriving/deriving-smart-pointer.rs b/tests/ui/deriving/deriving-smart-pointer.rs
new file mode 100644
index 00000000000..cfc3369850b
--- /dev/null
+++ b/tests/ui/deriving/deriving-smart-pointer.rs
@@ -0,0 +1,55 @@
+//@ run-pass
+#![feature(derive_smart_pointer, arbitrary_self_types)]
+
+use std::marker::SmartPointer;
+
+#[derive(SmartPointer)]
+struct MyPointer<'a, #[pointee] T: ?Sized> {
+    ptr: &'a T,
+}
+
+impl<T: ?Sized> Copy for MyPointer<'_, T> {}
+impl<T: ?Sized> Clone for MyPointer<'_, T> {
+    fn clone(&self) -> Self {
+        Self { ptr: self.ptr }
+    }
+}
+
+impl<'a, T: ?Sized> core::ops::Deref for MyPointer<'a, T> {
+    type Target = T;
+    fn deref(&self) -> &'a T {
+        self.ptr
+    }
+}
+
+struct MyValue(u32);
+impl MyValue {
+    fn through_pointer(self: MyPointer<'_, Self>) -> u32 {
+        self.ptr.0
+    }
+}
+
+trait MyTrait {
+    fn through_trait(&self) -> u32;
+    fn through_trait_and_pointer(self: MyPointer<'_, Self>) -> u32;
+}
+
+impl MyTrait for MyValue {
+    fn through_trait(&self) -> u32 {
+        self.0
+    }
+
+    fn through_trait_and_pointer(self: MyPointer<'_, Self>) -> u32 {
+        self.ptr.0
+    }
+}
+
+pub fn main() {
+    let v = MyValue(10);
+    let ptr = MyPointer { ptr: &v };
+    assert_eq!(v.0, ptr.through_pointer());
+    assert_eq!(v.0, ptr.through_pointer());
+    let dptr = ptr as MyPointer<dyn MyTrait>;
+    assert_eq!(v.0, dptr.through_trait());
+    assert_eq!(v.0, dptr.through_trait_and_pointer());
+}
diff --git a/tests/ui/feature-gates/feature-gate-derive-smart-pointer.rs b/tests/ui/feature-gates/feature-gate-derive-smart-pointer.rs
new file mode 100644
index 00000000000..ae8005592cd
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-derive-smart-pointer.rs
@@ -0,0 +1,9 @@
+use std::marker::SmartPointer; //~ ERROR use of unstable library feature 'derive_smart_pointer'
+
+#[derive(SmartPointer)] //~ ERROR use of unstable library feature 'derive_smart_pointer'
+struct MyPointer<'a, #[pointee] T: ?Sized> {
+    //~^ ERROR the `#[pointee]` attribute is an experimental feature
+    ptr: &'a T,
+}
+
+fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-derive-smart-pointer.stderr b/tests/ui/feature-gates/feature-gate-derive-smart-pointer.stderr
new file mode 100644
index 00000000000..0ffd82fb9e1
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-derive-smart-pointer.stderr
@@ -0,0 +1,33 @@
+error[E0658]: use of unstable library feature 'derive_smart_pointer'
+  --> $DIR/feature-gate-derive-smart-pointer.rs:3:10
+   |
+LL | #[derive(SmartPointer)]
+   |          ^^^^^^^^^^^^
+   |
+   = note: see issue #123430 <https://github.com/rust-lang/rust/issues/123430> for more information
+   = help: add `#![feature(derive_smart_pointer)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the `#[pointee]` attribute is an experimental feature
+  --> $DIR/feature-gate-derive-smart-pointer.rs:4:22
+   |
+LL | struct MyPointer<'a, #[pointee] T: ?Sized> {
+   |                      ^^^^^^^^^^
+   |
+   = note: see issue #123430 <https://github.com/rust-lang/rust/issues/123430> for more information
+   = help: add `#![feature(derive_smart_pointer)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable library feature 'derive_smart_pointer'
+  --> $DIR/feature-gate-derive-smart-pointer.rs:1:5
+   |
+LL | use std::marker::SmartPointer;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #123430 <https://github.com/rust-lang/rust/issues/123430> for more information
+   = help: add `#![feature(derive_smart_pointer)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.