Skip to content

Commit 5c5ed92

Browse files
Simplify trait error message for CoercePointee validation
1 parent 96d966b commit 5c5ed92

File tree

6 files changed

+199
-50
lines changed

6 files changed

+199
-50
lines changed

Diff for: compiler/rustc_hir_analysis/messages.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ hir_analysis_coerce_pointee_not_struct = `derive(CoercePointee)` is only applica
9999
100100
hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout
101101
102+
hir_analysis_coerce_unsized_field_validity = for `{$ty}` to have a valid implementation of `{$trait_name}`, it must be possible to coerce the field of type `{$field_ty}`
103+
.label = `{$field_ty}` must be a pointer, reference, or smart pointer that is allowed to be unsized
104+
102105
hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures
103106
104107
hir_analysis_coerce_zero = implementing `{$trait_name}` requires a field to be coerced

Diff for: compiler/rustc_hir_analysis/src/coherence/builtin.rs

+69-47
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_middle::ty::print::PrintTraitRefExt as _;
1717
use rustc_middle::ty::{
1818
self, Ty, TyCtxt, TypeVisitableExt, TypingMode, suggest_constraining_type_params,
1919
};
20-
use rustc_span::{DUMMY_SP, Span};
20+
use rustc_span::{DUMMY_SP, Span, sym};
2121
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
2222
use rustc_trait_selection::traits::misc::{
2323
ConstParamTyImplementationError, CopyImplementationError, InfringingFieldsReason,
@@ -199,13 +199,21 @@ fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), E
199199
tcx.at(span).ensure_ok().coerce_unsized_info(impl_did)
200200
}
201201

202+
fn is_from_coerce_pointee_derive(tcx: TyCtxt<'_>, span: Span) -> bool {
203+
span.ctxt()
204+
.outer_expn_data()
205+
.macro_def_id
206+
.is_some_and(|def_id| tcx.is_diagnostic_item(sym::CoercePointee, def_id))
207+
}
208+
202209
fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
203210
let tcx = checker.tcx;
204211
let impl_did = checker.impl_def_id;
205212
let trait_ref = checker.impl_header.trait_ref.instantiate_identity();
206213
debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did);
207214

208215
let span = tcx.def_span(impl_did);
216+
let trait_name = "DispatchFromDyn";
209217

210218
let dispatch_from_dyn_trait = tcx.require_lang_item(LangItem::DispatchFromDyn, Some(span));
211219

@@ -240,17 +248,15 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
240248
(&RawPtr(_, a_mutbl), &RawPtr(_, b_mutbl)) if a_mutbl == b_mutbl => Ok(()),
241249
(&Adt(def_a, args_a), &Adt(def_b, args_b)) if def_a.is_struct() && def_b.is_struct() => {
242250
if def_a != def_b {
243-
if def_a != def_b {
244-
let source_path = tcx.def_path_str(def_a.did());
245-
let target_path = tcx.def_path_str(def_b.did());
246-
return Err(tcx.dcx().emit_err(errors::CoerceSameStruct {
247-
span,
248-
trait_name: "DispatchFromDyn",
249-
note: true,
250-
source_path,
251-
target_path,
252-
}));
253-
}
251+
let source_path = tcx.def_path_str(def_a.did());
252+
let target_path = tcx.def_path_str(def_b.did());
253+
return Err(tcx.dcx().emit_err(errors::CoerceSameStruct {
254+
span,
255+
trait_name,
256+
note: true,
257+
source_path,
258+
target_path,
259+
}));
254260
}
255261

256262
if def_a.repr().c() || def_a.repr().packed() {
@@ -311,40 +317,46 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
311317
if coerced_fields.is_empty() {
312318
return Err(tcx.dcx().emit_err(errors::CoerceNoField {
313319
span,
314-
trait_name: "DispatchFromDyn",
320+
trait_name,
315321
note: true,
316322
}));
317-
} else if coerced_fields.len() > 1 {
318-
return Err(tcx.dcx().emit_err(errors::CoerceMulti {
319-
span,
320-
trait_name: "DispatchFromDyn",
321-
number: coerced_fields.len(),
322-
fields: coerced_fields.iter().map(|(_, _, _, s)| *s).collect::<Vec<_>>().into(),
323-
}));
324-
} else {
323+
} else if let &[(_, ty_a, ty_b, field_span)] = &coerced_fields[..] {
325324
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
326-
for (_, ty_a, ty_b, _) in coerced_fields {
327-
ocx.register_obligation(Obligation::new(
328-
tcx,
329-
cause.clone(),
330-
param_env,
331-
ty::TraitRef::new(tcx, dispatch_from_dyn_trait, [ty_a, ty_b]),
332-
));
333-
}
325+
ocx.register_obligation(Obligation::new(
326+
tcx,
327+
cause.clone(),
328+
param_env,
329+
ty::TraitRef::new(tcx, dispatch_from_dyn_trait, [ty_a, ty_b]),
330+
));
334331
let errors = ocx.select_all_or_error();
335332
if !errors.is_empty() {
336-
return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
333+
if is_from_coerce_pointee_derive(tcx, span) {
334+
return Err(tcx.dcx().emit_err(errors::CoerceFieldValidity {
335+
span,
336+
trait_name,
337+
ty: trait_ref.self_ty(),
338+
field_span,
339+
field_ty: ty_a,
340+
}));
341+
} else {
342+
return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
343+
}
337344
}
338345

339346
// Finally, resolve all regions.
340347
ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?;
341-
}
342348

343-
Ok(())
349+
Ok(())
350+
} else {
351+
return Err(tcx.dcx().emit_err(errors::CoerceMulti {
352+
span,
353+
trait_name,
354+
number: coerced_fields.len(),
355+
fields: coerced_fields.iter().map(|(_, _, _, s)| *s).collect::<Vec<_>>().into(),
356+
}));
357+
}
344358
}
345-
_ => Err(tcx
346-
.dcx()
347-
.emit_err(errors::CoerceUnsizedNonStruct { span, trait_name: "DispatchFromDyn" })),
359+
_ => Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name })),
348360
}
349361
}
350362

