Skip to content

Commit 9d89276

Browse files
introduce CoercePointeeWellformed for coherence checks at typeck stage
1 parent 01a26c0 commit 9d89276

File tree

15 files changed

+337
-36
lines changed

15 files changed

+337
-36
lines changed

compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs

+55-9
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ use ast::ptr::P;
33
use rustc_ast::mut_visit::MutVisitor;
44
use rustc_ast::visit::BoundKind;
55
use rustc_ast::{
6-
self as ast, GenericArg, GenericBound, GenericParamKind, ItemKind, MetaItem,
6+
self as ast, GenericArg, GenericBound, GenericParamKind, Generics, ItemKind, MetaItem,
77
TraitBoundModifiers, VariantData, WherePredicate,
88
};
99
use rustc_attr_parsing as attr;
1010
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
11+
use rustc_errors::E0802;
1112
use rustc_expand::base::{Annotatable, ExtCtxt};
1213
use rustc_macros::Diagnostic;
1314
use rustc_span::{Ident, Span, Symbol, sym};
@@ -88,8 +89,7 @@ pub(crate) fn expand_deriving_coerce_pointee(
8889
} else {
8990
let mut pointees = type_params
9091
.iter()
91-
.filter_map(|&(idx, span, is_pointee)| is_pointee.then_some((idx, span)))
92-
.fuse();
92+
.filter_map(|&(idx, span, is_pointee)| is_pointee.then_some((idx, span)));
9393
match (pointees.next(), pointees.next()) {
9494
(Some((idx, _span)), None) => idx,
9595
(None, _) => {
@@ -110,6 +110,52 @@ pub(crate) fn expand_deriving_coerce_pointee(
110110
// Declare helper function that adds implementation blocks.
111111
// FIXME(dingxiangfei2009): Investigate the set of attributes on target struct to be propagated to impls
112112
let attrs = thin_vec![cx.attr_word(sym::automatically_derived, span),];
113+
// # Wellformed-ness assertion
114+
{
115+
let trait_path =
116+
cx.path_all(span, true, path!(span, core::marker::CoercePointeeWellformed), vec![]);
117+
let trait_ref = cx.trait_ref(trait_path);
118+
push(Annotatable::Item(
119+
cx.item(
120+
span,
121+
Ident::empty(),
122+
attrs.clone(),
123+
ast::ItemKind::Impl(Box::new(ast::Impl {
124+
safety: ast::Safety::Default,
125+
polarity: ast::ImplPolarity::Positive,
126+
defaultness: ast::Defaultness::Final,
127+
constness: ast::Const::No,
128+
generics: Generics {
129+
params: generics
130+
.params
131+
.iter()
132+
.map(|p| match &p.kind {
133+
GenericParamKind::Lifetime => {
134+
cx.lifetime_param(p.span(), p.ident, p.bounds.clone())
135+
}
136+
GenericParamKind::Type { default: _ } => {
137+
cx.typaram(p.span(), p.ident, p.bounds.clone(), None)
138+
}
139+
GenericParamKind::Const { ty, kw_span: _, default: _ } => cx
140+
.const_param(
141+
p.span(),
142+
p.ident,
143+
p.bounds.clone(),
144+
ty.clone(),
145+
None,
146+
),
147+
})
148+
.collect(),
149+
where_clause: generics.where_clause.clone(),
150+
span: generics.span,
151+
},
152+
of_trait: Some(trait_ref),
153+
self_ty: self_type.clone(),
154+
items: ThinVec::new(),
155+
})),
156+
),
157+
));
158+
}
113159
let mut add_impl_block = |generics, trait_symbol, trait_args| {
114160
let mut parts = path!(span, core::ops);
115161
parts.push(Ident::new(trait_symbol, span));
@@ -430,35 +476,35 @@ impl<'a, 'b> rustc_ast::visit::Visitor<'a> for AlwaysErrorOnGenericParam<'a, 'b>
430476
}
431477

432478
#[derive(Diagnostic)]
433-
#[diag(builtin_macros_coerce_pointee_requires_transparent)]
479+
#[diag(builtin_macros_coerce_pointee_requires_transparent, code = E0802)]
434480
struct RequireTransparent {
435481
#[primary_span]
436482
span: Span,
437483
}
438484

439485
#[derive(Diagnostic)]
440-
#[diag(builtin_macros_coerce_pointee_requires_one_field)]
486+
#[diag(builtin_macros_coerce_pointee_requires_one_field, code = E0802)]
441487
struct RequireOneField {
442488
#[primary_span]
443489
span: Span,
444490
}
445491

446492
#[derive(Diagnostic)]
447-
#[diag(builtin_macros_coerce_pointee_requires_one_generic)]
493+
#[diag(builtin_macros_coerce_pointee_requires_one_generic, code = E0802)]
448494
struct RequireOneGeneric {
449495
#[primary_span]
450496
span: Span,
451497
}
452498

453499
#[derive(Diagnostic)]
454-
#[diag(builtin_macros_coerce_pointee_requires_one_pointee)]
500+
#[diag(builtin_macros_coerce_pointee_requires_one_pointee, code = E0802)]
455501
struct RequireOnePointee {
456502
#[primary_span]
457503
span: Span,
458504
}
459505

460506
#[derive(Diagnostic)]
461-
#[diag(builtin_macros_coerce_pointee_too_many_pointees)]
507+
#[diag(builtin_macros_coerce_pointee_too_many_pointees, code = E0802)]
462508
struct TooManyPointees {
463509
#[primary_span]
464510
one: Span,
@@ -467,7 +513,7 @@ struct TooManyPointees {
467513
}
468514

469515
#[derive(Diagnostic)]
470-
#[diag(builtin_macros_coerce_pointee_requires_maybe_sized)]
516+
#[diag(builtin_macros_coerce_pointee_requires_maybe_sized, code = E0802)]
471517
struct RequiresMaybeSized {
472518
#[primary_span]
473519
span: Span,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
The target of `derive(CoercePointee)` macro has inadmissible specification for
2+
a meaningful use.
3+
4+
Erroneous code examples:
5+
6+
The target data is not a `struct`.
7+
8+
```compile_fail,E0802
9+
#[derive(CoercePointee)]
10+
enum NotStruct<'a, T: ?Sized> {
11+
Variant(&'a T),
12+
}
13+
```
14+
15+
The target data has a layout that is not transparent, or `repr(transparent)`
16+
in other words.
17+
18+
```compile_fail,E0802
19+
#[derive(CoercePointee)]
20+
struct NotTransparent<'a, #[pointee] T: ?Sized> {
21+
ptr: &'a T,
22+
}
23+
```
24+
25+
The target data has no data field.
26+
27+
```compile_fail,E0802
28+
#[derive(CoercePointee)]
29+
#[repr(transparent)]
30+
struct NoField<'a, #[pointee] T: ?Sized> {}
31+
```
32+
33+
The target data is not generic over any data, or has no generic type parameter.
34+
35+
```compile_fail,E0802
36+
#[derive(CoercePointee)]
37+
#[repr(transparent)]
38+
struct NoGeneric<'a>(&'a u8);
39+
```
40+
41+
The target data has multiple generic type parameters, but none is designated as
42+
a pointee for coercion.
43+
44+
```compile_fail,E0802
45+
#[derive(CoercePointee)]
46+
#[repr(transparent)]
47+
struct AmbiguousPointee<'a, T1: ?Sized, T2: ?Sized> {
48+
a: (&'a T1, &'a T2),
49+
}
50+
```
51+
52+
The target data has multiple generic type parameters that are designated as
53+
pointees for coercion.
54+
55+
```compile_fail,E0802
56+
#[derive(CoercePointee)]
57+
#[repr(transparent)]
58+
struct TooManyPointees<
59+
'a,
60+
#[pointee] A: ?Sized,
61+
#[pointee] B: ?Sized>
62+
((&'a A, &'a B));
63+
```
64+
65+
The type parameter that is designated as a pointee is not marked `?Sized`.
66+
67+
```compile_fail,E0802
68+
#[derive(CoercePointee)]
69+
#[repr(transparent)]
70+
struct NoMaybeSized<'a, #[pointee] T> {
71+
ptr: &'a T,
72+
}
73+
```
74+
75+
In summary, the `CoercePointee` macro demands the type to be a `struct` that is
76+
generic over at least one type or over more types, one of which is marked with
77+
`#[pointee]`, and has at least one data field and adopts a `repr(transparent)`
78+
layout.
79+
The only generic type or the type marked with `#[pointee]` has to be also
80+
marked as `?Sized`.

compiler/rustc_error_codes/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,7 @@ E0798: 0798,
545545
E0799: 0799,
546546
E0800: 0800,
547547
E0801: 0801,
548+
E0802: 0802,
548549
);
549550
)
550551
}

compiler/rustc_expand/src/build.rs

+38-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use rustc_ast::ptr::P;
22
use rustc_ast::util::literal;
33
use rustc_ast::{
4-
self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp, attr, token,
4+
self as ast, AnonConst, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp,
5+
attr, token,
56
};
67
use rustc_span::source_map::Spanned;
78
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
@@ -138,6 +139,42 @@ impl<'a> ExtCtxt<'a> {
138139
}
139140
}
140141

142+
pub fn lifetime_param(
143+
&self,
144+
span: Span,
145+
ident: Ident,
146+
bounds: ast::GenericBounds,
147+
) -> ast::GenericParam {
148+
ast::GenericParam {
149+
id: ast::DUMMY_NODE_ID,
150+
ident: ident.with_span_pos(span),
151+
attrs: AttrVec::new(),
152+
bounds,
153+
is_placeholder: false,
154+
kind: ast::GenericParamKind::Lifetime,
155+
colon_span: None,
156+
}
157+
}
158+
159+
pub fn const_param(
160+
&self,
161+
span: Span,
162+
ident: Ident,
163+
bounds: ast::GenericBounds,
164+
ty: P<ast::Ty>,
165+
default: Option<AnonConst>,
166+
) -> ast::GenericParam {
167+
ast::GenericParam {
168+
id: ast::DUMMY_NODE_ID,
169+
ident: ident.with_span_pos(span),
170+
attrs: AttrVec::new(),
171+
bounds,
172+
is_placeholder: false,
173+
kind: ast::GenericParamKind::Const { ty, kw_span: DUMMY_SP, default },
174+
colon_span: None,
175+
}
176+
}
177+
141178
pub fn trait_ref(&self, path: ast::Path) -> ast::TraitRef {
142179
ast::TraitRef { path, ref_id: ast::DUMMY_NODE_ID }
143180
}

compiler/rustc_hir/src/lang_items.rs

+6
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,8 @@ language_item_table! {
369369

370370
PointerLike, sym::pointer_like, pointer_like, Target::Trait, GenericRequirement::Exact(0);
371371

372+
CoercePointeeWellformed, sym::coerce_pointee_wellformed, coerce_pointee_wellformed_trait, Target::Trait, GenericRequirement::Exact(0);
373+
372374
ConstParamTy, sym::const_param_ty, const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0);
373375
UnsizedConstParamTy, sym::unsized_const_param_ty, unsized_const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0);
374376

@@ -419,9 +421,13 @@ language_item_table! {
419421
CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
420422
}
421423

424+
/// The requirement imposed on the generics of a lang item
422425
pub enum GenericRequirement {
426+
/// No restriction on the generics
423427
None,
428+
/// A minimum number of generics that is demanded on a lang item
424429
Minimum(usize),
430+
/// The number of generics must match precisely as stipulated
425431
Exact(usize),
426432
}
427433

compiler/rustc_hir_analysis/messages.ftl

+6
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ hir_analysis_cmse_output_stack_spill =
8585
.note1 = functions with the `"{$abi_name}"` ABI must pass their result via the available return registers
8686
.note2 = the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
8787
88+
hir_analysis_coerce_pointee_not_concrete_ty = `derive(CoercePointee)` is only applicable to `struct`
89+
90+
hir_analysis_coerce_pointee_not_struct = `derive(CoercePointee)` is only applicable to `struct`, instead of `{$kind}`
91+
92+
hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout
93+
8894
hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures
8995
9096
hir_analysis_coerce_unsized_multi = implementing the trait `CoerceUnsized` requires multiple coercions

compiler/rustc_hir_analysis/src/coherence/builtin.rs

+28
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ pub(super) fn check_trait<'tcx>(
4949
checker
5050
.check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn)?;
5151
checker.check(lang_items.pointer_like(), visit_implementation_of_pointer_like)?;
52+
checker.check(
53+
lang_items.coerce_pointee_wellformed_trait(),
54+
visit_implementation_of_coerce_pointee_wellformed,
55+
)?;
5256
Ok(())
5357
}
5458

