Skip to content

Commit

Permalink
Check that #[pointee] is applied only to generic arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
Brezak committed Oct 6, 2024
1 parent 2305aad commit cc9d8b5
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 1 deletion.
2 changes: 2 additions & 0 deletions compiler/rustc_builtin_macros/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ builtin_macros_non_exhaustive_default = default variant must be exhaustive
.label = declared `#[non_exhaustive]` here
.help = consider a manual implementation of `Default`
builtin_macros_non_generic_pointee = the `#[pointee]` attribute may only be used on generic parameters
builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants
.help = consider a manual implementation of `Default`
Expand Down
62 changes: 62 additions & 0 deletions compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ use rustc_span::symbol::{Ident, sym};
use rustc_span::{Span, Symbol};
use thin_vec::{ThinVec, thin_vec};

use crate::errors;

macro_rules! path {
($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] }
}
Expand All @@ -25,6 +27,8 @@ pub(crate) fn expand_deriving_smart_ptr(
push: &mut dyn FnMut(Annotatable),
_is_const: bool,
) {
item.visit_with(&mut DetectNonGenericPointeeAttr { cx });

let (name_ident, generics) = if let Annotatable::Item(aitem) = item
&& let ItemKind::Struct(struct_data, g) = &aitem.kind
{
Expand Down Expand Up @@ -396,3 +400,61 @@ impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> {
}
}
}

struct DetectNonGenericPointeeAttr<'a, 'b> {
cx: &'a ExtCtxt<'b>,
}

impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonGenericPointeeAttr<'a, 'b> {
fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) -> Self::Result {
if attr.has_name(sym::pointee) {
self.cx.dcx().emit_err(errors::NonGenericPointee { span: attr.span });
}
}

fn visit_generic_param(&mut self, param: &'a rustc_ast::GenericParam) -> Self::Result {
let mut error_on_pointee = AlwaysErrorOnGenericParam { cx: self.cx };

match &param.kind {
GenericParamKind::Type { default } => {
// The `default` may end up containing a block expression. The problem is block expressions
// may define structs with generics. A user may attach a #[pointee] attribute to one of these
// generics and we want to catch that. The simple solution is to just always raise a `NonGenericPointee`
// error in these cases.
//
// We do actually end up rejecting valid rust programs while handling this edge case.
// Note that such a program would have to, in order:
// - Define a smart pointer struct.
// - Somewhere in this struct definition reference a type with a const generic argument.
// - Calculate this const generic in a expression block.
// - Define a new smart pointer type in this block.
// - Have this smart pointer type have more than 1 generic type.
// In this case, the inner smart pointer derive would be complaining that it needs a pointer attribute.
// Meanwhile, the outer macro would be complaining about a #[pointee] attribute attached to a generic type argument,
// while also complaining that a #[pointee] attribute can only be attached to generic type arguments.
rustc_ast::visit::visit_opt!(error_on_pointee, visit_ty, default);
}

GenericParamKind::Const { .. } | GenericParamKind::Lifetime => {
rustc_ast::visit::walk_generic_param(&mut error_on_pointee, param);
}
}
}

fn visit_ty(&mut self, t: &'a rustc_ast::Ty) -> Self::Result {
let mut error_on_pointee = AlwaysErrorOnGenericParam { cx: self.cx };
error_on_pointee.visit_ty(t)
}
}

struct AlwaysErrorOnGenericParam<'a, 'b> {
cx: &'a ExtCtxt<'b>,
}

impl<'a, 'b> rustc_ast::visit::Visitor<'a> for AlwaysErrorOnGenericParam<'a, 'b> {
fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) -> Self::Result {
if attr.has_name(sym::pointee) {
self.cx.dcx().emit_err(errors::NonGenericPointee { span: attr.span });
}
}
}
7 changes: 7 additions & 0 deletions compiler/rustc_builtin_macros/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -937,3 +937,10 @@ pub(crate) struct NakedFunctionTestingAttribute {
#[label]
pub testing_span: Span,
}

