From 39148351bd919d16920b00e0bd70203b73db53bb Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Fri, 23 Aug 2024 21:07:18 +0800 Subject: [PATCH] derive(SmartPointer): assume pointee from the single generic and better error messages --- .../src/deriving/smart_ptr.rs | 75 ++++++++++++------- .../deriving-smart-pointer-expanded.rs | 6 ++ .../deriving-smart-pointer-expanded.stdout | 15 ++++ .../ui/deriving/deriving-smart-pointer-neg.rs | 24 ++++-- .../deriving-smart-pointer-neg.stderr | 32 +++++--- 5 files changed, 108 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs index 7eb1f17a59ce3..b3695d5fb4986 100644 --- a/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs +++ b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs @@ -11,7 +11,6 @@ use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, Symbol}; -use smallvec::{smallvec, SmallVec}; use thin_vec::{thin_vec, ThinVec}; macro_rules! path { @@ -68,43 +67,63 @@ pub fn expand_deriving_smart_ptr( }; // Convert generic parameters (from the struct) into generic args. - let mut pointee_param = None; - let mut multiple_pointee_diag: SmallVec<[_; 2]> = smallvec![]; - let self_params = generics + let self_params: Vec<_> = generics .params .iter() - .enumerate() - .map(|(idx, p)| match p.kind { + .map(|p| match p.kind { GenericParamKind::Lifetime => GenericArg::Lifetime(cx.lifetime(p.span(), p.ident)), - GenericParamKind::Type { .. } => { - if p.attrs().iter().any(|attr| attr.has_name(sym::pointee)) { - if pointee_param.is_some() { - multiple_pointee_diag.push(cx.dcx().struct_span_err( - p.span(), - "`SmartPointer` can only admit one type as pointee", - )); - } else { - pointee_param = Some(idx); - } - } - GenericArg::Type(cx.ty_ident(p.span(), p.ident)) - } + GenericParamKind::Type { .. } => GenericArg::Type(cx.ty_ident(p.span(), p.ident)), GenericParamKind::Const { .. } => GenericArg::Const(cx.const_ident(p.span(), p.ident)), }) - .collect::>(); - let Some(pointee_param_idx) = pointee_param else { + .collect(); + let type_params: Vec<_> = generics + .params + .iter() + .enumerate() + .filter_map(|(idx, p)| { + if let GenericParamKind::Type { .. } = p.kind { + Some((idx, p.span(), p.attrs().iter().any(|attr| attr.has_name(sym::pointee)))) + } else { + None + } + }) + .collect(); + + let pointee_param_idx = if type_params.is_empty() { + // `#[derive(SmartPointer)]` requires at least one generic type on the target `struct` cx.dcx().struct_span_err( span, - "At least one generic type should be designated as `#[pointee]` in order to derive `SmartPointer` traits", + "`SmartPointer` can only be derived on `struct`s that are generic over at least one type", ).emit(); return; - }; - if !multiple_pointee_diag.is_empty() { - for diag in multiple_pointee_diag { - diag.emit(); + } else if type_params.len() == 1 { + // Regardless of the only type param being designed as `#[pointee]` or not, we can just use it as such + type_params[0].0 + } else { + let mut pointees = type_params + .iter() + .filter_map(|&(idx, span, is_pointee)| is_pointee.then_some((idx, span))) + .fuse(); + match (pointees.next(), pointees.next()) { + (Some((idx, _span)), None) => idx, + (None, _) => { + cx.dcx().struct_span_err( + span, + "exactly one generic type parameter must be marked as #[pointee] to derive SmartPointer traits", + ).emit(); + return; + } + (Some((_, one)), Some((_, another))) => { + cx.dcx() + .struct_span_err( + vec![one, another], + "only one type parameter can be marked as `#[pointee]` when deriving SmartPointer traits", + ) + .emit(); + return; + } } - return; - } + }; // Create the type of `self`. let path = cx.path_all(span, false, vec![name_ident], self_params.clone()); diff --git a/tests/ui/deriving/deriving-smart-pointer-expanded.rs b/tests/ui/deriving/deriving-smart-pointer-expanded.rs index b78258c25290e..e48ad3dd4bc6c 100644 --- a/tests/ui/deriving/deriving-smart-pointer-expanded.rs +++ b/tests/ui/deriving/deriving-smart-pointer-expanded.rs @@ -20,3 +20,9 @@ where data: &'a mut T, x: core::marker::PhantomData, } + +#[derive(SmartPointer)] +#[repr(transparent)] +struct MyPointerWithoutPointee<'a, T: ?Sized> { + ptr: &'a T, +} diff --git a/tests/ui/deriving/deriving-smart-pointer-expanded.stdout b/tests/ui/deriving/deriving-smart-pointer-expanded.stdout index 3c7e719818042..68ef17f2b0547 100644 --- a/tests/ui/deriving/deriving-smart-pointer-expanded.stdout +++ b/tests/ui/deriving/deriving-smart-pointer-expanded.stdout @@ -42,3 +42,18 @@ impl<'a, Y, Z: MyTrait + MyTrait<__S>, T: ?Sized + MyTrait + MyTrait<__S>> ::core::ops::CoerceUnsized> for MyPointer2<'a, Y, Z, T, X> where Y: MyTrait, Y: MyTrait<__S> { } + +#[repr(transparent)] +struct MyPointerWithoutPointee<'a, T: ?Sized> { + ptr: &'a T, +} +#[automatically_derived] +impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized> + ::core::ops::DispatchFromDyn> for + MyPointerWithoutPointee<'a, T> { +} +#[automatically_derived] +impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized> + ::core::ops::CoerceUnsized> for + MyPointerWithoutPointee<'a, T> { +} diff --git a/tests/ui/deriving/deriving-smart-pointer-neg.rs b/tests/ui/deriving/deriving-smart-pointer-neg.rs index 04f52a154fe42..f02fb56130fa7 100644 --- a/tests/ui/deriving/deriving-smart-pointer-neg.rs +++ b/tests/ui/deriving/deriving-smart-pointer-neg.rs @@ -9,13 +9,6 @@ enum NotStruct<'a, T: ?Sized> { Variant(&'a T), } -#[derive(SmartPointer)] -//~^ ERROR: At least one generic type should be designated as `#[pointee]` in order to derive `SmartPointer` traits -#[repr(transparent)] -struct NoPointee<'a, T: ?Sized> { - ptr: &'a T, -} - #[derive(SmartPointer)] //~^ ERROR: `SmartPointer` can only be derived on `struct`s with at least one field #[repr(transparent)] @@ -30,6 +23,23 @@ struct NoFieldUnit<'a, #[pointee] T: ?Sized>(); //~^ ERROR: lifetime parameter `'a` is never used //~| ERROR: type parameter `T` is never used +#[derive(SmartPointer)] +//~^ ERROR: `SmartPointer` can only be derived on `struct`s that are generic over at least one type +#[repr(transparent)] +struct NoGeneric<'a>(&'a u8); + +#[derive(SmartPointer)] +//~^ ERROR: exactly one generic type parameter must be marked as #[pointee] to derive SmartPointer traits +#[repr(transparent)] +struct AmbiguousPointee<'a, T1: ?Sized, T2: ?Sized> { + a: (&'a T1, &'a T2), +} + +#[derive(SmartPointer)] +#[repr(transparent)] +struct TooManyPointees<'a, #[pointee] A: ?Sized, #[pointee] B: ?Sized>((&'a A, &'a B)); +//~^ ERROR: only one type parameter can be marked as `#[pointee]` when deriving SmartPointer traits + #[derive(SmartPointer)] //~^ ERROR: `SmartPointer` can only be derived on `struct`s with `#[repr(transparent)]` struct NotTransparent<'a, #[pointee] T: ?Sized> { diff --git a/tests/ui/deriving/deriving-smart-pointer-neg.stderr b/tests/ui/deriving/deriving-smart-pointer-neg.stderr index 8b0f91d41fb88..e7c2afc8b00c7 100644 --- a/tests/ui/deriving/deriving-smart-pointer-neg.stderr +++ b/tests/ui/deriving/deriving-smart-pointer-neg.stderr @@ -6,7 +6,7 @@ LL | #[derive(SmartPointer)] | = note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info) -error: At least one generic type should be designated as `#[pointee]` in order to derive `SmartPointer` traits +error: `SmartPointer` can only be derived on `struct`s with at least one field --> $DIR/deriving-smart-pointer-neg.rs:12:10 | LL | #[derive(SmartPointer)] @@ -22,7 +22,7 @@ LL | #[derive(SmartPointer)] | = note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `SmartPointer` can only be derived on `struct`s with at least one field +error: `SmartPointer` can only be derived on `struct`s that are generic over at least one type --> $DIR/deriving-smart-pointer-neg.rs:26:10 | LL | #[derive(SmartPointer)] @@ -30,8 +30,22 @@ LL | #[derive(SmartPointer)] | = note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info) +error: exactly one generic type parameter must be marked as #[pointee] to derive SmartPointer traits + --> $DIR/deriving-smart-pointer-neg.rs:31:10 + | +LL | #[derive(SmartPointer)] + | ^^^^^^^^^^^^ + | + = note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: only one type parameter can be marked as `#[pointee]` when deriving SmartPointer traits + --> $DIR/deriving-smart-pointer-neg.rs:40:39 + | +LL | struct TooManyPointees<'a, #[pointee] A: ?Sized, #[pointee] B: ?Sized>((&'a A, &'a B)); + | ^ ^ + error: `SmartPointer` can only be derived on `struct`s with `#[repr(transparent)]` - --> $DIR/deriving-smart-pointer-neg.rs:33:10 + --> $DIR/deriving-smart-pointer-neg.rs:43:10 | LL | #[derive(SmartPointer)] | ^^^^^^^^^^^^ @@ -39,13 +53,13 @@ LL | #[derive(SmartPointer)] = note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info) error: `derive(SmartPointer)` requires T to be marked `?Sized` - --> $DIR/deriving-smart-pointer-neg.rs:41:36 + --> $DIR/deriving-smart-pointer-neg.rs:51:36 | LL | struct NoMaybeSized<'a, #[pointee] T> { | ^ error[E0392]: lifetime parameter `'a` is never used - --> $DIR/deriving-smart-pointer-neg.rs:22:16 + --> $DIR/deriving-smart-pointer-neg.rs:15:16 | LL | struct NoField<'a, #[pointee] T: ?Sized> {} | ^^ unused lifetime parameter @@ -53,7 +67,7 @@ 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-smart-pointer-neg.rs:22:31 + --> $DIR/deriving-smart-pointer-neg.rs:15:31 | LL | struct NoField<'a, #[pointee] T: ?Sized> {} | ^ unused type parameter @@ -61,7 +75,7 @@ LL | struct NoField<'a, #[pointee] T: ?Sized> {} = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` error[E0392]: lifetime parameter `'a` is never used - --> $DIR/deriving-smart-pointer-neg.rs:29:20 + --> $DIR/deriving-smart-pointer-neg.rs:22:20 | LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>(); | ^^ unused lifetime parameter @@ -69,13 +83,13 @@ 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-smart-pointer-neg.rs:29:35 + --> $DIR/deriving-smart-pointer-neg.rs:22: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 10 previous errors +error: aborting due to 12 previous errors For more information about this error, try `rustc --explain E0392`.