Skip to content

Commit ea3a13b

Browse files
derive(SmartPointer): assume pointee from the single generic and better error messages
1 parent 1a94d83 commit ea3a13b

File tree

5 files changed

+95
-44
lines changed

5 files changed

+95
-44
lines changed

compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs

+49-28
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
1111
use rustc_expand::base::{Annotatable, ExtCtxt};
1212
use rustc_span::symbol::{sym, Ident};
1313
use rustc_span::{Span, Symbol};
14-
use smallvec::{smallvec, SmallVec};
1514
use thin_vec::{thin_vec, ThinVec};
1615

1716
macro_rules! path {
@@ -68,43 +67,65 @@ pub fn expand_deriving_smart_ptr(
6867
};
6968

7069
// Convert generic parameters (from the struct) into generic args.
71-
let mut pointee_param = None;
72-
let mut multiple_pointee_diag: SmallVec<[_; 2]> = smallvec![];
73-
let self_params = generics
70+
let self_params: Vec<_> = generics
7471
.params
7572
.iter()
76-
.enumerate()
77-
.map(|(idx, p)| match p.kind {
73+
.map(|p| match p.kind {
7874
GenericParamKind::Lifetime => GenericArg::Lifetime(cx.lifetime(p.span(), p.ident)),
79-
GenericParamKind::Type { .. } => {
80-
if p.attrs().iter().any(|attr| attr.has_name(sym::pointee)) {
81-
if pointee_param.is_some() {
82-
multiple_pointee_diag.push(cx.dcx().struct_span_err(
83-
p.span(),
84-
"`SmartPointer` can only admit one type as pointee",
85-
));
86-
} else {
87-
pointee_param = Some(idx);
88-
}
89-
}
90-
GenericArg::Type(cx.ty_ident(p.span(), p.ident))
91-
}
75+
GenericParamKind::Type { .. } => GenericArg::Type(cx.ty_ident(p.span(), p.ident)),
9276
GenericParamKind::Const { .. } => GenericArg::Const(cx.const_ident(p.span(), p.ident)),
9377
})
94-
.collect::<Vec<_>>();
95-
let Some(pointee_param_idx) = pointee_param else {
78+
.collect();
79+
let type_params: Vec<_> = generics
80+
.params
81+
.iter()
82+
.enumerate()
83+
.filter_map(|(idx, p)| {
84+
if let GenericParamKind::Type { .. } = p.kind {
85+
Some((idx, p.span(), p.attrs().iter().any(|attr| attr.has_name(sym::pointee))))
86+
} else {
87+
None
88+
}
89+
})
90+
.collect();
91+
92+
// `#[derive(SmartPointer)]` requires at least one generic type on the target `struct`
93+
if type_params.is_empty() {
9694
cx.dcx().struct_span_err(
9795
span,
98-
"At least one generic type should be designated as `#[pointee]` in order to derive `SmartPointer` traits",
96+
"`SmartPointer` can only be derived on `struct`s that are generic over at least one type",
9997
).emit();
10098
return;
101-
};
102-
if !multiple_pointee_diag.is_empty() {
103-
for diag in multiple_pointee_diag {
104-
diag.emit();
105-
}
106-
return;
10799
}
100+
let pointee_param_idx = if type_params.len() == 1 {
101+
// Regardless of the only type param being designed as `#[pointee]` or not, we can just use it as such
102+
type_params[0].0
103+
} else {
104+
let mut pointees = type_params
105+
.iter()
106+
.filter_map(|&(idx, span, is_pointee)| is_pointee.then_some((idx, span)))
107+
.fuse();
108+
match (pointees.next(), pointees.next()) {
109+
(Some((idx, _span)), None) => idx,
110+
(None, _) => {
111+
cx.dcx().struct_span_err(
112+
span,
113+
"Exactly one generic parameters when there are at least two generic type parameters \
114+
should be designated as `#[pointee]` in order to derive `SmartPointer` traits",
115+
).emit();
116+
return;
117+
}
118+
(Some((_, one)), Some((_, another))) => {
119+
cx.dcx()
120+
.struct_span_err(
121+
vec![one, another],
122+
"`SmartPointer` can only admit one type as pointee",
123+
)
124+
.emit();
125+
return;
126+
}
127+
}
128+
};
108129

109130
// Create the type of `self`.
110131
let path = cx.path_all(span, false, vec![name_ident], self_params.clone());

tests/ui/deriving/deriving-smart-pointer-expanded.rs

+6
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,9 @@ where
2020
data: &'a mut T,
2121
x: core::marker::PhantomData<X>,
2222
}
23+
24+
#[derive(SmartPointer)]
25+
#[repr(transparent)]
26+
struct MyPointerWithoutPointee<'a, T: ?Sized> {
27+
ptr: &'a T,
28+
}

tests/ui/deriving/deriving-smart-pointer-expanded.stdout

+15
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,18 @@ impl<'a, Y, Z: MyTrait<T> + MyTrait<__S>, T: ?Sized + MyTrait<T> +
4242
MyTrait<__S>> ::core::ops::CoerceUnsized<MyPointer2<'a, Y, Z, __S, X>> for
4343
MyPointer2<'a, Y, Z, T, X> where Y: MyTrait<T>, Y: MyTrait<__S> {
4444
}
45+
46+
#[repr(transparent)]
47+
struct MyPointerWithoutPointee<'a, T: ?Sized> {
48+
ptr: &'a T,
49+
}
50+
#[automatically_derived]
51+
impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized>
52+
::core::ops::DispatchFromDyn<MyPointerWithoutPointee<'a, __S>> for
53+
MyPointerWithoutPointee<'a, T> {
54+
}
55+
#[automatically_derived]
56+
impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized>
57+
::core::ops::CoerceUnsized<MyPointerWithoutPointee<'a, __S>> for
58+
MyPointerWithoutPointee<'a, T> {
59+
}

