Skip to content

Commit bc0b0ad

Browse files
committed
WIP: PoC for derive(SmartPointer)
Tracking issue: #123430
1 parent 96eaf55 commit bc0b0ad

File tree

7 files changed

+187
-0
lines changed

7 files changed

+187
-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 mod decodable;
2727
pub mod default;
2828
pub mod encodable;
2929
pub mod hash;
30+
pub mod smart_ptr;
3031

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

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_span/src/symbol.rs

+6
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,
@@ -297,8 +299,10 @@ symbols! {
297299
Saturating,
298300
Send,
299301
SeqCst,
302+
Sized,
300303
SliceIndex,
301304
SliceIter,
305+
SmartPointer,
302306
Some,
303307
SpanCtxt,
304308
String,
@@ -321,6 +325,7 @@ symbols! {
321325
TyCtxt,
322326
TyKind,
323327
Unknown,
328+
Unsize,
324329
Upvars,
325330
Vec,
326331
VecDeque,
@@ -1282,6 +1287,7 @@ symbols! {
12821287
on,
12831288
on_unimplemented,
12841289
opaque,
1290+
ops,
12851291
opt_out_copy,
12861292
optimize,
12871293
optimize_attribute,

library/core/src/lib.rs

+9
Original file line numberDiff line numberDiff line change
@@ -473,3 +473,12 @@ pub mod simd {
473473
}
474474

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

library/std/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,10 @@ pub use core::u8;
564564
#[allow(deprecated, deprecated_in_future)]
565565
pub use core::usize;
566566

567+
#[cfg(not(bootstrap))]
568+
#[unstable(feature = "derive_smart_pointer", issue = "123430")]
569+
pub use core::SmartPointer;
570+
567571
pub mod f32;
568572
pub mod f64;
569573

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//@ run-pass
2+
#![feature(derive_smart_pointer)]
3+
4+
#[derive(SmartPointer)]
5+
struct MyPointer<'a, 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)