about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_builtin_macros/messages.ftl2
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs64
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs7
-rw-r--r--tests/ui/deriving/deriving-smart-pointer-neg.rs33
-rw-r--r--tests/ui/deriving/deriving-smart-pointer-neg.stderr26
5 files changed, 131 insertions, 1 deletions
diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index b25892242b5..77cb8dc63c4 100644
--- a/compiler/rustc_builtin_macros/messages.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -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`
 
diff --git a/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
index 78028df2aa0..fab1906eecd 100644
--- a/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
@@ -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),)*] }
 }
@@ -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
     {
@@ -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 });
+        }
+    }
+}
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index f13ca224a45..639c2aa231c 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -940,3 +940,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,
+}
diff --git a/tests/ui/deriving/deriving-smart-pointer-neg.rs b/tests/ui/deriving/deriving-smart-pointer-neg.rs
index f02fb56130f..41d3039236f 100644
--- a/tests/ui/deriving/deriving-smart-pointer-neg.rs
+++ b/tests/ui/deriving/deriving-smart-pointer-neg.rs
@@ -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)]
diff --git a/tests/ui/deriving/deriving-smart-pointer-neg.stderr b/tests/ui/deriving/deriving-smart-pointer-neg.stderr
index e7c2afc8b00..9ab117698c7 100644
--- a/tests/ui/deriving/deriving-smart-pointer-neg.stderr
+++ b/tests/ui/deriving/deriving-smart-pointer-neg.stderr
@@ -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
    |
@@ -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`.