rustc_builtin_macros/deriving/
coerce_pointee.rs1use ast::HasAttrs;
2use rustc_ast::mut_visit::MutVisitor;
3use rustc_ast::visit::{BoundKind, Visitor};
4use rustc_ast::{
5 self as ast, GenericArg, GenericBound, GenericParamKind, ItemKind, TraitBoundModifiers,
6 VariantData, WherePredicate,
7};
8use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
9use rustc_errors::E0802;
10use rustc_expand::base::ExtCtxt;
11use rustc_macros::Diagnostic;
12use rustc_span::{Ident, Span, Symbol, sym};
13use thin_vec::{ThinVec, thin_vec};
14
15use crate::deriving::generic::*;
16use crate::deriving::{new_path, path_std};
17use crate::diagnostics;
18
19pub(crate) fn expand_deriving_coerce_pointee(
20 cx: &ExtCtxt<'_>,
21 span: Span,
22 item: &ast::Item,
23 push: &mut dyn FnMut(Box<ast::Item>),
24 _is_const: bool,
25) {
26 DetectNonGenericPointeeAttr { cx }.visit_item(item);
27
28 let (name_ident, generics) = if let ItemKind::Struct(ident, g, struct_data) = &item.kind {
29 if !#[allow(non_exhaustive_omitted_patterns)] match struct_data {
VariantData::Struct { fields, recovered: _ } |
VariantData::Tuple(fields, _) if !fields.is_empty() => true,
_ => false,
}matches!(
30 struct_data,
31 VariantData::Struct { fields, recovered: _ } | VariantData::Tuple(fields, _)
32 if !fields.is_empty())
33 {
34 cx.dcx().emit_err(RequireOneField { span });
35 return;
36 }
37 (*ident, g)
38 } else {
39 cx.dcx().emit_err(RequireTransparent { span });
40 return;
41 };
42
43 let self_params: Vec<_> =
45 generics.params.iter().map(|p| generic_param_to_arg(cx, p, p.span())).collect();
46 let type_params: Vec<_> = generics
47 .params
48 .iter()
49 .enumerate()
50 .filter_map(|(idx, p)| {
51 if let GenericParamKind::Type { .. } = p.kind {
52 Some((idx, p.span(), p.attrs().iter().any(|attr| attr.has_name(sym::pointee))))
53 } else {
54 None
55 }
56 })
57 .collect();
58
59 let pointee_param_idx = if type_params.is_empty() {
60 cx.dcx().emit_err(RequireOneGeneric { span });
62 return;
63 } else if type_params.len() == 1 {
64 type_params[0].0
66 } else {
67 let mut pointees = type_params
68 .iter()
69 .filter_map(|&(idx, span, is_pointee)| is_pointee.then_some((idx, span)));
70 match (pointees.next(), pointees.next()) {
71 (Some((idx, _span)), None) => idx,
72 (None, _) => {
73 cx.dcx().emit_err(RequireOnePointee { span });
74 return;
75 }
76 (Some((_, one)), Some((_, another))) => {
77 cx.dcx().emit_err(TooManyPointees { one, another });
78 return;
79 }
80 }
81 };
82
83 let 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(),
[name_ident]))vec![name_ident], self_params.clone());
85 let self_type = cx.ty_path(path);
86
87 let attrs = {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.attr_word(sym::automatically_derived, span));
vec
}thin_vec![cx.attr_word(sym::automatically_derived, span),];
90 {
92 let trait_path = new_path(cx, span, { &[sym::marker, sym::CoercePointeeValidated] },
::alloc::vec::Vec::new())path_std!(cx, span, marker::CoercePointeeValidated);
93 push(cx.item_trait_impl(
94 span,
95 attrs.clone(),
96 generics_without_defaults(generics),
97 ast::Safety::Default,
98 false,
99 cx.trait_ref(trait_path),
100 self_type.clone(),
101 ThinVec::new(),
102 ));
103 }
104 let mut add_impl_block = |generics, trait_symbol, trait_args| {
105 let trait_path = new_path(cx, span, &[sym::ops, trait_symbol], trait_args);
106 let trait_ref = cx.trait_ref(trait_path);
107 let item = cx.item_trait_impl(
108 span,
109 attrs.clone(),
110 generics,
111 ast::Safety::Default,
112 false,
113 trait_ref,
114 self_type.clone(),
115 ThinVec::new(),
116 );
117 push(item);
118 };
119
120 let s_ty = cx.ty_ident(span, Ident::new(sym::__S, span));
123 let mut alt_self_params = self_params;
124 alt_self_params[pointee_param_idx] = GenericArg::Type(s_ty.clone());
125 let alt_self_type = 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(),
[name_ident]))vec![name_ident], alt_self_params));
126
127 let mut impl_generics = generics.clone();
131 let pointee_ty_ident = generics.params[pointee_param_idx].ident;
132 let mut self_bounds;
133 {
134 let pointee = &mut impl_generics.params[pointee_param_idx];
135 self_bounds = pointee.bounds.clone();
136 if !contains_maybe_sized_bound(&self_bounds)
137 && !contains_maybe_sized_bound_on_pointee(
138 &generics.where_clause.predicates,
139 pointee_ty_ident.name,
140 )
141 {
142 cx.dcx().emit_err(RequiresMaybeSized {
143 span: pointee_ty_ident.span,
144 name: pointee_ty_ident,
145 });
146 return;
147 }
148 let arg = GenericArg::Type(s_ty.clone());
149 let unsize = cx.path_all(span, true, cx.std_path(&[sym::marker, sym::Unsize]), ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[arg]))vec![arg]);
150 pointee.bounds.push(cx.trait_bound(unsize, false));
151 pointee.attrs.retain(|attr| !attr.has_name(sym::pointee));
153 }
154
155 for (idx, (params, orig_params)) in
175 impl_generics.params.iter_mut().zip(&generics.params).enumerate()
176 {
177 match &mut params.kind {
180 ast::GenericParamKind::Const { default, .. } => *default = None,
181 ast::GenericParamKind::Type { default } => *default = None,
182 ast::GenericParamKind::Lifetime => {}
183 }
184 if idx != pointee_param_idx {
189 for bound in &orig_params.bounds {
190 let mut bound = bound.clone();
191 let mut substitution = TypeSubstitution {
192 from_name: pointee_ty_ident.name,
193 to_ty: &s_ty,
194 rewritten: false,
195 };
196 substitution.visit_param_bound(&mut bound, BoundKind::Bound);
197 if substitution.rewritten {
198 params.bounds.push(bound);
201 }
202 }
203 }
204 }
205
206 {
211 let mut substitution =
212 TypeSubstitution { from_name: pointee_ty_ident.name, to_ty: &s_ty, rewritten: false };
213 for bound in &mut self_bounds {
214 substitution.visit_param_bound(bound, BoundKind::Bound);
215 }
216 }
217
218 for predicate in &generics.where_clause.predicates {
245 if let ast::WherePredicateKind::BoundPredicate(bound) = &predicate.kind {
246 let mut substitution = TypeSubstitution {
247 from_name: pointee_ty_ident.name,
248 to_ty: &s_ty,
249 rewritten: false,
250 };
251 let mut kind = ast::WherePredicateKind::BoundPredicate(bound.clone());
252 substitution.visit_where_predicate_kind(&mut kind);
253 if substitution.rewritten {
254 let predicate = ast::WherePredicate {
255 attrs: predicate.attrs.clone(),
256 kind,
257 span: predicate.span,
258 id: ast::DUMMY_NODE_ID,
259 is_placeholder: false,
260 };
261 impl_generics.where_clause.predicates.push(predicate);
262 }
263 }
264 }
265
266 let extra_param = cx.typaram(Ident::new(sym::__S, span), self_bounds, None);
267 impl_generics.params.insert(pointee_param_idx + 1, extra_param);
268
269 let gen_args = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[alt_self_type]))vec![alt_self_type];
271 add_impl_block(impl_generics.clone(), sym::DispatchFromDyn, gen_args.clone());
272 add_impl_block(impl_generics, sym::CoerceUnsized, gen_args);
273}
274
275fn contains_maybe_sized_bound_on_pointee(predicates: &[WherePredicate], pointee: Symbol) -> bool {
276 for bound in predicates {
277 if let ast::WherePredicateKind::BoundPredicate(bound) = &bound.kind
278 && bound.bounded_ty.kind.is_simple_path().is_some_and(|name| name == pointee)
279 {
280 if contains_maybe_sized_bound(&bound.bounds) {
281 return true;
282 }
283 }
284 }
285 false
286}
287
288fn is_maybe_sized_bound(bound: &GenericBound) -> bool {
289 if let GenericBound::Trait(trait_ref) = bound
290 && let TraitBoundModifiers { polarity: ast::BoundPolarity::Maybe(_), .. } =
291 trait_ref.modifiers
292 && is_sized_marker(&trait_ref.trait_ref.path)
293 {
294 true
295 } else {
296 false
297 }
298}
299
300fn contains_maybe_sized_bound(bounds: &[GenericBound]) -> bool {
301 bounds.iter().any(is_maybe_sized_bound)
302}
303
304fn is_sized_marker(path: &ast::Path) -> bool {
305 const CORE_UNSIZE: [Symbol; 3] = [sym::core, sym::marker, sym::Sized];
306 const STD_UNSIZE: [Symbol; 3] = [sym::std, sym::marker, sym::Sized];
307 let segments = || path.segments.iter().map(|segment| segment.ident.name);
308 if path.is_global() {
309 segments().skip(1).eq(CORE_UNSIZE) || segments().skip(1).eq(STD_UNSIZE)
310 } else {
311 segments().eq(CORE_UNSIZE) || segments().eq(STD_UNSIZE) || *path == sym::Sized
312 }
313}
314
315struct TypeSubstitution<'a> {
316 from_name: Symbol,
317 to_ty: &'a ast::Ty,
318 rewritten: bool,
319}
320
321impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> {
322 fn visit_ty(&mut self, ty: &mut ast::Ty) {
323 if let Some(name) = ty.kind.is_simple_path()
324 && name == self.from_name
325 {
326 *ty = self.to_ty.clone();
327 self.rewritten = true;
328 } else {
329 ast::mut_visit::walk_ty(self, ty);
330 }
331 }
332
333 fn visit_where_predicate_kind(&mut self, kind: &mut ast::WherePredicateKind) {
334 match kind {
335 rustc_ast::WherePredicateKind::BoundPredicate(bound) => {
336 bound
337 .bound_generic_params
338 .flat_map_in_place(|param| self.flat_map_generic_param(param));
339 self.visit_ty(&mut bound.bounded_ty);
340 for bound in &mut bound.bounds {
341 self.visit_param_bound(bound, BoundKind::Bound)
342 }
343 }
344 rustc_ast::WherePredicateKind::RegionPredicate(_) => {}
345 }
346 }
347}
348
349struct DetectNonGenericPointeeAttr<'a, 'b> {
350 cx: &'a ExtCtxt<'b>,
351}
352
353impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonGenericPointeeAttr<'a, 'b> {
354 fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) -> Self::Result {
355 if attr.has_name(sym::pointee) {
356 self.cx.dcx().emit_err(diagnostics::NonGenericPointee { span: attr.span });
357 }
358 }
359
360 fn visit_generic_param(&mut self, param: &'a rustc_ast::GenericParam) -> Self::Result {
361 let mut error_on_pointee = AlwaysErrorOnGenericParam { cx: self.cx };
362
363 match ¶m.kind {
364 GenericParamKind::Type { default } => {
365 if let Some(x) = default {
match ::rustc_ast_ir::visit::VisitorResult::branch(error_on_pointee.visit_ty(x))
{
core::ops::ControlFlow::Continue(()) =>
(),
#[allow(unreachable_code)]
core::ops::ControlFlow::Break(r) => {
return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
}
};
};rustc_ast::visit::visit_opt!(error_on_pointee, visit_ty, default);
383 }
384
385 GenericParamKind::Const { .. } | GenericParamKind::Lifetime => {
386 rustc_ast::visit::walk_generic_param(&mut error_on_pointee, param);
387 }
388 }
389 }
390
391 fn visit_ty(&mut self, t: &'a rustc_ast::Ty) -> Self::Result {
392 let mut error_on_pointee = AlwaysErrorOnGenericParam { cx: self.cx };
393 error_on_pointee.visit_ty(t)
394 }
395}
396
397struct AlwaysErrorOnGenericParam<'a, 'b> {
398 cx: &'a ExtCtxt<'b>,
399}
400
401impl<'a, 'b> rustc_ast::visit::Visitor<'a> for AlwaysErrorOnGenericParam<'a, 'b> {
402 fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) -> Self::Result {
403 if attr.has_name(sym::pointee) {
404 self.cx.dcx().emit_err(diagnostics::NonGenericPointee { span: attr.span });
405 }
406 }
407}
408
409#[derive(const _: () =
{
impl<'_sess> rustc_errors::Diagnostic<'_sess> for RequireTransparent {
#[track_caller]
fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
level: rustc_errors::Level) -> rustc_errors::Diag<'_sess> {
match self {
RequireTransparent { span: __binding_0 } => {
let mut diag =
rustc_errors::Diag::new(dcx, level,
rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("`CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]`")));
diag.code(E0802);
;
diag.span(__binding_0);
diag
}
}
}
}
};Diagnostic)]
410#[diag("`CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]`", code = E0802)]
411struct RequireTransparent {
412 #[primary_span]
413 span: Span,
414}
415
416#[derive(const _: () =
{
impl<'_sess> rustc_errors::Diagnostic<'_sess> for RequireOneField {
#[track_caller]
fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
level: rustc_errors::Level) -> rustc_errors::Diag<'_sess> {
match self {
RequireOneField { span: __binding_0 } => {
let mut diag =
rustc_errors::Diag::new(dcx, level,
rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("`CoercePointee` can only be derived on `struct`s with at least one field")));
diag.code(E0802);
;
diag.span(__binding_0);
diag
}
}
}
}
};Diagnostic)]
417#[diag("`CoercePointee` can only be derived on `struct`s with at least one field", code = E0802)]
418struct RequireOneField {
419 #[primary_span]
420 span: Span,
421}
422
423#[derive(const _: () =
{
impl<'_sess> rustc_errors::Diagnostic<'_sess> for RequireOneGeneric {
#[track_caller]
fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
level: rustc_errors::Level) -> rustc_errors::Diag<'_sess> {
match self {
RequireOneGeneric { span: __binding_0 } => {
let mut diag =
rustc_errors::Diag::new(dcx, level,
rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("`CoercePointee` can only be derived on `struct`s that are generic over at least one type")));
diag.code(E0802);
;
diag.span(__binding_0);
diag
}
}
}
}
};Diagnostic)]
424#[diag("`CoercePointee` can only be derived on `struct`s that are generic over at least one type", code = E0802)]
425struct RequireOneGeneric {
426 #[primary_span]
427 span: Span,
428}
429
430#[derive(const _: () =
{
impl<'_sess> rustc_errors::Diagnostic<'_sess> for RequireOnePointee {
#[track_caller]
fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
level: rustc_errors::Level) -> rustc_errors::Diag<'_sess> {
match self {
RequireOnePointee { span: __binding_0 } => {
let mut diag =
rustc_errors::Diag::new(dcx, level,
rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits")));
diag.code(E0802);
;
diag.span(__binding_0);
diag
}
}
}
}
};Diagnostic)]
431#[diag("exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits", code = E0802)]
432struct RequireOnePointee {
433 #[primary_span]
434 span: Span,
435}
436
437#[derive(const _: () =
{
impl<'_sess> rustc_errors::Diagnostic<'_sess> for TooManyPointees {
#[track_caller]
fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
level: rustc_errors::Level) -> rustc_errors::Diag<'_sess> {
match self {
TooManyPointees { one: __binding_0, another: __binding_1 }
=> {
let mut diag =
rustc_errors::Diag::new(dcx, level,
rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits")));
diag.code(E0802);
;
diag.span(__binding_0);
diag.span_label(__binding_1,
rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("here another type parameter is marked as `#[pointee]`")));
diag
}
}
}
}
};Diagnostic)]
438#[diag("only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits", code = E0802)]
439struct TooManyPointees {
440 #[primary_span]
441 one: Span,
442 #[label("here another type parameter is marked as `#[pointee]`")]
443 another: Span,
444}
445
446#[derive(const _: () =
{
impl<'_sess> rustc_errors::Diagnostic<'_sess> for RequiresMaybeSized {
#[track_caller]
fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
level: rustc_errors::Level) -> rustc_errors::Diag<'_sess> {
match self {
RequiresMaybeSized { span: __binding_0, name: __binding_1 }
=> {
let mut diag =
rustc_errors::Diag::new(dcx, level,
rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("`derive(CoercePointee)` requires `{$name}` to be marked `?Sized`")));
diag.code(E0802);
;
diag.arg("name", __binding_1);
diag.span(__binding_0);
diag
}
}
}
}
};Diagnostic)]
447#[diag("`derive(CoercePointee)` requires `{$name}` to be marked `?Sized`", code = E0802)]
448struct RequiresMaybeSized {
449 #[primary_span]
450 span: Span,
451 name: Ident,
452}