Skip to content

Commit 8c5ea61

Browse files
authored
Rollup merge of #109136 - compiler-errors:simplify-proc-macro-checking, r=oli-obk
Simplify proc macro signature validity check Use an `ObligationCtxt` instead of `normalize_erasing_regions` + `DeepRejectCtxt`. This should both give us a more accurate error message, and also avoid issues like not-well-formed proc macro signatures. Also, let's fall back on the regular type mismatch error reporting for making these diagnostic notes, instead of hard-coding a bunch of specific diagnostics. Fixes #109129
2 parents d133c36 + 00dc3b2 commit 8c5ea61

17 files changed

+212
-275
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -5139,6 +5139,7 @@ dependencies = [
51395139
"rustc_session",
51405140
"rustc_span",
51415141
"rustc_target",
5142+
"rustc_trait_selection",
51425143
"tracing",
51435144
]
51445145

compiler/rustc_passes/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ rustc_span = { path = "../rustc_span" }
2222
rustc_lexer = { path = "../rustc_lexer" }
2323
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
2424
rustc_feature = { path = "../rustc_feature" }
25+
rustc_trait_selection = { path = "../rustc_trait_selection" }

compiler/rustc_passes/messages.ftl

+1-20
Original file line numberDiff line numberDiff line change
@@ -720,26 +720,7 @@ passes_ignored_derived_impls =
720720
*[other] traits {$trait_list}, but these are
721721
} intentionally ignored during dead code analysis
722722
723-
passes_proc_macro_typeerror = mismatched {$kind} signature
724-
.label = found {$found}, expected type `proc_macro::TokenStream`
725-
.note = {$kind}s must have a signature of `{$expected_signature}`
726-
727-
passes_proc_macro_diff_arg_count = mismatched {$kind} signature
728-
.label = found unexpected {$count ->
729-
[one] argument
730-
*[other] arguments
731-
}
732-
.note = {$kind}s must have a signature of `{$expected_signature}`
733-
734-
passes_proc_macro_missing_args = mismatched {$kind} signature
735-
.label = {$kind} must have {$expected_input_count ->
736-
[one] one argument
737-
*[other] two arguments
738-
} of type `proc_macro::TokenStream`
739-
740-
passes_proc_macro_invalid_abi = proc macro functions may not be `extern "{$abi}"`
741-
742-
passes_proc_macro_unsafe = proc macro functions may not be `unsafe`
723+
passes_proc_macro_bad_sig = {$kind} has incorrect signature
743724
744725
passes_skipping_const_checks = skipping const checks
745726

compiler/rustc_passes/src/check_attr.rs

