about summary refs log tree commit diff
diff options
context:
space:
mode:
authormejrs <>2023-01-08 01:37:22 +0100
committermejrs <>2023-01-12 00:11:32 +0100
commita8e3abd04cf85080d921c2d1875e0094b2db5155 (patch)
treed02e0ba92d23c51baa2756343a225b7b2e74defe
parentf92000816e0cf8bdfe6ad422464ce0fa6144b496 (diff)
downloadrust-a8e3abd04cf85080d921c2d1875e0094b2db5155.tar.gz
rust-a8e3abd04cf85080d921c2d1875e0094b2db5155.zip
Address feedback
-rw-r--r--compiler/rustc_passes/src/check_attr.rs56
-rw-r--r--tests/ui/proc-macro/allowed-signatures.rs24
-rw-r--r--tests/ui/proc-macro/proc-macro-abi.rs6
3 files changed, 63 insertions, 23 deletions
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 517bf2533c5..c2ce46d965c 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -23,6 +23,7 @@ use rustc_hir::{
 use rustc_hir::{MethodKind, Target, Unsafety};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::resolve_lifetime::ObjectLifetimeDefault;
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{ParamEnv, TyCtxt};
 use rustc_session::lint::builtin::{
@@ -84,6 +85,8 @@ impl IntoDiagnosticArg for ProcMacroKind {
 
 struct CheckAttrVisitor<'tcx> {
     tcx: TyCtxt<'tcx>,
+
+    // Whether or not this visitor should abort after finding errors
     abort: Cell<bool>,
 }
 
@@ -2084,6 +2087,9 @@ impl CheckAttrVisitor<'_> {
         );
     }
 
+    /// A best effort attempt to create an error for a mismatching proc macro signature.
+    ///
+    /// If this best effort goes wrong, it will just emit a worse error later (see #102923)
     fn check_proc_macro(&self, hir_id: HirId, target: Target, kind: ProcMacroKind) {
         let expected_input_count = match kind {
             ProcMacroKind::Attribute => 2,
@@ -2103,23 +2109,30 @@ impl CheckAttrVisitor<'_> {
             let id = hir_id.expect_owner();
             let hir_sig = tcx.hir().fn_sig_by_hir_id(hir_id).unwrap();
 
-            let sig = tcx.fn_sig(id);
+            let sig = tcx.liberate_late_bound_regions(id.to_def_id(), tcx.fn_sig(id));
+            let sig = tcx.normalize_erasing_regions(ParamEnv::empty(), sig);
+
+            // We don't currently require that the function signature is equal to
+            // `fn(TokenStream) -> TokenStream`, but instead monomorphizes to
+            // `fn(TokenStream) -> TokenStream` after some substitution of generic arguments.
+            //
+            // Properly checking this means pulling in additional `rustc` crates, so we don't.
+            let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer };
 
-            if sig.abi() != Abi::Rust {
-                tcx.sess
-                    .emit_err(ProcMacroInvalidAbi { span: hir_sig.span, abi: sig.abi().name() });
+            if sig.abi != Abi::Rust {
+                tcx.sess.emit_err(ProcMacroInvalidAbi { span: hir_sig.span, abi: sig.abi.name() });
                 self.abort.set(true);
             }
 
-            if sig.unsafety() == Unsafety::Unsafe {
+            if sig.unsafety == Unsafety::Unsafe {
                 tcx.sess.emit_err(ProcMacroUnsafe { span: hir_sig.span });
                 self.abort.set(true);
             }
 
-            let output = sig.output().skip_binder();
+            let output = sig.output();
 
             // Typecheck the output
-            if tcx.normalize_erasing_regions(ParamEnv::empty(), output) != tokenstream {
+            if !drcx.types_may_unify(output, tokenstream) {
                 tcx.sess.emit_err(ProcMacroTypeError {
                     span: hir_sig.decl.output.span(),
                     found: output,
@@ -2129,11 +2142,22 @@ impl CheckAttrVisitor<'_> {
                 self.abort.set(true);
             }
 
-            // Typecheck "expected_input_count" inputs, emitting
-            // `ProcMacroMissingArguments` if there are not enough.
-            if let Some(args) = sig.inputs().skip_binder().get(0..expected_input_count) {
-                for (arg, input) in args.iter().zip(hir_sig.decl.inputs) {
-                    if tcx.normalize_erasing_regions(ParamEnv::empty(), *arg) != tokenstream {
+            if sig.inputs().len() < expected_input_count {
+                tcx.sess.emit_err(ProcMacroMissingArguments {
+                    expected_input_count,
+                    span: hir_sig.span,
+                    kind,
+                    expected_signature,
+                });
+                self.abort.set(true);
+            }
+
+            // Check that the inputs are correct, if there are enough.
+            if sig.inputs().len() >= expected_input_count {
+                for (arg, input) in
+                    sig.inputs().iter().zip(hir_sig.decl.inputs).take(expected_input_count)
+                {
+                    if !drcx.types_may_unify(*arg, tokenstream) {
                         tcx.sess.emit_err(ProcMacroTypeError {
                             span: input.span,
                             found: *arg,
@@ -2143,14 +2167,6 @@ impl CheckAttrVisitor<'_> {
                         self.abort.set(true);
                     }
                 }
-            } else {
-                tcx.sess.emit_err(ProcMacroMissingArguments {
-                    expected_input_count,
-                    span: hir_sig.span,
-                    kind,
-                    expected_signature,
-                });
-                self.abort.set(true);
             }
 
             // Check that there are not too many arguments
diff --git a/tests/ui/proc-macro/allowed-signatures.rs b/tests/ui/proc-macro/allowed-signatures.rs
new file mode 100644
index 00000000000..03c6ef86632
--- /dev/null
+++ b/tests/ui/proc-macro/allowed-signatures.rs
@@ -0,0 +1,24 @@
+// check-pass
+
+#![crate_type = "proc-macro"]
+#![allow(private_in_public)]
+extern crate proc_macro;
+use proc_macro::TokenStream;
+
+#[proc_macro]
+pub fn foo<T>(t: T) -> TokenStream {
+  TokenStream::new()
+}
+
+trait Project {
+    type Assoc;
+}
+
+impl Project for () {
+    type Assoc = TokenStream;
+}
+
+#[proc_macro]
+pub fn uwu(_input: <() as Project>::Assoc) -> <() as Project>::Assoc {
+    TokenStream::new()
+}
diff --git a/tests/ui/proc-macro/proc-macro-abi.rs b/tests/ui/proc-macro/proc-macro-abi.rs
index 2a40ddd496c..8e9d50d7832 100644
--- a/tests/ui/proc-macro/proc-macro-abi.rs
+++ b/tests/ui/proc-macro/proc-macro-abi.rs
@@ -6,19 +6,19 @@ use proc_macro::TokenStream;
 
 #[proc_macro]
 pub extern "C" fn abi(a: TokenStream) -> TokenStream {
-    //~^ ERROR proc macro functions may not be `extern
+    //~^ ERROR proc macro functions may not be `extern "C"`
     a
 }
 
 #[proc_macro]
 pub extern "system" fn abi2(a: TokenStream) -> TokenStream {
-    //~^ ERROR proc macro functions may not be `extern
+    //~^ ERROR proc macro functions may not be `extern "system"`
     a
 }
 
 #[proc_macro]
 pub extern fn abi3(a: TokenStream) -> TokenStream {
-    //~^ ERROR proc macro functions may not be `extern
+    //~^ ERROR proc macro functions may not be `extern "C"`
     a
 }