@@ -790,3 +794,27 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err
790794
.with_note(why_disqualified)
791795
.emit())
792796
}
797+
798+
fn visit_implementation_of_coerce_pointee_wellformed(
799+
checker: &Checker<'_>,
800+
) -> Result<(), ErrorGuaranteed> {
801+
let tcx = checker.tcx;
802+
let self_ty = tcx.impl_trait_ref(checker.impl_def_id).unwrap().instantiate_identity().self_ty();
803+
let ty::Adt(def, _args) = self_ty.kind() else {
804+
return Err(tcx.dcx().emit_err(errors::CoercePointeeNotConcreteType {
805+
span: tcx.def_span(checker.impl_def_id),
806+
}));
807+
};
808+
let did = def.did();
809+
let span =
810+
if let Some(local) = did.as_local() { tcx.source_span(local) } else { tcx.def_span(did) };
811+
if !def.is_struct() {
812+
return Err(tcx
813+
.dcx()
814+
.emit_err(errors::CoercePointeeNotStruct { span, kind: def.descr().into() }));
815+
}
816+
if !def.repr().transparent() {
817+
return Err(tcx.dcx().emit_err(errors::CoercePointeeNotTransparent { span }));
818+
}
819+
Ok(())
820+
}

compiler/rustc_hir_analysis/src/errors.rs

