Skip to content

Commit ebdf4b0

Browse files
authored
Rollup merge of rust-lang#136107 - dingxiangfei2009:coerce-pointee-wellformed, r=compiler-errors
Introduce CoercePointeeWellformed for coherence checks at typeck stage Fix rust-lang#135206 This is the first PR to introduce the "wellformedness" check for `derive(CoercePointee)`. This patch introduces a new error code to cover all the prerequisites of the said macro. The checks that is enforced with this patch is whether the data is indeed `struct` and whether the layout is set to `repr(transparent)`. A following series of patch will arrive later to address the following concern. 1. rust-lang#135217 so that we would only admit one single coercion on one type parameter, and leave the rest for future consideration in tandem of development of other coercion rules. 1. Enforcement of data field requirements. **An open question** is whether there is a good schema to encode the `#[pointee]` as well, so that we could also check if the `#[pointee]` type parameter is indeed `?Sized`. `@rustbot` label F-derive_coerce_pointee
2 parents 4bb6ec0 + 17026e2 commit ebdf4b0

File tree

15 files changed

+385
-54
lines changed

15 files changed

+385
-54
lines changed

compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs

+55-19
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ 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
};
9-
use rustc_attr_parsing as attr;
109
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
10+
use rustc_errors::E0802;
1111
use rustc_expand::base::{Annotatable, ExtCtxt};
1212
use rustc_macros::Diagnostic;
1313
use rustc_span::{Ident, Span, Symbol, sym};
@@ -32,15 +32,6 @@ pub(crate) fn expand_deriving_coerce_pointee(
3232
let (name_ident, generics) = if let Annotatable::Item(aitem) = item
3333
&& let ItemKind::Struct(struct_data, g) = &aitem.kind
3434
{
35-
let is_transparent = aitem.attrs.iter().any(|attr| {
36-
attr::find_repr_attrs(cx.sess, attr)
37-
.into_iter()
38-
.any(|r| matches!(r, attr::ReprTransparent))
39-
});
40-
if !is_transparent {
41-
cx.dcx().emit_err(RequireTransparent { span });
42-
return;
43-
}
4435
if !matches!(
4536
struct_data,
4637
VariantData::Struct { fields, recovered: _ } | VariantData::Tuple(fields, _)
@@ -88,8 +79,7 @@ pub(crate) fn expand_deriving_coerce_pointee(
8879
} else {
8980
let mut pointees = type_params
9081
.iter()
91-
.filter_map(|&(idx, span, is_pointee)| is_pointee.then_some((idx, span)))
92-
.fuse();
82+
.filter_map(|&(idx, span, is_pointee)| is_pointee.then_some((idx, span)));
9383
match (pointees.next(), pointees.next()) {
9484
(Some((idx, _span)), None) => idx,
9585
(None, _) => {
@@ -110,6 +100,52 @@ pub(crate) fn expand_deriving_coerce_pointee(
110100
// Declare helper function that adds implementation blocks.
111101
// FIXME(dingxiangfei2009): Investigate the set of attributes on target struct to be propagated to impls
112102
let attrs = thin_vec![cx.attr_word(sym::automatically_derived, span),];
103+
// # Validity assertion which will be checked later in `rustc_hir_analysis::coherence::builtins`.
104+
{
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+
Ident::empty(),
112+
attrs.clone(),
113+
ast::ItemKind::Impl(Box::new(ast::Impl {
114+
safety: ast::Safety::Default,
115+
polarity: ast::ImplPolarity::Positive,
116+
defaultness: ast::Defaultness::Final,
117+
constness: ast::Const::No,
118+
generics: Generics {
119+
params: generics
120+
.params
121+
.iter()
122+
.map(|p| match &p.kind {
123+
GenericParamKind::Lifetime => {
124+
cx.lifetime_param(p.span(), p.ident, p.bounds.clone())
125+
}
126+
GenericParamKind::Type { default: _ } => {
127+
cx.typaram(p.span(), p.ident, p.bounds.clone(), None)
128+
}
129+
GenericParamKind::Const { ty, kw_span: _, default: _ } => cx
130+
.const_param(
131+
p.span(),
132+
p.ident,
133+
p.bounds.clone(),
134+
ty.clone(),
135+
None,
136+
),
137+
})
138+
.collect(),
139+
where_clause: generics.where_clause.clone(),
140+
span: generics.span,
141+
},
142+
of_trait: Some(trait_ref),
143+
self_ty: self_type.clone(),
144+
items: ThinVec::new(),
145+
})),
146+
),
147+
));
148+
}
113149
let mut add_impl_block = |generics, trait_symbol, trait_args| {
114150
let mut parts = path!(span, core::ops);
115151
parts.push(Ident::new(trait_symbol, span));
@@ -430,35 +466,35 @@ impl<'a, 'b> rustc_ast::visit::Visitor<'a> for AlwaysErrorOnGenericParam<'a, 'b>
430466
}
431467

432468
#[derive(Diagnostic)]
433-
#[diag(builtin_macros_coerce_pointee_requires_transparent)]
469+
#[diag(builtin_macros_coerce_pointee_requires_transparent, code = E0802)]
434470
struct RequireTransparent {
435471
#[primary_span]
436472
span: Span,
437473
}
438474

439475
#[derive(Diagnostic)]
440-
#[diag(builtin_macros_coerce_pointee_requires_one_field)]
476+
#[diag(builtin_macros_coerce_pointee_requires_one_field, code = E0802)]
441477
struct RequireOneField {
442478
#[primary_span]
443479
span: Span,
444480
}
445481

446482
#[derive(Diagnostic)]
447-
#[diag(builtin_macros_coerce_pointee_requires_one_generic)]
483+
#[diag(builtin_macros_coerce_pointee_requires_one_generic, code = E0802)]
448484
struct RequireOneGeneric {
449485
#[primary_span]
450486
span: Span,
451487
}
452488

453489
#[derive(Diagnostic)]
454-
#[diag(builtin_macros_coerce_pointee_requires_one_pointee)]
490+
#[diag(builtin_macros_coerce_pointee_requires_one_pointee, code = E0802)]
455491
struct RequireOnePointee {
456492
#[primary_span]
457493
span: Span,
458494
}
459495

460496
#[derive(Diagnostic)]
461-
#[diag(builtin_macros_coerce_pointee_too_many_pointees)]
497+
#[diag(builtin_macros_coerce_pointee_too_many_pointees, code = E0802)]
462498
struct TooManyPointees {
463499
#[primary_span]
464500
one: Span,
@@ -467,7 +503,7 @@ struct TooManyPointees {
467503
}
468504

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

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

373+
CoercePointeeValidated, sym::coerce_pointee_validated, coerce_pointee_validated_trait, Target::Trait, GenericRequirement::Exact(0);
374+
373375
ConstParamTy, sym::const_param_ty, const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0);
374376
UnsizedConstParamTy, sym::unsized_const_param_ty, unsized_const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0);
375377

@@ -429,9 +431,13 @@ language_item_table! {
429431
ContractCheckRequires, sym::contract_check_requires, contract_check_requires_fn, Target::Fn, GenericRequirement::None;
430432
}
431433

434+
/// The requirement imposed on the generics of a lang item
432435
pub enum GenericRequirement {
436+
/// No restriction on the generics
433437
None,
438+
/// A minimum number of generics that is demanded on a lang item
434439
Minimum(usize),
440+
/// The number of generics must match precisely as stipulated
435441
Exact(usize),
436442
}
437443

compiler/rustc_hir_analysis/messages.ftl

+10
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,16 @@ 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_no_field = `CoercePointee` can only be derived on `struct`s with at least one field
89+
90+
hir_analysis_coerce_pointee_no_user_validity_assertion = asserting applicability of `derive(CoercePointee)` on a target data is forbidden
91+
92+
hir_analysis_coerce_pointee_not_concrete_ty = `derive(CoercePointee)` is only applicable to `struct`
93+
94+
hir_analysis_coerce_pointee_not_struct = `derive(CoercePointee)` is only applicable to `struct`, instead of `{$kind}`
95+
96+
hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout
97+
8898
hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures
8999
90100
hir_analysis_coerce_unsized_multi = implementing the trait `CoerceUnsized` requires multiple coercions

compiler/rustc_hir_analysis/src/coherence/builtin.rs

+33
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ pub(super) fn check_trait<'tcx>(
4848
checker
4949
.check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn)?;
5050
checker.check(lang_items.pointer_like(), visit_implementation_of_pointer_like)?;
51+
checker.check(
52+
lang_items.coerce_pointee_validated_trait(),
53+
visit_implementation_of_coerce_pointee_validity,
54+
)?;
5155
Ok(())
5256
}
5357

@@ -783,3 +787,32 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err
783787
.with_note(why_disqualified)
784788
.emit())
785789
}
790+
791+
fn visit_implementation_of_coerce_pointee_validity(
792+
checker: &Checker<'_>,
793+
) -> Result<(), ErrorGuaranteed> {
794+
let tcx = checker.tcx;
795+
let self_ty = tcx.impl_trait_ref(checker.impl_def_id).unwrap().instantiate_identity().self_ty();
796+
let span = tcx.def_span(checker.impl_def_id);
797+
if !tcx.is_builtin_derived(checker.impl_def_id.into()) {
798+
return Err(tcx.dcx().emit_err(errors::CoercePointeeNoUserValidityAssertion { span }));
799+
}
800+
let ty::Adt(def, _args) = self_ty.kind() else {
801+
return Err(tcx.dcx().emit_err(errors::CoercePointeeNotConcreteType { span }));
802+
};
803+
let did = def.did();
804+
// Now get a more precise span of the `struct`.
805+
let span = tcx.def_span(did);
806+
if !def.is_struct() {
807+
return Err(tcx
808+
.dcx()
809+
.emit_err(errors::CoercePointeeNotStruct { span, kind: def.descr().into() }));
810+
}
811+
if !def.repr().transparent() {
812+
return Err(tcx.dcx().emit_err(errors::CoercePointeeNotTransparent { span }));
813+
}
814+
if def.all_fields().next().is_none() {
815+
return Err(tcx.dcx().emit_err(errors::CoercePointeeNoField { span }));
816+
}
817+
Ok(())
818+
}

0 commit comments

Comments
 (0)