Skip to content

Commit a33685e

Browse files
SmartPointer derive-macro
Co-authored-by: Wedson Almeida Filho <[email protected]>
1 parent a6a017d commit a33685e

File tree

10 files changed

+264
-0
lines changed

10 files changed

+264
-0
lines changed

Diff for: 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;
+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
use std::mem::swap;
2+
3+
use ast::HasAttrs;
4+
use rustc_ast::{
5+
self as 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+
pub fn expand_deriving_smart_ptr(
19+
cx: &ExtCtxt<'_>,
20+
span: Span,
21+
_mitem: &MetaItem,
22+
item: &Annotatable,
23+
push: &mut dyn FnMut(Annotatable),
24+
_is_const: bool,
25+
) {
26+
let (name_ident, generics) = if let Annotatable::Item(aitem) = item
27+
&& let ItemKind::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+
let mut pointee_param = None;
37+
let mut 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+
let Some(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+
let mut add_impl_block = |generics, trait_symbol, trait_args| {
82+
let mut 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 first type arg is replace 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+
let mut 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 first type parameter and add an `Unsize<__S>` bound to it.
112+
let mut 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+
let mut 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+
}

Diff for: 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);

Diff for: 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!(pointee)
534+
),
535+
530536
// ==========================================================================
531537
// Internal attributes: Stability, deprecation, and unsafe:
532538
// ==========================================================================

Diff for: 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.79.0", Some(123430)),
441443
/// Allows deref patterns.
442444
(incomplete, deref_patterns, "1.79.0", Some(87121)),
443445
/// Controls errors in trait implementations.

Diff for: 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,

Diff for: library/core/src/marker.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1019,3 +1019,12 @@ pub trait FnPtr: Copy + Clone {
10191019
#[lang = "fn_ptr_addr"]
10201020
fn addr(self) -> *const ();
10211021
}
1022+
1023+
/// Derive macro generating impls of traits related to smart pointers.
1024+
#[cfg(not(bootstrap))]
1025+
#[rustc_builtin_macro]
1026+
#[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)]
1027+
#[unstable(feature = "derive_smart_pointer", issue = "123430")]
1028+
pub macro SmartPointer($item:item) {
1029+
/* compiler built-in */
1030+
}

Diff for: tests/ui/deriving/deriving-smart-pointer.rs

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//@ run-pass
2+
#![feature(derive_smart_pointer, arbitrary_self_types)]
3+
4+
use std::marker::SmartPointer;
5+
6+
#[derive(SmartPointer)]
7+
struct MyPointer<'a, #[pointee] T: ?Sized> {
8+
ptr: &'a T,
9+
}
10+
11+
impl<T: ?Sized> Copy for MyPointer<'_, T> {}
12+
impl<T: ?Sized> Clone for MyPointer<'_, T> {
13+
fn clone(&self) -> Self {
14+
Self { ptr: self.ptr }
15+
}
16+
}
17+
18+
impl<'a, T: ?Sized> core::ops::Deref for MyPointer<'a, T> {
19+
type Target = T;
20+
fn deref(&self) -> &'a T {
21+
self.ptr
22+
}
23+
}
24+
25+
struct MyValue(u32);
26+
impl MyValue {
27+
fn through_pointer(self: MyPointer<'_, Self>) -> u32 {
28+
self.ptr.0
29+
}
30+
}
31+
32+
trait MyTrait {
33+
fn through_trait(&self) -> u32;
34+
fn through_trait_and_pointer(self: MyPointer<'_, Self>) -> u32;
35+
}
36+
37+
impl MyTrait for MyValue {
38+
fn through_trait(&self) -> u32 {
39+
self.0
40+
}
41+
42+
fn through_trait_and_pointer(self: MyPointer<'_, Self>) -> u32 {
43+
self.ptr.0
44+
}
45+
}
46+
47+
pub fn main() {
48+
let v = MyValue(10);
49+
let ptr = MyPointer { ptr: &v };
50+
assert_eq!(v.0, ptr.through_pointer());
51+
assert_eq!(v.0, ptr.through_pointer());
52+
let dptr = ptr as MyPointer<dyn MyTrait>;
53+
assert_eq!(v.0, dptr.through_trait());
54+
assert_eq!(v.0, dptr.through_trait_and_pointer());
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use std::marker::SmartPointer; //~ ERROR use of unstable library feature 'derive_smart_pointer'
2+
3+
#[derive(SmartPointer)] //~ ERROR use of unstable library feature 'derive_smart_pointer'
4+
struct MyPointer<'a, #[pointee] T: ?Sized> {
5+
//~^ ERROR the `#[pointee]` attribute is an experimental feature
6+
ptr: &'a T,
7+
}
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
error[E0658]: use of unstable library feature 'derive_smart_pointer'
2+
--> $DIR/feature-gate-derive-smart-pointer.rs:3:10
3+
|
4+
LL | #[derive(SmartPointer)]
5+
| ^^^^^^^^^^^^
6+
|
7+
= note: see issue #123430 <https://github.com/rust-lang/rust/issues/123430> for more information
8+
= help: add `#![feature(derive_smart_pointer)]` to the crate attributes to enable
9+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
10+
11+
error[E0658]: the `#[pointee]` attribute is an experimental feature
12+
--> $DIR/feature-gate-derive-smart-pointer.rs:4:22
13+
|
14+
LL | struct MyPointer<'a, #[pointee] T: ?Sized> {
15+
| ^^^^^^^^^^
16+
|
17+
= note: see issue #123430 <https://github.com/rust-lang/rust/issues/123430> for more information
18+
= help: add `#![feature(derive_smart_pointer)]` to the crate attributes to enable
19+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
20+
21+
error[E0658]: use of unstable library feature 'derive_smart_pointer'
22+
--> $DIR/feature-gate-derive-smart-pointer.rs:1:5
23+
|
24+
LL | use std::marker::SmartPointer;
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
26+
|
27+
= note: see issue #123430 <https://github.com/rust-lang/rust/issues/123430> for more information
28+
= help: add `#![feature(derive_smart_pointer)]` to the crate attributes to enable
29+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
30+
31+
error: aborting due to 3 previous errors
32+
33+
For more information about this error, try `rustc --explain E0658`.

0 commit comments

Comments
 (0)