From f1be59fa72b06c4a28acc59a0f0dff1484db0778 Mon Sep 17 00:00:00 2001 From: Xiangfei Ding Date: Sun, 26 May 2024 17:57:13 +0800 Subject: [PATCH] SmartPointer derive-macro Co-authored-by: Wedson Almeida Filho --- .../rustc_builtin_macros/src/deriving/mod.rs | 1 + .../src/deriving/smart_ptr.rs | 140 ++++++++++++++++++ compiler/rustc_builtin_macros/src/lib.rs | 1 + compiler/rustc_feature/src/builtin_attrs.rs | 6 + compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_span/src/symbol.rs | 8 + library/core/src/marker.rs | 9 ++ tests/ui/deriving/deriving-smart-pointer.rs | 55 +++++++ .../feature-gate-derive-smart-pointer.rs | 9 ++ .../feature-gate-derive-smart-pointer.stderr | 33 +++++ 10 files changed, 264 insertions(+) create mode 100644 compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs create mode 100644 tests/ui/deriving/deriving-smart-pointer.rs create mode 100644 tests/ui/feature-gates/feature-gate-derive-smart-pointer.rs create mode 100644 tests/ui/feature-gates/feature-gate-derive-smart-pointer.stderr 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 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::>(); + 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 struct 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 @@ pub fn internal(&self, feature: Symbol) -> bool { (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 @@ Center, Cleanup, Clone, + CoerceUnsized, Command, ConstParamTy, Context, @@ -189,6 +190,7 @@ DiagMessage, Diagnostic, DirBuilder, + DispatchFromDyn, Display, DoubleEndedIterator, Duration, @@ -299,8 +301,10 @@ Saturating, Send, SeqCst, + Sized, SliceIndex, SliceIter, + SmartPointer, Some, SpanCtxt, String, @@ -323,6 +327,7 @@ TyCtxt, TyKind, Unknown, + Unsize, Upvars, Vec, VecDeque, @@ -707,6 +712,7 @@ derive, derive_const, derive_default_enum, + derive_smart_pointer, destruct, destructuring_assignment, diagnostic, @@ -1315,6 +1321,7 @@ on, on_unimplemented, opaque, + ops, opt_out_copy, optimize, optimize_attribute, @@ -1389,6 +1396,7 @@ 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 Copy for MyPointer<'_, T> {} +impl 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; + 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 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 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 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`.