@@ -354,13 +366,14 @@ pub(crate) fn coerce_unsized_info<'tcx>(
354366
) -> Result<CoerceUnsizedInfo, ErrorGuaranteed> {
355367
debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did);
356368
let span = tcx.def_span(impl_did);
369+
let trait_name = "CoerceUnsized";
357370

358371
let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span));
359-
360372
let unsize_trait = tcx.require_lang_item(LangItem::Unsize, Some(span));
361373

362374
let source = tcx.type_of(impl_did).instantiate_identity();
363375
let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity();
376+
364377
assert_eq!(trait_ref.def_id, coerce_unsized_trait);
365378
let target = trait_ref.args.type_at(1);
366379
debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target);
@@ -387,9 +400,9 @@ pub(crate) fn coerce_unsized_info<'tcx>(
387400
)
388401
.emit();
389402
}
390-
(mt_a.ty, mt_b.ty, unsize_trait, None)
403+
(mt_a.ty, mt_b.ty, unsize_trait, None, span)
391404
};
392-
let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) {
405+
let (source, target, trait_def_id, kind, field_span) = match (source.kind(), target.kind()) {
393406
(&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {
394407
infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
395408
let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
@@ -412,7 +425,7 @@ pub(crate) fn coerce_unsized_info<'tcx>(
412425
let target_path = tcx.def_path_str(def_b.did());
413426
return Err(tcx.dcx().emit_err(errors::CoerceSameStruct {
414427
span,
415-
trait_name: "CoerceUnsized",
428+
trait_name,
416429
note: true,
417430
source_path,
418431
target_path,
@@ -499,7 +512,7 @@ pub(crate) fn coerce_unsized_info<'tcx>(
499512
if diff_fields.is_empty() {
500513
return Err(tcx.dcx().emit_err(errors::CoerceNoField {
501514
span,
502-
trait_name: "CoerceUnsized",
515+
trait_name,
503516
note: true,
504517
}));
505518
} else if diff_fields.len() > 1 {
@@ -512,21 +525,19 @@ pub(crate) fn coerce_unsized_info<'tcx>(
512525

513526
return Err(tcx.dcx().emit_err(errors::CoerceMulti {
514527
span,
515-
trait_name: "CoerceUnsized",
528+
trait_name,
516529
number: diff_fields.len(),
517530
fields: diff_fields.iter().map(|(_, _, _, s)| *s).collect::<Vec<_>>().into(),
518531
}));
519532
}
520533

521-
let (i, a, b, _) = diff_fields[0];
534+
let (i, a, b, field_span) = diff_fields[0];
522535
let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
523-
(a, b, coerce_unsized_trait, Some(kind))
536+
(a, b, coerce_unsized_trait, Some(kind), field_span)
524537
}
525538

526539
_ => {
527-
return Err(tcx
528-
.dcx()
529-
.emit_err(errors::CoerceUnsizedNonStruct { span, trait_name: "CoerceUnsized" }));
540+
return Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name }));
530541
}
531542
};
532543

@@ -541,8 +552,19 @@ pub(crate) fn coerce_unsized_info<'tcx>(
541552
);
542553
ocx.register_obligation(obligation);
543554
let errors = ocx.select_all_or_error();
555+
544556
if !errors.is_empty() {
545-
return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
557+
if is_from_coerce_pointee_derive(tcx, span) {
558+
return Err(tcx.dcx().emit_err(errors::CoerceFieldValidity {
559+
span,
560+
trait_name,
561+
ty: trait_ref.self_ty(),
562+
field_span,
563+
field_ty: source,
564+
}));
565+
} else {
566+
return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
567+
}
546568
}
547569

548570
// Finally, resolve all regions.

Diff for: compiler/rustc_hir_analysis/src/errors.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1321,6 +1321,18 @@ pub(crate) struct CoerceSameStruct {
13211321
pub target_path: String,
13221322
}
13231323

1324+
#[derive(Diagnostic)]
1325+
#[diag(hir_analysis_coerce_unsized_field_validity)]
1326+
pub(crate) struct CoerceFieldValidity<'tcx> {
1327+
#[primary_span]
1328+
pub span: Span,
1329+
pub ty: Ty<'tcx>,
1330+
pub trait_name: &'static str,
1331+
#[label]
1332+
pub field_span: Span,
1333+
pub field_ty: Ty<'tcx>,
1334+
}
1335+
13241336
#[derive(Diagnostic)]
13251337
#[diag(hir_analysis_trait_cannot_impl_for_ty, code = E0204)]
13261338
pub(crate) struct TraitCannotImplForTy {

Diff for: library/core/src/marker.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1302,6 +1302,7 @@ pub trait FnPtr: Copy + Clone {
13021302
/// ```
13031303
#[rustc_builtin_macro(CoercePointee, attributes(pointee))]
13041304
#[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize, coerce_pointee_validated)]
1305+
#[cfg_attr(not(test), rustc_diagnostic_item = "CoercePointee")]
13051306
#[unstable(feature = "derive_coerce_pointee", issue = "123430")]
13061307
pub macro CoercePointee($item:item) {
13071308
/* compiler built-in */

Diff for: tests/ui/deriving/deriving-coerce-pointee-neg.rs

+26
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,30 @@ struct TryToWipeRepr<'a, #[pointee] T: ?Sized> {
142142
ptr: &'a T,
143143
}
144144

145+
#[repr(transparent)]
146+
#[derive(CoercePointee)]
147+
//~^ ERROR for `RcWithId<T>` to have a valid implementation of `CoerceUnsized`, it must be possible to coerce the field of type `Rc<(i32, Box<T>)>`
148+
//~| ERROR for `RcWithId<T>` to have a valid implementation of `DispatchFromDyn`, it must be possible to coerce the field of type `Rc<(i32, Box<T>)>`
149+
struct RcWithId<T: ?Sized> {
150+
inner: std::rc::Rc<(i32, Box<T>)>,
151+
}
152+
153+
#[repr(transparent)]
154+
#[derive(CoercePointee)]
155+
//~^ ERROR implementing `CoerceUnsized` does not allow multiple fields to be coerced
156+
//~| ERROR implementing `DispatchFromDyn` does not allow multiple fields to be coerced
157+
struct MoreThanOneField<T: ?Sized> {
158+
//~^ ERROR transparent struct needs at most one field with non-trivial size or alignment, but has 2
159+
inner1: Box<T>,
160+
inner2: Box<T>,
161+
}
162+
163+
struct NotCoercePointeeData<T: ?Sized>(T);
164+
165+
#[repr(transparent)]
166+
#[derive(CoercePointee)]
167+
//~^ ERROR for `UsingNonCoercePointeeData<T>` to have a valid implementation of `CoerceUnsized`, it must be possible to coerce the field of type `NotCoercePointeeData<T>`
168+
//~| ERROR for `UsingNonCoercePointeeData<T>` to have a valid implementation of `DispatchFromDyn`, it must be possible to coerce the field of type `NotCoercePointeeData<T>`
169+
struct UsingNonCoercePointeeData<T: ?Sized>(NotCoercePointeeData<T>);
170+
145171
fn main() {}

Diff for: tests/ui/deriving/deriving-coerce-pointee-neg.stderr

+88-3
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,92 @@ error[E0802]: `derive(CoercePointee)` is only applicable to `struct` with `repr(
118118
LL | struct TryToWipeRepr<'a, #[pointee] T: ?Sized> {
119119
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
120120

121-
error: aborting due to 17 previous errors
121+
error: for `RcWithId<T>` to have a valid implementation of `DispatchFromDyn`, it must be possible to coerce the field of type `Rc<(i32, Box<T>)>`
122+
--> $DIR/deriving-coerce-pointee-neg.rs:146:10
123+
|
124+
LL | #[derive(CoercePointee)]
125+
| ^^^^^^^^^^^^^
126+
...
127+
LL | inner: std::rc::Rc<(i32, Box<T>)>,
128+
| --------------------------------- `Rc<(i32, Box<T>)>` must be a pointer, reference, or smart pointer that is allowed to be unsized
129+
|
130+
= note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info)
131+
132+
error[E0375]: implementing `DispatchFromDyn` does not allow multiple fields to be coerced
133+
--> $DIR/deriving-coerce-pointee-neg.rs:154:10
134+
|
135+
LL | #[derive(CoercePointee)]
136+
| ^^^^^^^^^^^^^
137+
|
138+
note: the trait `DispatchFromDyn` may only be implemented when a single field is being coerced
139+
--> $DIR/deriving-coerce-pointee-neg.rs:159:5
140+
|
141+
LL | inner1: Box<T>,
142+
| ^^^^^^^^^^^^^^
143+
LL | inner2: Box<T>,
144+
| ^^^^^^^^^^^^^^
145+
= note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info)
146+
147+
error: for `UsingNonCoercePointeeData<T>` to have a valid implementation of `DispatchFromDyn`, it must be possible to coerce the field of type `NotCoercePointeeData<T>`
148+
--> $DIR/deriving-coerce-pointee-neg.rs:166:10
149+
|
150+
LL | #[derive(CoercePointee)]
151+
| ^^^^^^^^^^^^^
152+
...
153+
LL | struct UsingNonCoercePointeeData<T: ?Sized>(NotCoercePointeeData<T>);
154+
| ----------------------- `NotCoercePointeeData<T>` must be a pointer, reference, or smart pointer that is allowed to be unsized
155+
|
156+
= note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info)
157+
158+
error: for `RcWithId<T>` to have a valid implementation of `CoerceUnsized`, it must be possible to coerce the field of type `Rc<(i32, Box<T>)>`
159+
--> $DIR/deriving-coerce-pointee-neg.rs:146:10
160+
|
161+
LL | #[derive(CoercePointee)]
162+
| ^^^^^^^^^^^^^
163+
...
164+
LL | inner: std::rc::Rc<(i32, Box<T>)>,
165+
| --------------------------------- `Rc<(i32, Box<T>)>` must be a pointer, reference, or smart pointer that is allowed to be unsized
166+
|
167+
= note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info)
168+
169+
error[E0375]: implementing `CoerceUnsized` does not allow multiple fields to be coerced
170+
--> $DIR/deriving-coerce-pointee-neg.rs:154:10
171+
|
172+
LL | #[derive(CoercePointee)]
173+
| ^^^^^^^^^^^^^
174+
|
175+
note: the trait `CoerceUnsized` may only be implemented when a single field is being coerced
176+
--> $DIR/deriving-coerce-pointee-neg.rs:159:5
177+
|
178+
LL | inner1: Box<T>,
179+
| ^^^^^^^^^^^^^^
180+
LL | inner2: Box<T>,
181+
| ^^^^^^^^^^^^^^
182+
= note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info)
183+
184+
error: for `UsingNonCoercePointeeData<T>` to have a valid implementation of `CoerceUnsized`, it must be possible to coerce the field of type `NotCoercePointeeData<T>`
185+
--> $DIR/deriving-coerce-pointee-neg.rs:166:10
186+
|
187+
LL | #[derive(CoercePointee)]
188+
| ^^^^^^^^^^^^^
189+
...
190+
LL | struct UsingNonCoercePointeeData<T: ?Sized>(NotCoercePointeeData<T>);
191+
| ----------------------- `NotCoercePointeeData<T>` must be a pointer, reference, or smart pointer that is allowed to be unsized
192+
|
193+
= note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info)
194+
195+
error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2
196+
--> $DIR/deriving-coerce-pointee-neg.rs:157:1
197+
|
198+
LL | struct MoreThanOneField<T: ?Sized> {
199+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2
200+
LL |
201+
LL | inner1: Box<T>,
202+
| -------------- this field has non-zero size or requires alignment
203+
LL | inner2: Box<T>,
204+
| -------------- this field has non-zero size or requires alignment
205+
206+
error: aborting due to 24 previous errors
122207

123-
Some errors have detailed explanations: E0392, E0802.
124-
For more information about an error, try `rustc --explain E0392`.
208+
Some errors have detailed explanations: E0375, E0392, E0690, E0802.
209+
For more information about an error, try `rustc --explain E0375`.

0 commit comments

Comments
 (0)