1use ast::HasAttrs;
2use rustc_ast::mut_visit::MutVisitor;
3use rustc_ast::visit::{BoundKind, Visitor};
4use rustc_ast::{
5 self as ast, GenericArg, GenericBound, GenericParamKind, Generics, ItemKind,
6 TraitBoundModifiers, 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::diagnostics;
16
17macro_rules! path {
18 ($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] }
19}
20
21pub(crate) fn expand_deriving_coerce_pointee(
22 cx: &ExtCtxt<'_>,
23 span: Span,
24 item: &ast::Item,
25 push: &mut dyn FnMut(Box<ast::Item>),
26 _is_const: bool,
27) {
28 DetectNonGenericPointeeAttr { cx }.visit_item(item);
29
30 let (name_ident, generics) = if let ItemKind::Struct(ident, g, struct_data) = &item.kind {
31 if !#[allow(non_exhaustive_omitted_patterns)] match struct_data {
VariantData::Struct { fields, recovered: _ } |
VariantData::Tuple(fields, _) if !fields.is_empty() => true,
_ => false,
}matches!(
32 struct_data,
33 VariantData::Struct { fields, recovered: _ } | VariantData::Tuple(fields, _)
34 if !fields.is_empty())
35 {
36 cx.dcx().emit_err(RequireOneField { span });
37 return;
38 }
39 (*ident, g)
40 } else {
41 cx.dcx().emit_err(RequireTransparent { span });
42 return;
43 };
44
45 let self_params: Vec<_> = generics
47 .params
48 .iter()
49 .map(|p| match p.kind {
50 GenericParamKind::Lifetime => GenericArg::Lifetime(cx.lifetime(p.span(), p.ident)),
51 GenericParamKind::Type { .. } => GenericArg::Type(cx.ty_ident(p.span(), p.ident)),
52 GenericParamKind::Const { .. } => GenericArg::Const(cx.const_ident(p.span(), p.ident)),
53 })
54 .collect();
55 let type_params: Vec<_> = generics
56 .params
57 .iter()
58 .enumerate()
59 .filter_map(|(idx, p)| {
60 if let GenericParamKind::Type { .. } = p.kind {
61 Some((idx, p.span(), p.attrs().iter().any(|attr| attr.has_name(sym::pointee))))
62 } else {
63 None
64 }
65 })
66 .collect();
67
68 let pointee_param_idx = if type_params.is_empty() {
69 cx.dcx().emit_err(RequireOneGeneric { span });
71 return;
72 } else if type_params.len() == 1 {
73 type_params[0].0
75 } else {
76 let mut pointees = type_params
77 .iter()
78 .filter_map(|&(idx, span, is_pointee)| is_pointee.then_some((idx, span)));
79 match (pointees.next(), pointees.next()) {
80 (Some((idx, _span)), None) => idx,
81 (None, _) => {
82 cx.dcx().emit_err(RequireOnePointee { span });
83 return;
84 }
85 (Some((_, one)), Some((_, another))) => {
86 cx.dcx().emit_err(TooManyPointees { one, another });
87 return;
88 }
89 }
90 };
91
92 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());
94 let self_type = cx.ty_path(path);
95
96 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),];
99 {
101 let trait_path =
102 cx.path_all(span, true, ::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),
Ident::new(sym::CoercePointeeValidated, span)]))path!(span, core::marker::CoercePointeeValidated), ::alloc::vec::Vec::new()vec![]);
103 let trait_ref = cx.trait_ref(trait_path);
104 push(
105 cx.item(
106 span,
107 attrs.clone(),
108 ast::ItemKind::Impl(ast::Impl {
109 generics: Generics {
110 params: generics
111 .params
112 .iter()
113 .map(|p| match &p.kind {
114 GenericParamKind::Lifetime => {
115 cx.lifetime_param(p.span(), p.ident, p.bounds.clone())
116 }
117 GenericParamKind::Type { default: _ } => {
118 cx.typaram(p.span(), p.ident, p.bounds.clone(), None)
119 }
120 GenericParamKind::Const { ty, span: _, default: _ } => cx
121 .const_param(
122 p.span(),
123 p.ident,
124 p.bounds.clone(),
125 ty.clone(),
126 None,
127 ),
128 })
129 .collect(),
130 where_clause: generics.where_clause.clone(),
131 span: generics.span,
132 },
133 of_trait: Some(Box::new(ast::TraitImplHeader {
134 safety: ast::Safety::Default,
135 polarity: ast::ImplPolarity::Positive,
136 defaultness: ast::Defaultness::Implicit,
137 trait_ref,
138 })),
139 constness: ast::Const::No,
140 self_ty: self_type.clone(),
141 items: ThinVec::new(),
142 }),
143 ),
144 );
145 }
146 let mut add_impl_block = |generics, trait_symbol, trait_args| {
147 let mut 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::ops, span)]))path!(span, core::ops);
148 parts.push(Ident::new(trait_symbol, span));
149 let trait_path = cx.path_all(span, true, parts, trait_args);
150 let trait_ref = cx.trait_ref(trait_path);
151 let item = cx.item(
152 span,
153 attrs.clone(),
154 ast::ItemKind::Impl(ast::Impl {
155 generics,
156 of_trait: Some(Box::new(ast::TraitImplHeader {
157 safety: ast::Safety::Default,
158 polarity: ast::ImplPolarity::Positive,
159 defaultness: ast::Defaultness::Implicit,
160 trait_ref,
161 })),
162 constness: ast::Const::No,
163 self_ty: self_type.clone(),
164 items: ThinVec::new(),
165 }),
166 );
167 push(item);
168 };
169
170 let s_ty = cx.ty_ident(span, Ident::new(sym::__S, span));
173 let mut alt_self_params = self_params;
174 alt_self_params[pointee_param_idx] = GenericArg::Type(s_ty.clone());
175 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));
176
177 let mut impl_generics = generics.clone();
181 let pointee_ty_ident = generics.params[pointee_param_idx].ident;
182 let mut self_bounds;
183 {
184 let pointee = &mut impl_generics.params[pointee_param_idx];
185 self_bounds = pointee.bounds.clone();
186 if !contains_maybe_sized_bound(&self_bounds)
187 && !contains_maybe_sized_bound_on_pointee(
188 &generics.where_clause.predicates,
189 pointee_ty_ident.name,
190 )
191 {
192 cx.dcx().emit_err(RequiresMaybeSized {
193 span: pointee_ty_ident.span,
194 name: pointee_ty_ident,
195 });
196 return;
197 }
198 let arg = GenericArg::Type(s_ty.clone());
199 let unsize = cx.path_all(span, true, ::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),
Ident::new(sym::Unsize, span)]))path!(span, core::marker::Unsize), ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[arg]))vec![arg]);
200 pointee.bounds.push(cx.trait_bound(unsize, false));
201 pointee.attrs.retain(|attr| !attr.has_name(sym::pointee));
203 }
204
205 for (idx, (params, orig_params)) in
225 impl_generics.params.iter_mut().zip(&generics.params).enumerate()
226 {
227 match &mut params.kind {
230 ast::GenericParamKind::Const { default, .. } => *default = None,
231 ast::GenericParamKind::Type { default } => *default = None,
232 ast::GenericParamKind::Lifetime => {}
233 }
234 if idx != pointee_param_idx {
239 for bound in &orig_params.bounds {
240 let mut bound = bound.clone();
241 let mut substitution = TypeSubstitution {
242 from_name: pointee_ty_ident.name,
243 to_ty: &s_ty,
244 rewritten: false,
245 };
246 substitution.visit_param_bound(&mut bound, BoundKind::Bound);
247 if substitution.rewritten {
248 params.bounds.push(bound);
251 }
252 }
253 }
254 }
255
256 {
261 let mut substitution =
262 TypeSubstitution { from_name: pointee_ty_ident.name, to_ty: &s_ty, rewritten: false };
263 for bound in &mut self_bounds {
264 substitution.visit_param_bound(bound, BoundKind::Bound);
265 }
266 }
267
268 for predicate in &generics.where_clause.predicates {
295 if let ast::WherePredicateKind::BoundPredicate(bound) = &predicate.kind {
296 let mut substitution = TypeSubstitution {
297 from_name: pointee_ty_ident.name,
298 to_ty: &s_ty,
299 rewritten: false,
300 };
301 let mut kind = ast::WherePredicateKind::BoundPredicate(bound.clone());
302 substitution.visit_where_predicate_kind(&mut kind);
303 if substitution.rewritten {
304 let predicate = ast::WherePredicate {
305 attrs: predicate.attrs.clone(),
306 kind,
307 span: predicate.span,
308 id: ast::DUMMY_NODE_ID,
309 is_placeholder: false,
310 };
311 impl_generics.where_clause.predicates.push(predicate);
312 }
313 }
314 }
315
316 let extra_param = cx.typaram(span, Ident::new(sym::__S, span), self_bounds, None);
317 impl_generics.params.insert(pointee_param_idx + 1, extra_param);
318
319 let gen_args = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[GenericArg::Type(alt_self_type)]))vec![GenericArg::Type(alt_self_type)];
321 add_impl_block(impl_generics.clone(), sym::DispatchFromDyn, gen_args.clone());
322 add_impl_block(impl_generics, sym::CoerceUnsized, gen_args);
323}
324
325fn contains_maybe_sized_bound_on_pointee(predicates: &[WherePredicate], pointee: Symbol) -> bool {
326 for bound in predicates {
327 if let ast::WherePredicateKind::BoundPredicate(bound) = &bound.kind
328 && bound.bounded_ty.kind.is_simple_path().is_some_and(|name| name == pointee)
329 {
330 if contains_maybe_sized_bound(&bound.bounds) {
331 return true;
332 }
333 }
334 }
335 false
336}
337
338fn is_maybe_sized_bound(bound: &GenericBound) -> bool {
339 if let GenericBound::Trait(trait_ref) = bound
340 && let TraitBoundModifiers { polarity: ast::BoundPolarity::Maybe(_), .. } =
341 trait_ref.modifiers
342 && is_sized_marker(&trait_ref.trait_ref.path)
343 {
344 true
345 } else {
346 false
347 }
348}
349
350fn contains_maybe_sized_bound(bounds: &[GenericBound]) -> bool {
351 bounds.iter().any(is_maybe_sized_bound)
352}
353
354fn is_sized_marker(path: &ast::Path) -> bool {
355 const CORE_UNSIZE: [Symbol; 3] = [sym::core, sym::marker, sym::Sized];
356 const STD_UNSIZE: [Symbol; 3] = [sym::std, sym::marker, sym::Sized];
357 let segments = || path.segments.iter().map(|segment| segment.ident.name);
358 if path.is_global() {
359 segments().skip(1).eq(CORE_UNSIZE) || segments().skip(1).eq(STD_UNSIZE)
360 } else {
361 segments().eq(CORE_UNSIZE) || segments().eq(STD_UNSIZE) || *path == sym::Sized
362 }
363}
364
365struct TypeSubstitution<'a> {
366 from_name: Symbol,
367 to_ty: &'a ast::Ty,
368 rewritten: bool,
369}
370
371impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> {
372 fn visit_ty(&mut self, ty: &mut ast::Ty) {
373 if let Some(name) = ty.kind.is_simple_path()
374 && name == self.from_name
375 {
376 *ty = self.to_ty.clone();
377 self.rewritten = true;
378 } else {
379 ast::mut_visit::walk_ty(self, ty);
380 }
381 }
382
383 fn visit_where_predicate_kind(&mut self, kind: &mut ast::WherePredicateKind) {
384 match kind {
385 rustc_ast::WherePredicateKind::BoundPredicate(bound) => {
386 bound
387 .bound_generic_params
388 .flat_map_in_place(|param| self.flat_map_generic_param(param));
389 self.visit_ty(&mut bound.bounded_ty);
390 for bound in &mut bound.bounds {
391 self.visit_param_bound(bound, BoundKind::Bound)
392 }
393 }
394 rustc_ast::WherePredicateKind::RegionPredicate(_) => {}
395 }
396 }
397}
398
399struct DetectNonGenericPointeeAttr<'a, 'b> {
400 cx: &'a ExtCtxt<'b>,
401}
402
403impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonGenericPointeeAttr<'a, 'b> {
404 fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) -> Self::Result {
405 if attr.has_name(sym::pointee) {
406 self.cx.dcx().emit_err(diagnostics::NonGenericPointee { span: attr.span });
407 }
408 }
409
410 fn visit_generic_param(&mut self, param: &'a rustc_ast::GenericParam) -> Self::Result {
411 let mut error_on_pointee = AlwaysErrorOnGenericParam { cx: self.cx };
412
413 match ¶m.kind {
414 GenericParamKind::Type { default } => {
415 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);
433 }
434
435 GenericParamKind::Const { .. } | GenericParamKind::Lifetime => {
436 rustc_ast::visit::walk_generic_param(&mut error_on_pointee, param);
437 }
438 }
439 }
440
441 fn visit_ty(&mut self, t: &'a rustc_ast::Ty) -> Self::Result {
442 let mut error_on_pointee = AlwaysErrorOnGenericParam { cx: self.cx };
443 error_on_pointee.visit_ty(t)
444 }
445}
446
447struct AlwaysErrorOnGenericParam<'a, 'b> {
448 cx: &'a ExtCtxt<'b>,
449}
450
451impl<'a, 'b> rustc_ast::visit::Visitor<'a> for AlwaysErrorOnGenericParam<'a, 'b> {
452 fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) -> Self::Result {
453 if attr.has_name(sym::pointee) {
454 self.cx.dcx().emit_err(diagnostics::NonGenericPointee { span: attr.span });
455 }
456 }
457}
458
459#[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)]
460#[diag("`CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]`", code = E0802)]
461struct RequireTransparent {
462 #[primary_span]
463 span: Span,
464}
465
466#[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)]
467#[diag("`CoercePointee` can only be derived on `struct`s with at least one field", code = E0802)]
468struct RequireOneField {
469 #[primary_span]
470 span: Span,
471}
472
473#[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)]
474#[diag("`CoercePointee` can only be derived on `struct`s that are generic over at least one type", code = E0802)]
475struct RequireOneGeneric {
476 #[primary_span]
477 span: Span,
478}
479
480#[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)]
481#[diag("exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits", code = E0802)]
482struct RequireOnePointee {
483 #[primary_span]
484 span: Span,
485}
486
487#[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)]
488#[diag("only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits", code = E0802)]
489struct TooManyPointees {
490 #[primary_span]
491 one: Span,
492 #[label("here another type parameter is marked as `#[pointee]`")]
493 another: Span,
494}
495
496#[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)]
497#[diag("`derive(CoercePointee)` requires `{$name}` to be marked `?Sized`", code = E0802)]
498struct RequiresMaybeSized {
499 #[primary_span]
500 span: Span,
501 name: Ident,
502}