Skip to main content

rustc_builtin_macros/deriving/
reborrow.rs

1use rustc_ast::{
2    self as ast, AttrArgs, GenericArg, GenericParamKind, Generics, ItemKind, MetaItem, token,
3};
4use rustc_errors::E0802;
5use rustc_expand::base::{Annotatable, ExtCtxt};
6use rustc_macros::Diagnostic;
7use rustc_span::{Ident, Span, Symbol, sym};
8use thin_vec::ThinVec;
9
10macro_rules! path {
11    ($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] }
12}
13
14pub(crate) fn expand_deriving_reborrow(
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 Some((ident, generics)) = struct_def(cx, span, item, sym::Reborrow) else {
23        return;
24    };
25
26    push_marker_impl(cx, span, ident, generics, sym::Reborrow, Vec::new(), push);
27}
28
29pub(crate) fn expand_deriving_coerce_shared(
30    cx: &ExtCtxt<'_>,
31    span: Span,
32    _mitem: &MetaItem,
33    item: &Annotatable,
34    push: &mut dyn FnMut(Annotatable),
35    _is_const: bool,
36) {
37    let Some((ident, generics)) = struct_def(cx, span, item, sym::CoerceShared) else {
38        return;
39    };
40    let Some(target) = coerce_shared_target(cx, span, item) else {
41        return;
42    };
43
44    push_marker_impl(
45        cx,
46        span,
47        ident,
48        generics,
49        sym::CoerceShared,
50        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [GenericArg::Type(target)]))vec![GenericArg::Type(target)],
51        push,
52    );
53}
54
55fn struct_def<'a>(
56    cx: &ExtCtxt<'_>,
57    span: Span,
58    item: &'a Annotatable,
59    trait_name: Symbol,
60) -> Option<(Ident, &'a Generics)> {
61    match item {
62        Annotatable::Item(item) => match &item.kind {
63            ItemKind::Struct(ident, generics, _) => Some((*ident, generics)),
64            ItemKind::Enum(..) => {
65                cx.dcx().emit_err(UnsupportedItem { span, trait_name, kind: "enum" });
66                None
67            }
68            ItemKind::Union(..) => {
69                cx.dcx().emit_err(UnsupportedItem { span, trait_name, kind: "union" });
70                None
71            }
72            _ => {
73                cx.dcx().emit_err(UnsupportedItem { span, trait_name, kind: "item" });
74                None
75            }
76        },
77        _ => {
78            cx.dcx().emit_err(UnsupportedItem { span, trait_name, kind: "item" });
79            None
80        }
81    }
82}
83
84fn coerce_shared_target(cx: &ExtCtxt<'_>, span: Span, item: &Annotatable) -> Option<Box<ast::Ty>> {
85    let Annotatable::Item(item) = item else {
86        cx.dcx().emit_err(MissingTarget { span });
87        return None;
88    };
89
90    let mut attrs = item.attrs.iter().filter(|attr| attr.has_name(sym::coerce_shared));
91    let Some(attr) = attrs.next() else {
92        cx.dcx().emit_err(MissingTarget { span });
93        return None;
94    };
95    if let Some(duplicate) = attrs.next() {
96        cx.dcx().emit_err(DuplicateTarget { first: attr.span, duplicate: duplicate.span });
97        return None;
98    }
99
100    let AttrArgs::Delimited(args) = &attr.get_normal_item().args else {
101        cx.dcx().emit_err(MalformedTarget { span: attr.span });
102        return None;
103    };
104    if args.delim != token::Delimiter::Parenthesis || args.tokens.is_empty() {
105        cx.dcx().emit_err(MalformedTarget { span: attr.span });
106        return None;
107    }
108
109    let mut parser = cx.new_parser_from_tts(args.tokens.clone());
110    let target = match parser.parse_ty() {
111        Ok(target) => target,
112        Err(err) => {
113            err.cancel();
114            cx.dcx().emit_err(MalformedTarget { span: attr.span });
115            return None;
116        }
117    };
118    if parser.token != token::Eof {
119        cx.dcx().emit_err(MalformedTarget { span: attr.span });
120        return None;
121    }
122
123    Some(target)
124}
125
126fn push_marker_impl(
127    cx: &ExtCtxt<'_>,
128    span: Span,
129    ident: Ident,
130    generics: &Generics,
131    trait_name: Symbol,
132    trait_args: Vec<GenericArg>,
133    push: &mut dyn FnMut(Annotatable),
134) {
135    let mut trait_parts = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [Ident::new(sym::core, span), Ident::new(sym::marker, span)]))path!(span, core::marker);
136    trait_parts.push(Ident::new(trait_name, span));
137    let trait_path = cx.path_all(span, true, trait_parts, trait_args);
138    let trait_ref = cx.trait_ref(trait_path);
139
140    let self_params: Vec<_> = generics
141        .params
142        .iter()
143        .map(|param| match param.kind {
144            GenericParamKind::Lifetime => {
145                GenericArg::Lifetime(cx.lifetime(param.span(), param.ident))
146            }
147            GenericParamKind::Type { .. } => {
148                GenericArg::Type(cx.ty_ident(param.span(), param.ident))
149            }
150            GenericParamKind::Const { .. } => {
151                GenericArg::Const(cx.const_ident(param.span(), param.ident))
152            }
153        })
154        .collect();
155    let self_ty = cx.ty_path(cx.path_all(span, false, ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [ident]))vec![ident], self_params));
156
157    push(Annotatable::Item(cx.item(
158        span,
159        {
    let len = [()].len();
    let mut vec = ::thin_vec::ThinVec::with_capacity(len);
    vec.push(cx.attr_word(sym::automatically_derived, span));
    vec
}thin_vec::thin_vec![cx.attr_word(sym::automatically_derived, span)],
160        ast::ItemKind::Impl(ast::Impl {
161            generics: impl_generics(cx, generics),
162            of_trait: Some(Box::new(ast::TraitImplHeader {
163                safety: ast::Safety::Default,
164                polarity: ast::ImplPolarity::Positive,
165                defaultness: ast::Defaultness::Implicit,
166                trait_ref,
167            })),
168            constness: ast::Const::No,
169            self_ty,
170            items: ThinVec::new(),
171        }),
172    )));
173}
174
175fn impl_generics(cx: &ExtCtxt<'_>, generics: &Generics) -> Generics {
176    // Rebuild the generic parameter declarations because defaults are allowed on structs but
177    // rejected on impls. Preserve lifetime, type, and const parameters and their bounds, const
178    // parameter types, and the where-clause, while omitting type and const defaults.
179    Generics {
180        params: generics
181            .params
182            .iter()
183            .map(|param| match &param.kind {
184                GenericParamKind::Lifetime => {
185                    cx.lifetime_param(param.span(), param.ident, param.bounds.clone())
186                }
187                GenericParamKind::Type { default: _ } => {
188                    cx.typaram(param.span(), param.ident, param.bounds.clone(), None)
189                }
190                GenericParamKind::Const { ty, span: _, default: _ } => cx.const_param(
191                    param.span(),
192                    param.ident,
193                    param.bounds.clone(),
194                    ty.clone(),
195                    None,
196                ),
197            })
198            .collect(),
199        where_clause: generics.where_clause.clone(),
200        span: generics.span,
201    }
202}
203
204#[derive(const _: () =
    {
        impl<'_sess, G> rustc_errors::Diagnostic<'_sess, G> for
            UnsupportedItem where G: rustc_errors::EmissionGuarantee {
            #[track_caller]
            fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
                level: rustc_errors::Level) -> rustc_errors::Diag<'_sess, G> {
                match self {
                    UnsupportedItem {
                        span: __binding_0,
                        trait_name: __binding_1,
                        kind: __binding_2 } => {
                        let mut diag =
                            rustc_errors::Diag::new(dcx, level,
                                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("`derive({$trait_name})` is only supported for structs, not {$kind}s")));
                        diag.code(E0802);
                        ;
                        diag.arg("trait_name", __binding_1);
                        diag.arg("kind", __binding_2);
                        diag.span(__binding_0);
                        diag
                    }
                }
            }
        }
    };Diagnostic)]