#[derive(Diagnostic)]
#[diag(builtin_macros_non_generic_pointee)]
pub(crate) struct NonGenericPointee {
#[primary_span]
pub span: Span,
}
29 changes: 29 additions & 0 deletions tests/ui/deriving/deriving-smart-pointer-neg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,35 @@ struct NoMaybeSized<'a, #[pointee] T> {
ptr: &'a T,
}

#[derive(SmartPointer)]
#[repr(transparent)]
struct PointeeOnField<'a, #[pointee] T: ?Sized> {
#[pointee]
//~^ ERROR: the `#[pointee]` attribute may only be used on generic parameters
ptr: &'a T
}

#[derive(SmartPointer)]
#[repr(transparent)]
struct PointeeInTypeConstBlock<'a, T: ?Sized = [u32; const { struct UhOh<#[pointee] T>(T); 10 }]> {
//~^ ERROR: the `#[pointee]` attribute may only be used on generic parameters
ptr: &'a T,
}

#[derive(SmartPointer)]
#[repr(transparent)]
struct PointeeInConstConstBlock<'a, T: ?Sized, const V: u32 = { struct UhOh<#[pointee] T>(T); 10 }> {

Check failure on line 73 in tests/ui/deriving/deriving-smart-pointer-neg.rs

View workflow job for this annotation

GitHub Actions / PR - mingw-check-tidy

line longer than 100 chars
//~^ ERROR: the `#[pointee]` attribute may only be used on generic parameters
ptr: &'a T,
}

#[derive(SmartPointer)]
#[repr(transparent)]
struct PointeeInAnotherTypeConstBlock<'a, #[pointee] T: ?Sized> {
ptr: PointeeInConstConstBlock<'a, T, { struct UhOh<#[pointee] T>(T); 0 }>
//~^ ERROR: the `#[pointee]` attribute may only be used on generic parameters
}

// However, reordering attributes should work nevertheless.
#[repr(transparent)]
#[derive(SmartPointer)]
Expand Down
26 changes: 25 additions & 1 deletion tests/ui/deriving/deriving-smart-pointer-neg.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,30 @@ error: `derive(SmartPointer)` requires T to be marked `?Sized`
LL | struct NoMaybeSized<'a, #[pointee] T> {
| ^

error: the `#[pointee]` attribute may only be used on generic parameters
--> $DIR/deriving-smart-pointer-neg.rs:59:5
|
LL | #[pointee]
| ^^^^^^^^^^

error: the `#[pointee]` attribute may only be used on generic parameters
--> $DIR/deriving-smart-pointer-neg.rs:66:74
|
LL | struct PointeeInTypeConstBlock<'a, T: ?Sized = [u32; const { struct UhOh<#[pointee] T>(T); 10 }]> {
| ^^^^^^^^^^

error: the `#[pointee]` attribute may only be used on generic parameters
--> $DIR/deriving-smart-pointer-neg.rs:73:77
|
LL | struct PointeeInConstConstBlock<'a, T: ?Sized, const V: u32 = { struct UhOh<#[pointee] T>(T); 10 }> {
| ^^^^^^^^^^

error: the `#[pointee]` attribute may only be used on generic parameters
--> $DIR/deriving-smart-pointer-neg.rs:81:56
|
LL | ptr: PointeeInConstConstBlock<'a, T, { struct UhOh<#[pointee] T>(T); 0 }>
| ^^^^^^^^^^

error[E0392]: lifetime parameter `'a` is never used
--> $DIR/deriving-smart-pointer-neg.rs:15:16
|
Expand Down Expand Up @@ -90,6 +114,6 @@ LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>();
|
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`

error: aborting due to 12 previous errors
error: aborting due to 16 previous errors

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

0 comments on commit cc9d8b5

Please sign in to comment.