Skip to content

Perform more validation on CoercePointee #136789

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions compiler/rustc_builtin_macros/messages.ftl
Original file line number Diff line number Diff line change
@@ -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
119 changes: 72 additions & 47 deletions compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
Original file line number Diff line number Diff line change
@@ -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,
}
7 changes: 7 additions & 0 deletions compiler/rustc_codegen_cranelift/example/mini_core.rs
Original file line number Diff line number Diff line change
@@ -29,6 +29,13 @@ pub trait Unsize<T: ?Sized> {}
#[lang = "coerce_unsized"]
pub trait CoerceUnsized<T> {}

#[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>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
53 changes: 39 additions & 14 deletions compiler/rustc_codegen_gcc/example/mini_core.rs
Original file line number Diff line number Diff line change
@@ -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<T: ?Sized> {}
#[lang = "coerce_unsized"]
pub trait CoerceUnsized<T> {}

#[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>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
@@ -35,13 +50,13 @@ impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
pub trait DispatchFromDyn<T> {}

// &T -> &U
impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {}
// &mut T -> &mut U
impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {}
// *const T -> *const U
impl<T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {}
// *mut T -> *mut U
impl<T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Box<U, ()>> for Box<T, ()> {}

#[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;

94 changes: 94 additions & 0 deletions compiler/rustc_error_codes/src/error_codes/E0802.md
Original file line number Diff line number Diff line change
@@ -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`.
1 change: 1 addition & 0 deletions compiler/rustc_error_codes/src/lib.rs
Original file line number Diff line number Diff line change
@@ -545,6 +545,7 @@ E0798: 0798,
E0799: 0799,
E0800: 0800,
E0801: 0801,
E0802: 0802,
);
)
}
61 changes: 60 additions & 1 deletion compiler/rustc_expand/src/build.rs
Original file line number Diff line number Diff line change
@@ -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<ast::Ty>,
default: Option<AnonConst>,
) -> 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<ast::AssocItem> {
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,
7 changes: 7 additions & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
@@ -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),
}

25 changes: 25 additions & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
@@ -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
388 changes: 345 additions & 43 deletions compiler/rustc_hir_analysis/src/coherence/builtin.rs

Large diffs are not rendered by default.

142 changes: 142 additions & 0 deletions compiler/rustc_hir_analysis/src/coherence/builtin/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -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<TyCtxt<'tcx>>>(
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<TyCtxt<'tcx>> 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
}
3 changes: 2 additions & 1 deletion compiler/rustc_hir_analysis/src/coherence/mod.rs
Original file line number Diff line number Diff line change
@@ -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
};
}
74 changes: 74 additions & 0 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
@@ -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<Span>,
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<Span>,
}

#[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]
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/arena.rs
Original file line number Diff line number Diff line change
@@ -90,6 +90,10 @@ macro_rules! arena_types {
[] autodiff_item: rustc_ast::expand::autodiff_attrs::AutoDiffItem,
[] ordered_name_set: rustc_data_structures::fx::FxIndexSet<rustc_span::Symbol>,
[] 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
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
@@ -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<ty::CoercePointeeInfo<'tcx>> {
desc { |tcx| "computing CoercePointee data types" }
}

query typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> {
desc { |tcx| "type-checking `{}`", tcx.def_path_str(key) }
29 changes: 29 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
@@ -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 {
4 changes: 4 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
@@ -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,
3 changes: 3 additions & 0 deletions compiler/rustc_trait_selection/messages.ftl
Original file line number Diff line number Diff line change
@@ -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 ->
10 changes: 10 additions & 0 deletions compiler/rustc_trait_selection/src/errors.rs
Original file line number Diff line number Diff line change
@@ -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)]
92 changes: 91 additions & 1 deletion compiler/rustc_trait_selection/src/traits/specialize/mod.rs
Original file line number Diff line number Diff line change
@@ -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.
20 changes: 16 additions & 4 deletions compiler/rustc_type_ir/src/error.rs
Original file line number Diff line number Diff line change
@@ -19,13 +19,25 @@ impl<T> ExpectedFound<T> {

// 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<I: Interner> {
Mismatch,
PolarityMismatch(#[type_visitable(ignore)] ExpectedFound<ty::PredicatePolarity>),
SafetyMismatch(#[type_visitable(ignore)] ExpectedFound<I::Safety>),
AbiMismatch(#[type_visitable(ignore)] ExpectedFound<I::Abi>),
PolarityMismatch(
#[type_visitable(ignore)]
#[type_foldable(identity)]
ExpectedFound<ty::PredicatePolarity>,
),
SafetyMismatch(
#[type_visitable(ignore)]
#[type_foldable(identity)]
ExpectedFound<I::Safety>,
),
AbiMismatch(
#[type_visitable(ignore)]
#[type_foldable(identity)]
ExpectedFound<I::Abi>,
),
Mutability,
ArgumentMutability(usize),
TupleSize(ExpectedFound<usize>),
20 changes: 19 additions & 1 deletion library/core/src/marker.rs
Original file line number Diff line number Diff line change
@@ -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;
}
6 changes: 6 additions & 0 deletions tests/auxiliary/minicore.rs
Original file line number Diff line number Diff line change
@@ -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;
}
31 changes: 31 additions & 0 deletions tests/ui/deriving/auxiliary/malicious-macro.rs
Original file line number Diff line number Diff line change
@@ -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()
}
4 changes: 4 additions & 0 deletions tests/ui/deriving/built-in-proc-macro-scope.stdout
Original file line number Diff line number Diff line change
@@ -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<Ptr<'a, __S>> for Ptr<'a, T> {
}
16 changes: 16 additions & 0 deletions tests/ui/deriving/deriving-coerce-pointee-expanded.stdout
Original file line number Diff line number Diff line change
@@ -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<MyPointer<'a, __S>> for MyPointer<'a, T> {
}
@@ -31,6 +36,12 @@ pub struct MyPointer2<'a, Y, Z: MyTrait<T>, #[pointee] T: ?Sized + MyTrait<T>,
x: core::marker::PhantomData<X>,
}
#[automatically_derived]
impl<'a, Y, Z: MyTrait<T>, T: ?Sized + MyTrait<T>, X: MyTrait<T>>
::core::marker::CoercePointeeValidated for MyPointer2<'a, Y, Z, T, X>
where Y: MyTrait<T> {
type Pointee = T;
}
#[automatically_derived]
impl<'a, Y, Z: MyTrait<T> + MyTrait<__S>, T: ?Sized + MyTrait<T> +
::core::marker::Unsize<__S>, __S: ?Sized + MyTrait<__S>, X: MyTrait<T> +
MyTrait<__S>> ::core::ops::DispatchFromDyn<MyPointer2<'a, Y, Z, __S, X>>
@@ -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<MyPointerWithoutPointee<'a, __S>> for
MyPointerWithoutPointee<'a, T> {
40 changes: 34 additions & 6 deletions tests/ui/deriving/deriving-coerce-pointee-neg.rs
Original file line number Diff line number Diff line change
@@ -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<T>: Unsize<Box<T {coerced}>>` is not satisfied [E0277]
//~| ERROR: the trait bound `Box<T>: Unsize<Box<T {coerced}>>` is not satisfied [E0277]
struct RcWithId<T: ?Sized> {
inner: Rc<(i32, Box<T>)>,
}

fn main() {}
133 changes: 85 additions & 48 deletions tests/ui/deriving/deriving-coerce-pointee-neg.stderr
Original file line number Diff line number Diff line change
@@ -1,119 +1,156 @@
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
|
= 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<T>: Unsize<Box<T {coerced}>>` is not satisfied
--> $DIR/deriving-coerce-pointee-neg.rs:155:10
|
LL | #[derive(CoercePointee)]
| ^^^^^^^^^^^^^ the trait `Unsize<Box<T {coerced}>>` is not implemented for `Box<T>`
|
= note: all implementations of `Unsize` are provided automatically by the compiler, see <https://doc.rust-lang.org/stable/std/marker/trait.Unsize.html> 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<T>: Unsize<Box<T {coerced}>>` is not satisfied
--> $DIR/deriving-coerce-pointee-neg.rs:155:10
|
LL | #[derive(CoercePointee)]
| ^^^^^^^^^^^^^ the trait `Unsize<Box<T {coerced}>>` is not implemented for `Box<T>`
|
= note: all implementations of `Unsize` are provided automatically by the compiler, see <https://doc.rust-lang.org/stable/std/marker/trait.Unsize.html> 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
|
= 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`.