205#[diag("`derive({$trait_name})` is only supported for structs, not {$kind}s", code = E0802)]
206struct UnsupportedItem {
207    #[primary_span]
208    span: Span,
209    trait_name: Symbol,
210    kind: &'static str,
211}
212
213#[derive(const _: () =
    {
        impl<'_sess, G> rustc_errors::Diagnostic<'_sess, G> for MissingTarget
            where G: rustc_errors::EmissionGuarantee {
            #[track_caller]
            fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
                level: rustc_errors::Level) -> rustc_errors::Diag<'_sess, G> {
                match self {
                    MissingTarget { span: __binding_0 } => {
                        let mut diag =
                            rustc_errors::Diag::new(dcx, level,
                                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("`derive(CoerceShared)` requires exactly one `#[coerce_shared(Target)]` attribute")));
                        diag.code(E0802);
                        ;
                        diag.span(__binding_0);
                        diag
                    }
                }
            }
        }
    };Diagnostic)]
214#[diag("`derive(CoerceShared)` requires exactly one `#[coerce_shared(Target)]` attribute", code = E0802)]
215struct MissingTarget {
216    #[primary_span]
217    span: Span,
218}
219
220#[derive(const _: () =
    {
        impl<'_sess, G> rustc_errors::Diagnostic<'_sess, G> for
            DuplicateTarget where G: rustc_errors::EmissionGuarantee {
            #[track_caller]
            fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
                level: rustc_errors::Level) -> rustc_errors::Diag<'_sess, G> {
                match self {
                    DuplicateTarget { duplicate: __binding_0, first: __binding_1
                        } => {
                        let mut diag =
                            rustc_errors::Diag::new(dcx, level,
                                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("duplicate `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)`")));
                        diag.code(E0802);
                        ;
                        diag.span(__binding_0);
                        diag.span_note(__binding_1,
                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("first `#[coerce_shared(Target)]` attribute is here")));
                        diag
                    }
                }
            }
        }
    };Diagnostic)]
221#[diag("duplicate `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)`", code = E0802)]
222struct DuplicateTarget {
223    #[primary_span]
224    duplicate: Span,
225    #[note("first `#[coerce_shared(Target)]` attribute is here")]
226    first: Span,
227}
228
229#[derive(const _: () =
    {
        impl<'_sess, G> rustc_errors::Diagnostic<'_sess, G> for
            MalformedTarget where G: rustc_errors::EmissionGuarantee {
            #[track_caller]
            fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
                level: rustc_errors::Level) -> rustc_errors::Diag<'_sess, G> {
                match self {
                    MalformedTarget { span: __binding_0 } => {
                        let mut diag =
                            rustc_errors::Diag::new(dcx, level,
                                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("malformed `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)`")));
                        diag.code(E0802);
                        diag.note(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("expected a single target type, for example `#[coerce_shared(Target<'a, T>)]`")));
                        ;
                        diag.span(__binding_0);
                        diag
                    }
                }
            }
        }
    };Diagnostic)]
230#[diag("malformed `#[coerce_shared(Target)]` attribute for `derive(CoerceShared)`", code = E0802)]
231#[note("expected a single target type, for example `#[coerce_shared(Target<'a, T>)]`")]
232struct MalformedTarget {
233    #[primary_span]
234    span: Span,
235}