Skip to content

Commit

Permalink
WIP: PoC for derive(SmartPointer)
Browse files Browse the repository at this point in the history
Tracking issue: rust-lang#123430
  • Loading branch information
wedsonaf committed Apr 4, 2024
1 parent 96eaf55 commit bc0b0ad
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 0 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/deriving/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub mod decodable;
pub mod default;
pub mod encodable;
pub mod hash;
pub mod smart_ptr;

#[path = "cmp/eq.rs"]
pub mod eq;
Expand Down
113 changes: 113 additions & 0 deletions compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
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 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) = match item {
Annotatable::Item(aitem) => match &aitem.kind {
ItemKind::Struct(_, g) => (aitem.ident, g),
// FIXME: Improve error reporting.
_ => cx.dcx().span_bug(span, "`#[derive(SmartPointer)]` on wrong kind"),
},
_ => cx.dcx().span_bug(span, "`#[derive(SmartPointer)]` on wrong item"),
};

// Convert generic parameters (from the struct) into generic args.
let self_params = generics
.params
.iter()
.map(|p| match p.kind {
GenericParamKind::Lifetime => GenericArg::Lifetime(cx.lifetime(span, p.ident)),
GenericParamKind::Type { .. } => GenericArg::Type(cx.ty_ident(span, p.ident)),
GenericParamKind::Const { .. } => GenericArg::Const(cx.const_ident(span, p.ident)),
})
.collect::<Vec<_>>();

// 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: Copy attrs from struct?
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 {
unsafety: ast::Unsafe::No,
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 first type arg is replace 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;
for a in &mut alt_self_params {
if matches!(*a, GenericArg::Type(_)) {
*a = GenericArg::Type(s_ty.clone());
break;
}
}
let alt_self_type = cx.ty_path(cx.path_all(span, false, vec![name_ident], alt_self_params));

// Find the first type parameter and add an `Unsize<__S>` bound to it.
let mut impl_generics = generics.clone();
for p in &mut impl_generics.params {
if matches!(p.kind, ast::GenericParamKind::Type { .. }) {
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));
break;
}
}

// 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`, `CoerceUnsized`, and `Receiver`.
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());
add_impl_block(generics.clone(), sym::Receiver, Vec::new());
}
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ symbols! {
Center,
Cleanup,
Clone,
CoerceUnsized,
Command,
ConstParamTy,
Context,
Expand All @@ -187,6 +188,7 @@ symbols! {
DiagMessage,
Diagnostic,
DirBuilder,
DispatchFromDyn,
Display,
DoubleEndedIterator,
Duration,
Expand Down Expand Up @@ -297,8 +299,10 @@ symbols! {
Saturating,
Send,
SeqCst,
Sized,
SliceIndex,
SliceIter,
SmartPointer,
Some,
SpanCtxt,
String,
Expand All @@ -321,6 +325,7 @@ symbols! {
TyCtxt,
TyKind,
Unknown,
Unsize,
Upvars,
Vec,
VecDeque,
Expand Down Expand Up @@ -1282,6 +1287,7 @@ symbols! {
on,
on_unimplemented,
opaque,
ops,
opt_out_copy,
optimize,
optimize_attribute,
Expand Down
9 changes: 9 additions & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,3 +473,12 @@ pub mod simd {
}

include!("primitive_docs.rs");

/// Derive macro generating impls of traits related to smart pointers.
#[cfg(not(bootstrap))]
#[rustc_builtin_macro]
#[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, receiver_trait, unsize)]
#[unstable(feature = "derive_smart_pointer", issue = "123430")]
pub macro SmartPointer($item:item) {
/* compiler built-in */
}
4 changes: 4 additions & 0 deletions library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,10 @@ pub use core::u8;
#[allow(deprecated, deprecated_in_future)]
pub use core::usize;

#[cfg(not(bootstrap))]
#[unstable(feature = "derive_smart_pointer", issue = "123430")]
pub use core::SmartPointer;

pub mod f32;
pub mod f64;

Expand Down
53 changes: 53 additions & 0 deletions tests/ui/deriving/deriving-smart-pointer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//@ run-pass
#![feature(derive_smart_pointer)]

#[derive(SmartPointer)]
struct MyPointer<'a, T: ?Sized> {
ptr: &'a T,
}

impl<T: ?Sized> Copy for MyPointer<'_, T> {}
impl<T: ?Sized> 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<dyn MyTrait>;
assert_eq!(v.0, dptr.through_trait());
assert_eq!(v.0, dptr.through_trait_and_pointer());
}

0 comments on commit bc0b0ad

Please sign in to comment.