rustc_builtin_macros/deriving/
coerce_pointee.rs1use ast::HasAttrs;
2use ast::ptr::P;
3use rustc_ast::mut_visit::MutVisitor;
4use rustc_ast::visit::BoundKind;
5use rustc_ast::{
6 self as ast, GenericArg, GenericBound, GenericParamKind, Generics, ItemKind, MetaItem,
7 TraitBoundModifiers, VariantData, WherePredicate,
8};
9use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
10use rustc_errors::E0802;
11use rustc_expand::base::{Annotatable, ExtCtxt};
12use rustc_macros::Diagnostic;
13use rustc_span::{Ident, Span, Symbol, sym};
14use thin_vec::{ThinVec, thin_vec};
15
16use crate::errors;
17
18macro_rules! path {
19 ($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] }
20}
21
22pub(crate) fn expand_deriving_coerce_pointee(
23 cx: &ExtCtxt<'_>,
24 span: Span,
25 _mitem: &MetaItem,
26 item: &Annotatable,
27 push: &mut dyn FnMut(Annotatable),
28 _is_const: bool,
29) {
30 item.visit_with(&mut DetectNonGenericPointeeAttr { cx });
31
32 let (name_ident, generics) = if let Annotatable::Item(aitem) = item
33 && let ItemKind::Struct(ident, g, struct_data) = &aitem.kind
34 {
35 if !matches!(
36 struct_data,
37 VariantData::Struct { fields, recovered: _ } | VariantData::Tuple(fields, _)
38 if !fields.is_empty())
39 {
40 cx.dcx().emit_err(RequireOneField { span });
41 return;
42 }
43 (*ident, g)
44 } else {
45 cx.dcx().emit_err(RequireTransparent { span });
46 return;
47 };
48
49 let self_params: Vec<_> = generics
51 .params
52 .iter()
53 .map(|p| match p.kind {
54 GenericParamKind::Lifetime => GenericArg::Lifetime(cx.lifetime(p.span(), p.ident)),
55 GenericParamKind::Type { .. } => GenericArg::Type(cx.ty_ident(p.span(), p.ident)),
56 GenericParamKind::Const { .. } => GenericArg::Const(cx.const_ident(p.span(), p.ident)),
57 })
58 .collect();
59 let type_params: Vec<_> = generics
60 .params
61 .iter()
62 .enumerate()
63 .filter_map(|(idx, p)| {
64 if let GenericParamKind::Type { .. } = p.kind {
65 Some((idx, p.span(), p.attrs().iter().any(|attr| attr.has_name(sym::pointee))))
66 } else {
67 None
68 }
69 })
70 .collect();
71
72 let pointee_param_idx = if type_params.is_empty() {
73 cx.dcx().emit_err(RequireOneGeneric { span });
75 return;
76 } else if type_params.len() == 1 {
77 type_params[0].0
79 } else {
80 let mut pointees = type_params
81 .iter()
82 .filter_map(|&(idx, span, is_pointee)| is_pointee.then_some((idx, span)));
83 match (pointees.next(), pointees.next()) {
84 (Some((idx, _span)), None) => idx,
85 (None, _) => {
86 cx.dcx().emit_err(RequireOnePointee { span });
87 return;
88 }
89 (Some((_, one)), Some((_, another))) => {
90 cx.dcx().emit_err(TooManyPointees { one, another });
91 return;
92 }
93 }
94 };
95
96 let path = cx.path_all(span, false, vec![name_ident], self_params.clone());
98 let self_type = cx.ty_path(path);
99
100 let attrs = thin_vec![cx.attr_word(sym::automatically_derived, span),];
103 {
105 let trait_path =
106 cx.path_all(span, true, path!(span, core::marker::CoercePointeeValidated), vec![]);
107 let trait_ref = cx.trait_ref(trait_path);
108 push(Annotatable::Item(
109 cx.item(
110 span,
111 attrs.clone(),
112 ast::ItemKind::Impl(Box::new(ast::Impl {
113 safety: ast::Safety::Default,
114 polarity: ast::ImplPolarity::Positive,
115 defaultness: ast::Defaultness::Final,
116 constness: ast::Const::No,
117 generics: Generics {
118 params: generics
119 .params
120 .iter()
121 .map(|p| match &p.kind {
122 GenericParamKind::Lifetime => {
123 cx.lifetime_param(p.span(), p.ident, p.bounds.clone())
124 }
125 GenericParamKind::Type { default: _ } => {
126 cx.typaram(p.span(), p.ident, p.bounds.clone(), None)
127 }
128 GenericParamKind::Const { ty, kw_span: _, default: _ } => cx
129 .const_param(
130 p.span(),
131 p.ident,
132 p.bounds.clone(),
133 ty.clone(),
134 None,
135 ),
136 })
137 .collect(),
138 where_clause: generics.where_clause.clone(),
139 span: generics.span,
140 },
141 of_trait: Some(trait_ref),
142 self_ty: self_type.clone(),
143 items: ThinVec::new(),
144 })),
145 ),
146 ));
147 }
148 let mut add_impl_block = |generics, trait_symbol, trait_args| {
149 let mut parts = path!(span, core::ops);
150 parts.push(Ident::new(trait_symbol, span));
151 let trait_path = cx.path_all(span, true, parts, trait_args);
152 let trait_ref = cx.trait_ref(trait_path);
153 let item = cx.item(
154 span,
155 attrs.clone(),
156 ast::ItemKind::Impl(Box::new(ast::Impl {
157 safety: ast::Safety::Default,
158 polarity: ast::ImplPolarity::Positive,
159 defaultness: ast::Defaultness::Final,
160 constness: ast::Const::No,
161 generics,
162 of_trait: Some(trait_ref),
163 self_ty: self_type.clone(),
164 items: ThinVec::new(),
165 })),
166 );
167 push(Annotatable::Item(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, 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, path!(span, core::marker::Unsize), 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 = vec![GenericArg::Type(alt_self_type)];
321 add_impl_block(impl_generics.clone(), sym::DispatchFromDyn, gen_args.clone());
322 add_impl_block(impl_generics.clone(), 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 for bound in &bound.bounds {
331 if is_maybe_sized_bound(bound) {
332 return true;
333 }
334 }
335 }
336 }
337 false
338}
339
340fn is_maybe_sized_bound(bound: &GenericBound) -> bool {
341 if let GenericBound::Trait(trait_ref) = bound
342 && let TraitBoundModifiers { polarity: ast::BoundPolarity::Maybe(_), .. } =
343 trait_ref.modifiers
344 && is_sized_marker(&trait_ref.trait_ref.path)
345 {
346 true
347 } else {
348 false
349 }
350}
351
352fn contains_maybe_sized_bound(bounds: &[GenericBound]) -> bool {
353 bounds.iter().any(is_maybe_sized_bound)
354}
355
356fn path_segment_is_exact_match(path_segments: &[ast::PathSegment], syms: &[Symbol]) -> bool {
357 path_segments.iter().zip(syms).all(|(segment, &symbol)| segment.ident.name == symbol)
358}
359
360fn is_sized_marker(path: &ast::Path) -> bool {
361 const CORE_UNSIZE: [Symbol; 3] = [sym::core, sym::marker, sym::Sized];
362 const STD_UNSIZE: [Symbol; 3] = [sym::std, sym::marker, sym::Sized];
363 if path.segments.len() == 4 && path.is_global() {
364 path_segment_is_exact_match(&path.segments[1..], &CORE_UNSIZE)
365 || path_segment_is_exact_match(&path.segments[1..], &STD_UNSIZE)
366 } else if path.segments.len() == 3 {
367 path_segment_is_exact_match(&path.segments, &CORE_UNSIZE)
368 || path_segment_is_exact_match(&path.segments, &STD_UNSIZE)
369 } else {
370 *path == sym::Sized
371 }
372}
373
374struct TypeSubstitution<'a> {
375 from_name: Symbol,
376 to_ty: &'a ast::Ty,
377 rewritten: bool,
378}
379
380impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> {
381 fn visit_ty(&mut self, ty: &mut P<ast::Ty>) {
382 if let Some(name) = ty.kind.is_simple_path()
383 && name == self.from_name
384 {
385 **ty = self.to_ty.clone();
386 self.rewritten = true;
387 } else {
388 ast::mut_visit::walk_ty(self, ty);
389 }
390 }
391
392 fn visit_where_predicate_kind(&mut self, kind: &mut ast::WherePredicateKind) {
393 match kind {
394 rustc_ast::WherePredicateKind::BoundPredicate(bound) => {
395 bound
396 .bound_generic_params
397 .flat_map_in_place(|param| self.flat_map_generic_param(param));
398 self.visit_ty(&mut bound.bounded_ty);
399 for bound in &mut bound.bounds {
400 self.visit_param_bound(bound, BoundKind::Bound)
401 }
402 }
403 rustc_ast::WherePredicateKind::RegionPredicate(_)
404 | rustc_ast::WherePredicateKind::EqPredicate(_) => {}
405 }
406 }
407}
408
409struct DetectNonGenericPointeeAttr<'a, 'b> {
410 cx: &'a ExtCtxt<'b>,
411}
412
413impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonGenericPointeeAttr<'a, 'b> {
414 fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) -> Self::Result {
415 if attr.has_name(sym::pointee) {
416 self.cx.dcx().emit_err(errors::NonGenericPointee { span: attr.span });
417 }
418 }
419
420 fn visit_generic_param(&mut self, param: &'a rustc_ast::GenericParam) -> Self::Result {
421 let mut error_on_pointee = AlwaysErrorOnGenericParam { cx: self.cx };
422
423 match ¶m.kind {
424 GenericParamKind::Type { default } => {
425 rustc_ast::visit::visit_opt!(error_on_pointee, visit_ty, default);
443 }
444
445 GenericParamKind::Const { .. } | GenericParamKind::Lifetime => {
446 rustc_ast::visit::walk_generic_param(&mut error_on_pointee, param);
447 }
448 }
449 }
450
451 fn visit_ty(&mut self, t: &'a rustc_ast::Ty) -> Self::Result {
452 let mut error_on_pointee = AlwaysErrorOnGenericParam { cx: self.cx };
453 error_on_pointee.visit_ty(t)
454 }
455}
456
457struct AlwaysErrorOnGenericParam<'a, 'b> {
458 cx: &'a ExtCtxt<'b>,
459}
460
461impl<'a, 'b> rustc_ast::visit::Visitor<'a> for AlwaysErrorOnGenericParam<'a, 'b> {
462 fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) -> Self::Result {
463 if attr.has_name(sym::pointee) {
464 self.cx.dcx().emit_err(errors::NonGenericPointee { span: attr.span });
465 }
466 }
467}
468
469#[derive(Diagnostic)]
470#[diag(builtin_macros_coerce_pointee_requires_transparent, code = E0802)]
471struct RequireTransparent {
472 #[primary_span]
473 span: Span,
474}
475
476#[derive(Diagnostic)]
477#[diag(builtin_macros_coerce_pointee_requires_one_field, code = E0802)]
478struct RequireOneField {
479 #[primary_span]
480 span: Span,
481}
482
483#[derive(Diagnostic)]
484#[diag(builtin_macros_coerce_pointee_requires_one_generic, code = E0802)]
485struct RequireOneGeneric {
486 #[primary_span]
487 span: Span,
488}
489
490#[derive(Diagnostic)]
491#[diag(builtin_macros_coerce_pointee_requires_one_pointee, code = E0802)]
492struct RequireOnePointee {
493 #[primary_span]
494 span: Span,
495}
496
497#[derive(Diagnostic)]
498#[diag(builtin_macros_coerce_pointee_too_many_pointees, code = E0802)]
499struct TooManyPointees {
500 #[primary_span]
501 one: Span,
502 #[label]
503 another: Span,
504}
505
506#[derive(Diagnostic)]
507#[diag(builtin_macros_coerce_pointee_requires_maybe_sized, code = E0802)]
508struct RequiresMaybeSized {
509 #[primary_span]
510 span: Span,
511 name: Ident,
512}