Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 0c32103

Browse files
dingxiangfei2009wedsonaf
andcommittedMay 26, 2024
SmartPointer derive-macro
Co-authored-by: Wedson Almeida Filho <walmeida@microsoft.com>
1 parent bd184cc commit 0c32103

File tree

9 files changed

+218
-0
lines changed

9 files changed

+218
-0
lines changed
 

‎compiler/rustc_builtin_macros/src/deriving/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub(crate) mod decodable;
2727
pub(crate) mod default;
2828
pub(crate) mod encodable;
2929
pub(crate) mod hash;
30+
pub(crate) mod smart_ptr;
3031

3132
#[path = "cmp/eq.rs"]
3233
pub(crate) mod eq;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
use ast::HasAttrs;
2+
use rustc_ast::{
3+
self as ast, GenericArg, GenericBound, GenericParamKind, ItemKind, MetaItem,
4+
TraitBoundModifiers,
5+
};
6+
use rustc_expand::base::{Annotatable, ExtCtxt};
7+
use rustc_span::symbol::{sym, Ident};
8+
use rustc_span::Span;
9+
use smallvec::{smallvec, SmallVec};
10+
use thin_vec::{thin_vec, ThinVec};
11+
12+
macro_rules! path {
13+
($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] }
14+
}
15+
16+
pub fn expand_deriving_smart_ptr(
17+
cx: &ExtCtxt<'_>,
18+
span: Span,
19+
_mitem: &MetaItem,
20+
item: &Annotatable,
21+
push: &mut dyn FnMut(Annotatable),
22+
_is_const: bool,
23+
) {
24+
let (name_ident, generics) = if let Annotatable::Item(aitem) = item
25+
&& let ItemKind::Struct(_, g) = &aitem.kind
26+
{
27+
(aitem.ident, g)
28+
} else {
29+
cx.dcx().struct_span_err(span, "`SmartPointer` can only be derived on `struct`s").emit();
30+
return;
31+
};
32+
33+
// Convert generic parameters (from the struct) into generic args.
34+
let mut pointee_param = None;
35+
let mut multiple_pointee_diag: SmallVec<[_; 2]> = smallvec![];
36+
let self_params = generics
37+
.params
38+
.iter()
39+
.enumerate()
40+
.map(|(idx, p)| match p.kind {
41+
GenericParamKind::Lifetime => GenericArg::Lifetime(cx.lifetime(p.span(), p.ident)),
42+
GenericParamKind::Type { .. } => {
43+
if p.attrs().iter().any(|attr| attr.has_name(sym::pointee)) {
44+
if pointee_param.is_some() {
45+
multiple_pointee_diag.push(cx.dcx().struct_span_err(
46+
p.span(),
47+
"`SmartPointer` can only admit one type as pointee",
48+
));
49+
} else {
50+
pointee_param = Some(idx);
51+
}
52+
}
53+
GenericArg::Type(cx.ty_ident(p.span(), p.ident))
54+
}
55+
GenericParamKind::Const { .. } => GenericArg::Const(cx.const_ident(p.span(), p.ident)),
56+
})
57+
.collect::<Vec<_>>();
58+
let Some(pointee_param_idx) = pointee_param else {
59+
cx.dcx().struct_span_err(
60+
span,
61+
"At least one generic type should be designated as `#[pointee]` in order to derive `SmartPointer` traits",
62+
).emit();
63+
return;
64+
};
65+
if !multiple_pointee_diag.is_empty() {
66+
for diag in multiple_pointee_diag {
67+
diag.emit();
68+
}
69+
return;
70+
}
71+
72+
// Create the type of `self`.
73+
let path = cx.path_all(span, false, vec![name_ident], self_params.clone());
74+
let self_type = cx.ty_path(path);
75+
76+
// Declare helper function that adds implementation blocks.
77+
// FIXME: Copy attrs from struct?
78+
let attrs = thin_vec![cx.attr_word(sym::automatically_derived, span),];
79+
let mut add_impl_block = |generics, trait_symbol, trait_args| {
80+
let mut parts = path!(span, core::ops);
81+
parts.push(Ident::new(trait_symbol, span));
82+
let trait_path = cx.path_all(span, true, parts, trait_args);
83+
let trait_ref = cx.trait_ref(trait_path);
84+
let item = cx.item(
85+
span,
86+
Ident::empty(),
87+
attrs.clone(),
88+
ast::ItemKind::Impl(Box::new(ast::Impl {
89+
safety: ast::Safety::Default,
90+
polarity: ast::ImplPolarity::Positive,
91+
defaultness: ast::Defaultness::Final,
92+
constness: ast::Const::No,
93+
generics,
94+
of_trait: Some(trait_ref),
95+
self_ty: self_type.clone(),
96+
items: ThinVec::new(),
97+
})),
98+
);
99+
push(Annotatable::Item(item));
100+
};
101+
102+
// Create unsized `self`, that is, one where the first type arg is replace with `__S`. For
103+
// example, instead of `MyType<'a, T>`, it will be `MyType<'a, __S>`.
104+
let s_ty = cx.ty_ident(span, Ident::new(sym::__S, span));
105+
let mut alt_self_params = self_params;
106+
alt_self_params[pointee_param_idx] = GenericArg::Type(s_ty.clone());
107+
let alt_self_type = cx.ty_path(cx.path_all(span, false, vec![name_ident], alt_self_params));
108+
109+
// Find the first type parameter and add an `Unsize<__S>` bound to it.
110+
let mut impl_generics = generics.clone();
111+
{
112+
let p = &mut impl_generics.params[pointee_param_idx];
113+
let arg = GenericArg::Type(s_ty.clone());
114+
let unsize = cx.path_all(span, true, path!(span, core::marker::Unsize), vec![arg]);
115+
p.bounds.push(cx.trait_bound(unsize, false));
116+
}
117+
118+
// Add the `__S: ?Sized` extra parameter to the impl block.
119+
let sized = cx.path_global(span, path!(span, core::marker::Sized));
120+
let bound = GenericBound::Trait(
121+
cx.poly_trait_ref(span, sized),
122+
TraitBoundModifiers {
123+
polarity: ast::BoundPolarity::Maybe(span),
124+
constness: ast::BoundConstness::Never,
125+
asyncness: ast::BoundAsyncness::Normal,
126+
},
127+
);
128+
let extra_param = cx.typaram(span, Ident::new(sym::__S, span), vec![bound], None);
129+
impl_generics.params.push(extra_param);
130+
131+
// Add the impl blocks for `DispatchFromDyn` and `CoerceUnsized`.
132+
let gen_args = vec![GenericArg::Type(alt_self_type.clone())];
133+
add_impl_block(impl_generics.clone(), sym::DispatchFromDyn, gen_args.clone());
134+
add_impl_block(impl_generics.clone(), sym::CoerceUnsized, gen_args.clone());
135+
}

