diff options
| author | Dylan DPC <99973273+Dylan-DPC@users.noreply.github.com> | 2023-03-15 17:51:32 +0530 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-15 17:51:32 +0530 |
| commit | 8c5ea6188fe54e0cf0879ccc6b1c5665bc35936d (patch) | |
| tree | 33f0bff641df2b21e31c53f1055f27e2e0bae0ca /compiler | |
| parent | d133c36fa982676e092b876ba65908d889877bec (diff) | |
| parent | 00dc3b24b7cf3db69ce4b3648e1fb9d1cd539c67 (diff) | |
| download | rust-8c5ea6188fe54e0cf0879ccc6b1c5665bc35936d.tar.gz rust-8c5ea6188fe54e0cf0879ccc6b1c5665bc35936d.zip | |
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
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_passes/Cargo.toml | 1 | ||||
| -rw-r--r-- | compiler/rustc_passes/messages.ftl | 21 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/check_attr.rs | 173 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/errors.rs | 45 |
4 files changed, 92 insertions, 148 deletions
diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml index faa9c493d88..44f991f8c15 100644 --- a/compiler/rustc_passes/Cargo.toml +++ b/compiler/rustc_passes/Cargo.toml @@ -22,3 +22,4 @@ rustc_span = { path = "../rustc_span" } rustc_lexer = { path = "../rustc_lexer" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_feature = { path = "../rustc_feature" } +rustc_trait_selection = { path = "../rustc_trait_selection" } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 3fa78efc290..d063b51c8b8 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -720,26 +720,7 @@ passes_ignored_derived_impls = *[other] traits {$trait_list}, but these are } intentionally ignored during dead code analysis -passes_proc_macro_typeerror = mismatched {$kind} signature - .label = found {$found}, expected type `proc_macro::TokenStream` - .note = {$kind}s must have a signature of `{$expected_signature}` - -passes_proc_macro_diff_arg_count = mismatched {$kind} signature - .label = found unexpected {$count -> - [one] argument - *[other] arguments - } - .note = {$kind}s must have a signature of `{$expected_signature}` - -passes_proc_macro_missing_args = mismatched {$kind} signature - .label = {$kind} must have {$expected_input_count -> - [one] one argument - *[other] two arguments - } of type `proc_macro::TokenStream` - -passes_proc_macro_invalid_abi = proc macro functions may not be `extern "{$abi}"` - -passes_proc_macro_unsafe = proc macro functions may not be `unsafe` +passes_proc_macro_bad_sig = {$kind} has incorrect signature passes_skipping_const_checks = skipping const checks diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index c8d371dd084..8bed7888142 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -19,9 +19,10 @@ use rustc_hir::{ use rustc_hir::{MethodKind, Target, Unsafety}; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; -use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{ParamEnv, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::builtin::{ CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS, UNUSED_ATTRIBUTES, @@ -30,6 +31,9 @@ use rustc_session::parse::feature_err; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs}; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; +use rustc_trait_selection::traits::ObligationCtxt; use std::cell::Cell; use std::collections::hash_map::Entry; @@ -2188,100 +2192,99 @@ impl CheckAttrVisitor<'_> { /// /// 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, - ProcMacroKind::Derive | ProcMacroKind::FunctionLike => 1, - }; - - let expected_signature = match kind { - ProcMacroKind::Attribute => "fn(TokenStream, TokenStream) -> TokenStream", - ProcMacroKind::Derive | ProcMacroKind::FunctionLike => "fn(TokenStream) -> TokenStream", - }; + if target != Target::Fn { + return; + } let tcx = self.tcx; - if target == Target::Fn { - let Some(tokenstream) = tcx.get_diagnostic_item(sym::TokenStream) else {return}; - let tokenstream = tcx.type_of(tokenstream).subst_identity(); - - let id = hir_id.expect_owner(); - let hir_sig = tcx.hir().fn_sig_by_hir_id(hir_id).unwrap(); - - let sig = - tcx.liberate_late_bound_regions(id.to_def_id(), tcx.fn_sig(id).subst_identity()); - 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::AsCandidateKey }; - - if sig.abi != Abi::Rust { - tcx.sess.emit_err(errors::ProcMacroInvalidAbi { - span: hir_sig.span, - abi: sig.abi.name(), - }); - self.abort.set(true); - } + let Some(token_stream_def_id) = tcx.get_diagnostic_item(sym::TokenStream) else { return; }; + let Some(token_stream) = tcx.type_of(token_stream_def_id).no_bound_vars() else { return; }; - if sig.unsafety == Unsafety::Unsafe { - tcx.sess.emit_err(errors::ProcMacroUnsafe { span: hir_sig.span }); - self.abort.set(true); - } + let def_id = hir_id.expect_owner().def_id; + let param_env = ty::ParamEnv::empty(); - let output = sig.output(); + let infcx = tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); - // Typecheck the output - if !drcx.types_may_unify(output, tokenstream) { - tcx.sess.emit_err(errors::ProcMacroTypeError { - span: hir_sig.decl.output.span(), - found: output, - kind, - expected_signature, - }); - self.abort.set(true); - } + let span = tcx.def_span(def_id); + let fresh_substs = infcx.fresh_substs_for_item(span, def_id.to_def_id()); + let sig = tcx.liberate_late_bound_regions( + def_id.to_def_id(), + tcx.fn_sig(def_id).subst(tcx, fresh_substs), + ); - if sig.inputs().len() < expected_input_count { - tcx.sess.emit_err(errors::ProcMacroMissingArguments { - expected_input_count, - span: hir_sig.span, - kind, - expected_signature, - }); - self.abort.set(true); - } + let mut cause = ObligationCause::misc(span, def_id); + let sig = ocx.normalize(&cause, param_env, sig); - // 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(errors::ProcMacroTypeError { - span: input.span, - found: *arg, - kind, - expected_signature, - }); - self.abort.set(true); + // proc macro is not WF. + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + return; + } + + let expected_sig = tcx.mk_fn_sig( + std::iter::repeat(token_stream).take(match kind { + ProcMacroKind::Attribute => 2, + ProcMacroKind::Derive | ProcMacroKind::FunctionLike => 1, + }), + token_stream, + false, + Unsafety::Normal, + Abi::Rust, + ); + + if let Err(terr) = ocx.eq(&cause, param_env, expected_sig, sig) { + let mut diag = tcx.sess.create_err(errors::ProcMacroBadSig { span, kind }); + + let hir_sig = tcx.hir().fn_sig_by_hir_id(hir_id); + if let Some(hir_sig) = hir_sig { + match terr { + TypeError::ArgumentMutability(idx) | TypeError::ArgumentSorts(_, idx) => { + if let Some(ty) = hir_sig.decl.inputs.get(idx) { + diag.set_span(ty.span); + cause.span = ty.span; + } else if idx == hir_sig.decl.inputs.len() { + let span = hir_sig.decl.output.span(); + diag.set_span(span); + cause.span = span; + } + } + TypeError::ArgCount => { + if let Some(ty) = hir_sig.decl.inputs.get(expected_sig.inputs().len()) { + diag.set_span(ty.span); + cause.span = ty.span; + } } + TypeError::UnsafetyMismatch(_) => { + // FIXME: Would be nice if we had a span here.. + } + TypeError::AbiMismatch(_) => { + // FIXME: Would be nice if we had a span here.. + } + TypeError::VariadicMismatch(_) => { + // FIXME: Would be nice if we had a span here.. + } + _ => {} } } - // Check that there are not too many arguments - let body_id = tcx.hir().body_owned_by(id.def_id); - let excess = tcx.hir().body(body_id).params.get(expected_input_count..); - if let Some(excess @ [begin @ end] | excess @ [begin, .., end]) = excess { - tcx.sess.emit_err(errors::ProcMacroDiffArguments { - span: begin.span.to(end.span), - count: excess.len(), - kind, - expected_signature, - }); - self.abort.set(true); - } + infcx.err_ctxt().note_type_err( + &mut diag, + &cause, + None, + Some(ValuePairs::Sigs(ExpectedFound { expected: expected_sig, found: sig })), + terr, + false, + false, + ); + diag.emit(); + self.abort.set(true); + } + + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + infcx.err_ctxt().report_fulfillment_errors(&errors); + self.abort.set(true); } } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 9f1c0b5a0b7..1b0cd5d91ab 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1546,52 +1546,11 @@ pub struct ChangeFieldsToBeOfUnitType { } #[derive(Diagnostic)] -#[diag(passes_proc_macro_typeerror)] -#[note] -pub(crate) struct ProcMacroTypeError<'tcx> { - #[primary_span] - #[label] - pub span: Span, - pub found: Ty<'tcx>, - pub kind: ProcMacroKind, - pub expected_signature: &'static str, -} - -#[derive(Diagnostic)] -#[diag(passes_proc_macro_diff_arg_count)] -pub(crate) struct ProcMacroDiffArguments { - #[primary_span] - #[label] - pub span: Span, - pub count: usize, - pub kind: ProcMacroKind, - pub expected_signature: &'static str, -} - -#[derive(Diagnostic)] -#[diag(passes_proc_macro_missing_args)] -pub(crate) struct ProcMacroMissingArguments { +#[diag(passes_proc_macro_bad_sig)] +pub(crate) struct ProcMacroBadSig { #[primary_span] - #[label] pub span: Span, - pub expected_input_count: usize, pub kind: ProcMacroKind, - pub expected_signature: &'static str, -} - -#[derive(Diagnostic)] -#[diag(passes_proc_macro_invalid_abi)] -pub(crate) struct ProcMacroInvalidAbi { - #[primary_span] - pub span: Span, - pub abi: &'static str, -} - -#[derive(Diagnostic)] -#[diag(passes_proc_macro_unsafe)] -pub(crate) struct ProcMacroUnsafe { - #[primary_span] - pub span: Span, } #[derive(Diagnostic)] |