+22
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,28 @@ pub(crate) struct DispatchFromDynRepr {
11801180
pub span: Span,
11811181
}
11821182

1183+
#[derive(Diagnostic)]
1184+
#[diag(hir_analysis_coerce_pointee_not_struct, code = E0802)]
1185+
pub(crate) struct CoercePointeeNotStruct {
1186+
#[primary_span]
1187+
pub span: Span,
1188+
pub kind: String,
1189+
}
1190+
1191+
#[derive(Diagnostic)]
1192+
#[diag(hir_analysis_coerce_pointee_not_concrete_ty, code = E0802)]
1193+
pub(crate) struct CoercePointeeNotConcreteType {
1194+
#[primary_span]
1195+
pub span: Span,
1196+
}
1197+
1198+
#[derive(Diagnostic)]
1199+
#[diag(hir_analysis_coerce_pointee_not_transparent, code = E0802)]
1200+
pub(crate) struct CoercePointeeNotTransparent {
1201+
#[primary_span]
1202+
pub span: Span,
1203+
}
1204+
11831205
#[derive(Diagnostic)]
11841206
#[diag(hir_analysis_inherent_ty_outside_relevant, code = E0390)]
11851207
#[help]

compiler/rustc_span/src/symbol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ symbols! {
191191
Cleanup,
192192
Clone,
193193
CoercePointee,
194+
CoercePointeeWellformed,
194195
CoerceUnsized,
195196
Command,
196197
ConstParamTy,
@@ -617,6 +618,7 @@ symbols! {
617618
cmp_partialord_lt,
618619
cmpxchg16b_target_feature,
619620
cmse_nonsecure_entry,
621+
coerce_pointee_wellformed,
620622
coerce_unsized,
621623
cold,
622624
cold_path,

library/core/src/marker.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1091,3 +1091,9 @@ pub trait FnPtr: Copy + Clone {
10911091
pub macro CoercePointee($item:item) {
10921092
/* compiler built-in */
10931093
}
1094+
1095+
#[cfg(not(bootstrap))]
1096+
#[lang = "coerce_pointee_wellformed"]
1097+
#[unstable(feature = "derive_coerce_pointee", issue = "123430")]
1098+
#[doc(hidden)]
1099+
pub trait CoercePointeeWellformed {}

0 commit comments

Comments
 (0)