SmartPointer derive-macro
Co-authored-by: Wedson Almeida Filho <walmeida@microsoft.com>
This commit is contained in:
parent
aabbf84b45
commit
f1be59fa72
@ -27,6 +27,7 @@
|
|||||||
pub(crate) mod default;
|
pub(crate) mod default;
|
||||||
pub(crate) mod encodable;
|
pub(crate) mod encodable;
|
||||||
pub(crate) mod hash;
|
pub(crate) mod hash;
|
||||||
|
pub(crate) mod smart_ptr;
|
||||||
|
|
||||||
#[path = "cmp/eq.rs"]
|
#[path = "cmp/eq.rs"]
|
||||||
pub(crate) mod eq;
|
pub(crate) mod eq;
|
||||||
|
140
compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
Normal file
140
compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
use std::mem::swap;
|
||||||
|
|
||||||
|
use ast::HasAttrs;
|
||||||
|
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 smallvec::{smallvec, SmallVec};
|
||||||
|
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) = if let Annotatable::Item(aitem) = item
|
||||||
|
&& let ItemKind::Struct(_, g) = &aitem.kind
|
||||||
|
{
|
||||||
|
(aitem.ident, g)
|
||||||
|
} else {
|
||||||
|
cx.dcx().struct_span_err(span, "`SmartPointer` can only be derived on `struct`s").emit();
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert generic parameters (from the struct) into generic args.
|
||||||
|
let mut pointee_param = None;
|
||||||
|
let mut multiple_pointee_diag: SmallVec<[_; 2]> = smallvec![];
|
||||||
|
let self_params = generics
|
||||||
|
.params
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(idx, p)| match p.kind {
|
||||||
|
GenericParamKind::Lifetime => GenericArg::Lifetime(cx.lifetime(p.span(), p.ident)),
|
||||||
|
GenericParamKind::Type { .. } => {
|
||||||
|
if p.attrs().iter().any(|attr| attr.has_name(sym::pointee)) {
|
||||||
|
if pointee_param.is_some() {
|
||||||
|
multiple_pointee_diag.push(cx.dcx().struct_span_err(
|
||||||
|
p.span(),
|
||||||
|
"`SmartPointer` can only admit one type as pointee",
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
pointee_param = Some(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GenericArg::Type(cx.ty_ident(p.span(), p.ident))
|
||||||
|
}
|
||||||
|
GenericParamKind::Const { .. } => GenericArg::Const(cx.const_ident(p.span(), p.ident)),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let Some(pointee_param_idx) = pointee_param else {
|
||||||
|
cx.dcx().struct_span_err(
|
||||||
|
span,
|
||||||
|
"At least one generic type should be designated as `#[pointee]` in order to derive `SmartPointer` traits",
|
||||||
|
).emit();
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if !multiple_pointee_diag.is_empty() {
|
||||||
|
for diag in multiple_pointee_diag {
|
||||||
|
diag.emit();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(dingxiangfei2009): Investigate the set of attributes on target struct to be propagated to impls
|
||||||
|
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 {
|
||||||
|
safety: ast::Safety::Default,
|
||||||
|
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 `#[pointee]` type arg is replaced 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;
|
||||||
|
alt_self_params[pointee_param_idx] = GenericArg::Type(s_ty.clone());
|
||||||
|
let alt_self_type = cx.ty_path(cx.path_all(span, false, vec![name_ident], alt_self_params));
|
||||||
|
|
||||||
|
// Find the `#[pointee]` parameter and add an `Unsize<__S>` bound to it.
|
||||||
|
let mut impl_generics = generics.clone();
|
||||||
|
{
|
||||||
|
let p = &mut impl_generics.params[pointee_param_idx];
|
||||||
|
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));
|
||||||
|
let mut attrs = thin_vec![];
|
||||||
|
swap(&mut p.attrs, &mut attrs);
|
||||||
|
p.attrs = attrs.into_iter().filter(|attr| !attr.has_name(sym::pointee)).collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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` and `CoerceUnsized`.
|
||||||
|
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());
|
||||||
|
}
|
@ -127,6 +127,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
|
|||||||
PartialOrd: partial_ord::expand_deriving_partial_ord,
|
PartialOrd: partial_ord::expand_deriving_partial_ord,
|
||||||
RustcDecodable: decodable::expand_deriving_rustc_decodable,
|
RustcDecodable: decodable::expand_deriving_rustc_decodable,
|
||||||
RustcEncodable: encodable::expand_deriving_rustc_encodable,
|
RustcEncodable: encodable::expand_deriving_rustc_encodable,
|
||||||
|
SmartPointer: smart_ptr::expand_deriving_smart_ptr,
|
||||||
}
|
}
|
||||||
|
|
||||||
let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote);
|
let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote);
|
||||||
|
@ -575,6 +575,12 @@ pub struct BuiltinAttribute {
|
|||||||
EncodeCrossCrate::No, coroutines, experimental!(coroutines)
|
EncodeCrossCrate::No, coroutines, experimental!(coroutines)
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// `#[pointee]` attribute to designate the pointee type in SmartPointer derive-macro
|
||||||
|
gated!(
|
||||||
|
pointee, Normal, template!(Word), ErrorFollowing,
|
||||||
|
EncodeCrossCrate::No, derive_smart_pointer, experimental!(pointee)
|
||||||
|
),
|
||||||
|
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// Internal attributes: Stability, deprecation, and unsafe:
|
// Internal attributes: Stability, deprecation, and unsafe:
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
@ -436,6 +436,8 @@ pub fn internal(&self, feature: Symbol) -> bool {
|
|||||||
(unstable, deprecated_suggestion, "1.61.0", Some(94785)),
|
(unstable, deprecated_suggestion, "1.61.0", Some(94785)),
|
||||||
/// Allows deref patterns.
|
/// Allows deref patterns.
|
||||||
(incomplete, deref_patterns, "1.79.0", Some(87121)),
|
(incomplete, deref_patterns, "1.79.0", Some(87121)),
|
||||||
|
/// Allows deriving `SmartPointer` traits
|
||||||
|
(unstable, derive_smart_pointer, "1.79.0", Some(123430)),
|
||||||
/// Controls errors in trait implementations.
|
/// Controls errors in trait implementations.
|
||||||
(unstable, do_not_recommend, "1.67.0", Some(51992)),
|
(unstable, do_not_recommend, "1.67.0", Some(51992)),
|
||||||
/// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.
|
/// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.
|
||||||
|
@ -174,6 +174,7 @@
|
|||||||
Center,
|
Center,
|
||||||
Cleanup,
|
Cleanup,
|
||||||
Clone,
|
Clone,
|
||||||
|
CoerceUnsized,
|
||||||
Command,
|
Command,
|
||||||
ConstParamTy,
|
ConstParamTy,
|
||||||
Context,
|
Context,
|
||||||
@ -189,6 +190,7 @@
|
|||||||
DiagMessage,
|
DiagMessage,
|
||||||
Diagnostic,
|
Diagnostic,
|
||||||
DirBuilder,
|
DirBuilder,
|
||||||
|
DispatchFromDyn,
|
||||||
Display,
|
Display,
|
||||||
DoubleEndedIterator,
|
DoubleEndedIterator,
|
||||||
Duration,
|
Duration,
|
||||||
@ -299,8 +301,10 @@
|
|||||||
Saturating,
|
Saturating,
|
||||||
Send,
|
Send,
|
||||||
SeqCst,
|
SeqCst,
|
||||||
|
Sized,
|
||||||
SliceIndex,
|
SliceIndex,
|
||||||
SliceIter,
|
SliceIter,
|
||||||
|
SmartPointer,
|
||||||
Some,
|
Some,
|
||||||
SpanCtxt,
|
SpanCtxt,
|
||||||
String,
|
String,
|
||||||
@ -323,6 +327,7 @@
|
|||||||
TyCtxt,
|
TyCtxt,
|
||||||
TyKind,
|
TyKind,
|
||||||
Unknown,
|
Unknown,
|
||||||
|
Unsize,
|
||||||
Upvars,
|
Upvars,
|
||||||
Vec,
|
Vec,
|
||||||
VecDeque,
|
VecDeque,
|
||||||
@ -707,6 +712,7 @@
|
|||||||
derive,
|
derive,
|
||||||
derive_const,
|
derive_const,
|
||||||
derive_default_enum,
|
derive_default_enum,
|
||||||
|
derive_smart_pointer,
|
||||||
destruct,
|
destruct,
|
||||||
destructuring_assignment,
|
destructuring_assignment,
|
||||||
diagnostic,
|
diagnostic,
|
||||||
@ -1315,6 +1321,7 @@
|
|||||||
on,
|
on,
|
||||||
on_unimplemented,
|
on_unimplemented,
|
||||||
opaque,
|
opaque,
|
||||||
|
ops,
|
||||||
opt_out_copy,
|
opt_out_copy,
|
||||||
optimize,
|
optimize,
|
||||||
optimize_attribute,
|
optimize_attribute,
|
||||||
@ -1389,6 +1396,7 @@
|
|||||||
plugin,
|
plugin,
|
||||||
plugin_registrar,
|
plugin_registrar,
|
||||||
plugins,
|
plugins,
|
||||||
|
pointee,
|
||||||
pointee_trait,
|
pointee_trait,
|
||||||
pointer,
|
pointer,
|
||||||
pointer_like,
|
pointer_like,
|
||||||
|
@ -1018,3 +1018,12 @@ pub trait FnPtr: Copy + Clone {
|
|||||||
#[lang = "fn_ptr_addr"]
|
#[lang = "fn_ptr_addr"]
|
||||||
fn addr(self) -> *const ();
|
fn addr(self) -> *const ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Derive macro generating impls of traits related to smart pointers.
|
||||||
|
#[cfg(not(bootstrap))]
|
||||||
|
#[rustc_builtin_macro]
|
||||||
|
#[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)]
|
||||||
|
#[unstable(feature = "derive_smart_pointer", issue = "123430")]
|
||||||
|
pub macro SmartPointer($item:item) {
|
||||||
|
/* compiler built-in */
|
||||||
|
}
|
||||||
|
55
tests/ui/deriving/deriving-smart-pointer.rs
Normal file
55
tests/ui/deriving/deriving-smart-pointer.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
//@ run-pass
|
||||||
|
#![feature(derive_smart_pointer, arbitrary_self_types)]
|
||||||
|
|
||||||
|
use std::marker::SmartPointer;
|
||||||
|
|
||||||
|
#[derive(SmartPointer)]
|
||||||
|
struct MyPointer<'a, #[pointee] 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,0 +1,9 @@
|
|||||||
|
use std::marker::SmartPointer; //~ ERROR use of unstable library feature 'derive_smart_pointer'
|
||||||
|
|
||||||
|
#[derive(SmartPointer)] //~ ERROR use of unstable library feature 'derive_smart_pointer'
|
||||||
|
struct MyPointer<'a, #[pointee] T: ?Sized> {
|
||||||
|
//~^ ERROR the `#[pointee]` attribute is an experimental feature
|
||||||
|
ptr: &'a T,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,33 @@
|
|||||||
|
error[E0658]: use of unstable library feature 'derive_smart_pointer'
|
||||||
|
--> $DIR/feature-gate-derive-smart-pointer.rs:3:10
|
||||||
|
|
|
||||||
|
LL | #[derive(SmartPointer)]
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #123430 <https://github.com/rust-lang/rust/issues/123430> for more information
|
||||||
|
= help: add `#![feature(derive_smart_pointer)]` to the crate attributes to enable
|
||||||
|
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||||
|
|
||||||
|
error[E0658]: the `#[pointee]` attribute is an experimental feature
|
||||||
|
--> $DIR/feature-gate-derive-smart-pointer.rs:4:22
|
||||||
|
|
|
||||||
|
LL | struct MyPointer<'a, #[pointee] T: ?Sized> {
|
||||||
|
| ^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #123430 <https://github.com/rust-lang/rust/issues/123430> for more information
|
||||||
|
= help: add `#![feature(derive_smart_pointer)]` to the crate attributes to enable
|
||||||
|
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||||
|
|
||||||
|
error[E0658]: use of unstable library feature 'derive_smart_pointer'
|
||||||
|
--> $DIR/feature-gate-derive-smart-pointer.rs:1:5
|
||||||
|
|
|
||||||
|
LL | use std::marker::SmartPointer;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #123430 <https://github.com/rust-lang/rust/issues/123430> for more information
|
||||||
|
= help: add `#![feature(derive_smart_pointer)]` to the crate attributes to enable
|
||||||
|
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0658`.
|
Loading…
Reference in New Issue
Block a user