tests/ui/deriving/deriving-smart-pointer-neg.rs

+10-7
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,6 @@ enum NotStruct<'a, T: ?Sized> {
99
Variant(&'a T),
1010
}
1111

12-
#[derive(SmartPointer)]
13-
//~^ ERROR: At least one generic type should be designated as `#[pointee]` in order to derive `SmartPointer` traits
14-
#[repr(transparent)]
15-
struct NoPointee<'a, T: ?Sized> {
16-
ptr: &'a T,
17-
}
18-
1912
#[derive(SmartPointer)]
2013
//~^ ERROR: `SmartPointer` can only be derived on `struct`s with at least one field
2114
#[repr(transparent)]
@@ -30,6 +23,16 @@ struct NoFieldUnit<'a, #[pointee] T: ?Sized>();
3023
//~^ ERROR: lifetime parameter `'a` is never used
3124
//~| ERROR: type parameter `T` is never used
3225

26+
#[derive(SmartPointer)]
27+
//~^ ERROR: `SmartPointer` can only be derived on `struct`s that are generic over at least one type
28+
#[repr(transparent)]
29+
struct NoGeneric<'a>(&'a u8);
30+
31+
#[derive(SmartPointer)]
32+
#[repr(transparent)]
33+
struct TooManyPointees<'a, #[pointee] A: ?Sized, #[pointee] B: ?Sized>((&'a A, &'a B));
34+
//~^ ERROR: `SmartPointer` can only admit one type as pointee
35+
3336
#[derive(SmartPointer)]
3437
//~^ ERROR: `SmartPointer` can only be derived on `struct`s with `#[repr(transparent)]`
3538
struct NotTransparent<'a, #[pointee] T: ?Sized> {

tests/ui/deriving/deriving-smart-pointer-neg.stderr

+15-9
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | #[derive(SmartPointer)]
66
|
77
= note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
88

9-
error: At least one generic type should be designated as `#[pointee]` in order to derive `SmartPointer` traits
9+
error: `SmartPointer` can only be derived on `struct`s with at least one field
1010
--> $DIR/deriving-smart-pointer-neg.rs:12:10
1111
|
1212
LL | #[derive(SmartPointer)]
@@ -22,60 +22,66 @@ LL | #[derive(SmartPointer)]
2222
|
2323
= note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
2424

25-
error: `SmartPointer` can only be derived on `struct`s with at least one field
25+
error: `SmartPointer` can only be derived on `struct`s that are generic over at least one type
2626
--> $DIR/deriving-smart-pointer-neg.rs:26:10
2727
|
2828
LL | #[derive(SmartPointer)]
2929
| ^^^^^^^^^^^^
3030
|
3131
= note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
3232

33+
error: `SmartPointer` can only admit one type as pointee
34+
--> $DIR/deriving-smart-pointer-neg.rs:33:39
35+
|
36+
LL | struct TooManyPointees<'a, #[pointee] A: ?Sized, #[pointee] B: ?Sized>((&'a A, &'a B));
37+
| ^ ^
38+
3339
error: `SmartPointer` can only be derived on `struct`s with `#[repr(transparent)]`
34-
--> $DIR/deriving-smart-pointer-neg.rs:33:10
40+
--> $DIR/deriving-smart-pointer-neg.rs:36:10
3541
|
3642
LL | #[derive(SmartPointer)]
3743
| ^^^^^^^^^^^^
3844
|
3945
= note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
4046

4147
error: `derive(SmartPointer)` requires T to be marked `?Sized`
42-
--> $DIR/deriving-smart-pointer-neg.rs:41:36
48+
--> $DIR/deriving-smart-pointer-neg.rs:44:36
4349
|
4450
LL | struct NoMaybeSized<'a, #[pointee] T> {
4551
| ^
4652

4753
error[E0392]: lifetime parameter `'a` is never used
48-
--> $DIR/deriving-smart-pointer-neg.rs:22:16
54+
--> $DIR/deriving-smart-pointer-neg.rs:15:16
4955
|
5056
LL | struct NoField<'a, #[pointee] T: ?Sized> {}
5157
| ^^ unused lifetime parameter
5258
|
5359
= help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`
5460

5561
error[E0392]: type parameter `T` is never used
56-
--> $DIR/deriving-smart-pointer-neg.rs:22:31
62+
--> $DIR/deriving-smart-pointer-neg.rs:15:31
5763
|
5864
LL | struct NoField<'a, #[pointee] T: ?Sized> {}
5965
| ^ unused type parameter
6066
|
6167
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
6268

6369
error[E0392]: lifetime parameter `'a` is never used
64-
--> $DIR/deriving-smart-pointer-neg.rs:29:20
70+
--> $DIR/deriving-smart-pointer-neg.rs:22:20
6571
|
6672
LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>();
6773
| ^^ unused lifetime parameter
6874
|
6975
= help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`
7076

7177
error[E0392]: type parameter `T` is never used
72-
--> $DIR/deriving-smart-pointer-neg.rs:29:35
78+
--> $DIR/deriving-smart-pointer-neg.rs:22:35
7379
|
7480
LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>();
7581
| ^ unused type parameter
7682
|
7783
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
7884

79-
error: aborting due to 10 previous errors
85+
error: aborting due to 11 previous errors
8086

8187
For more information about this error, try `rustc --explain E0392`.

0 commit comments

Comments
 (0)