From 5044be43e9df6b7fdcd20388e5db8f2609614723 Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Tue, 18 Feb 2025 01:59:06 +0800 Subject: [PATCH 1/2] redact coerced types away --- compiler/rustc_hir_analysis/messages.ftl | 13 ++ .../src/coherence/builtin.rs | 137 ++++++++++++++---- .../src/coherence/builtin/diagnostics.rs | 136 +++++++++++++++++ compiler/rustc_hir_analysis/src/errors.rs | 24 +++ .../deriving/deriving-coerce-pointee-neg.rs | 18 +++ .../deriving-coerce-pointee-neg.stderr | 51 ++++++- 6 files changed, 350 insertions(+), 29 deletions(-) create mode 100644 compiler/rustc_hir_analysis/src/coherence/builtin/diagnostics.rs diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 5560d087e96f0..3bb9033eeb47a 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -85,6 +85,19 @@ hir_analysis_cmse_output_stack_spill = .note1 = functions with the `{$abi}` ABI must pass their result via the available return registers .note2 = the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size +hir_analysis_coerce_pointee_cannot_coerce_unsize = `{$ty}` cannot be coerced to an unsized type + .note = `derive(CoercePointee)` demands that `{$ty}` can be coerced to an unsized type + .help = the standard pointers such as `Arc`, `Rc`, `Box`, and other types with `derive(CoercePointee)` can be coerced to their corresponding unsized types + +hir_analysis_coerce_pointee_cannot_unsize = `{$ty}` cannot be coerced to any unsized value + .note = `derive(CoercePointee)` demands that `{$ty}` can be coerced to an unsized type + .help = `derive(CoercePointee)` requires exactly one copy of `#[pointee]` type at the end of the `struct` definition, without any further pointer or reference indirection + +hir_analysis_coerce_pointee_multiple_targets = `derive(CoercePointee)` only admits exactly one data field, {$diag_trait -> + [DispatchFromDyn] to which `dyn` methods shall be dispatched + *[CoerceUnsized] on which unsize coercion shall be performed + } + hir_analysis_coerce_pointee_no_field = `CoercePointee` can only be derived on `struct`s with at least one field hir_analysis_coerce_pointee_no_user_validity_assertion = asserting applicability of `derive(CoercePointee)` on a target data is forbidden diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index b46b805f0a9d2..5269b16f7a3ca 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -1,9 +1,12 @@ //! Check properties that are required by built-in traits and set //! up data structures required by type-checking/codegen. +mod diagnostics; + use std::assert_matches::assert_matches; use std::collections::BTreeMap; +use diagnostics::{extract_coerce_pointee_data, redact_fulfillment_err_for_coerce_pointee}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; @@ -12,6 +15,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::{self, RegionResolutionError, TyCtxtInferExt}; use rustc_infer::traits::Obligation; +use rustc_middle::bug; use rustc_middle::ty::adjustment::CoerceUnsizedInfo; use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{ @@ -307,29 +311,45 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() .collect::>(); if coerced_fields.is_empty() { - res = Err(tcx.dcx().emit_err(errors::DispatchFromDynSingle { - span, - trait_name: "DispatchFromDyn", - note: true, - })); + if extract_coerce_pointee_data(tcx, def_a.did()).is_some() { + res = Err(tcx.dcx().span_delayed_bug( + span, + "a specialised message for CoercePointee is expected", + )); + } else { + res = Err(tcx.dcx().emit_err(errors::DispatchFromDynSingle { + span, + trait_name: "DispatchFromDyn", + note: true, + })); + } } else if coerced_fields.len() > 1 { - res = Err(tcx.dcx().emit_err(errors::DispatchFromDynMulti { - span, - coercions_note: true, - number: coerced_fields.len(), - coercions: coerced_fields - .iter() - .map(|field| { - format!( - "`{}` (`{}` to `{}`)", - field.name, - field.ty(tcx, args_a), - field.ty(tcx, args_b), - ) - }) - .collect::>() - .join(", "), - })); + if extract_coerce_pointee_data(tcx, def_a.did()).is_some() { + let spans = + coerced_fields.iter().map(|field| tcx.def_span(field.did)).collect(); + res = Err(tcx.dcx().emit_err(errors::CoercePointeeMultipleTargets { + spans, + diag_trait: "DispatchFromDyn", + })); + } else { + res = Err(tcx.dcx().emit_err(errors::DispatchFromDynMulti { + span, + coercions_note: true, + number: coerced_fields.len(), + coercions: coerced_fields + .iter() + .map(|field| { + format!( + "`{}` (`{}` to `{}`)", + field.name, + field.ty(tcx, args_a), + field.ty(tcx, args_b), + ) + }) + .collect::>() + .join(", "), + })); + } } else { let ocx = ObligationCtxt::new_with_diagnostics(&infcx); for field in coerced_fields { @@ -344,9 +364,31 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() ), )); } - let errors = ocx.select_all_or_error(); + let mut errors = ocx.select_all_or_error(); if !errors.is_empty() { - res = Err(infcx.err_ctxt().report_fulfillment_errors(errors)); + if let Some((pointee_idx, _)) = extract_coerce_pointee_data(tcx, def_a.did()) { + let target_pointee = args_b.type_at(pointee_idx); + + errors = errors + .into_iter() + .filter_map(|err| { + redact_fulfillment_err_for_coerce_pointee( + tcx, + err, + target_pointee, + span, + ) + }) + .collect(); + } + if errors.is_empty() { + res = Err(tcx.dcx().span_delayed_bug( + span, + "a specialised CoercePointee error is expected", + )); + } else { + res = Err(infcx.err_ctxt().report_fulfillment_errors(errors)); + } } // Finally, resolve all regions. @@ -372,9 +414,11 @@ pub(crate) fn coerce_unsized_info<'tcx>( let unsize_trait = tcx.require_lang_item(LangItem::Unsize, Some(span)); let source = tcx.type_of(impl_did).instantiate_identity(); + let self_ty = source; let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); assert_eq!(trait_ref.def_id, coerce_unsized_trait); let target = trait_ref.args.type_at(1); + let coerced_ty = target; debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target); let param_env = tcx.param_env(impl_did); @@ -509,12 +553,28 @@ pub(crate) fn coerce_unsized_info<'tcx>( .collect::>(); if diff_fields.is_empty() { + if extract_coerce_pointee_data(tcx, def_a.did()).is_some() { + return Err(tcx.dcx().span_delayed_bug( + span, + "a specialised message for CoercePointee is expected", + )); + } return Err(tcx.dcx().emit_err(errors::CoerceUnsizedOneField { span, trait_name: "CoerceUnsized", note: true, })); } else if diff_fields.len() > 1 { + if extract_coerce_pointee_data(tcx, def_a.did()).is_some() { + let spans = diff_fields + .iter() + .map(|&(idx, _, _)| tcx.def_span(fields[idx].did)) + .collect(); + return Err(tcx.dcx().emit_err(errors::CoercePointeeMultipleTargets { + spans, + diag_trait: "CoerceUnsized", + })); + } let item = tcx.hir().expect_item(impl_did); let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(t), .. }) = &item.kind { t.path.span @@ -547,8 +607,16 @@ pub(crate) fn coerce_unsized_info<'tcx>( }; // Register an obligation for `A: Trait`. + let coerce_pointee_data = if let ty::Adt(def, _) = self_ty.kind() { + extract_coerce_pointee_data(tcx, def.did()) + } else { + None + }; let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - let cause = traits::ObligationCause::misc(span, impl_did); + let cause = traits::ObligationCause::misc( + span, + coerce_pointee_data.map_or(impl_did, |(_, impl_did)| impl_did.expect_local()), + ); let obligation = Obligation::new( tcx, cause, @@ -556,9 +624,24 @@ pub(crate) fn coerce_unsized_info<'tcx>( ty::TraitRef::new(tcx, trait_def_id, [source, target]), ); ocx.register_obligation(obligation); - let errors = ocx.select_all_or_error(); + let mut errors = ocx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(errors); + if let Some((pointee, _)) = coerce_pointee_data { + let ty::Adt(_def, args) = coerced_ty.kind() else { bug!() }; + let target_pointee = args.type_at(pointee); + let ty::Adt(_def, _) = self_ty.kind() else { bug!() }; + errors = errors + .into_iter() + .filter_map(|err| { + redact_fulfillment_err_for_coerce_pointee(tcx, err, target_pointee, span) + }) + .collect(); + } + if errors.is_empty() { + tcx.dcx().span_delayed_bug(span, "a specialised CoercePointee error is expected"); + } else { + infcx.err_ctxt().report_fulfillment_errors(errors); + } } // Finally, resolve all regions. diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin/diagnostics.rs b/compiler/rustc_hir_analysis/src/coherence/builtin/diagnostics.rs new file mode 100644 index 0000000000000..890381f5ce3c0 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/coherence/builtin/diagnostics.rs @@ -0,0 +1,136 @@ +use rustc_hir::LangItem; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::fast_reject::SimplifiedType; +use rustc_middle::ty::{ + self, GenericParamDefKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, +}; +use rustc_span::{Span, sym}; +use rustc_trait_selection::traits::FulfillmentError; +use tracing::instrument; + +use crate::errors; + +#[instrument(level = "debug", skip(tcx), ret)] +pub(super) fn extract_coerce_pointee_data<'tcx>( + tcx: TyCtxt<'tcx>, + adt_did: DefId, +) -> Option<(usize, DefId)> { + // It is decided that a query to cache these results is not necessary + // for error reporting. + // We can afford to recompute it on-demand. + if let Some(impls) = tcx.lang_items().get(LangItem::CoercePointeeValidated).and_then(|did| { + tcx.trait_impls_of(did).non_blanket_impls().get(&SimplifiedType::Adt(adt_did)) + }) && let [impl_did, ..] = impls[..] + { + // Search for the `#[pointee]` + let mut first_type = None; + for (idx, param) in tcx.generics_of(adt_did).own_params.iter().enumerate() { + if let GenericParamDefKind::Type { .. } = param.kind { + first_type = if first_type.is_some() { None } else { Some(idx) }; + } + if tcx.has_attr(param.def_id, sym::pointee) { + return Some((idx, impl_did)); + } + } + if let Some(idx) = first_type { + return Some((idx, impl_did)); + } + } + None +} + +fn contains_coerce_pointee_target_pointee<'tcx>(ty: Ty<'tcx>, target_pointee_ty: Ty<'tcx>) -> bool { + struct Search<'tcx> { + pointee: Ty<'tcx>, + found: bool, + } + impl<'tcx> TypeVisitor> for Search<'tcx> { + fn visit_binder>>( + &mut self, + t: &rustc_type_ir::Binder, T>, + ) { + if self.found { + return; + } + t.super_visit_with(self) + } + + fn visit_ty(&mut self, t: Ty<'tcx>) { + if self.found { + return; + } + if t == self.pointee { + self.found = true; + } else { + t.super_visit_with(self) + } + } + + fn visit_region(&mut self, r: as ty::Interner>::Region) { + if self.found { + return; + } + if let rustc_type_ir::ReError(guar) = r.kind() { + self.visit_error(guar) + } + } + + fn visit_const(&mut self, c: as ty::Interner>::Const) { + if self.found { + return; + } + c.super_visit_with(self) + } + + fn visit_predicate(&mut self, p: as ty::Interner>::Predicate) { + if self.found { + return; + } + p.super_visit_with(self) + } + + fn visit_clauses(&mut self, p: as ty::Interner>::Clauses) { + if self.found { + return; + } + p.super_visit_with(self) + } + } + let mut search = Search { pointee: target_pointee_ty, found: false }; + ty.visit_with(&mut search); + search.found +} + +#[instrument(level = "debug", skip(tcx))] +pub(super) fn redact_fulfillment_err_for_coerce_pointee<'tcx>( + tcx: TyCtxt<'tcx>, + err: FulfillmentError<'tcx>, + target_pointee_ty: Ty<'tcx>, + span: Span, +) -> Option> { + if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = + err.obligation.predicate.kind().skip_binder() + { + let mentions_pointee = || { + contains_coerce_pointee_target_pointee( + pred.trait_ref.args.type_at(1), + target_pointee_ty, + ) + }; + let source = pred.trait_ref.self_ty(); + if tcx.is_lang_item(pred.def_id(), LangItem::Unsize) { + if mentions_pointee() { + // We should redact it + tcx.dcx() + .emit_err(errors::CoercePointeeCannotUnsize { ty: source.to_string(), span }); + return None; + } + } else if tcx.is_lang_item(pred.def_id(), LangItem::CoerceUnsized) && mentions_pointee() { + // We should redact it + tcx.dcx() + .emit_err(errors::CoercePointeeCannotCoerceUnsize { ty: source.to_string(), span }); + return None; + } + } + Some(err) +} diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 14ea10461cbea..3f6ab15683727 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1219,6 +1219,30 @@ pub(crate) struct CoercePointeeNoField { pub span: Span, } +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_pointee_multiple_targets, code = E0802)] +pub(crate) struct CoercePointeeMultipleTargets { + #[primary_span] + pub spans: Vec, + pub diag_trait: &'static str, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_pointee_cannot_unsize, code = E0802)] +pub(crate) struct CoercePointeeCannotUnsize { + #[primary_span] + pub span: Span, + pub ty: String, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_pointee_cannot_coerce_unsize, code = E0802)] +pub(crate) struct CoercePointeeCannotCoerceUnsize { + #[primary_span] + pub span: Span, + pub ty: String, +} + #[derive(Diagnostic)] #[diag(hir_analysis_inherent_ty_outside_relevant, code = E0390)] #[help] diff --git a/tests/ui/deriving/deriving-coerce-pointee-neg.rs b/tests/ui/deriving/deriving-coerce-pointee-neg.rs index 6577500d8eb0f..eccc24a39db5a 100644 --- a/tests/ui/deriving/deriving-coerce-pointee-neg.rs +++ b/tests/ui/deriving/deriving-coerce-pointee-neg.rs @@ -142,4 +142,22 @@ struct TryToWipeRepr<'a, #[pointee] T: ?Sized> { ptr: &'a T, } +#[repr(transparent)] +#[derive(CoercePointee)] +//~^ ERROR: `Box` cannot be coerced to any unsized value [E0802] +//~| ERROR: `Box` cannot be coerced to any unsized value [E0802] +struct RcWithId { + inner: std::rc::Rc<(i32, Box)>, +} + +#[repr(transparent)] +#[derive(CoercePointee)] +struct MoreThanOneField { + //~^ ERROR: transparent struct needs at most one field with non-trivial size or alignment, but has 2 [E0690] + inner1: Box, + //~^ ERROR: `derive(CoercePointee)` only admits exactly one data field, to which `dyn` methods shall be dispatched [E0802] + //~| ERROR: `derive(CoercePointee)` only admits exactly one data field, on which unsize coercion shall be performed [E0802] + inner2: Box, +} + fn main() {} diff --git a/tests/ui/deriving/deriving-coerce-pointee-neg.stderr b/tests/ui/deriving/deriving-coerce-pointee-neg.stderr index 999214bfa9fe3..5f7faad0ffb29 100644 --- a/tests/ui/deriving/deriving-coerce-pointee-neg.stderr +++ b/tests/ui/deriving/deriving-coerce-pointee-neg.stderr @@ -118,7 +118,54 @@ error[E0802]: `derive(CoercePointee)` is only applicable to `struct` with `repr( LL | struct TryToWipeRepr<'a, #[pointee] T: ?Sized> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 17 previous errors +error[E0802]: `Box` cannot be coerced to any unsized value + --> $DIR/deriving-coerce-pointee-neg.rs:146:10 + | +LL | #[derive(CoercePointee)] + | ^^^^^^^^^^^^^ + | + = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0802]: `derive(CoercePointee)` only admits exactly one data field, to which `dyn` methods shall be dispatched + --> $DIR/deriving-coerce-pointee-neg.rs:157:5 + | +LL | inner1: Box, + | ^^^^^^^^^^^^^^ +... +LL | inner2: Box, + | ^^^^^^^^^^^^^^ + +error[E0802]: `Box` cannot be coerced to any unsized value + --> $DIR/deriving-coerce-pointee-neg.rs:146:10 + | +LL | #[derive(CoercePointee)] + | ^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0802]: `derive(CoercePointee)` only admits exactly one data field, on which unsize coercion shall be performed + --> $DIR/deriving-coerce-pointee-neg.rs:157:5 + | +LL | inner1: Box, + | ^^^^^^^^^^^^^^ +... +LL | inner2: Box, + | ^^^^^^^^^^^^^^ + +error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2 + --> $DIR/deriving-coerce-pointee-neg.rs:155:1 + | +LL | struct MoreThanOneField { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2 +LL | +LL | inner1: Box, + | -------------- this field has non-zero size or requires alignment +... +LL | inner2: Box, + | -------------- this field has non-zero size or requires alignment + +error: aborting due to 22 previous errors -Some errors have detailed explanations: E0392, E0802. +Some errors have detailed explanations: E0392, E0690, E0802. For more information about an error, try `rustc --explain E0392`. From 60f6670bbba3119d8631dd24ef3c9c5a49629c5e Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Wed, 19 Feb 2025 18:29:17 +0800 Subject: [PATCH 2/2] apply suggestions --- compiler/rustc_hir_analysis/messages.ftl | 8 +- .../src/coherence/builtin.rs | 363 +++++++++--------- .../src/coherence/builtin/diagnostics.rs | 94 ++--- compiler/rustc_hir_analysis/src/errors.rs | 16 +- .../deriving/deriving-coerce-pointee-neg.rs | 45 ++- .../deriving-coerce-pointee-neg.stderr | 100 ++--- 6 files changed, 330 insertions(+), 296 deletions(-) diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 3bb9033eeb47a..779193e599773 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -85,11 +85,15 @@ hir_analysis_cmse_output_stack_spill = .note1 = functions with the `{$abi}` ABI must pass their result via the available return registers .note2 = the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size -hir_analysis_coerce_pointee_cannot_coerce_unsize = `{$ty}` cannot be coerced to an unsized type +hir_analysis_coerce_pointee_cannot_coerce_unsized = `{$ty}` cannot be coerced to an unsized type .note = `derive(CoercePointee)` demands that `{$ty}` can be coerced to an unsized type .help = the standard pointers such as `Arc`, `Rc`, `Box`, and other types with `derive(CoercePointee)` can be coerced to their corresponding unsized types -hir_analysis_coerce_pointee_cannot_unsize = `{$ty}` cannot be coerced to any unsized value +hir_analysis_coerce_pointee_cannot_dispatch_from_dyn = `{$ty}` cannot be coerced to an unsized type, to which `dyn` methods can be dispatched + .note = `derive(CoercePointee)` demands that `dyn` methods can be dispatched when `{$ty}` can be coerced to an unsized type + .help = `dyn` methods can be dispatched to the standard pointers such as `Arc`, `Rc`, `Box`, and other types with `derive(CoercePointee)` + +hir_analysis_coerce_pointee_cannot_unsize = `{$ty}` cannot be coerced to an unsized value .note = `derive(CoercePointee)` demands that `{$ty}` can be coerced to an unsized type .help = `derive(CoercePointee)` requires exactly one copy of `#[pointee]` type at the end of the `struct` definition, without any further pointer or reference indirection diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 5269b16f7a3ca..63a09f453d4a3 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -314,7 +314,8 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() if extract_coerce_pointee_data(tcx, def_a.did()).is_some() { res = Err(tcx.dcx().span_delayed_bug( span, - "a specialised message for CoercePointee is expected", + "a specialised diagnostics emitted from CoercePointee \ + on missing data field is expected but none is found", )); } else { res = Err(tcx.dcx().emit_err(errors::DispatchFromDynSingle { @@ -352,21 +353,21 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() } } else { let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - for field in coerced_fields { - ocx.register_obligation(Obligation::new( + debug_assert_eq!(coerced_fields.len(), 1); + let field = coerced_fields[0]; + ocx.register_obligation(Obligation::new( + tcx, + cause.clone(), + param_env, + ty::TraitRef::new( tcx, - cause.clone(), - param_env, - ty::TraitRef::new( - tcx, - dispatch_from_dyn_trait, - [field.ty(tcx, args_a), field.ty(tcx, args_b)], - ), - )); - } + dispatch_from_dyn_trait, + [field.ty(tcx, args_a), field.ty(tcx, args_b)], + ), + )); let mut errors = ocx.select_all_or_error(); if !errors.is_empty() { - if let Some((pointee_idx, _)) = extract_coerce_pointee_data(tcx, def_a.did()) { + if let Some(pointee_idx) = extract_coerce_pointee_data(tcx, def_a.did()) { let target_pointee = args_b.type_at(pointee_idx); errors = errors @@ -376,7 +377,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() tcx, err, target_pointee, - span, + tcx.def_span(field.did), ) }) .collect(); @@ -413,18 +414,16 @@ pub(crate) fn coerce_unsized_info<'tcx>( let unsize_trait = tcx.require_lang_item(LangItem::Unsize, Some(span)); - let source = tcx.type_of(impl_did).instantiate_identity(); - let self_ty = source; + let self_ty = tcx.type_of(impl_did).instantiate_identity(); let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); assert_eq!(trait_ref.def_id, coerce_unsized_trait); - let target = trait_ref.args.type_at(1); - let coerced_ty = target; - debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target); + let coerced_ty = trait_ref.args.type_at(1); + debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", self_ty, coerced_ty); let param_env = tcx.param_env(impl_did); - assert!(!source.has_escaping_bound_vars()); + assert!(!self_ty.has_escaping_bound_vars()); - debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target); + debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", self_ty, coerced_ty); let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let cause = ObligationCause::misc(span, impl_did); @@ -438,173 +437,179 @@ pub(crate) fn coerce_unsized_info<'tcx>( &cause, param_env, mk_ptr(mt_b.ty), - target, + coerced_ty, ty::error::TypeError::Mutability, ) .emit(); } - (mt_a.ty, mt_b.ty, unsize_trait, None) + (mt_a.ty, mt_b.ty, unsize_trait, None, DUMMY_SP) }; - let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) { - (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => { - infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a); - let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; - let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }; - check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ref(tcx, r_b, ty)) - } - - (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) - | (&ty::RawPtr(ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) => { - let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; - let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }; - check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ptr(tcx, ty)) - } + let (source, target, trait_def_id, kind, coercion_span) = + match (self_ty.kind(), coerced_ty.kind()) { + (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => { + infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a); + let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; + let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }; + check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ref(tcx, r_b, ty)) + } - (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b)) - if def_a.is_struct() && def_b.is_struct() => - { - if def_a != def_b { - let source_path = tcx.def_path_str(def_a.did()); - let target_path = tcx.def_path_str(def_b.did()); - return Err(tcx.dcx().emit_err(errors::DispatchFromDynSame { - span, - trait_name: "CoerceUnsized", - note: true, - source_path, - target_path, - })); + (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) + | (&ty::RawPtr(ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) => { + let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; + let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }; + check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ptr(tcx, ty)) } - // Here we are considering a case of converting - // `S` to `S`. As an example, let's imagine a struct `Foo`, - // which acts like a pointer to `U`, but carries along some extra data of type `T`: - // - // struct Foo { - // extra: T, - // ptr: *mut U, - // } - // - // We might have an impl that allows (e.g.) `Foo` to be unsized - // to `Foo`. That impl would look like: - // - // impl, V> CoerceUnsized> for Foo {} - // - // Here `U = [i32; 3]` and `V = [i32]`. At runtime, - // when this coercion occurs, we would be changing the - // field `ptr` from a thin pointer of type `*mut [i32; - // 3]` to a wide pointer of type `*mut [i32]` (with - // extra data `3`). **The purpose of this check is to - // make sure that we know how to do this conversion.** - // - // To check if this impl is legal, we would walk down - // the fields of `Foo` and consider their types with - // both generic parameters. We are looking to find that - // exactly one (non-phantom) field has changed its - // type, which we will expect to be the pointer that - // is becoming fat (we could probably generalize this - // to multiple thin pointers of the same type becoming - // fat, but we don't). In this case: - // - // - `extra` has type `T` before and type `T` after - // - `ptr` has type `*mut U` before and type `*mut V` after - // - // Since just one field changed, we would then check - // that `*mut U: CoerceUnsized<*mut V>` is implemented - // (in other words, that we know how to do this - // conversion). This will work out because `U: - // Unsize`, and we have a builtin rule that `*mut - // U` can be coerced to `*mut V` if `U: Unsize`. - let fields = &def_a.non_enum_variant().fields; - let diff_fields = fields - .iter_enumerated() - .filter_map(|(i, f)| { - let (a, b) = (f.ty(tcx, args_a), f.ty(tcx, args_b)); + (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b)) + if def_a.is_struct() && def_b.is_struct() => + { + if def_a != def_b { + let source_path = tcx.def_path_str(def_a.did()); + let target_path = tcx.def_path_str(def_b.did()); + return Err(tcx.dcx().emit_err(errors::DispatchFromDynSame { + span, + trait_name: "CoerceUnsized", + note: true, + source_path, + target_path, + })); + } - // Ignore PhantomData fields - let unnormalized_ty = tcx.type_of(f.did).instantiate_identity(); - if tcx - .try_normalize_erasing_regions( - ty::TypingEnv::non_body_analysis(tcx, def_a.did()), - unnormalized_ty, - ) - .unwrap_or(unnormalized_ty) - .is_phantom_data() - { - return None; - } + // Here we are considering a case of converting + // `S` to `S`. As an example, let's imagine a struct `Foo`, + // which acts like a pointer to `U`, but carries along some extra data of type `T`: + // + // struct Foo { + // extra: T, + // ptr: *mut U, + // } + // + // We might have an impl that allows (e.g.) `Foo` to be unsized + // to `Foo`. That impl would look like: + // + // impl, V> CoerceUnsized> for Foo {} + // + // Here `U = [i32; 3]` and `V = [i32]`. At runtime, + // when this coercion occurs, we would be changing the + // field `ptr` from a thin pointer of type `*mut [i32; + // 3]` to a wide pointer of type `*mut [i32]` (with + // extra data `3`). **The purpose of this check is to + // make sure that we know how to do this conversion.** + // + // To check if this impl is legal, we would walk down + // the fields of `Foo` and consider their types with + // both generic parameters. We are looking to find that + // exactly one (non-phantom) field has changed its + // type, which we will expect to be the pointer that + // is becoming fat (we could probably generalize this + // to multiple thin pointers of the same type becoming + // fat, but we don't). In this case: + // + // - `extra` has type `T` before and type `T` after + // - `ptr` has type `*mut U` before and type `*mut V` after + // + // Since just one field changed, we would then check + // that `*mut U: CoerceUnsized<*mut V>` is implemented + // (in other words, that we know how to do this + // conversion). This will work out because `U: + // Unsize`, and we have a builtin rule that `*mut + // U` can be coerced to `*mut V` if `U: Unsize`. + let fields = &def_a.non_enum_variant().fields; + let diff_fields = fields + .iter_enumerated() + .filter_map(|(i, f)| { + let (a, b) = (f.ty(tcx, args_a), f.ty(tcx, args_b)); + + // Ignore PhantomData fields + let unnormalized_ty = tcx.type_of(f.did).instantiate_identity(); + if tcx + .try_normalize_erasing_regions( + ty::TypingEnv::non_body_analysis(tcx, def_a.did()), + unnormalized_ty, + ) + .unwrap_or(unnormalized_ty) + .is_phantom_data() + { + return None; + } - // Ignore fields that aren't changed; it may - // be that we could get away with subtyping or - // something more accepting, but we use - // equality because we want to be able to - // perform this check without computing - // variance or constraining opaque types' hidden types. - // (This is because we may have to evaluate constraint - // expressions in the course of execution.) - // See e.g., #41936. - if a == b { - return None; - } + // Ignore fields that aren't changed; it may + // be that we could get away with subtyping or + // something more accepting, but we use + // equality because we want to be able to + // perform this check without computing + // variance or constraining opaque types' hidden types. + // (This is because we may have to evaluate constraint + // expressions in the course of execution.) + // See e.g., #41936. + if a == b { + return None; + } - // Collect up all fields that were significantly changed - // i.e., those that contain T in coerce_unsized T -> U - Some((i, a, b)) - }) - .collect::>(); + // Collect up all fields that were significantly changed + // i.e., those that contain T in coerce_unsized T -> U + Some((i, a, b)) + }) + .collect::>(); - if diff_fields.is_empty() { - if extract_coerce_pointee_data(tcx, def_a.did()).is_some() { - return Err(tcx.dcx().span_delayed_bug( + if diff_fields.is_empty() { + if extract_coerce_pointee_data(tcx, def_a.did()).is_some() { + return Err(tcx.dcx().span_delayed_bug( + span, + "a specialised diagnostic on multiple fields \ + to be unsize-coerced is expected from CoercePointee \ + but none was emitted", + )); + } + return Err(tcx.dcx().emit_err(errors::CoerceUnsizedOneField { span, - "a specialised message for CoercePointee is expected", - )); - } - return Err(tcx.dcx().emit_err(errors::CoerceUnsizedOneField { - span, - trait_name: "CoerceUnsized", - note: true, - })); - } else if diff_fields.len() > 1 { - if extract_coerce_pointee_data(tcx, def_a.did()).is_some() { - let spans = diff_fields - .iter() - .map(|&(idx, _, _)| tcx.def_span(fields[idx].did)) - .collect(); - return Err(tcx.dcx().emit_err(errors::CoercePointeeMultipleTargets { - spans, - diag_trait: "CoerceUnsized", + trait_name: "CoerceUnsized", + note: true, + })); + } else if diff_fields.len() > 1 { + if extract_coerce_pointee_data(tcx, def_a.did()).is_some() { + let spans = diff_fields + .iter() + .map(|&(idx, _, _)| tcx.def_span(fields[idx].did)) + .collect(); + return Err(tcx.dcx().emit_err(errors::CoercePointeeMultipleTargets { + spans, + diag_trait: "CoerceUnsized", + })); + } + let item = tcx.hir().expect_item(impl_did); + let span = + if let ItemKind::Impl(hir::Impl { of_trait: Some(t), .. }) = &item.kind { + t.path.span + } else { + tcx.def_span(impl_did) + }; + + return Err(tcx.dcx().emit_err(errors::CoerceUnsizedMulti { + span, + coercions_note: true, + number: diff_fields.len(), + coercions: diff_fields + .iter() + .map(|&(i, a, b)| format!("`{}` (`{}` to `{}`)", fields[i].name, a, b)) + .collect::>() + .join(", "), })); } - let item = tcx.hir().expect_item(impl_did); - let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(t), .. }) = &item.kind { - t.path.span - } else { - tcx.def_span(impl_did) - }; - return Err(tcx.dcx().emit_err(errors::CoerceUnsizedMulti { + let (i, a, b) = diff_fields[0]; + let kind = ty::adjustment::CustomCoerceUnsized::Struct(i); + let source_span = tcx.def_span(fields[i].did); + (a, b, coerce_unsized_trait, Some(kind), source_span) + } + + _ => { + return Err(tcx.dcx().emit_err(errors::DispatchFromDynStruct { span, - coercions_note: true, - number: diff_fields.len(), - coercions: diff_fields - .iter() - .map(|&(i, a, b)| format!("`{}` (`{}` to `{}`)", fields[i].name, a, b)) - .collect::>() - .join(", "), + trait_name: "CoerceUnsized", })); } - - let (i, a, b) = diff_fields[0]; - let kind = ty::adjustment::CustomCoerceUnsized::Struct(i); - (a, b, coerce_unsized_trait, Some(kind)) - } - - _ => { - return Err(tcx - .dcx() - .emit_err(errors::DispatchFromDynStruct { span, trait_name: "CoerceUnsized" })); - } - }; + }; // Register an obligation for `A: Trait`. let coerce_pointee_data = if let ty::Adt(def, _) = self_ty.kind() { @@ -613,10 +618,7 @@ pub(crate) fn coerce_unsized_info<'tcx>( None }; let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - let cause = traits::ObligationCause::misc( - span, - coerce_pointee_data.map_or(impl_did, |(_, impl_did)| impl_did.expect_local()), - ); + let cause = traits::ObligationCause::misc(span, impl_did); let obligation = Obligation::new( tcx, cause, @@ -626,19 +628,28 @@ pub(crate) fn coerce_unsized_info<'tcx>( ocx.register_obligation(obligation); let mut errors = ocx.select_all_or_error(); if !errors.is_empty() { - if let Some((pointee, _)) = coerce_pointee_data { + if let Some(pointee) = coerce_pointee_data { let ty::Adt(_def, args) = coerced_ty.kind() else { bug!() }; let target_pointee = args.type_at(pointee); let ty::Adt(_def, _) = self_ty.kind() else { bug!() }; errors = errors .into_iter() .filter_map(|err| { - redact_fulfillment_err_for_coerce_pointee(tcx, err, target_pointee, span) + redact_fulfillment_err_for_coerce_pointee( + tcx, + err, + target_pointee, + coercion_span, + ) }) .collect(); } if errors.is_empty() { - tcx.dcx().span_delayed_bug(span, "a specialised CoercePointee error is expected"); + tcx.dcx().span_delayed_bug( + span, + "all unstable trait fulfillment errors are redacted \ + but those specialised diagnostics are not emitted", + ); } else { infcx.err_ctxt().report_fulfillment_errors(errors); } diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin/diagnostics.rs b/compiler/rustc_hir_analysis/src/coherence/builtin/diagnostics.rs index 890381f5ce3c0..db6cd80030cfc 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin/diagnostics.rs @@ -14,26 +14,36 @@ use crate::errors; pub(super) fn extract_coerce_pointee_data<'tcx>( tcx: TyCtxt<'tcx>, adt_did: DefId, -) -> Option<(usize, DefId)> { +) -> Option { // It is decided that a query to cache these results is not necessary // for error reporting. // We can afford to recompute it on-demand. - if let Some(impls) = tcx.lang_items().get(LangItem::CoercePointeeValidated).and_then(|did| { - tcx.trait_impls_of(did).non_blanket_impls().get(&SimplifiedType::Adt(adt_did)) - }) && let [impl_did, ..] = impls[..] - { + if tcx.lang_items().get(LangItem::CoercePointeeValidated).map_or(false, |did| { + tcx.trait_impls_of(did).non_blanket_impls().contains_key(&SimplifiedType::Adt(adt_did)) + }) { // Search for the `#[pointee]` - let mut first_type = None; + enum Pointee { + None, + First(usize), + Ambiguous, + } + let mut first_type = Pointee::None; for (idx, param) in tcx.generics_of(adt_did).own_params.iter().enumerate() { if let GenericParamDefKind::Type { .. } = param.kind { - first_type = if first_type.is_some() { None } else { Some(idx) }; + match first_type { + Pointee::None => { + first_type = Pointee::First(idx); + } + Pointee::First(_) => first_type = Pointee::Ambiguous, + Pointee::Ambiguous => {} + } } if tcx.has_attr(param.def_id, sym::pointee) { - return Some((idx, impl_did)); + return Some(idx); } } - if let Some(idx) = first_type { - return Some((idx, impl_did)); + if let Pointee::First(idx) = first_type { + return Some(idx); } } None @@ -45,56 +55,13 @@ fn contains_coerce_pointee_target_pointee<'tcx>(ty: Ty<'tcx>, target_pointee_ty: found: bool, } impl<'tcx> TypeVisitor> for Search<'tcx> { - fn visit_binder>>( - &mut self, - t: &rustc_type_ir::Binder, T>, - ) { - if self.found { - return; - } - t.super_visit_with(self) - } - fn visit_ty(&mut self, t: Ty<'tcx>) { - if self.found { - return; - } if t == self.pointee { self.found = true; } else { t.super_visit_with(self) } } - - fn visit_region(&mut self, r: as ty::Interner>::Region) { - if self.found { - return; - } - if let rustc_type_ir::ReError(guar) = r.kind() { - self.visit_error(guar) - } - } - - fn visit_const(&mut self, c: as ty::Interner>::Const) { - if self.found { - return; - } - c.super_visit_with(self) - } - - fn visit_predicate(&mut self, p: as ty::Interner>::Predicate) { - if self.found { - return; - } - p.super_visit_with(self) - } - - fn visit_clauses(&mut self, p: as ty::Interner>::Clauses) { - if self.found { - return; - } - p.super_visit_with(self) - } } let mut search = Search { pointee: target_pointee_ty, found: false }; ty.visit_with(&mut search); @@ -118,14 +85,19 @@ pub(super) fn redact_fulfillment_err_for_coerce_pointee<'tcx>( ) }; let source = pred.trait_ref.self_ty(); - if tcx.is_lang_item(pred.def_id(), LangItem::Unsize) { - if mentions_pointee() { - // We should redact it - tcx.dcx() - .emit_err(errors::CoercePointeeCannotUnsize { ty: source.to_string(), span }); - return None; - } - } else if tcx.is_lang_item(pred.def_id(), LangItem::CoerceUnsized) && mentions_pointee() { + if tcx.is_lang_item(pred.def_id(), LangItem::DispatchFromDyn) && mentions_pointee() { + tcx.dcx().emit_err(errors::CoercePointeeCannotDispatchFromDyn { + ty: source.to_string(), + span, + }); + return None; + } + if tcx.is_lang_item(pred.def_id(), LangItem::Unsize) && mentions_pointee() { + // We should redact it + tcx.dcx().emit_err(errors::CoercePointeeCannotUnsize { ty: source.to_string(), span }); + return None; + } + if tcx.is_lang_item(pred.def_id(), LangItem::CoerceUnsized) && mentions_pointee() { // We should redact it tcx.dcx() .emit_err(errors::CoercePointeeCannotCoerceUnsize { ty: source.to_string(), span }); diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 3f6ab15683727..0d1ff84840d93 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1229,6 +1229,8 @@ pub(crate) struct CoercePointeeMultipleTargets { #[derive(Diagnostic)] #[diag(hir_analysis_coerce_pointee_cannot_unsize, code = E0802)] +#[note] +#[help] pub(crate) struct CoercePointeeCannotUnsize { #[primary_span] pub span: Span, @@ -1236,13 +1238,25 @@ pub(crate) struct CoercePointeeCannotUnsize { } #[derive(Diagnostic)] -#[diag(hir_analysis_coerce_pointee_cannot_coerce_unsize, code = E0802)] +#[diag(hir_analysis_coerce_pointee_cannot_coerce_unsized, code = E0802)] +#[note] +#[help] pub(crate) struct CoercePointeeCannotCoerceUnsize { #[primary_span] pub span: Span, pub ty: String, } +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_pointee_cannot_dispatch_from_dyn, code = E0802)] +#[note] +#[help] +pub(crate) struct CoercePointeeCannotDispatchFromDyn { + #[primary_span] + pub span: Span, + pub ty: String, +} + #[derive(Diagnostic)] #[diag(hir_analysis_inherent_ty_outside_relevant, code = E0390)] #[help] diff --git a/tests/ui/deriving/deriving-coerce-pointee-neg.rs b/tests/ui/deriving/deriving-coerce-pointee-neg.rs index eccc24a39db5a..f530d7b17257a 100644 --- a/tests/ui/deriving/deriving-coerce-pointee-neg.rs +++ b/tests/ui/deriving/deriving-coerce-pointee-neg.rs @@ -8,31 +8,38 @@ use std::marker::CoercePointee; #[derive(CoercePointee)] //~^ ERROR: `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]` +//~| NOTE: in this expansion of #[derive(CoercePointee)] enum NotStruct<'a, T: ?Sized> { Variant(&'a T), } #[derive(CoercePointee)] //~^ ERROR: `CoercePointee` can only be derived on `struct`s with at least one field +//~| NOTE: in this expansion of #[derive(CoercePointee)] #[repr(transparent)] -struct NoField<'a, #[pointee] T: ?Sized> {} -//~^ ERROR: lifetime parameter `'a` is never used -//~| ERROR: type parameter `T` is never used +struct NoField<#[pointee] T: ?Sized> {} +//~^ ERROR: type parameter `T` is never used +//~| NOTE: unused type parameter +//~| HELP: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` #[derive(CoercePointee)] //~^ ERROR: `CoercePointee` can only be derived on `struct`s with at least one field +//~| NOTE: in this expansion of #[derive(CoercePointee)] #[repr(transparent)] -struct NoFieldUnit<'a, #[pointee] T: ?Sized>(); -//~^ ERROR: lifetime parameter `'a` is never used -//~| ERROR: type parameter `T` is never used +struct NoFieldUnit<#[pointee] T: ?Sized>(); +//~^ ERROR: type parameter `T` is never used +//~| NOTE: unused type parameter +//~| HELP: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` #[derive(CoercePointee)] //~^ ERROR: `CoercePointee` can only be derived on `struct`s that are generic over at least one type +//~| NOTE: in this expansion of #[derive(CoercePointee)] #[repr(transparent)] struct NoGeneric<'a>(&'a u8); #[derive(CoercePointee)] //~^ ERROR: exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits +//~| NOTE: in this expansion of #[derive(CoercePointee)] #[repr(transparent)] struct AmbiguousPointee<'a, T1: ?Sized, T2: ?Sized> { a: (&'a T1, &'a T2), @@ -42,6 +49,7 @@ struct AmbiguousPointee<'a, T1: ?Sized, T2: ?Sized> { #[repr(transparent)] struct TooManyPointees<'a, #[pointee] A: ?Sized, #[pointee] B: ?Sized>((&'a A, &'a B)); //~^ ERROR: only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits +//~| NOTE: here another type parameter is marked as `#[pointee]` #[derive(CoercePointee)] struct NotTransparent<'a, #[pointee] T: ?Sized> { @@ -144,20 +152,41 @@ struct TryToWipeRepr<'a, #[pointee] T: ?Sized> { #[repr(transparent)] #[derive(CoercePointee)] -//~^ ERROR: `Box` cannot be coerced to any unsized value [E0802] -//~| ERROR: `Box` cannot be coerced to any unsized value [E0802] struct RcWithId { inner: std::rc::Rc<(i32, Box)>, + //~^ ERROR: `Box` cannot be coerced to an unsized value [E0802] + //~| NOTE: `derive(CoercePointee)` demands that `Box` can be coerced to an unsized type + //~| HELP: `derive(CoercePointee)` requires exactly one copy of `#[pointee]` type at the end of the `struct` definition, without any further pointer or reference indirection + //~| ERROR: `Box` cannot be coerced to an unsized value [E0802] + //~| NOTE: `derive(CoercePointee)` demands that `Box` can be coerced to an unsized type + //~| HELP: `derive(CoercePointee)` requires exactly one copy of `#[pointee]` type at the end of the `struct` definition, without any further pointer or reference indirection + //~| NOTE: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` } #[repr(transparent)] #[derive(CoercePointee)] struct MoreThanOneField { //~^ ERROR: transparent struct needs at most one field with non-trivial size or alignment, but has 2 [E0690] + //~| NOTE: needs at most one field with non-trivial size or alignment, but has 2 inner1: Box, //~^ ERROR: `derive(CoercePointee)` only admits exactly one data field, to which `dyn` methods shall be dispatched [E0802] //~| ERROR: `derive(CoercePointee)` only admits exactly one data field, on which unsize coercion shall be performed [E0802] + //~| NOTE: this field has non-zero size or requires alignment inner2: Box, + //~^ NOTE: this field has non-zero size or requires alignment } +struct NotCoercePointeeData(T); + +#[repr(transparent)] +#[derive(CoercePointee)] +struct UsingNonCoercePointeeData(NotCoercePointeeData); +//~^ ERROR: `NotCoercePointeeData` cannot be coerced to an unsized type [E0802] +//~| NOTE: `derive(CoercePointee)` demands that `NotCoercePointeeData` can be coerced to an unsized type +//~| HELP: the standard pointers such as `Arc`, `Rc`, `Box`, and other types with `derive(CoercePointee)` can be coerced to their corresponding unsized types +//~| ERROR: `NotCoercePointeeData` cannot be coerced to an unsized type, to which `dyn` methods can be dispatched [E0802] +//~| NOTE: `derive(CoercePointee)` demands that `dyn` methods can be dispatched when `NotCoercePointeeData` can be coerced to an unsized type +//~| HELP: `dyn` methods can be dispatched to the standard pointers such as `Arc`, `Rc`, `Box`, and other types with `derive(CoercePointee)` + + fn main() {} diff --git a/tests/ui/deriving/deriving-coerce-pointee-neg.stderr b/tests/ui/deriving/deriving-coerce-pointee-neg.stderr index 5f7faad0ffb29..30987419bf3f9 100644 --- a/tests/ui/deriving/deriving-coerce-pointee-neg.stderr +++ b/tests/ui/deriving/deriving-coerce-pointee-neg.stderr @@ -7,7 +7,7 @@ LL | #[derive(CoercePointee)] = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0802]: `CoercePointee` can only be derived on `struct`s with at least one field - --> $DIR/deriving-coerce-pointee-neg.rs:15:10 + --> $DIR/deriving-coerce-pointee-neg.rs:16:10 | LL | #[derive(CoercePointee)] | ^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | #[derive(CoercePointee)] = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0802]: `CoercePointee` can only be derived on `struct`s with at least one field - --> $DIR/deriving-coerce-pointee-neg.rs:22:10 + --> $DIR/deriving-coerce-pointee-neg.rs:25:10 | LL | #[derive(CoercePointee)] | ^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | #[derive(CoercePointee)] = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0802]: `CoercePointee` can only be derived on `struct`s that are generic over at least one type - --> $DIR/deriving-coerce-pointee-neg.rs:29:10 + --> $DIR/deriving-coerce-pointee-neg.rs:34:10 | LL | #[derive(CoercePointee)] | ^^^^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | #[derive(CoercePointee)] = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0802]: exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits - --> $DIR/deriving-coerce-pointee-neg.rs:34:10 + --> $DIR/deriving-coerce-pointee-neg.rs:40:10 | LL | #[derive(CoercePointee)] | ^^^^^^^^^^^^^ @@ -39,95 +39,80 @@ LL | #[derive(CoercePointee)] = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0802]: only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits - --> $DIR/deriving-coerce-pointee-neg.rs:43:39 + --> $DIR/deriving-coerce-pointee-neg.rs:50:39 | LL | struct TooManyPointees<'a, #[pointee] A: ?Sized, #[pointee] B: ?Sized>((&'a A, &'a B)); | ^ - here another type parameter is marked as `#[pointee]` error[E0802]: `derive(CoercePointee)` requires `T` to be marked `?Sized` - --> $DIR/deriving-coerce-pointee-neg.rs:54:36 + --> $DIR/deriving-coerce-pointee-neg.rs:62:36 | LL | struct NoMaybeSized<'a, #[pointee] T> { | ^ error: the `#[pointee]` attribute may only be used on generic parameters - --> $DIR/deriving-coerce-pointee-neg.rs:62:5 + --> $DIR/deriving-coerce-pointee-neg.rs:70:5 | LL | #[pointee] | ^^^^^^^^^^ error: the `#[pointee]` attribute may only be used on generic parameters - --> $DIR/deriving-coerce-pointee-neg.rs:72:33 + --> $DIR/deriving-coerce-pointee-neg.rs:80:33 | LL | struct UhOh<#[pointee] T>(T); | ^^^^^^^^^^ error: the `#[pointee]` attribute may only be used on generic parameters - --> $DIR/deriving-coerce-pointee-neg.rs:86:21 + --> $DIR/deriving-coerce-pointee-neg.rs:94:21 | LL | struct UhOh<#[pointee] T>(T); | ^^^^^^^^^^ error: the `#[pointee]` attribute may only be used on generic parameters - --> $DIR/deriving-coerce-pointee-neg.rs:101:25 + --> $DIR/deriving-coerce-pointee-neg.rs:109:25 | LL | struct UhOh<#[pointee] T>(T); | ^^^^^^^^^^ -error[E0392]: lifetime parameter `'a` is never used - --> $DIR/deriving-coerce-pointee-neg.rs:18:16 - | -LL | struct NoField<'a, #[pointee] T: ?Sized> {} - | ^^ unused lifetime parameter - | - = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` - error[E0392]: type parameter `T` is never used - --> $DIR/deriving-coerce-pointee-neg.rs:18:31 + --> $DIR/deriving-coerce-pointee-neg.rs:20:27 | -LL | struct NoField<'a, #[pointee] T: ?Sized> {} - | ^ unused type parameter +LL | struct NoField<#[pointee] T: ?Sized> {} + | ^ unused type parameter | = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` -error[E0392]: lifetime parameter `'a` is never used - --> $DIR/deriving-coerce-pointee-neg.rs:25:20 - | -LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>(); - | ^^ unused lifetime parameter - | - = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` - error[E0392]: type parameter `T` is never used - --> $DIR/deriving-coerce-pointee-neg.rs:25:35 + --> $DIR/deriving-coerce-pointee-neg.rs:29:31 | -LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>(); - | ^ unused type parameter +LL | struct NoFieldUnit<#[pointee] T: ?Sized>(); + | ^ unused type parameter | = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` error[E0802]: `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout - --> $DIR/deriving-coerce-pointee-neg.rs:47:1 + --> $DIR/deriving-coerce-pointee-neg.rs:55:1 | LL | struct NotTransparent<'a, #[pointee] T: ?Sized> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0802]: `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout - --> $DIR/deriving-coerce-pointee-neg.rs:140:1 + --> $DIR/deriving-coerce-pointee-neg.rs:148:1 | LL | struct TryToWipeRepr<'a, #[pointee] T: ?Sized> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0802]: `Box` cannot be coerced to any unsized value - --> $DIR/deriving-coerce-pointee-neg.rs:146:10 +error[E0802]: `Box` cannot be coerced to an unsized value + --> $DIR/deriving-coerce-pointee-neg.rs:156:5 | -LL | #[derive(CoercePointee)] - | ^^^^^^^^^^^^^ +LL | inner: std::rc::Rc<(i32, Box)>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: `derive(CoercePointee)` demands that `Box` can be coerced to an unsized type + = help: `derive(CoercePointee)` requires exactly one copy of `#[pointee]` type at the end of the `struct` definition, without any further pointer or reference indirection error[E0802]: `derive(CoercePointee)` only admits exactly one data field, to which `dyn` methods shall be dispatched - --> $DIR/deriving-coerce-pointee-neg.rs:157:5 + --> $DIR/deriving-coerce-pointee-neg.rs:171:5 | LL | inner1: Box, | ^^^^^^^^^^^^^^ @@ -135,17 +120,27 @@ LL | inner1: Box, LL | inner2: Box, | ^^^^^^^^^^^^^^ -error[E0802]: `Box` cannot be coerced to any unsized value - --> $DIR/deriving-coerce-pointee-neg.rs:146:10 +error[E0802]: `NotCoercePointeeData` cannot be coerced to an unsized type, to which `dyn` methods can be dispatched + --> $DIR/deriving-coerce-pointee-neg.rs:183:45 | -LL | #[derive(CoercePointee)] - | ^^^^^^^^^^^^^ +LL | struct UsingNonCoercePointeeData(NotCoercePointeeData); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `derive(CoercePointee)` demands that `dyn` methods can be dispatched when `NotCoercePointeeData` can be coerced to an unsized type + = help: `dyn` methods can be dispatched to the standard pointers such as `Arc`, `Rc`, `Box`, and other types with `derive(CoercePointee)` + +error[E0802]: `Box` cannot be coerced to an unsized value + --> $DIR/deriving-coerce-pointee-neg.rs:156:5 + | +LL | inner: std::rc::Rc<(i32, Box)>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: `derive(CoercePointee)` demands that `Box` can be coerced to an unsized type + = help: `derive(CoercePointee)` requires exactly one copy of `#[pointee]` type at the end of the `struct` definition, without any further pointer or reference indirection = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0802]: `derive(CoercePointee)` only admits exactly one data field, on which unsize coercion shall be performed - --> $DIR/deriving-coerce-pointee-neg.rs:157:5 + --> $DIR/deriving-coerce-pointee-neg.rs:171:5 | LL | inner1: Box, | ^^^^^^^^^^^^^^ @@ -153,12 +148,21 @@ LL | inner1: Box, LL | inner2: Box, | ^^^^^^^^^^^^^^ +error[E0802]: `NotCoercePointeeData` cannot be coerced to an unsized type + --> $DIR/deriving-coerce-pointee-neg.rs:183:45 + | +LL | struct UsingNonCoercePointeeData(NotCoercePointeeData); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `derive(CoercePointee)` demands that `NotCoercePointeeData` can be coerced to an unsized type + = help: the standard pointers such as `Arc`, `Rc`, `Box`, and other types with `derive(CoercePointee)` can be coerced to their corresponding unsized types + error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2 - --> $DIR/deriving-coerce-pointee-neg.rs:155:1 + --> $DIR/deriving-coerce-pointee-neg.rs:168:1 | LL | struct MoreThanOneField { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2 -LL | +... LL | inner1: Box, | -------------- this field has non-zero size or requires alignment ...