Skip to content

Commit ed460d2

Browse files
Rollup merge of rust-lang#125575 - dingxiangfei2009:derive-smart-ptr, r=davidtwco
SmartPointer derive-macro <!-- If this PR is related to an unstable feature or an otherwise tracked effort, please link to the relevant tracking issue here. If you don't know of a related tracking issue or there are none, feel free to ignore this. This PR will get automatically assigned to a reviewer. In case you would like a specific user to review your work, you can assign it to them by using r​? <reviewer name> --> Possibly replacing rust-lang#123472 for continued upkeep of the proposal rust-lang/rfcs#3621 and implementation of the tracking issue rust-lang#123430. cc `@Darksonn` `@wedsonaf`
2 parents c77dc28 + f1be59f commit ed460d2

File tree

10 files changed

+264
-0
lines changed

10 files changed

+264
-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,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 `#[pointee]` type arg is replaced 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 `#[pointee]` 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+
}

compiler/rustc_builtin_macros/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
127127
PartialOrd: partial_ord::expand_deriving_partial_ord,
128128
RustcDecodable: decodable::expand_deriving_rustc_decodable,
129129
RustcEncodable: encodable::expand_deriving_rustc_encodable,
130+
SmartPointer: smart_ptr::expand_deriving_smart_ptr,
130131
}
131132

132133
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
@@ -575,6 +575,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
575575
EncodeCrossCrate::No, coroutines, experimental!(coroutines)
576576
),
577577

578+
// `#[pointee]` attribute to designate the pointee type in SmartPointer derive-macro
579+
gated!(
580+
pointee, Normal, template!(Word), ErrorFollowing,
581+
EncodeCrossCrate::No, derive_smart_pointer, experimental!(pointee)
582+
),
583+
578584
// ==========================================================================
579585
// Internal attributes: Stability, deprecation, and unsafe:
580586
// ==========================================================================

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,8 @@ declare_features! (
436436
(unstable, deprecated_suggestion, "1.61.0", Some(94785)),
437437
/// Allows deref patterns.
438438
(incomplete, deref_patterns, "1.79.0", Some(87121)),
439+
/// Allows deriving `SmartPointer` traits
440+
(unstable, derive_smart_pointer, "1.79.0", Some(123430)),
439441
/// Controls errors in trait implementations.
440442
(unstable, do_not_recommend, "1.67.0", Some(51992)),
441443
/// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.

compiler/rustc_span/src/symbol.rs

+8
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ symbols! {
174174
Center,
175175
Cleanup,
176176
Clone,
177+
CoerceUnsized,
177178
Command,
178179
ConstParamTy,
179180
Context,
@@ -189,6 +190,7 @@ symbols! {
189190
DiagMessage,
190191
Diagnostic,
191192
DirBuilder,
193+
DispatchFromDyn,
192194
Display,
193195
DoubleEndedIterator,
194196
Duration,
@@ -299,8 +301,10 @@ symbols! {
299301
Saturating,
300302
Send,
301303
SeqCst,
304+
Sized,
302305
SliceIndex,
303306
SliceIter,
307+
SmartPointer,
304308
Some,
305309
SpanCtxt,
306310
String,
@@ -323,6 +327,7 @@ symbols! {
323327
TyCtxt,
324328
TyKind,
325329
Unknown,
330+
Unsize,
326331
Upvars,
327332
Vec,
328333
VecDeque,
@@ -707,6 +712,7 @@ symbols! {
707712
derive,
708713
derive_const,
709714
derive_default_enum,
715+
derive_smart_pointer,
710716
destruct,
711717
destructuring_assignment,
712718
diagnostic,
@@ -1315,6 +1321,7 @@ symbols! {
13151321
on,
13161322
on_unimplemented,
13171323
opaque,
1324+
ops,
13181325
opt_out_copy,
13191326
optimize,
13201327
optimize_attribute,
@@ -1389,6 +1396,7 @@ symbols! {
13891396
plugin,
13901397
plugin_registrar,
13911398
plugins,
1399+
pointee,
13921400
pointee_trait,
13931401
pointer,
13941402
pointer_like,

library/core/src/marker.rs

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