diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 4cac7cb93f581..e67dd98d92678 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -95,10 +95,6 @@ builtin_macros_cfg_accessible_literal_path = `cfg_accessible` path cannot be a l builtin_macros_cfg_accessible_multiple_paths = multiple `cfg_accessible` paths are specified builtin_macros_cfg_accessible_unspecified_path = `cfg_accessible` path is not specified -builtin_macros_coerce_pointee_requires_maybe_sized = `derive(CoercePointee)` requires `{$name}` to be marked `?Sized` - -builtin_macros_coerce_pointee_requires_one_field = `CoercePointee` can only be derived on `struct`s with at least one field - builtin_macros_coerce_pointee_requires_one_generic = `CoercePointee` can only be derived on `struct`s that are generic over at least one type builtin_macros_coerce_pointee_requires_one_pointee = exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 82417a86dd9ee..af787ce45ef21 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -3,11 +3,11 @@ use ast::ptr::P; use rustc_ast::mut_visit::MutVisitor; use rustc_ast::visit::BoundKind; use rustc_ast::{ - self as ast, GenericArg, GenericBound, GenericParamKind, ItemKind, MetaItem, - TraitBoundModifiers, VariantData, WherePredicate, + self as ast, GenericArg, GenericBound, GenericParamKind, Generics, ItemKind, MetaItem, + TraitBoundModifiers, TyAlias, WherePredicate, }; -use rustc_attr_parsing as attr; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; +use rustc_errors::E0802; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_macros::Diagnostic; use rustc_span::{Ident, Span, Symbol, sym}; @@ -30,25 +30,8 @@ pub(crate) fn expand_deriving_coerce_pointee( item.visit_with(&mut DetectNonGenericPointeeAttr { cx }); let (name_ident, generics) = if let Annotatable::Item(aitem) = item - && let ItemKind::Struct(struct_data, g) = &aitem.kind + && let ItemKind::Struct(_struct_data, g) = &aitem.kind { - let is_transparent = aitem.attrs.iter().any(|attr| { - attr::find_repr_attrs(cx.sess, attr) - .into_iter() - .any(|r| matches!(r, attr::ReprTransparent)) - }); - if !is_transparent { - cx.dcx().emit_err(RequireTransparent { span }); - return; - } - if !matches!( - struct_data, - VariantData::Struct { fields, recovered: _ } | VariantData::Tuple(fields, _) - if !fields.is_empty()) - { - cx.dcx().emit_err(RequireOneField { span }); - return; - } (aitem.ident, g) } else { cx.dcx().emit_err(RequireTransparent { span }); @@ -88,8 +71,7 @@ pub(crate) fn expand_deriving_coerce_pointee( } else { let mut pointees = type_params .iter() - .filter_map(|&(idx, span, is_pointee)| is_pointee.then_some((idx, span))) - .fuse(); + .filter_map(|&(idx, span, is_pointee)| is_pointee.then_some((idx, span))); match (pointees.next(), pointees.next()) { (Some((idx, _span)), None) => idx, (None, _) => { @@ -102,6 +84,7 @@ pub(crate) fn expand_deriving_coerce_pointee( } } }; + let pointee_ty_ident = generics.params[pointee_param_idx].ident; // Create the type of `self`. let path = cx.path_all(span, false, vec![name_ident], self_params.clone()); @@ -110,6 +93,67 @@ pub(crate) fn expand_deriving_coerce_pointee( // Declare helper function that adds implementation blocks. // FIXME(dingxiangfei2009): Investigate the set of attributes on target struct to be propagated to impls let attrs = thin_vec![cx.attr_word(sym::automatically_derived, span),]; + // # Validity assertion + // This will be checked later in `rustc_hir_analysis::coherence::builtins`. + { + let trait_path = + cx.path_all(span, true, path!(span, core::marker::CoercePointeeValidated), vec![]); + let trait_ref = cx.trait_ref(trait_path); + let pointee_assoc_item = cx.assoc_item( + span, + Ident::new(sym::Pointee, span), + thin_vec![], + ast::AssocItemKind::Type(Box::new(TyAlias { + defaultness: ast::Defaultness::Final, + generics: ast::Generics::default(), + where_clauses: ast::TyAliasWhereClauses::default(), + bounds: vec![], + ty: Some( + cx.ty(span, ast::TyKind::Path(None, cx.path_ident(span, pointee_ty_ident))), + ), + })), + ); + push(Annotatable::Item( + cx.item( + span, + Ident::empty(), + attrs.clone(), + ast::ItemKind::Impl(Box::new(ast::Impl { + safety: ast::Safety::Default, + polarity: ast::ImplPolarity::Positive, + defaultness: ast::Defaultness::Final, + constness: ast::Const::No, + generics: Generics { + params: generics + .params + .iter() + .map(|p| match &p.kind { + GenericParamKind::Lifetime => { + cx.lifetime_param(p.span(), p.ident, p.bounds.clone()) + } + GenericParamKind::Type { default: _ } => { + cx.typaram(p.span(), p.ident, p.bounds.clone(), None) + } + GenericParamKind::Const { ty, kw_span: _, default: _ } => cx + .const_param( + p.span(), + p.ident, + p.bounds.clone(), + ty.clone(), + None, + ), + }) + .collect(), + where_clause: generics.where_clause.clone(), + span: generics.span, + }, + of_trait: Some(trait_ref), + self_ty: self_type.clone(), + items: thin_vec![pointee_assoc_item], + })), + ), + )); + } let mut add_impl_block = |generics, trait_symbol, trait_args| { let mut parts = path!(span, core::ops); parts.push(Ident::new(trait_symbol, span)); @@ -144,7 +188,6 @@ pub(crate) fn expand_deriving_coerce_pointee( // // Find the `#[pointee]` parameter and add an `Unsize<__S>` bound to it. let mut impl_generics = generics.clone(); - let pointee_ty_ident = generics.params[pointee_param_idx].ident; let mut self_bounds; { let pointee = &mut impl_generics.params[pointee_param_idx]; @@ -155,10 +198,7 @@ pub(crate) fn expand_deriving_coerce_pointee( pointee_ty_ident.name, ) { - cx.dcx().emit_err(RequiresMaybeSized { - span: pointee_ty_ident.span, - name: pointee_ty_ident, - }); + cx.dcx().span_delayed_bug(pointee_ty_ident.span, "?Sized should be checked"); return; } let arg = GenericArg::Type(s_ty.clone()); @@ -430,46 +470,31 @@ impl<'a, 'b> rustc_ast::visit::Visitor<'a> for AlwaysErrorOnGenericParam<'a, 'b> } #[derive(Diagnostic)] -#[diag(builtin_macros_coerce_pointee_requires_transparent)] +#[diag(builtin_macros_coerce_pointee_requires_transparent, code = E0802)] struct RequireTransparent { #[primary_span] span: Span, } #[derive(Diagnostic)] -#[diag(builtin_macros_coerce_pointee_requires_one_field)] -struct RequireOneField { - #[primary_span] - span: Span, -} - -#[derive(Diagnostic)] -#[diag(builtin_macros_coerce_pointee_requires_one_generic)] +#[diag(builtin_macros_coerce_pointee_requires_one_generic, code = E0802)] struct RequireOneGeneric { #[primary_span] span: Span, } #[derive(Diagnostic)] -#[diag(builtin_macros_coerce_pointee_requires_one_pointee)] +#[diag(builtin_macros_coerce_pointee_requires_one_pointee, code = E0802)] struct RequireOnePointee { #[primary_span] span: Span, } #[derive(Diagnostic)] -#[diag(builtin_macros_coerce_pointee_too_many_pointees)] +#[diag(builtin_macros_coerce_pointee_too_many_pointees, code = E0802)] struct TooManyPointees { #[primary_span] one: Span, #[label] another: Span, } - -#[derive(Diagnostic)] -#[diag(builtin_macros_coerce_pointee_requires_maybe_sized)] -struct RequiresMaybeSized { - #[primary_span] - span: Span, - name: Ident, -} diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index a0a381638c061..da5c083df85f7 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -29,6 +29,13 @@ pub trait Unsize {} #[lang = "coerce_unsized"] pub trait CoerceUnsized {} +#[lang = "coerce_pointee_validated"] +pub trait CoercePointeeValidated { + /* compiler built-in */ + #[lang = "coerce_pointee_validated_pointee"] + type Pointee: ?Sized; +} + impl<'a, 'b: 'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {} impl, U: ?Sized> CoerceUnsized<*const U> for *const T {} diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs index 5a4ee0a198cef..e615e61c2018a 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core.rs @@ -1,6 +1,14 @@ #![feature( - no_core, lang_items, intrinsics, unboxed_closures, extern_types, - decl_macro, rustc_attrs, transparent_unions, auto_traits, freeze_impls, + no_core, + lang_items, + intrinsics, + unboxed_closures, + extern_types, + decl_macro, + rustc_attrs, + transparent_unions, + auto_traits, + freeze_impls, thread_local )] #![no_core] @@ -26,6 +34,13 @@ pub trait Unsize {} #[lang = "coerce_unsized"] pub trait CoerceUnsized {} +#[lang = "coerce_pointee_validated"] +pub trait CoercePointeeValidated { + /* compiler built-in */ + #[lang = "coerce_pointee_validated_pointee"] + type Pointee: ?Sized; +} + impl<'a, 'b: 'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {} impl, U: ?Sized> CoerceUnsized<*const U> for *const T {} @@ -35,13 +50,13 @@ impl, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} pub trait DispatchFromDyn {} // &T -> &U -impl<'a, T: ?Sized+Unsize, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} +impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} // &mut T -> &mut U -impl<'a, T: ?Sized+Unsize, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {} +impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {} // *const T -> *const U -impl, U: ?Sized> DispatchFromDyn<*const U> for *const T {} +impl, U: ?Sized> DispatchFromDyn<*const U> for *const T {} // *mut T -> *mut U -impl, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {} +impl, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {} impl, U: ?Sized> DispatchFromDyn> for Box {} #[lang = "legacy_receiver"] @@ -289,7 +304,6 @@ impl PartialEq for u32 { } } - impl PartialEq for u64 { fn eq(&self, other: &u64) -> bool { (*self) == (*other) @@ -476,7 +490,11 @@ fn panic_in_cleanup() -> ! { #[track_caller] fn panic_bounds_check(index: usize, len: usize) -> ! { unsafe { - libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index); + libc::printf( + "index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, + len, + index, + ); intrinsics::abort(); } } @@ -504,8 +522,7 @@ pub trait Deref { fn deref(&self) -> &Self::Target; } -pub trait Allocator { -} +pub trait Allocator {} impl Allocator for () {} @@ -699,19 +716,27 @@ pub struct VaList<'a>(&'a mut VaListImpl); #[rustc_builtin_macro] #[rustc_macro_transparency = "semitransparent"] -pub macro stringify($($t:tt)*) { /* compiler built-in */ } +pub macro stringify($($t:tt)*) { + /* compiler built-in */ +} #[rustc_builtin_macro] #[rustc_macro_transparency = "semitransparent"] -pub macro file() { /* compiler built-in */ } +pub macro file() { + /* compiler built-in */ +} #[rustc_builtin_macro] #[rustc_macro_transparency = "semitransparent"] -pub macro line() { /* compiler built-in */ } +pub macro line() { + /* compiler built-in */ +} #[rustc_builtin_macro] #[rustc_macro_transparency = "semitransparent"] -pub macro cfg() { /* compiler built-in */ } +pub macro cfg() { + /* compiler built-in */ +} pub static A_STATIC: u8 = 42; diff --git a/compiler/rustc_error_codes/src/error_codes/E0802.md b/compiler/rustc_error_codes/src/error_codes/E0802.md new file mode 100644 index 0000000000000..59061ff04359d --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0802.md @@ -0,0 +1,94 @@ +The target of `derive(CoercePointee)` macro has inadmissible specification for +a meaningful use. + +Erroneous code examples: + +The target data is not a `struct`. + +```compile_fail,E0802 +#![feature(coerce_pointee)] +use std::marker::CoercePointee; +#[derive(CoercePointee)] +enum NotStruct<'a, T: ?Sized> { + Variant(&'a T), +} +``` + +The target data has a layout that is not transparent, or `repr(transparent)` +in other words. + +```compile_fail,E0802 +#![feature(coerce_pointee)] +use std::marker::CoercePointee; +#[derive(CoercePointee)] +struct NotTransparent<'a, #[pointee] T: ?Sized> { + ptr: &'a T, +} +``` + +The target data has no data field. + +```compile_fail,E0802 +#![feature(coerce_pointee)] +use std::marker::CoercePointee; +#[derive(CoercePointee)] +#[repr(transparent)] +struct NoField<'a, #[pointee] T: ?Sized> {} +``` + +The target data is not generic over any data, or has no generic type parameter. + +```compile_fail,E0802 +#![feature(coerce_pointee)] +use std::marker::CoercePointee; +#[derive(CoercePointee)] +#[repr(transparent)] +struct NoGeneric<'a>(&'a u8); +``` + +The target data has multiple generic type parameters, but none is designated as +a pointee for coercion. + +```compile_fail,E0802 +#![feature(coerce_pointee)] +use std::marker::CoercePointee; +#[derive(CoercePointee)] +#[repr(transparent)] +struct AmbiguousPointee<'a, T1: ?Sized, T2: ?Sized> { + a: (&'a T1, &'a T2), +} +``` + +The target data has multiple generic type parameters that are designated as +pointees for coercion. + +```compile_fail,E0802 +#![feature(coerce_pointee)] +use std::marker::CoercePointee; +#[derive(CoercePointee)] +#[repr(transparent)] +struct TooManyPointees< + 'a, + #[pointee] A: ?Sized, + #[pointee] B: ?Sized> +((&'a A, &'a B)); +``` + +The type parameter that is designated as a pointee is not marked `?Sized`. + +```compile_fail,E0802 +#![feature(coerce_pointee)] +use std::marker::CoercePointee; +#[derive(CoercePointee)] +#[repr(transparent)] +struct NoMaybeSized<'a, #[pointee] T> { + ptr: &'a T, +} +``` + +In summary, the `CoercePointee` macro demands the type to be a `struct` that is +generic over at least one type or over more types, one of which is marked with +`#[pointee]`, and has at least one data field and adopts a `repr(transparent)` +layout. +The only generic type or the type marked with `#[pointee]` has to be also +marked as `?Sized`. diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index 0a30bdb48a09c..e970b16f61064 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -545,6 +545,7 @@ E0798: 0798, E0799: 0799, E0800: 0800, E0801: 0801, +E0802: 0802, ); ) } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 8bf09cf96b3f7..17603af5a11c2 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -1,7 +1,8 @@ use rustc_ast::ptr::P; use rustc_ast::util::literal; use rustc_ast::{ - self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp, attr, token, + self as ast, AnonConst, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp, + attr, token, }; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; @@ -138,6 +139,42 @@ impl<'a> ExtCtxt<'a> { } } + pub fn lifetime_param( + &self, + span: Span, + ident: Ident, + bounds: ast::GenericBounds, + ) -> ast::GenericParam { + ast::GenericParam { + id: ast::DUMMY_NODE_ID, + ident: ident.with_span_pos(span), + attrs: AttrVec::new(), + bounds, + is_placeholder: false, + kind: ast::GenericParamKind::Lifetime, + colon_span: None, + } + } + + pub fn const_param( + &self, + span: Span, + ident: Ident, + bounds: ast::GenericBounds, + ty: P, + default: Option, + ) -> ast::GenericParam { + ast::GenericParam { + id: ast::DUMMY_NODE_ID, + ident: ident.with_span_pos(span), + attrs: AttrVec::new(), + bounds, + is_placeholder: false, + kind: ast::GenericParamKind::Const { ty, kw_span: DUMMY_SP, default }, + colon_span: None, + } + } + pub fn trait_ref(&self, path: ast::Path) -> ast::TraitRef { ast::TraitRef { path, ref_id: ast::DUMMY_NODE_ID } } @@ -645,6 +682,28 @@ impl<'a> ExtCtxt<'a> { }) } + pub fn assoc_item( + &self, + span: Span, + name: Ident, + attrs: ast::AttrVec, + kind: ast::AssocItemKind, + ) -> P { + P(ast::Item { + ident: name, + attrs, + id: ast::DUMMY_NODE_ID, + kind, + vis: ast::Visibility { + span: span.shrink_to_lo(), + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, + span, + tokens: None, + }) + } + pub fn item_static( &self, span: Span, diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index b6689c95c4bb2..c37344f5df644 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -370,6 +370,9 @@ language_item_table! { PointerLike, sym::pointer_like, pointer_like, Target::Trait, GenericRequirement::Exact(0); + CoercePointeeValidated, sym::coerce_pointee_validated, coerce_pointee_validated_trait, Target::Trait, GenericRequirement::Exact(0); + CoercePointeeValidatedPointee, sym::coerce_pointee_validated_pointee, coerce_pointee_validated_pointee, Target::AssocTy, GenericRequirement::Exact(0); + ConstParamTy, sym::const_param_ty, const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0); UnsizedConstParamTy, sym::unsized_const_param_ty, unsized_const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0); @@ -429,9 +432,13 @@ language_item_table! { ContractCheckRequires, sym::contract_check_requires, contract_check_requires_fn, Target::Fn, GenericRequirement::None; } +/// The requirement imposed on the generics of a lang item pub enum GenericRequirement { + /// No restriction on the generics None, + /// A minimum number of generics that is demanded on a lang item Minimum(usize), + /// The number of generics must match precisely as stipulated Exact(usize), } diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index c28c1afcfe66c..c13156b4e6774 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -85,6 +85,31 @@ hir_analysis_cmse_output_stack_spill = .note1 = functions with the `"{$abi_name}"` 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_missing_maybe_sized = `derive(CoercePointee)` requires the `#[pointee]` to be `?Sized` + +hir_analysis_coerce_pointee_multiple_derive = `derive(CoercePointee)` is derived multiple times + .label = another derivation originates from here + +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 with a `pointee` type + +hir_analysis_coerce_pointee_no_generic_pointee = `CoercePointee` requires a `#[pointee]` generic type, but `{$got}` is designated as one + +hir_analysis_coerce_pointee_no_pointee = `CoercePointee` requires a `#[pointee]` generic type + .label = it was set to `{$ty}` but it is among the generic parameters + +hir_analysis_coerce_pointee_no_user_validity_assertion = asserting applicability of `derive(CoercePointee)` on a target data is forbidden + +hir_analysis_coerce_pointee_not_concrete_ty = `derive(CoercePointee)` is only applicable to `struct` + +hir_analysis_coerce_pointee_not_struct = `derive(CoercePointee)` is only applicable to `struct`, instead of `{$kind}` + +hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout + hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures hir_analysis_coerce_unsized_multi = implementing the trait `CoerceUnsized` requires multiple coercions diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 3511dbc625295..a56d8b54fc845 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -1,30 +1,35 @@ //! 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 std::u32; -use rustc_data_structures::fx::FxHashSet; +use diagnostics::redact_fulfillment_err_for_coerce_pointee; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::ItemKind; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::{DefId, DefIdMap, DefIdMapEntry, 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::{ self, Ty, TyCtxt, TypeVisitableExt, TypingMode, suggest_constraining_type_params, }; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::misc::{ ConstParamTyImplementationError, CopyImplementationError, InfringingFieldsReason, type_allowed_to_implement_const_param_ty, type_allowed_to_implement_copy, }; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCtxt}; -use tracing::debug; +use tracing::{debug, instrument}; use crate::errors; @@ -48,6 +53,10 @@ pub(super) fn check_trait<'tcx>( checker .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn)?; checker.check(lang_items.pointer_like(), visit_implementation_of_pointer_like)?; + checker.check( + lang_items.coerce_pointee_validated_trait(), + visit_implementation_of_coerce_pointee_validity, + )?; Ok(()) } @@ -183,10 +192,10 @@ fn visit_implementation_of_const_param_ty( } } +#[instrument(level = "debug", skip(checker), fields(impl_did = ?checker.impl_def_id))] fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { let tcx = checker.tcx; let impl_did = checker.impl_def_id; - debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did); // Just compute this for the side-effects, in particular reporting // errors; other parts of the code may demand it for the info of @@ -195,11 +204,11 @@ fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), E tcx.at(span).ensure_ok().coerce_unsized_info(impl_did) } +#[instrument(level = "debug", skip(checker), fields(impl_did = ?checker.impl_def_id))] fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { let tcx = checker.tcx; let impl_did = checker.impl_def_id; let trait_ref = checker.impl_header.trait_ref.instantiate_identity(); - debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did); let span = tcx.def_span(impl_did); @@ -212,18 +221,34 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() trait_ref.args.type_at(1) }; + let coerce_pointee_data = if let ty::Adt(def, source_args) = trait_ref.self_ty().kind() + && let ty::Adt(_, target_args) = target.kind() + && let Some(coerce_pointee_info) = tcx.coerce_pointee_data(()).get(&def.did()) + { + let (pointee_idx, impl_def_id) = + try_extract_coerce_pointee_data(tcx, span, coerce_pointee_info)?; + + let source_pointee_ty = source_args.type_at(pointee_idx); + let target_pointee_ty = target_args.type_at(pointee_idx); + Some((source_pointee_ty, target_pointee_ty, impl_def_id.expect_local())) + } else { + None + }; - debug!("visit_implementation_of_dispatch_from_dyn: {:?} -> {:?}", source, target); + debug!("{:?} -> {:?}", source, target); let param_env = tcx.param_env(impl_did); let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); - let cause = ObligationCause::misc(span, impl_did); + let cause = ObligationCause::misc( + span, + coerce_pointee_data.map_or(impl_did, |(_, _, impl_did)| impl_did), + ); // Later parts of the compiler rely on all DispatchFromDyn types to be ABI-compatible with raw // pointers. This is enforced here: we only allow impls for references, raw pointers, and things // that are effectively repr(transparent) newtypes around types that already hav a - // DispatchedFromDyn impl. We cannot literally use repr(transparent) on those types since some + // DispatchFromDyn impl. We cannot literally use repr(transparent) on those types since some // of them support an allocator, but we ensure that for the cases where the type implements this // trait, they *do* satisfy the repr(transparent) rules, and then we assume that everything else // in the compiler (in particular, all the call ABI logic) will treat them as repr(transparent) @@ -303,29 +328,46 @@ 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 coerce_pointee_data.is_some() { + // `CoercePointeeValidated` will report a more specific error + res = Err(tcx.dcx().span_delayed_bug( + span, + "a more specific error from 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 coerce_pointee_data.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 { @@ -339,8 +381,29 @@ 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() { + // Redact over `Unsize` bound errors if the origin of this impl is CoercePointee + if let Some((source_pointee_ty, target_pointee_ty, _impl_did)) = + coerce_pointee_data + { + let new_pointee_ty = Ty::new_param( + tcx, + u32::MAX, + Symbol::intern(&format!("{source_pointee_ty} {{coerced}}")), + ); + errors = errors + .into_iter() + .map(|err| { + redact_fulfillment_err_for_coerce_pointee( + tcx, + err, + target_pointee_ty, + new_pointee_ty, + ) + }) + .collect(); + } res = Err(infcx.err_ctxt().report_fulfillment_errors(errors)); } @@ -349,17 +412,20 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() } res } + (&Adt(def, _), _) if tcx.coerce_pointee_data(()).contains_key(&def.did()) => { + Err(tcx.dcx().span_delayed_bug(span, "a specific error for CoercePointee is expected")) + } _ => Err(tcx .dcx() .emit_err(errors::CoerceUnsizedMay { span, trait_name: "DispatchFromDyn" })), } } +#[instrument(level = "debug", skip(tcx))] pub(crate) fn coerce_unsized_info<'tcx>( tcx: TyCtxt<'tcx>, impl_did: LocalDefId, ) -> Result { - debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did); let span = tcx.def_span(impl_did); let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span)); @@ -370,12 +436,25 @@ pub(crate) fn coerce_unsized_info<'tcx>( 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); - debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target); + let coerce_pointee_data = if let ty::Adt(def, source_args) = trait_ref.self_ty().kind() + && let ty::Adt(_, target_args) = target.kind() + && let Some(coerce_pointee_info) = tcx.coerce_pointee_data(()).get(&def.did()) + { + let (pointee_idx, impl_def_id) = + try_extract_coerce_pointee_data(tcx, span, coerce_pointee_info)?; + + let source_pointee_ty = source_args.type_at(pointee_idx); + let target_pointee_ty = target_args.type_at(pointee_idx); + Some((def.did(), source_pointee_ty, target_pointee_ty, impl_def_id.expect_local())) + } else { + None + }; + debug!("{:?} -> {:?} (bound)", source, target); let param_env = tcx.param_env(impl_did); assert!(!source.has_escaping_bound_vars()); - debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target); + debug!("{:?} -> {:?} (free)", source, target); let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let cause = ObligationCause::misc(span, impl_did); @@ -504,12 +583,30 @@ pub(crate) fn coerce_unsized_info<'tcx>( .collect::>(); if diff_fields.is_empty() { - return Err(tcx.dcx().emit_err(errors::CoerceUnsizedOneField { - span, - trait_name: "CoerceUnsized", - note: true, - })); + if let Some((did, _source_ty, _target_ty, _impl_did)) = coerce_pointee_data { + // This is the case when `pointee` is not used in *one* data field + return Err(tcx + .dcx() + .emit_err(errors::CoercePointeeNoField { span: tcx.def_span(did) })); + } else { + return Err(tcx.dcx().emit_err(errors::CoerceUnsizedOneField { + span, + trait_name: "CoerceUnsized", + note: true, + })); + } } else if diff_fields.len() > 1 { + if coerce_pointee_data.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 @@ -541,9 +638,15 @@ pub(crate) fn coerce_unsized_info<'tcx>( } }; - // Register an obligation for `A: Trait`. + // Register an obligation for `A: Trait` where + // - `A` is the source + // - `B` is the target + // - `Trait` is either `Unsize` or `CoerceUnsized` itself again let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - let cause = traits::ObligationCause::misc(span, impl_did); + let cause = ObligationCause::misc( + span, + coerce_pointee_data.map_or(impl_did, |(_, _, _, impl_did)| impl_did), + ); let obligation = Obligation::new( tcx, cause, @@ -551,8 +654,26 @@ 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() { + if let Some((_did, source_pointee_ty, target_pointee_ty, _impl_did)) = coerce_pointee_data { + let new_pointee_ty = Ty::new_param( + tcx, + u32::MAX, + Symbol::intern(&format!("{source_pointee_ty} {{coerced}}")), + ); + errors = errors + .into_iter() + .map(|err| { + redact_fulfillment_err_for_coerce_pointee( + tcx, + err, + target_pointee_ty, + new_pointee_ty, + ) + }) + .collect(); + } infcx.err_ctxt().report_fulfillment_errors(errors); } @@ -782,3 +903,184 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err .with_note(why_disqualified) .emit()) } + +fn visit_implementation_of_coerce_pointee_validity( + checker: &Checker<'_>, +) -> Result<(), ErrorGuaranteed> { + let tcx = checker.tcx; + let impl_did = checker.impl_def_id; + let self_ty = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity().self_ty(); + let span = tcx.def_span(impl_did); + if !tcx.is_builtin_derived(impl_did.into()) { + return Err(tcx.dcx().emit_err(errors::CoercePointeeNoUserValidityAssertion { span })); + } + let ty::Adt(def, args) = self_ty.kind() else { + return Err(tcx.dcx().emit_err(errors::CoercePointeeNotConcreteType { span })); + }; + let did = def.did(); + let Some(info) = tcx.coerce_pointee_data(()).get(&did) else { + return Err(tcx.dcx().emit_err(errors::CoercePointeeNoUserValidityAssertion { span })); + }; + let pointee_idx = match info { + &ty::CoercePointeeInfo::Validated { pointee_index_in_args, .. } => pointee_index_in_args, + ty::CoercePointeeInfo::PointeeUnnormalized { ty, .. } => { + return Err(tcx.dcx().emit_err(errors::CoercePointeePointeeNotGenericPointee { + span, + got: ty.to_string(), + })); + } + ty::CoercePointeeInfo::PointeeIsConst { konst, .. } => { + return Err(tcx.dcx().emit_err(errors::CoercePointeePointeeNotGenericPointee { + span, + got: konst.to_string(), + })); + } + ty::CoercePointeeInfo::Duplicated { impls } => { + return Err(tcx.dcx().emit_err(errors::CoercePointeePointeeMultipleDerive { + spans: impls.iter().copied().map(|did| tcx.def_span(did)).collect(), + })); + } + ty::CoercePointeeInfo::PointeeNotFound { ty, .. } => { + return Err(tcx + .dcx() + .emit_err(errors::CoercePointeeNoPointee { span, ty: ty.to_string() })); + } + }; + // Now get a more precise span of the `struct`. + let span = tcx.def_span(did); + if !def.is_struct() { + return Err(tcx + .dcx() + .emit_err(errors::CoercePointeeNotStruct { span, kind: def.descr().into() })); + } + if !def.repr().transparent() { + return Err(tcx.dcx().emit_err(errors::CoercePointeeNotTransparent { span })); + } + if def.all_fields().next().is_none() { + return Err(tcx.dcx().span_delayed_bug( + span, + "a specific error from CoercePointee is expected in CoerceUnsized coherence check", + )); + } + + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let param_env = tcx.param_env(impl_did); + let ocx = ObligationCtxt::new(&infcx); + let cause = ObligationCause::misc(span, impl_did); + let pointee_ty = args.type_at(pointee_idx); + let obligation = Obligation::new( + tcx, + cause, + param_env, + ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, None), [pointee_ty]), + ); + ocx.register_obligation(obligation); + if ocx.select_all_or_error().is_empty() { + return Err(tcx.dcx().emit_err(errors::CoercePointeePointeeMissingMaybeSized { span })); + } + + Ok(()) +} + +fn try_extract_coerce_pointee_data<'tcx>( + tcx: TyCtxt<'tcx>, + span: Span, + info: &ty::CoercePointeeInfo<'tcx>, +) -> Result<(usize, DefId), ErrorGuaranteed> { + match info { + &ty::CoercePointeeInfo::Validated { pointee_index_in_args, impl_def_id } => { + Ok((pointee_index_in_args, impl_def_id)) + } + ty::CoercePointeeInfo::PointeeUnnormalized { .. } + | ty::CoercePointeeInfo::PointeeIsConst { .. } + | ty::CoercePointeeInfo::PointeeNotFound { .. } + | ty::CoercePointeeInfo::Duplicated { .. } => Err(tcx.dcx().span_delayed_bug( + span, + "a more specific error for malformed CoercePointee is expected", + )), + } +} + +#[instrument(level = "debug", skip(tcx))] +pub(super) fn coerce_pointee_data<'tcx>( + tcx: TyCtxt<'tcx>, + _: (), +) -> &'tcx DefIdMap> { + let mut infos: DefIdMap> = <_>::default(); + let mut dups: FxIndexMap> = <_>::default(); + let coerce_pointee_validated = tcx.require_lang_item(LangItem::CoercePointeeValidated, None); + let coerce_pointee_validated_pointee = + tcx.require_lang_item(LangItem::CoercePointeeValidatedPointee, None); + 'impls: for impl_did in tcx.all_impls(coerce_pointee_validated) { + let ty = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity().self_ty(); + let ty::Adt(def, struct_args) = ty.kind() else { continue }; + let did = def.did(); + let mut insert_once = |info| { + fn extract_impl_did(info: &ty::CoercePointeeInfo<'_>) -> DefId { + use ty::CoercePointeeInfo::*; + match info { + PointeeIsConst { impl_def_id, .. } + | PointeeNotFound { impl_def_id, .. } + | PointeeUnnormalized { impl_def_id, .. } + | Validated { impl_def_id, .. } => *impl_def_id, + Duplicated { .. } => { + bug!("we shouldn't report duplicates yet") + } + } + } + if dups.contains_key(&did) { + dups.entry(did).or_default().push(impl_did) + } else { + match infos.entry(did) { + DefIdMapEntry::Occupied(entry) => { + let one_impl_did = extract_impl_did(&info); + let another_impl_did = extract_impl_did(&entry.remove()); + dups.insert(did, vec![one_impl_did, another_impl_did]); + } + DefIdMapEntry::Vacant(entry) => { + entry.insert(info); + } + } + } + }; + + let pointee = match tcx.try_normalize_erasing_regions( + ty::TypingEnv::non_body_analysis(tcx, did), + Ty::new_projection(tcx, coerce_pointee_validated_pointee, [ty]), + ) { + Ok(ty) => ty, + Err(ty::normalize_erasing_regions::NormalizationError::Type(ty)) => { + insert_once(ty::CoercePointeeInfo::PointeeUnnormalized { + ty, + impl_def_id: impl_did, + }); + continue; + } + Err(ty::normalize_erasing_regions::NormalizationError::Const(konst)) => { + insert_once(ty::CoercePointeeInfo::PointeeIsConst { konst, impl_def_id: impl_did }); + continue; + } + }; + for (idx, arg) in struct_args.iter().enumerate() { + if let Some(arg) = arg.as_type() + && arg == pointee + { + insert_once(ty::CoercePointeeInfo::Validated { + pointee_index_in_args: idx, + impl_def_id: impl_did, + }); + continue 'impls; + } + } + debug!(?struct_args, ?pointee, "pointee not found"); + insert_once(ty::CoercePointeeInfo::PointeeNotFound { ty: pointee, impl_def_id: impl_did }); + } + + for (did, impls) in dups { + infos.insert(did, ty::CoercePointeeInfo::Duplicated { + impls: tcx.arena.alloc_from_iter(impls), + }); + } + + tcx.arena.alloc(infos) +} 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..d60d822e2fa81 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/coherence/builtin/diagnostics.rs @@ -0,0 +1,142 @@ +use std::fmt::Debug; + +use rustc_infer::traits::ObligationCauseCode; +use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeFolder}; +use rustc_trait_selection::traits::{self, FulfillmentError}; +use rustc_type_ir::fold::TypeSuperFoldable; +use tracing::instrument; + +fn redact_coerce_pointee_target_pointee<'tcx, T: Debug + TypeFoldable>>( + tcx: TyCtxt<'tcx>, + target: T, + target_pointee: Ty<'tcx>, + new_pointee: Ty<'tcx>, +) -> T { + struct Redactor<'tcx> { + tcx: TyCtxt<'tcx>, + redacted: Ty<'tcx>, + redact_into: Ty<'tcx>, + } + + impl<'tcx> TypeFolder> for Redactor<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if ty == self.redacted { + return self.redact_into; + } + ty.super_fold_with(self) + } + } + target.fold_with(&mut Redactor { tcx, redacted: target_pointee, redact_into: new_pointee }) +} + +#[instrument(level = "debug", skip(tcx, err))] +pub(super) fn redact_fulfillment_err_for_coerce_pointee<'tcx>( + tcx: TyCtxt<'tcx>, + mut err: FulfillmentError<'tcx>, + target_pointee_ty: Ty<'tcx>, + new_pointee_ty: Ty<'tcx>, +) -> FulfillmentError<'tcx> { + use traits::FulfillmentErrorCode::*; + + err.obligation = redact_coerce_pointee_target_pointee( + tcx, + err.obligation, + target_pointee_ty, + new_pointee_ty, + ); + err.obligation.cause.map_code(|_| ObligationCauseCode::Misc); + err.root_obligation = redact_coerce_pointee_target_pointee( + tcx, + err.root_obligation, + target_pointee_ty, + new_pointee_ty, + ); + err.root_obligation.cause.map_code(|_| ObligationCauseCode::Misc); + err.code = match err.code { + Cycle(obs) => Cycle( + obs.into_iter() + .map(|obg| { + redact_coerce_pointee_target_pointee( + tcx, + obg, + target_pointee_ty, + new_pointee_ty, + ) + }) + .collect(), + ), + Select(selection_error) => { + use traits::SelectionError::*; + Select(match selection_error { + ConstArgHasWrongType { ct, ct_ty, expected_ty } => ConstArgHasWrongType { + ct: redact_coerce_pointee_target_pointee( + tcx, + ct, + target_pointee_ty, + new_pointee_ty, + ), + ct_ty: redact_coerce_pointee_target_pointee( + tcx, + ct_ty, + target_pointee_ty, + new_pointee_ty, + ), + expected_ty: redact_coerce_pointee_target_pointee( + tcx, + expected_ty, + target_pointee_ty, + new_pointee_ty, + ), + }, + SignatureMismatch(..) + | Unimplemented + | TraitDynIncompatible(..) + | NotConstEvaluatable(..) + | Overflow(..) + | OpaqueTypeAutoTraitLeakageUnknown(..) => selection_error, + }) + } + Project(mut err) => { + err.err = redact_coerce_pointee_target_pointee( + tcx, + err.err, + target_pointee_ty, + new_pointee_ty, + ); + Project(err) + } + Subtype(expected_found, type_error) => Subtype( + redact_coerce_pointee_target_pointee( + tcx, + expected_found, + target_pointee_ty, + new_pointee_ty, + ), + redact_coerce_pointee_target_pointee( + tcx, + type_error, + target_pointee_ty, + new_pointee_ty, + ), + ), + ConstEquate(expected_found, type_error) => ConstEquate( + redact_coerce_pointee_target_pointee( + tcx, + expected_found, + target_pointee_ty, + new_pointee_ty, + ), + redact_coerce_pointee_target_pointee( + tcx, + type_error, + target_pointee_ty, + new_pointee_ty, + ), + ), + err @ Ambiguity { .. } => err, + }; + err +} diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 1bc60087ab5ee..9c3872ace5190 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -124,7 +124,7 @@ fn enforce_empty_impls_for_marker_traits( } pub(crate) fn provide(providers: &mut Providers) { - use self::builtin::coerce_unsized_info; + use self::builtin::{coerce_pointee_data, coerce_unsized_info}; use self::inherent_impls::{ crate_incoherent_impls, crate_inherent_impls, crate_inherent_impls_validity_check, inherent_impls, @@ -141,6 +141,7 @@ pub(crate) fn provide(providers: &mut Providers) { crate_inherent_impls_overlap_check, coerce_unsized_info, orphan_check_impl, + coerce_pointee_data, ..*providers }; } diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 9769be302264d..0601798297687 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1180,6 +1180,80 @@ pub(crate) struct DispatchFromDynRepr { pub span: Span, } +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_pointee_not_struct, code = E0802)] +pub(crate) struct CoercePointeeNotStruct { + #[primary_span] + pub span: Span, + pub kind: String, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_pointee_not_concrete_ty, code = E0802)] +pub(crate) struct CoercePointeeNotConcreteType { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_pointee_no_user_validity_assertion, code = E0802)] +pub(crate) struct CoercePointeeNoUserValidityAssertion { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_pointee_not_transparent, code = E0802)] +pub(crate) struct CoercePointeeNotTransparent { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_pointee_no_field, code = E0802)] +pub(crate) struct CoercePointeeNoField { + #[primary_span] + 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_no_pointee, code = E0802)] +pub(crate) struct CoercePointeeNoPointee { + #[primary_span] + pub span: Span, + pub ty: String, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_pointee_no_generic_pointee, code = E0802)] +pub(crate) struct CoercePointeePointeeNotGenericPointee { + #[primary_span] + pub span: Span, + pub got: String, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_pointee_multiple_derive, code = E0802)] +pub(crate) struct CoercePointeePointeeMultipleDerive { + #[primary_span] + pub spans: Vec, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_pointee_missing_maybe_sized, code = E0802)] +pub(crate) struct CoercePointeePointeeMissingMaybeSized { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(hir_analysis_inherent_ty_outside_relevant, code = E0390)] #[help] diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 69b2f92f7166b..62524008e115b 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -90,6 +90,10 @@ macro_rules! arena_types { [] autodiff_item: rustc_ast::expand::autodiff_attrs::AutoDiffItem, [] ordered_name_set: rustc_data_structures::fx::FxIndexSet, [] pats: rustc_middle::ty::PatternKind<'tcx>, + [] coerce_pointee_data: rustc_data_structures::unord::UnordMap< + rustc_hir::def_id::DefId, + rustc_middle::ty::CoercePointeeInfo<'tcx>, + >, // Note that this deliberately duplicates items in the `rustc_hir::arena`, // since we need to allocate this type on both the `rustc_hir` arena diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d94efe2d7d6f8..416b58db644ab 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1096,6 +1096,10 @@ rustc_queries! { separate_provide_extern return_result_from_ensure_ok } + /// Caches `CoercePointee` data types + query coerce_pointee_data(key: ()) -> &'tcx DefIdMap> { + desc { |tcx| "computing CoercePointee data types" } + } query typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { desc { |tcx| "type-checking `{}`", tcx.def_path_str(key) } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 6fe1502c66dd6..563a28ac6c52b 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2233,6 +2233,35 @@ pub struct DestructuredConst<'tcx> { pub fields: &'tcx [ty::Const<'tcx>], } +/// Information collected on `CoercePointee` data for diagnostics and coherence checks +#[derive(Clone, Copy, Debug, TyEncodable, TyDecodable, HashStable)] +pub enum CoercePointeeInfo<'tcx> { + Validated { + pointee_index_in_args: usize, + /// The `DefId` of the implementation of `CoercePointeeValidated` trait + impl_def_id: DefId, + }, + /// `#[pointee]` cannot be resolved to a specific type generic + PointeeUnnormalized { + ty: Ty<'tcx>, + impl_def_id: DefId, + }, + /// `#[pointee]` is pointing to a const generic + PointeeIsConst { + konst: ty::Const<'tcx>, + impl_def_id: DefId, + }, + /// Duplicated CoercePointee validation requests. + /// The case that probably hints at an internal compiler issue. + Duplicated { + impls: &'tcx [DefId], + }, + PointeeNotFound { + ty: Ty<'tcx>, + impl_def_id: DefId, + }, +} + // Some types are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c819d43323583..28c9b86a7340d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -193,6 +193,7 @@ symbols! { Cleanup, Clone, CoercePointee, + CoercePointeeValidated, CoerceUnsized, Command, ConstParamTy, @@ -290,6 +291,7 @@ symbols! { PathBuf, Pending, PinCoerceUnsized, + Pointee, Pointer, Poll, ProcMacro, @@ -619,6 +621,8 @@ symbols! { cmp_partialord_lt, cmpxchg16b_target_feature, cmse_nonsecure_entry, + coerce_pointee_validated, + coerce_pointee_validated_pointee, coerce_unsized, cold, cold_path, diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 055a3edcc3293..171131c16068d 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -121,6 +121,9 @@ trait_selection_closure_kind_mismatch = expected a closure that implements the ` trait_selection_closure_kind_requirement = the requirement to implement `{$trait_prefix}{$expected}` derives from here +trait_selection_coerce_pointee_overlapping = the implementation of the unstable trait `{$trait_name}` conflicts with derivations from `derive(CoercePointee)` + .label = the conflicting implementation is here + trait_selection_compare_impl_item_obligation = ...so that the definition in impl matches the definition from the trait trait_selection_consider_specifying_length = consider specifying the actual array length trait_selection_data_flows = ...but data{$label_var1_exists -> diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 62cac5b17bd04..071c2c56f3d01 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1624,6 +1624,16 @@ pub enum TypeErrorAdditionalDiags { }, } +#[derive(Diagnostic)] +#[diag(trait_selection_coerce_pointee_overlapping)] +pub(crate) struct CoercePointeeOverlapping { + #[primary_span] + pub span: Span, + #[label] + pub conflicting: Span, + pub trait_name: &'static str, +} + #[derive(Diagnostic)] pub enum ObligationCauseFailureCode { #[diag(trait_selection_oc_method_compat, code = E0308)] diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index cb3e81f5477fc..2d8dbd6336f32 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -14,6 +14,7 @@ pub mod specialization_graph; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::codes::*; use rustc_errors::{Diag, EmissionGuarantee}; +use rustc_hir::LangItem; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::traits::Obligation; use rustc_middle::bug; @@ -27,7 +28,7 @@ use specialization_graph::GraphExt; use tracing::{debug, instrument}; use crate::error_reporting::traits::to_pretty_impl_header; -use crate::errors::NegativePositiveConflict; +use crate::errors::{self, NegativePositiveConflict}; use crate::infer::{InferCtxt, TyCtxtInferExt}; use crate::traits::select::IntercrateAmbiguityCause; use crate::traits::{ @@ -397,6 +398,9 @@ pub(super) fn specialization_graph_provider( ) -> Result<&'_ specialization_graph::Graph, ErrorGuaranteed> { let mut sg = specialization_graph::Graph::new(); let overlap_mode = specialization_graph::OverlapMode::get(tcx, trait_id); + let is_coerce_pointee_validated = tcx.is_lang_item(trait_id, LangItem::CoercePointeeValidated); + let is_coerce_unsized = tcx.is_lang_item(trait_id, LangItem::CoerceUnsized); + let is_dispatch_from_dyn = tcx.is_lang_item(trait_id, LangItem::DispatchFromDyn); let mut trait_impls: Vec<_> = tcx.all_impls(trait_id).collect(); @@ -421,6 +425,64 @@ pub(super) fn specialization_graph_provider( }; if let Some(overlap) = overlap { + if is_coerce_pointee_validated { + // If the trait is `CoercePointeeValidated`, skip over because it is + // reported by `coerce_pointee_data` query. + errored = errored.and(Err(tcx.dcx().span_delayed_bug( + tcx.def_span(impl_def_id), + "A special error for duplicated CoercePointee is expected", + ))); + continue; + } + if is_coerce_unsized || is_dispatch_from_dyn { + match try_extract_coerce_pointee_span(tcx, impl_def_id.to_def_id()) { + CoercePointeeExtractionResult::OneImpl(did) => { + // We approximate the impl assocation + // by checking the span containment + let main_span = tcx.def_span(did); + let one_span = tcx.def_span(impl_def_id); + let another_span = tcx.def_span(overlap.with_impl); + let conflicting = if main_span.contains(one_span) { + Some(another_span) + } else if main_span.contains(another_span) { + Some(one_span) + } else { + None + }; + if let Some(conflicting) = conflicting { + // This is generated by `derive(CoercePointee)` + errored = errored.and(Err(tcx.dcx().emit_err( + errors::CoercePointeeOverlapping { + span: main_span, + conflicting, + trait_name: if is_coerce_unsized { + "CoerceUnsized" + } else if is_dispatch_from_dyn { + "DispatchFromDyn" + } else { + bug!("incorrect enumeration of CoercePointee associated traits") + }, + }, + ))); + continue; + } + // Otherwise this is another pair of overlapping impl + // so we should report it + } + CoercePointeeExtractionResult::Duplicated => { + // Indeed this error should be reported by coherence check + // with better wording + errored = errored.and(Err(tcx.dcx().span_delayed_bug( + tcx.def_span(impl_def_id), + "A special error for duplicated CoercePointee is expected", + ))); + continue; + } + CoercePointeeExtractionResult::NotCoercePointee => { + // Continue with the error reporting + } + } + } errored = errored.and(report_overlap_conflict( tcx, overlap, @@ -438,6 +500,34 @@ pub(super) fn specialization_graph_provider( Ok(tcx.arena.alloc(sg)) } +enum CoercePointeeExtractionResult { + OneImpl(DefId), + Duplicated, + NotCoercePointee, +} + +fn try_extract_coerce_pointee_span<'tcx>( + tcx: TyCtxt<'tcx>, + impl_id: DefId, +) -> CoercePointeeExtractionResult { + let header = tcx.impl_trait_header(impl_id).unwrap(); + let ty::Adt(def, _args) = header.trait_ref.instantiate_identity().self_ty().kind() else { + return CoercePointeeExtractionResult::NotCoercePointee; + }; + let Some(info) = tcx.coerce_pointee_data(()).get(&def.did()) else { + return CoercePointeeExtractionResult::NotCoercePointee; + }; + CoercePointeeExtractionResult::OneImpl(match info { + ty::CoercePointeeInfo::Validated { impl_def_id, .. } + | ty::CoercePointeeInfo::PointeeUnnormalized { impl_def_id, .. } + | ty::CoercePointeeInfo::PointeeIsConst { impl_def_id, .. } + | ty::CoercePointeeInfo::PointeeNotFound { impl_def_id, .. } => *impl_def_id, + ty::CoercePointeeInfo::Duplicated { .. } => { + return CoercePointeeExtractionResult::Duplicated; + } + }) +} + // This function is only used when // encountering errors and inlining // it negatively impacts perf. diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs index 68b11489ae7c1..cd72678514bc7 100644 --- a/compiler/rustc_type_ir/src/error.rs +++ b/compiler/rustc_type_ir/src/error.rs @@ -19,13 +19,25 @@ impl ExpectedFound { // Data structures used in type unification #[derive_where(Clone, Copy, PartialEq, Eq, Debug; I: Interner)] -#[derive(TypeVisitable_Generic)] +#[derive(TypeFoldable_Generic, TypeVisitable_Generic)] #[cfg_attr(feature = "nightly", rustc_pass_by_value)] pub enum TypeError { Mismatch, - PolarityMismatch(#[type_visitable(ignore)] ExpectedFound), - SafetyMismatch(#[type_visitable(ignore)] ExpectedFound), - AbiMismatch(#[type_visitable(ignore)] ExpectedFound), + PolarityMismatch( + #[type_visitable(ignore)] + #[type_foldable(identity)] + ExpectedFound, + ), + SafetyMismatch( + #[type_visitable(ignore)] + #[type_foldable(identity)] + ExpectedFound, + ), + AbiMismatch( + #[type_visitable(ignore)] + #[type_foldable(identity)] + ExpectedFound, + ), Mutability, ArgumentMutability(usize), TupleSize(ExpectedFound), diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 1a8ef20dd7b9d..380573a9cf9c1 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1284,8 +1284,26 @@ pub trait FnPtr: Copy + Clone { /// } /// ``` #[rustc_builtin_macro(CoercePointee, attributes(pointee))] -#[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)] +#[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize, coerce_pointee_validated)] #[unstable(feature = "derive_coerce_pointee", issue = "123430")] pub macro CoercePointee($item:item) { /* compiler built-in */ } + +/// A validation trait that is implemented on data with `derive(CoercePointee)` +/// so that the compiler can enforce a set of rules that the target data must +/// conform to in order for the derived behaviours are safe and useful for +/// the purpose of the said macro. +/// +/// This trait will not ever be exposed for use as public part of the library +/// and shall not ever be stabilised. +#[cfg(not(bootstrap))] +#[lang = "coerce_pointee_validated"] +#[unstable(feature = "coerce_pointee_validated", issue = "none")] +#[doc(hidden)] +pub trait CoercePointeeValidated { + /// `Pointee` serves as an assertion that the `#[pointee]` type + /// is indeed allowed to be unsized. + #[lang = "coerce_pointee_validated_pointee"] + type Pointee: ?Sized; +} diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index a68552175c318..48ccf669cddba 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -108,3 +108,9 @@ macro_rules! stringify { /* compiler built-in */ }; } +#[lang = "coerce_pointee_validated"] +pub trait CoercePointeeValidated { + /* compiler built-in */ + #[lang = "coerce_pointee_validated_pointee"] + type Pointee: ?Sized; +} diff --git a/tests/ui/deriving/auxiliary/malicious-macro.rs b/tests/ui/deriving/auxiliary/malicious-macro.rs new file mode 100644 index 0000000000000..6665b40a14f2b --- /dev/null +++ b/tests/ui/deriving/auxiliary/malicious-macro.rs @@ -0,0 +1,31 @@ +#![feature(let_chains)] + +extern crate proc_macro; + +use proc_macro::{Delimiter, TokenStream, TokenTree}; + +#[proc_macro_attribute] +pub fn norepr(_: TokenStream, input: TokenStream) -> TokenStream { + let mut tokens = vec![]; + let mut tts = input.into_iter().fuse().peekable(); + loop { + let Some(token) = tts.next() else { break }; + if let TokenTree::Punct(punct) = &token + && punct.as_char() == '#' + { + if let Some(TokenTree::Group(group)) = tts.peek() + && let Delimiter::Bracket = group.delimiter() + && let Some(TokenTree::Ident(ident)) = group.stream().into_iter().next() + && ident.to_string() == "repr" + { + let _ = tts.next(); + // skip '#' and '[repr(..)] + } else { + tokens.push(token); + } + } else { + tokens.push(token); + } + } + tokens.into_iter().collect() +} diff --git a/tests/ui/deriving/built-in-proc-macro-scope.stdout b/tests/ui/deriving/built-in-proc-macro-scope.stdout index db97c7145ea09..60f8cbc849557 100644 --- a/tests/ui/deriving/built-in-proc-macro-scope.stdout +++ b/tests/ui/deriving/built-in-proc-macro-scope.stdout @@ -20,6 +20,10 @@ pub struct Ptr<'a, #[pointee] T: ?Sized> { data: &'a mut T, } #[automatically_derived] +impl<'a, T: ?Sized> ::core::marker::CoercePointeeValidated for Ptr<'a, T> { + type Pointee = T; +} +#[automatically_derived] impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized> ::core::ops::DispatchFromDyn> for Ptr<'a, T> { } diff --git a/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout b/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout index d6eaca5cba188..fd06884679602 100644 --- a/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout +++ b/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout @@ -16,6 +16,11 @@ struct MyPointer<'a, #[pointee] T: ?Sized> { ptr: &'a T, } #[automatically_derived] +impl<'a, T: ?Sized> ::core::marker::CoercePointeeValidated for + MyPointer<'a, T> { + type Pointee = T; +} +#[automatically_derived] impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized> ::core::ops::DispatchFromDyn> for MyPointer<'a, T> { } @@ -31,6 +36,12 @@ pub struct MyPointer2<'a, Y, Z: MyTrait, #[pointee] T: ?Sized + MyTrait, x: core::marker::PhantomData, } #[automatically_derived] +impl<'a, Y, Z: MyTrait, T: ?Sized + MyTrait, X: MyTrait> + ::core::marker::CoercePointeeValidated for MyPointer2<'a, Y, Z, T, X> + where Y: MyTrait { + type Pointee = T; +} +#[automatically_derived] impl<'a, Y, Z: MyTrait + MyTrait<__S>, T: ?Sized + MyTrait + ::core::marker::Unsize<__S>, __S: ?Sized + MyTrait<__S>, X: MyTrait + MyTrait<__S>> ::core::ops::DispatchFromDyn> @@ -48,6 +59,11 @@ struct MyPointerWithoutPointee<'a, T: ?Sized> { ptr: &'a T, } #[automatically_derived] +impl<'a, T: ?Sized> ::core::marker::CoercePointeeValidated for + MyPointerWithoutPointee<'a, T> { + type Pointee = T; +} +#[automatically_derived] impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized> ::core::ops::DispatchFromDyn> for MyPointerWithoutPointee<'a, T> { diff --git a/tests/ui/deriving/deriving-coerce-pointee-neg.rs b/tests/ui/deriving/deriving-coerce-pointee-neg.rs index da25c854c546a..24c789c34bad9 100644 --- a/tests/ui/deriving/deriving-coerce-pointee-neg.rs +++ b/tests/ui/deriving/deriving-coerce-pointee-neg.rs @@ -1,7 +1,11 @@ +//@ proc-macro: malicious-macro.rs #![feature(derive_coerce_pointee, arbitrary_self_types)] extern crate core; +extern crate malicious_macro; + use std::marker::CoercePointee; +use std::rc::Rc; #[derive(CoercePointee)] //~^ ERROR: `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]` @@ -10,17 +14,17 @@ enum NotStruct<'a, T: ?Sized> { } #[derive(CoercePointee)] -//~^ ERROR: `CoercePointee` can only be derived on `struct`s with at least one field #[repr(transparent)] struct NoField<'a, #[pointee] T: ?Sized> {} -//~^ ERROR: lifetime parameter `'a` is never used +//~^ ERROR: `CoercePointee` can only be derived on `struct`s with at least one field with a `pointee` type [E0802] +//~| ERROR: lifetime parameter `'a` is never used //~| ERROR: type parameter `T` is never used #[derive(CoercePointee)] -//~^ ERROR: `CoercePointee` can only be derived on `struct`s with at least one field #[repr(transparent)] struct NoFieldUnit<'a, #[pointee] T: ?Sized>(); -//~^ ERROR: lifetime parameter `'a` is never used +//~^ ERROR: `CoercePointee` can only be derived on `struct`s with at least one field with a `pointee` type [E0802] +//~| ERROR: lifetime parameter `'a` is never used //~| ERROR: type parameter `T` is never used #[derive(CoercePointee)] @@ -41,15 +45,15 @@ struct TooManyPointees<'a, #[pointee] A: ?Sized, #[pointee] B: ?Sized>((&'a A, & //~^ ERROR: only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits #[derive(CoercePointee)] -//~^ ERROR: `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]` struct NotTransparent<'a, #[pointee] T: ?Sized> { + //~^ ERROR: `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout ptr: &'a T, } #[derive(CoercePointee)] #[repr(transparent)] struct NoMaybeSized<'a, #[pointee] T> { - //~^ ERROR: `derive(CoercePointee)` requires `T` to be marked `?Sized` + //~^ ERROR: `derive(CoercePointee)` requires the `#[pointee]` to be `?Sized` ptr: &'a T, } @@ -131,4 +135,28 @@ struct GlobalCoreSized<'a, #[pointee] T: ?::core::marker::Sized> { ptr: &'a T, } +#[derive(CoercePointee)] +#[malicious_macro::norepr] +#[repr(transparent)] +struct TryToWipeRepr<'a, #[pointee] T: ?Sized> { + //~^ ERROR: `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout [E0802] + ptr: &'a T, +} + +#[derive(CoercePointee, CoercePointee)] +//~^ ERROR: `derive(CoercePointee)` is derived multiple times [E0802] +//~| ERROR: `derive(CoercePointee)` is derived multiple times [E0802] +#[repr(transparent)] +struct DuplicateDerive<'a, #[pointee] T: ?Sized> { + ptr: &'a T, +} + +#[repr(transparent)] +#[derive(CoercePointee)] +//~^ ERROR: the trait bound `Box: Unsize>` is not satisfied [E0277] +//~| ERROR: the trait bound `Box: Unsize>` is not satisfied [E0277] +struct RcWithId { + inner: Rc<(i32, Box)>, +} + fn main() {} diff --git a/tests/ui/deriving/deriving-coerce-pointee-neg.stderr b/tests/ui/deriving/deriving-coerce-pointee-neg.stderr index c1e8be49d37d3..88a729314b629 100644 --- a/tests/ui/deriving/deriving-coerce-pointee-neg.stderr +++ b/tests/ui/deriving/deriving-coerce-pointee-neg.stderr @@ -1,89 +1,59 @@ -error: `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]` - --> $DIR/deriving-coerce-pointee-neg.rs:6:10 +error[E0802]: `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]` + --> $DIR/deriving-coerce-pointee-neg.rs:10: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: `CoercePointee` can only be derived on `struct`s with at least one field - --> $DIR/deriving-coerce-pointee-neg.rs:12:10 +error[E0802]: `CoercePointee` can only be derived on `struct`s that are generic over at least one type + --> $DIR/deriving-coerce-pointee-neg.rs:30: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: `CoercePointee` can only be derived on `struct`s with at least one field - --> $DIR/deriving-coerce-pointee-neg.rs:19:10 +error[E0802]: exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits + --> $DIR/deriving-coerce-pointee-neg.rs:35: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: `CoercePointee` can only be derived on `struct`s that are generic over at least one type - --> $DIR/deriving-coerce-pointee-neg.rs:26: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: exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits - --> $DIR/deriving-coerce-pointee-neg.rs:31: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: only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits - --> $DIR/deriving-coerce-pointee-neg.rs:40:39 +error[E0802]: only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits + --> $DIR/deriving-coerce-pointee-neg.rs:44:39 | LL | struct TooManyPointees<'a, #[pointee] A: ?Sized, #[pointee] B: ?Sized>((&'a A, &'a B)); | ^ - here another type parameter is marked as `#[pointee]` -error: `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]` - --> $DIR/deriving-coerce-pointee-neg.rs:43: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: `derive(CoercePointee)` requires `T` to be marked `?Sized` - --> $DIR/deriving-coerce-pointee-neg.rs:51:36 - | -LL | struct NoMaybeSized<'a, #[pointee] T> { - | ^ - error: the `#[pointee]` attribute may only be used on generic parameters - --> $DIR/deriving-coerce-pointee-neg.rs:59:5 + --> $DIR/deriving-coerce-pointee-neg.rs:63:5 | LL | #[pointee] | ^^^^^^^^^^ error: the `#[pointee]` attribute may only be used on generic parameters - --> $DIR/deriving-coerce-pointee-neg.rs:69:33 + --> $DIR/deriving-coerce-pointee-neg.rs:73:33 | LL | struct UhOh<#[pointee] T>(T); | ^^^^^^^^^^ error: the `#[pointee]` attribute may only be used on generic parameters - --> $DIR/deriving-coerce-pointee-neg.rs:83:21 + --> $DIR/deriving-coerce-pointee-neg.rs:87:21 | LL | struct UhOh<#[pointee] T>(T); | ^^^^^^^^^^ error: the `#[pointee]` attribute may only be used on generic parameters - --> $DIR/deriving-coerce-pointee-neg.rs:98:25 + --> $DIR/deriving-coerce-pointee-neg.rs:102:25 | LL | struct UhOh<#[pointee] T>(T); | ^^^^^^^^^^ error[E0392]: lifetime parameter `'a` is never used - --> $DIR/deriving-coerce-pointee-neg.rs:15:16 + --> $DIR/deriving-coerce-pointee-neg.rs:18:16 | LL | struct NoField<'a, #[pointee] T: ?Sized> {} | ^^ unused lifetime parameter @@ -91,15 +61,81 @@ LL | struct NoField<'a, #[pointee] T: ?Sized> {} = 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:15:31 + --> $DIR/deriving-coerce-pointee-neg.rs:18:31 | LL | struct NoField<'a, #[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:48:1 + | +LL | struct NotTransparent<'a, #[pointee] T: ?Sized> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0802]: `derive(CoercePointee)` requires the `#[pointee]` to be `?Sized` + --> $DIR/deriving-coerce-pointee-neg.rs:55:1 + | +LL | struct NoMaybeSized<'a, #[pointee] T> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0802]: `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout + --> $DIR/deriving-coerce-pointee-neg.rs:141:1 + | +LL | struct TryToWipeRepr<'a, #[pointee] T: ?Sized> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0802]: `derive(CoercePointee)` is derived multiple times + --> $DIR/deriving-coerce-pointee-neg.rs:146:10 + | +LL | #[derive(CoercePointee, 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)` is derived multiple times + --> $DIR/deriving-coerce-pointee-neg.rs:146:10 + | +LL | #[derive(CoercePointee, 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[E0277]: the trait bound `Box: Unsize>` is not satisfied + --> $DIR/deriving-coerce-pointee-neg.rs:155:10 + | +LL | #[derive(CoercePointee)] + | ^^^^^^^^^^^^^ the trait `Unsize>` is not implemented for `Box` + | + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information + = 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 with a `pointee` type + --> $DIR/deriving-coerce-pointee-neg.rs:18:1 + | +LL | struct NoField<'a, #[pointee] T: ?Sized> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0802]: `CoercePointee` can only be derived on `struct`s with at least one field with a `pointee` type + --> $DIR/deriving-coerce-pointee-neg.rs:25:1 + | +LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `Box: Unsize>` is not satisfied + --> $DIR/deriving-coerce-pointee-neg.rs:155:10 + | +LL | #[derive(CoercePointee)] + | ^^^^^^^^^^^^^ the trait `Unsize>` is not implemented for `Box` + | + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information + = 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[E0392]: lifetime parameter `'a` is never used - --> $DIR/deriving-coerce-pointee-neg.rs:22:20 + --> $DIR/deriving-coerce-pointee-neg.rs:25:20 | LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>(); | ^^ unused lifetime parameter @@ -107,13 +143,14 @@ LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>(); = 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:22:35 + --> $DIR/deriving-coerce-pointee-neg.rs:25:35 | LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>(); | ^ unused type parameter | = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` -error: aborting due to 16 previous errors +error: aborting due to 21 previous errors -For more information about this error, try `rustc --explain E0392`. +Some errors have detailed explanations: E0277, E0392, E0802. +For more information about an error, try `rustc --explain E0277`.