+88-85
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ use rustc_hir::{
1919
use rustc_hir::{MethodKind, Target, Unsafety};
2020
use rustc_middle::hir::nested_filter;
2121
use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault;
22-
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
22+
use rustc_middle::traits::ObligationCause;
23+
use rustc_middle::ty::error::{ExpectedFound, TypeError};
2324
use rustc_middle::ty::query::Providers;
24-
use rustc_middle::ty::{ParamEnv, TyCtxt};
25+
use rustc_middle::ty::{self, TyCtxt};
2526
use rustc_session::lint::builtin::{
2627
CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS,
2728
UNUSED_ATTRIBUTES,
@@ -30,6 +31,9 @@ use rustc_session::parse::feature_err;
3031
use rustc_span::symbol::{kw, sym, Symbol};
3132
use rustc_span::{Span, DUMMY_SP};
3233
use rustc_target::spec::abi::Abi;
34+
use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs};
35+
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
36+
use rustc_trait_selection::traits::ObligationCtxt;
3337
use std::cell::Cell;
3438
use std::collections::hash_map::Entry;
3539

@@ -2188,100 +2192,99 @@ impl CheckAttrVisitor<'_> {
21882192
///
21892193
/// If this best effort goes wrong, it will just emit a worse error later (see #102923)
21902194
fn check_proc_macro(&self, hir_id: HirId, target: Target, kind: ProcMacroKind) {
2191-
let expected_input_count = match kind {
2192-
ProcMacroKind::Attribute => 2,
2193-
ProcMacroKind::Derive | ProcMacroKind::FunctionLike => 1,
2194-
};
2195-
2196-
let expected_signature = match kind {
2197-
ProcMacroKind::Attribute => "fn(TokenStream, TokenStream) -> TokenStream",
2198-
ProcMacroKind::Derive | ProcMacroKind::FunctionLike => "fn(TokenStream) -> TokenStream",
2199-
};
2195+
if target != Target::Fn {
2196+
return;
2197+
}
22002198

22012199
let tcx = self.tcx;
2202-
if target == Target::Fn {
2203-
let Some(tokenstream) = tcx.get_diagnostic_item(sym::TokenStream) else {return};
2204-
let tokenstream = tcx.type_of(tokenstream).subst_identity();
2205-
2206-
let id = hir_id.expect_owner();
2207-
let hir_sig = tcx.hir().fn_sig_by_hir_id(hir_id).unwrap();
2208-
2209-
let sig =
2210-
tcx.liberate_late_bound_regions(id.to_def_id(), tcx.fn_sig(id).subst_identity());
2211-
let sig = tcx.normalize_erasing_regions(ParamEnv::empty(), sig);
2212-
2213-
// We don't currently require that the function signature is equal to
2214-
// `fn(TokenStream) -> TokenStream`, but instead monomorphizes to
2215-
// `fn(TokenStream) -> TokenStream` after some substitution of generic arguments.
2216-
//
2217-
// Properly checking this means pulling in additional `rustc` crates, so we don't.
2218-
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey };
2219-
2220-
if sig.abi != Abi::Rust {
2221-
tcx.sess.emit_err(errors::ProcMacroInvalidAbi {
2222-
span: hir_sig.span,
2223-
abi: sig.abi.name(),
2224-
});
2225-
self.abort.set(true);
2226-
}
2200+
let Some(token_stream_def_id) = tcx.get_diagnostic_item(sym::TokenStream) else { return; };
2201+
let Some(token_stream) = tcx.type_of(token_stream_def_id).no_bound_vars() else { return; };
22272202

2228-
if sig.unsafety == Unsafety::Unsafe {
2229-
tcx.sess.emit_err(errors::ProcMacroUnsafe { span: hir_sig.span });
2230-
self.abort.set(true);
2231-
}
2203+
let def_id = hir_id.expect_owner().def_id;
2204+
let param_env = ty::ParamEnv::empty();
22322205

2233-
let output = sig.output();
2206+
let infcx = tcx.infer_ctxt().build();
2207+
let ocx = ObligationCtxt::new(&infcx);
22342208

2235-
// Typecheck the output
2236-
if !drcx.types_may_unify(output, tokenstream) {
2237-
tcx.sess.emit_err(errors::ProcMacroTypeError {
2238-
span: hir_sig.decl.output.span(),
2239-
found: output,
2240-
kind,
2241-
expected_signature,
2242-
});
2243-
self.abort.set(true);
2244-
}
2209+
let span = tcx.def_span(def_id);
2210+
let fresh_substs = infcx.fresh_substs_for_item(span, def_id.to_def_id());
2211+
let sig = tcx.liberate_late_bound_regions(
2212+
def_id.to_def_id(),
2213+
tcx.fn_sig(def_id).subst(tcx, fresh_substs),
2214+
);
22452215

2246-
if sig.inputs().len() < expected_input_count {
2247-
tcx.sess.emit_err(errors::ProcMacroMissingArguments {
2248-
expected_input_count,
2249-
span: hir_sig.span,
2250-
kind,
2251-
expected_signature,
2252-
});
2253-
self.abort.set(true);
2254-
}
2216+
let mut cause = ObligationCause::misc(span, def_id);
2217+
let sig = ocx.normalize(&cause, param_env, sig);
22552218

2256-
// Check that the inputs are correct, if there are enough.
2257-
if sig.inputs().len() >= expected_input_count {
2258-
for (arg, input) in
2259-
sig.inputs().iter().zip(hir_sig.decl.inputs).take(expected_input_count)
2260-
{
2261-
if !drcx.types_may_unify(*arg, tokenstream) {
2262-
tcx.sess.emit_err(errors::ProcMacroTypeError {
2263-
span: input.span,
2264-
found: *arg,
2265-
kind,
2266-
expected_signature,
2267-
});
2268-
self.abort.set(true);
2219+
// proc macro is not WF.
2220+
let errors = ocx.select_where_possible();
2221+
if !errors.is_empty() {
2222+
return;
2223+
}
2224+
2225+
let expected_sig = tcx.mk_fn_sig(
2226+
std::iter::repeat(token_stream).take(match kind {
2227+
ProcMacroKind::Attribute => 2,
2228+
ProcMacroKind::Derive | ProcMacroKind::FunctionLike => 1,
2229+
}),
2230+
token_stream,
2231+
false,
2232+
Unsafety::Normal,
2233+
Abi::Rust,
2234+
);
2235+
2236+
if let Err(terr) = ocx.eq(&cause, param_env, expected_sig, sig) {
2237+
let mut diag = tcx.sess.create_err(errors::ProcMacroBadSig { span, kind });
2238+
2239+
let hir_sig = tcx.hir().fn_sig_by_hir_id(hir_id);
2240+
if let Some(hir_sig) = hir_sig {
2241+
match terr {
2242+
TypeError::ArgumentMutability(idx) | TypeError::ArgumentSorts(_, idx) => {
2243+
if let Some(ty) = hir_sig.decl.inputs.get(idx) {
2244+
diag.set_span(ty.span);
2245+
cause.span = ty.span;
2246+
} else if idx == hir_sig.decl.inputs.len() {
2247+
let span = hir_sig.decl.output.span();
2248+
diag.set_span(span);
2249+
cause.span = span;
2250+
}
2251+
}
2252+
TypeError::ArgCount => {
2253+
if let Some(ty) = hir_sig.decl.inputs.get(expected_sig.inputs().len()) {
2254+
diag.set_span(ty.span);
2255+
cause.span = ty.span;
2256+
}
22692257
}
2258+
TypeError::UnsafetyMismatch(_) => {
2259+
// FIXME: Would be nice if we had a span here..
2260+
}
2261+
TypeError::AbiMismatch(_) => {
2262+
// FIXME: Would be nice if we had a span here..
2263+
}
2264+
TypeError::VariadicMismatch(_) => {
2265+
// FIXME: Would be nice if we had a span here..
2266+
}
2267+
_ => {}
22702268
}
22712269
}
22722270

2273-
// Check that there are not too many arguments
2274-
let body_id = tcx.hir().body_owned_by(id.def_id);
2275-
let excess = tcx.hir().body(body_id).params.get(expected_input_count..);
2276-
if let Some(excess @ [begin @ end] | excess @ [begin, .., end]) = excess {
2277-
tcx.sess.emit_err(errors::ProcMacroDiffArguments {
2278-
span: begin.span.to(end.span),
2279-
count: excess.len(),
2280-
kind,
2281-
expected_signature,
2282-
});
2283-
self.abort.set(true);
2284-
}
2271+
infcx.err_ctxt().note_type_err(
2272+
&mut diag,
2273+
&cause,
2274+
None,
2275+
Some(ValuePairs::Sigs(ExpectedFound { expected: expected_sig, found: sig })),
2276+
terr,
2277+
false,
2278+
false,
2279+
);
2280+
diag.emit();
2281+
self.abort.set(true);
2282+
}
2283+
2284+
let errors = ocx.select_all_or_error();
2285+
if !errors.is_empty() {
2286+
infcx.err_ctxt().report_fulfillment_errors(&errors);
2287+
self.abort.set(true);
22852288
}
22862289
}
22872290
}

compiler/rustc_passes/src/errors.rs

+2-43
Original file line numberDiff line numberDiff line change
@@ -1546,52 +1546,11 @@ pub struct ChangeFieldsToBeOfUnitType {
15461546
}
15471547

15481548
#[derive(Diagnostic)]
1549-
#[diag(passes_proc_macro_typeerror)]
1550-
#[note]
1551-
pub(crate) struct ProcMacroTypeError<'tcx> {
1552-
#[primary_span]
1553-
#[label]
1554-
pub span: Span,
1555-
pub found: Ty<'tcx>,
1556-
pub kind: ProcMacroKind,
1557-
pub expected_signature: &'static str,
1558-
}
1559-
1560-
#[derive(Diagnostic)]
1561-
#[diag(passes_proc_macro_diff_arg_count)]
1562-
pub(crate) struct ProcMacroDiffArguments {
1563-
#[primary_span]
1564-
#[label]
1565-
pub span: Span,
1566-
pub count: usize,
1567-
pub kind: ProcMacroKind,
1568-
pub expected_signature: &'static str,
1569-
}
1570-
1571-
#[derive(Diagnostic)]
1572-
#[diag(passes_proc_macro_missing_args)]
1573-
pub(crate) struct ProcMacroMissingArguments {
1549+
#[diag(passes_proc_macro_bad_sig)]
1550+
pub(crate) struct ProcMacroBadSig {
15741551
#[primary_span]
1575-
#[label]
15761552
pub span: Span,
1577-
pub expected_input_count: usize,
15781553
pub kind: ProcMacroKind,
1579-
pub expected_signature: &'static str,
1580-
}
1581-
1582-
#[derive(Diagnostic)]
1583-
#[diag(passes_proc_macro_invalid_abi)]
1584-
pub(crate) struct ProcMacroInvalidAbi {
1585-
#[primary_span]
1586-
pub span: Span,
1587-
pub abi: &'static str,
1588-
}
1589-
1590-
#[derive(Diagnostic)]
1591-
#[diag(passes_proc_macro_unsafe)]
1592-
pub(crate) struct ProcMacroUnsafe {
1593-
#[primary_span]
1594-
pub span: Span,
15951554
}
15961555

15971556
#[derive(Diagnostic)]

tests/ui/proc-macro/bad-projection.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// force-host
2+
// no-prefer-dynamic
3+
4+
#![crate_type = "proc-macro"]
5+
#![allow(warnings)]
6+
7+
extern crate proc_macro;
8+
9+
trait Project {
10+
type Assoc;
11+
}
12+
13+
#[proc_macro]
14+
pub fn uwu() -> <() as Project>::Assoc {}
15+
//~^ ERROR the trait bound `(): Project` is not satisfied
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0277]: the trait bound `(): Project` is not satisfied
2+
--> $DIR/bad-projection.rs:14:17
3+
|
4+
LL | pub fn uwu() -> <() as Project>::Assoc {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^ the trait `Project` is not implemented for `()`
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0277`.

tests/ui/proc-macro/proc-macro-abi.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,19 @@ use proc_macro::TokenStream;
99

1010
#[proc_macro]
1111
pub extern "C" fn abi(a: TokenStream) -> TokenStream {
12-
//~^ ERROR proc macro functions may not be `extern "C"`
12+
//~^ ERROR function-like proc macro has incorrect signature
1313
a
1414
}
1515

1616
#[proc_macro]
1717
pub extern "system" fn abi2(a: TokenStream) -> TokenStream {
18-
//~^ ERROR proc macro functions may not be `extern "system"`
18+
//~^ ERROR function-like proc macro has incorrect signature
1919
a
2020
}
2121

2222
#[proc_macro]
2323
pub extern fn abi3(a: TokenStream) -> TokenStream {
24-
//~^ ERROR proc macro functions may not be `extern "C"`
24+
//~^ ERROR function-like proc macro has incorrect signature
2525
a
2626
}
2727

+15-6
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
1-
error: proc macro functions may not be `extern "C"`
1+
error: function-like proc macro has incorrect signature
22
--> $DIR/proc-macro-abi.rs:11:1
33
|
44
LL | pub extern "C" fn abi(a: TokenStream) -> TokenStream {
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected "Rust" fn, found "C" fn
6+
|
7+
= note: expected signature `fn(proc_macro::TokenStream) -> proc_macro::TokenStream`
8+
found signature `extern "C" fn(proc_macro::TokenStream) -> proc_macro::TokenStream`
69

7-
error: proc macro functions may not be `extern "system"`
10+
error: function-like proc macro has incorrect signature
811
--> $DIR/proc-macro-abi.rs:17:1
912
|
1013
LL | pub extern "system" fn abi2(a: TokenStream) -> TokenStream {
11-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected "Rust" fn, found "system" fn
15+
|
16+
= note: expected signature `fn(proc_macro::TokenStream) -> proc_macro::TokenStream`
17+
found signature `extern "system" fn(proc_macro::TokenStream) -> proc_macro::TokenStream`
1218

13-
error: proc macro functions may not be `extern "C"`
19+
error: function-like proc macro has incorrect signature
1420
--> $DIR/proc-macro-abi.rs:23:1
1521
|
1622
LL | pub extern fn abi3(a: TokenStream) -> TokenStream {
17-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected "Rust" fn, found "C" fn
24+
|
25+
= note: expected signature `fn(proc_macro::TokenStream) -> proc_macro::TokenStream`
26+
found signature `extern "C" fn(proc_macro::TokenStream) -> proc_macro::TokenStream`
1827

1928
error: aborting due to 3 previous errors
2029

0 commit comments

Comments
 (0)