‎compiler/rustc_builtin_macros/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
125125
PartialOrd: partial_ord::expand_deriving_partial_ord,
126126
RustcDecodable: decodable::expand_deriving_rustc_decodable,
127127
RustcEncodable: encodable::expand_deriving_rustc_encodable,
128+
SmartPointer: smart_ptr::expand_deriving_smart_ptr,
128129
}
129130

130131
let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote);

‎compiler/rustc_feature/src/builtin_attrs.rs

+6
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
527527
EncodeCrossCrate::No, coroutines, experimental!(coroutines)
528528
),
529529

530+
// `#[pointee]` attribute to designate the pointee type in SmartPointer derive-macro
531+
gated!(
532+
pointee, Normal, template!(Word), ErrorFollowing,
533+
EncodeCrossCrate::No, derive_smart_pointer, experimental!(derive_smart_pointer)
534+
),
535+
530536
// ==========================================================================
531537
// Internal attributes: Stability, deprecation, and unsafe:
532538
// ==========================================================================

‎compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,8 @@ declare_features! (
438438
(unstable, deprecated_safe, "1.61.0", Some(94978)),
439439
/// Allows having using `suggestion` in the `#[deprecated]` attribute.
440440
(unstable, deprecated_suggestion, "1.61.0", Some(94785)),
441+
/// Allows deriving `SmartPointer` traits
442+
(unstable, derive_smart_pointer, "1.80.0", Some(123430)),
441443
/// Allows deref patterns.
442444
(incomplete, deref_patterns, "1.79.0", Some(87121)),
443445
/// Controls errors in trait implementations.

‎compiler/rustc_span/src/symbol.rs

+8
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ symbols! {
172172
Center,
173173
Cleanup,
174174
Clone,
175+
CoerceUnsized,
175176
Command,
176177
ConstParamTy,
177178
Context,
@@ -187,6 +188,7 @@ symbols! {
187188
DiagMessage,
188189
Diagnostic,
189190
DirBuilder,
191+
DispatchFromDyn,
190192
Display,
191193
DoubleEndedIterator,
192194
Duration,
@@ -298,8 +300,10 @@ symbols! {
298300
Saturating,
299301
Send,
300302
SeqCst,
303+
Sized,
301304
SliceIndex,
302305
SliceIter,
306+
SmartPointer,
303307
Some,
304308
SpanCtxt,
305309
String,
@@ -322,6 +326,7 @@ symbols! {
322326
TyCtxt,
323327
TyKind,
324328
Unknown,
329+
Unsize,
325330
Upvars,
326331
Vec,
327332
VecDeque,
@@ -699,6 +704,7 @@ symbols! {
699704
derive,
700705
derive_const,
701706
derive_default_enum,
707+
derive_smart_pointer,
702708
destruct,
703709
destructuring_assignment,
704710
diagnostic,
@@ -1302,6 +1308,7 @@ symbols! {
13021308
on,
13031309
on_unimplemented,
13041310
opaque,
1311+
ops,
13051312
opt_out_copy,
13061313
optimize,
13071314
optimize_attribute,
@@ -1376,6 +1383,7 @@ symbols! {
13761383
plugin,
13771384
plugin_registrar,
13781385
plugins,
1386+
pointee,
13791387
pointee_trait,
13801388
pointer,
13811389
pointer_like,

‎library/core/src/lib.rs

+9
Original file line numberDiff line numberDiff line change
@@ -475,3 +475,12 @@ pub mod simd {
475475
}
476476

477477
include!("primitive_docs.rs");
478+
479+
/// Derive macro generating impls of traits related to smart pointers.
480+
#[cfg(not(bootstrap))]
481+
#[rustc_builtin_macro]
482+
#[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, receiver_trait, unsize)]
483+
#[unstable(feature = "derive_smart_pointer", issue = "123430")]
484+
pub macro SmartPointer($item:item) {
485+
/* compiler built-in */
486+
}

‎library/std/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,9 @@ pub use core::u8;
569569
#[stable(feature = "rust1", since = "1.0.0")]
570570
#[allow(deprecated, deprecated_in_future)]
571571
pub use core::usize;
572+
#[cfg(not(bootstrap))]
573+
#[unstable(feature = "derive_smart_pointer", issue = "123430")]
574+
pub use core::SmartPointer;
572575

573576
#[unstable(feature = "f128", issue = "116909")]
574577
pub mod f128;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//@ run-pass
2+
#![feature(derive_smart_pointer, arbitrary_self_types)]
3+
4+
#[derive(SmartPointer)]
5+
struct MyPointer<'a, #[pointee] T: ?Sized> {
6+
ptr: &'a T,
7+
}
8+
9+
impl<T: ?Sized> Copy for MyPointer<'_, T> {}
10+
impl<T: ?Sized> Clone for MyPointer<'_, T> {
11+
fn clone(&self) -> Self {
12+
Self { ptr: self.ptr }
13+
}
14+
}
15+
16+
impl<'a, T: ?Sized> core::ops::Deref for MyPointer<'a, T> {
17+
type Target = T;
18+
fn deref(&self) -> &'a T {
19+
self.ptr
20+
}
21+
}
22+
23+
struct MyValue(u32);
24+
impl MyValue {
25+
fn through_pointer(self: MyPointer<'_, Self>) -> u32 {
26+
self.ptr.0
27+
}
28+
}
29+
30+
trait MyTrait {
31+
fn through_trait(&self) -> u32;
32+
fn through_trait_and_pointer(self: MyPointer<'_, Self>) -> u32;
33+
}
34+
35+
impl MyTrait for MyValue {
36+
fn through_trait(&self) -> u32 {
37+
self.0
38+
}
39+
40+
fn through_trait_and_pointer(self: MyPointer<'_, Self>) -> u32 {
41+
self.ptr.0
42+
}
43+
}
44+
45+
pub fn main() {
46+
let v = MyValue(10);
47+
let ptr = MyPointer { ptr: &v };
48+
assert_eq!(v.0, ptr.through_pointer());
49+
assert_eq!(v.0, ptr.through_pointer());
50+
let dptr = ptr as MyPointer<dyn MyTrait>;
51+
assert_eq!(v.0, dptr.through_trait());
52+
assert_eq!(v.0, dptr.through_trait_and_pointer());
53+
}

0 commit comments

Comments
 (0)
Failed to load comments.