Skip to content
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

Don't allow the #[pointee] attribute where it doesn't belong #128721

Merged
merged 1 commit into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
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
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
64 changes: 64 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,63 @@ 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
// We want to catch that. The simple solution is to just
// always raise a `NonGenericPointee` error when this happens.
//
// This solution does reject valid rust programs but,
// such a code would have to, in order:
// - Define a smart pointer struct.
// - Somewhere in this struct definition use 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
// that we attached a #[pointee] to a generic type argument while helpfully
// informing the user that #[pointee] can only be attached to generic pointer 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,
}
33 changes: 33 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,39 @@ 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 }>
//~^ 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:76:34
|
LL | 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:85: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`.
Loading