From f92000816e0cf8bdfe6ad422464ce0fa6144b496 Mon Sep 17 00:00:00 2001 From: mejrs <> Date: Tue, 3 Jan 2023 14:08:20 +0100 Subject: [PATCH 01/21] Improve proc macro attribute diagnostics --- .../locales/en-US/passes.ftl | 21 +++ compiler/rustc_passes/src/check_attr.rs | 130 +++++++++++++++++- compiler/rustc_passes/src/errors.rs | 50 +++++++ compiler/rustc_span/src/symbol.rs | 1 + library/proc_macro/src/lib.rs | 1 + tests/ui/proc-macro/proc-macro-abi.rs | 28 ++++ tests/ui/proc-macro/proc-macro-abi.stderr | 20 +++ .../signature-proc-macro-attribute.rs | 29 ++++ .../signature-proc-macro-attribute.stderr | 42 ++++++ .../proc-macro/signature-proc-macro-derive.rs | 28 ++++ .../signature-proc-macro-derive.stderr | 40 ++++++ tests/ui/proc-macro/signature-proc-macro.rs | 28 ++++ .../ui/proc-macro/signature-proc-macro.stderr | 40 ++++++ tests/ui/proc-macro/signature.rs | 6 +- tests/ui/proc-macro/signature.stderr | 46 +++++-- 15 files changed, 488 insertions(+), 22 deletions(-) create mode 100644 tests/ui/proc-macro/proc-macro-abi.rs create mode 100644 tests/ui/proc-macro/proc-macro-abi.stderr create mode 100644 tests/ui/proc-macro/signature-proc-macro-attribute.rs create mode 100644 tests/ui/proc-macro/signature-proc-macro-attribute.stderr create mode 100644 tests/ui/proc-macro/signature-proc-macro-derive.rs create mode 100644 tests/ui/proc-macro/signature-proc-macro-derive.stderr create mode 100644 tests/ui/proc-macro/signature-proc-macro.rs create mode 100644 tests/ui/proc-macro/signature-proc-macro.stderr diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl index 001e53d1d0e4c..63db6b6837b47 100644 --- a/compiler/rustc_error_messages/locales/en-US/passes.ftl +++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl @@ -707,3 +707,24 @@ passes_ignored_derived_impls = [one] trait {$trait_list}, but this is *[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` diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index c59c06ac31ed7..517bf2533c5e6 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -6,11 +6,12 @@ use crate::errors::{ self, AttrApplication, DebugVisualizerUnreadable, InvalidAttrAtCrateLevel, ObjectLifetimeErr, - OnlyHasEffectOn, TransparentIncompatible, UnrecognizedReprHint, + OnlyHasEffectOn, ProcMacroDiffArguments, ProcMacroInvalidAbi, ProcMacroMissingArguments, + ProcMacroTypeError, ProcMacroUnsafe, TransparentIncompatible, UnrecognizedReprHint, }; use rustc_ast::{ast, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{fluent, Applicability, MultiSpan}; +use rustc_errors::{fluent, Applicability, IntoDiagnosticArg, MultiSpan}; use rustc_expand::base::resolve_path; use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_hir as hir; @@ -19,11 +20,11 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID, }; -use rustc_hir::{MethodKind, Target}; +use rustc_hir::{MethodKind, Target, Unsafety}; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_lifetime::ObjectLifetimeDefault; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{ParamEnv, TyCtxt}; use rustc_session::lint::builtin::{ CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, UNUSED_ATTRIBUTES, }; @@ -31,6 +32,7 @@ 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 std::cell::Cell; use std::collections::hash_map::Entry; pub(crate) fn target_from_impl_item<'tcx>( @@ -62,8 +64,27 @@ enum ItemLike<'tcx> { ForeignItem, } +#[derive(Copy, Clone)] +pub(crate) enum ProcMacroKind { + FunctionLike, + Derive, + Attribute, +} + +impl IntoDiagnosticArg for ProcMacroKind { + fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { + match self { + ProcMacroKind::Attribute => "attribute proc macro", + ProcMacroKind::Derive => "derive proc macro", + ProcMacroKind::FunctionLike => "function-like proc macro", + } + .into_diagnostic_arg() + } +} + struct CheckAttrVisitor<'tcx> { tcx: TyCtxt<'tcx>, + abort: Cell, } impl CheckAttrVisitor<'_> { @@ -172,7 +193,7 @@ impl CheckAttrVisitor<'_> { sym::path => self.check_generic_attr(hir_id, attr, target, Target::Mod), sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target), sym::macro_export => self.check_macro_export(hir_id, attr, target), - sym::ignore | sym::should_panic | sym::proc_macro_derive => { + sym::ignore | sym::should_panic => { self.check_generic_attr(hir_id, attr, target, Target::Fn) } sym::automatically_derived => { @@ -182,6 +203,16 @@ impl CheckAttrVisitor<'_> { self.check_generic_attr(hir_id, attr, target, Target::Mod) } sym::rustc_object_lifetime_default => self.check_object_lifetime_default(hir_id), + sym::proc_macro => { + self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike) + } + sym::proc_macro_attribute => { + self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute); + } + sym::proc_macro_derive => { + self.check_generic_attr(hir_id, attr, target, Target::Fn); + self.check_proc_macro(hir_id, target, ProcMacroKind::Derive) + } _ => {} } @@ -2052,6 +2083,90 @@ impl CheckAttrVisitor<'_> { errors::Unused { attr_span: attr.span, note }, ); } + + 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", + }; + + 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); + + 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); + + 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 { + tcx.sess.emit_err(ProcMacroUnsafe { span: hir_sig.span }); + self.abort.set(true); + } + + let output = sig.output().skip_binder(); + + // Typecheck the output + if tcx.normalize_erasing_regions(ParamEnv::empty(), output) != tokenstream { + tcx.sess.emit_err(ProcMacroTypeError { + span: hir_sig.decl.output.span(), + found: output, + kind, + expected_signature, + }); + 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 { + tcx.sess.emit_err(ProcMacroTypeError { + span: input.span, + found: *arg, + kind, + expected_signature, + }); + 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 + 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(ProcMacroDiffArguments { + span: begin.span.to(end.span), + count: excess.len(), + kind, + expected_signature, + }); + self.abort.set(true); + } + } + } } impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { @@ -2214,12 +2329,15 @@ fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) } fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - let check_attr_visitor = &mut CheckAttrVisitor { tcx }; + let check_attr_visitor = &mut CheckAttrVisitor { tcx, abort: Cell::new(false) }; tcx.hir().visit_item_likes_in_module(module_def_id, check_attr_visitor); if module_def_id.is_top_level_module() { check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None); check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs()); } + if check_attr_visitor.abort.get() { + tcx.sess.abort_if_errors() + } } pub(crate) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index c6cd69add28a0..68103608ec9d9 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -12,6 +12,7 @@ use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{MainDefinition, Ty}; use rustc_span::{Span, Symbol, DUMMY_SP}; +use crate::check_attr::ProcMacroKind; use crate::lang_items::Duplicate; #[derive(LintDiagnostic)] @@ -1508,3 +1509,52 @@ pub struct ChangeFieldsToBeOfUnitType { #[suggestion_part(code = "()")] pub spans: Vec, } + +#[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 { + #[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, +} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index fbb12701d96ab..0cc9567d4d672 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -287,6 +287,7 @@ symbols! { Target, ToOwned, ToString, + TokenStream, Try, TryCaptureGeneric, TryCapturePrintable, diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index f0e4f5d8a8013..8bff40c279aaa 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -74,6 +74,7 @@ pub fn is_available() -> bool { /// /// This is both the input and output of `#[proc_macro]`, `#[proc_macro_attribute]` /// and `#[proc_macro_derive]` definitions. +#[rustc_diagnostic_item = "TokenStream"] #[stable(feature = "proc_macro_lib", since = "1.15.0")] #[derive(Clone)] pub struct TokenStream(Option); diff --git a/tests/ui/proc-macro/proc-macro-abi.rs b/tests/ui/proc-macro/proc-macro-abi.rs new file mode 100644 index 0000000000000..2a40ddd496ce8 --- /dev/null +++ b/tests/ui/proc-macro/proc-macro-abi.rs @@ -0,0 +1,28 @@ +#![crate_type = "proc-macro"] +#![allow(warnings)] + +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro] +pub extern "C" fn abi(a: TokenStream) -> TokenStream { + //~^ ERROR proc macro functions may not be `extern + a +} + +#[proc_macro] +pub extern "system" fn abi2(a: TokenStream) -> TokenStream { + //~^ ERROR proc macro functions may not be `extern + a +} + +#[proc_macro] +pub extern fn abi3(a: TokenStream) -> TokenStream { + //~^ ERROR proc macro functions may not be `extern + a +} + +#[proc_macro] +pub extern "Rust" fn abi4(a: TokenStream) -> TokenStream { + a +} diff --git a/tests/ui/proc-macro/proc-macro-abi.stderr b/tests/ui/proc-macro/proc-macro-abi.stderr new file mode 100644 index 0000000000000..fa5f5dc098998 --- /dev/null +++ b/tests/ui/proc-macro/proc-macro-abi.stderr @@ -0,0 +1,20 @@ +error: proc macro functions may not be `extern "C"` + --> $DIR/proc-macro-abi.rs:8:1 + | +LL | pub extern "C" fn abi(a: TokenStream) -> TokenStream { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: proc macro functions may not be `extern "system"` + --> $DIR/proc-macro-abi.rs:14:1 + | +LL | pub extern "system" fn abi2(a: TokenStream) -> TokenStream { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: proc macro functions may not be `extern "C"` + --> $DIR/proc-macro-abi.rs:20:1 + | +LL | pub extern fn abi3(a: TokenStream) -> TokenStream { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/proc-macro/signature-proc-macro-attribute.rs b/tests/ui/proc-macro/signature-proc-macro-attribute.rs new file mode 100644 index 0000000000000..fb17710950106 --- /dev/null +++ b/tests/ui/proc-macro/signature-proc-macro-attribute.rs @@ -0,0 +1,29 @@ +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro_attribute] +pub fn bad_input(input: String) -> TokenStream { + //~^ ERROR mismatched attribute proc macro signature + ::proc_macro::TokenStream::new() +} + +#[proc_macro_attribute] +pub fn bad_output(input: TokenStream) -> String { + //~^ ERROR mismatched attribute proc macro signature + //~| ERROR mismatched attribute proc macro signature + String::from("blah") +} + +#[proc_macro_attribute] +pub fn bad_everything(input: String) -> String { + //~^ ERROR mismatched attribute proc macro signature + //~| ERROR mismatched attribute proc macro signature + input +} + +#[proc_macro_attribute] +pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream { + //~^ ERROR mismatched attribute proc macro signature +} diff --git a/tests/ui/proc-macro/signature-proc-macro-attribute.stderr b/tests/ui/proc-macro/signature-proc-macro-attribute.stderr new file mode 100644 index 0000000000000..ecfd2b06e109c --- /dev/null +++ b/tests/ui/proc-macro/signature-proc-macro-attribute.stderr @@ -0,0 +1,42 @@ +error: mismatched attribute proc macro signature + --> $DIR/signature-proc-macro-attribute.rs:7:1 + | +LL | pub fn bad_input(input: String) -> TokenStream { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attribute proc macro must have two arguments of type `proc_macro::TokenStream` + +error: mismatched attribute proc macro signature + --> $DIR/signature-proc-macro-attribute.rs:13:42 + | +LL | pub fn bad_output(input: TokenStream) -> String { + | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` + | + = note: attribute proc macros must have a signature of `fn(TokenStream, TokenStream) -> TokenStream` + +error: mismatched attribute proc macro signature + --> $DIR/signature-proc-macro-attribute.rs:13:1 + | +LL | pub fn bad_output(input: TokenStream) -> String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attribute proc macro must have two arguments of type `proc_macro::TokenStream` + +error: mismatched attribute proc macro signature + --> $DIR/signature-proc-macro-attribute.rs:20:41 + | +LL | pub fn bad_everything(input: String) -> String { + | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` + | + = note: attribute proc macros must have a signature of `fn(TokenStream, TokenStream) -> TokenStream` + +error: mismatched attribute proc macro signature + --> $DIR/signature-proc-macro-attribute.rs:20:1 + | +LL | pub fn bad_everything(input: String) -> String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attribute proc macro must have two arguments of type `proc_macro::TokenStream` + +error: mismatched attribute proc macro signature + --> $DIR/signature-proc-macro-attribute.rs:27:49 + | +LL | pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream { + | ^^^^^^^^^ found unexpected argument + +error: aborting due to 6 previous errors + diff --git a/tests/ui/proc-macro/signature-proc-macro-derive.rs b/tests/ui/proc-macro/signature-proc-macro-derive.rs new file mode 100644 index 0000000000000..a079157538fb6 --- /dev/null +++ b/tests/ui/proc-macro/signature-proc-macro-derive.rs @@ -0,0 +1,28 @@ +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro_derive(Blah)] +pub fn bad_input(input: String) -> TokenStream { + //~^ ERROR mismatched derive proc macro signature + TokenStream::new() +} + +#[proc_macro_derive(Bleh)] +pub fn bad_output(input: TokenStream) -> String { + //~^ ERROR mismatched derive proc macro signature + String::from("blah") +} + +#[proc_macro_derive(Bluh)] +pub fn bad_everything(input: String) -> String { + //~^ ERROR mismatched derive proc macro signature + //~| ERROR mismatched derive proc macro signature + input +} + +#[proc_macro_derive(Blih)] +pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream { + //~^ ERROR mismatched derive proc macro signature +} diff --git a/tests/ui/proc-macro/signature-proc-macro-derive.stderr b/tests/ui/proc-macro/signature-proc-macro-derive.stderr new file mode 100644 index 0000000000000..bb2bd9a93d24b --- /dev/null +++ b/tests/ui/proc-macro/signature-proc-macro-derive.stderr @@ -0,0 +1,40 @@ +error: mismatched derive proc macro signature + --> $DIR/signature-proc-macro-derive.rs:7:25 + | +LL | pub fn bad_input(input: String) -> TokenStream { + | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` + | + = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream` + +error: mismatched derive proc macro signature + --> $DIR/signature-proc-macro-derive.rs:13:42 + | +LL | pub fn bad_output(input: TokenStream) -> String { + | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` + | + = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream` + +error: mismatched derive proc macro signature + --> $DIR/signature-proc-macro-derive.rs:19:41 + | +LL | pub fn bad_everything(input: String) -> String { + | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` + | + = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream` + +error: mismatched derive proc macro signature + --> $DIR/signature-proc-macro-derive.rs:19:30 + | +LL | pub fn bad_everything(input: String) -> String { + | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` + | + = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream` + +error: mismatched derive proc macro signature + --> $DIR/signature-proc-macro-derive.rs:26:33 + | +LL | pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ found unexpected arguments + +error: aborting due to 5 previous errors + diff --git a/tests/ui/proc-macro/signature-proc-macro.rs b/tests/ui/proc-macro/signature-proc-macro.rs new file mode 100644 index 0000000000000..35d5be2171283 --- /dev/null +++ b/tests/ui/proc-macro/signature-proc-macro.rs @@ -0,0 +1,28 @@ +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro] +pub fn bad_input(input: String) -> TokenStream { + //~^ ERROR mismatched function-like proc macro signature + ::proc_macro::TokenStream::new() +} + +#[proc_macro] +pub fn bad_output(input: TokenStream) -> String { + //~^ ERROR mismatched function-like proc macro signature + String::from("blah") +} + +#[proc_macro] +pub fn bad_everything(input: String) -> String { + //~^ ERROR mismatched function-like proc macro signature + //~| ERROR mismatched function-like proc macro signature + input +} + +#[proc_macro] +pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream { + //~^ ERROR mismatched function-like proc macro signature +} diff --git a/tests/ui/proc-macro/signature-proc-macro.stderr b/tests/ui/proc-macro/signature-proc-macro.stderr new file mode 100644 index 0000000000000..32241e1b9e00a --- /dev/null +++ b/tests/ui/proc-macro/signature-proc-macro.stderr @@ -0,0 +1,40 @@ +error: mismatched function-like proc macro signature + --> $DIR/signature-proc-macro.rs:7:25 + | +LL | pub fn bad_input(input: String) -> TokenStream { + | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` + | + = note: function-like proc macros must have a signature of `fn(TokenStream) -> TokenStream` + +error: mismatched function-like proc macro signature + --> $DIR/signature-proc-macro.rs:13:42 + | +LL | pub fn bad_output(input: TokenStream) -> String { + | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` + | + = note: function-like proc macros must have a signature of `fn(TokenStream) -> TokenStream` + +error: mismatched function-like proc macro signature + --> $DIR/signature-proc-macro.rs:19:41 + | +LL | pub fn bad_everything(input: String) -> String { + | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` + | + = note: function-like proc macros must have a signature of `fn(TokenStream) -> TokenStream` + +error: mismatched function-like proc macro signature + --> $DIR/signature-proc-macro.rs:19:30 + | +LL | pub fn bad_everything(input: String) -> String { + | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` + | + = note: function-like proc macros must have a signature of `fn(TokenStream) -> TokenStream` + +error: mismatched function-like proc macro signature + --> $DIR/signature-proc-macro.rs:26:33 + | +LL | pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ found unexpected arguments + +error: aborting due to 5 previous errors + diff --git a/tests/ui/proc-macro/signature.rs b/tests/ui/proc-macro/signature.rs index 2302238253e82..11187aa31bd80 100644 --- a/tests/ui/proc-macro/signature.rs +++ b/tests/ui/proc-macro/signature.rs @@ -8,6 +8,10 @@ extern crate proc_macro; #[proc_macro_derive(A)] pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 { - //~^ ERROR: expected a `Fn<(proc_macro::TokenStream,)>` closure, found `unsafe extern "C" fn + //~^ ERROR: mismatched derive proc macro signature + //~| mismatched derive proc macro signature + //~| mismatched derive proc macro signature + //~| proc macro functions may not be `extern + //~| proc macro functions may not be `unsafe loop {} } diff --git a/tests/ui/proc-macro/signature.stderr b/tests/ui/proc-macro/signature.stderr index 79f2001da0055..3dbe3f22a0df8 100644 --- a/tests/ui/proc-macro/signature.stderr +++ b/tests/ui/proc-macro/signature.stderr @@ -1,20 +1,36 @@ -error[E0277]: expected a `Fn<(proc_macro::TokenStream,)>` closure, found `unsafe extern "C" fn(i32, u32) -> u32 {foo}` +error: proc macro functions may not be `extern "C"` --> $DIR/signature.rs:10:1 | -LL | / pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 { -LL | | -LL | | loop {} -LL | | } - | | ^ - | | | - | |_call the function in a closure: `|| unsafe { /* code */ }` - | required by a bound introduced by this call +LL | pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: proc macro functions may not be `unsafe` + --> $DIR/signature.rs:10:1 + | +LL | pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: mismatched derive proc macro signature + --> $DIR/signature.rs:10:49 + | +LL | pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 { + | ^^^ found u32, expected type `proc_macro::TokenStream` + | + = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream` + +error: mismatched derive proc macro signature + --> $DIR/signature.rs:10:33 + | +LL | pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 { + | ^^^ found i32, expected type `proc_macro::TokenStream` + | + = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream` + +error: mismatched derive proc macro signature + --> $DIR/signature.rs:10:38 | - = help: the trait `Fn<(proc_macro::TokenStream,)>` is not implemented for fn item `unsafe extern "C" fn(i32, u32) -> u32 {foo}` - = note: unsafe function cannot be called generically without an unsafe block -note: required by a bound in `ProcMacro::custom_derive` - --> $SRC_DIR/proc_macro/src/bridge/client.rs:LL:COL +LL | pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 { + | ^^^^^^ found unexpected argument -error: aborting due to previous error +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0277`. From a8e3abd04cf85080d921c2d1875e0094b2db5155 Mon Sep 17 00:00:00 2001 From: mejrs <> Date: Sun, 8 Jan 2023 01:37:22 +0100 Subject: [PATCH 02/21] Address feedback --- compiler/rustc_passes/src/check_attr.rs | 56 +++++++++++++++-------- tests/ui/proc-macro/allowed-signatures.rs | 24 ++++++++++ tests/ui/proc-macro/proc-macro-abi.rs | 6 +-- 3 files changed, 63 insertions(+), 23 deletions(-) create mode 100644 tests/ui/proc-macro/allowed-signatures.rs diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 517bf2533c5e6..c2ce46d965cf3 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, } @@ -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 0000000000000..03c6ef86632df --- /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) -> 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 2a40ddd496ce8..8e9d50d7832d5 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 } From 2d824206655bfb26cb5eed43490ee396542b153e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 15 Jan 2023 22:36:46 +0000 Subject: [PATCH 03/21] Teach parser to understand fake anonymous enum syntax Parse `-> Ty | OtherTy`. Parse type ascription in top level patterns. --- compiler/rustc_ast/src/ast.rs | 3 + compiler/rustc_ast/src/mut_visit.rs | 2 +- compiler/rustc_ast/src/visit.rs | 4 +- compiler/rustc_ast_lowering/src/lib.rs | 1 + compiler/rustc_ast_pretty/src/pprust/state.rs | 3 + .../rustc_parse/src/parser/diagnostics.rs | 50 +++++++++--- compiler/rustc_parse/src/parser/pat.rs | 3 +- compiler/rustc_parse/src/parser/ty.rs | 67 ++++++++++++++- compiler/rustc_passes/src/hir_stats.rs | 1 + src/tools/rustfmt/src/types.rs | 4 +- tests/ui/parser/anon-enums.rs | 19 +++++ tests/ui/parser/anon-enums.stderr | 81 +++++++++++++++++++ .../issues/issue-87086-colon-path-sep.stderr | 72 ++++++++++++----- 13 files changed, 270 insertions(+), 40 deletions(-) create mode 100644 tests/ui/parser/anon-enums.rs create mode 100644 tests/ui/parser/anon-enums.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 7de594719ddc4..3aad51325dcd6 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2096,6 +2096,9 @@ pub enum TyKind { Err, /// Placeholder for a `va_list`. CVarArgs, + /// Placeholder for "anonymous enums", which don't exist, but keeping their + /// information around lets us produce better diagnostics. + AnonEnum(Vec>), } impl TyKind { diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 77f342d1eb322..894885cf0fea7 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -470,7 +470,7 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { vis.visit_fn_decl(decl); vis.visit_span(decl_span); } - TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)), + TyKind::AnonEnum(tys) | TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)), TyKind::Paren(ty) => vis.visit_ty(ty), TyKind::Path(qself, path) => { vis.visit_qself(qself); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index e8823eff83afe..1ab70f0309c01 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -400,8 +400,8 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) { walk_list!(visitor, visit_lifetime, opt_lifetime, LifetimeCtxt::Ref); visitor.visit_ty(&mutable_type.ty) } - TyKind::Tup(tuple_element_types) => { - walk_list!(visitor, visit_ty, tuple_element_types); + TyKind::AnonEnum(tys) | TyKind::Tup(tys) => { + walk_list!(visitor, visit_ty, tys); } TyKind::BareFn(function_declaration) => { walk_list!(visitor, visit_generic_param, &function_declaration.generic_params); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 41d4a5679f1a0..a60d02002da33 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1235,6 +1235,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let kind = match &t.kind { TyKind::Infer => hir::TyKind::Infer, TyKind::Err => hir::TyKind::Err, + TyKind::AnonEnum(_) => hir::TyKind::Err, TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)), TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)), TyKind::Ref(region, mt) => { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 6a8064b0e874e..3f9b96a6158a1 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1041,6 +1041,9 @@ impl<'a> State<'a> { } self.pclose(); } + ast::TyKind::AnonEnum(elts) => { + self.strsep("|", false, Inconsistent, elts, |s, ty| s.print_type(ty)); + } ast::TyKind::Paren(typ) => { self.popen(); self.print_type(typ); diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 4c918c6702ed9..07bd76dc39ba9 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2372,7 +2372,7 @@ impl<'a> Parser<'a> { /// Some special error handling for the "top-level" patterns in a match arm, /// `for` loop, `let`, &c. (in contrast to subpatterns within such). - pub(crate) fn maybe_recover_colon_colon_in_pat_typo( + pub(crate) fn maybe_recover_colon_colon_in_pat_typo_or_anon_enum( &mut self, mut first_pat: P, expected: Expected, @@ -2383,26 +2383,41 @@ impl<'a> Parser<'a> { if !matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..)) || !self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident()) { + let mut snapshot_type = self.create_snapshot_for_diagnostic(); + snapshot_type.bump(); // `:` + match snapshot_type.parse_ty() { + Err(inner_err) => { + inner_err.cancel(); + } + Ok(ty) => { + let Err(mut err) = self.expected_one_of_not_found(&[], &[]) else { + return first_pat; + }; + err.span_label(ty.span, "specifying the type of a pattern isn't supported"); + self.restore_snapshot(snapshot_type); + let span = first_pat.span.to(ty.span); + first_pat = self.mk_pat(span, PatKind::Wild); + err.emit(); + } + } return first_pat; } // The pattern looks like it might be a path with a `::` -> `:` typo: // `match foo { bar:baz => {} }` - let span = self.token.span; + let colon_span = self.token.span; // We only emit "unexpected `:`" error here if we can successfully parse the // whole pattern correctly in that case. - let snapshot = self.create_snapshot_for_diagnostic(); + let mut snapshot_pat = self.create_snapshot_for_diagnostic(); + let mut snapshot_type = self.create_snapshot_for_diagnostic(); // Create error for "unexpected `:`". match self.expected_one_of_not_found(&[], &[]) { Err(mut err) => { - self.bump(); // Skip the `:`. - match self.parse_pat_no_top_alt(expected) { + snapshot_pat.bump(); // Skip the `:`. + snapshot_type.bump(); // Skip the `:`. + match snapshot_pat.parse_pat_no_top_alt(expected) { Err(inner_err) => { - // Carry on as if we had not done anything, callers will emit a - // reasonable error. inner_err.cancel(); - err.cancel(); - self.restore_snapshot(snapshot); } Ok(mut pat) => { // We've parsed the rest of the pattern. @@ -2466,8 +2481,8 @@ impl<'a> Parser<'a> { _ => {} } if show_sugg { - err.span_suggestion( - span, + err.span_suggestion_verbose( + colon_span.until(self.look_ahead(1, |t| t.span)), "maybe write a path separator here", "::", Applicability::MaybeIncorrect, @@ -2475,13 +2490,22 @@ impl<'a> Parser<'a> { } else { first_pat = self.mk_pat(new_span, PatKind::Wild); } - err.emit(); + self.restore_snapshot(snapshot_pat); } } + match snapshot_type.parse_ty() { + Err(inner_err) => { + inner_err.cancel(); + } + Ok(ty) => { + err.span_label(ty.span, "specifying the type of a pattern isn't supported"); + self.restore_snapshot(snapshot_type); + } + } + err.emit(); } _ => { // Carry on as if we had not done anything. This should be unreachable. - self.restore_snapshot(snapshot); } }; first_pat diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index e73a17ced7deb..e5411538eea22 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -116,7 +116,8 @@ impl<'a> Parser<'a> { // Check if the user wrote `foo:bar` instead of `foo::bar`. if ra == RecoverColon::Yes { - first_pat = self.maybe_recover_colon_colon_in_pat_typo(first_pat, expected); + first_pat = + self.maybe_recover_colon_colon_in_pat_typo_or_anon_enum(first_pat, expected); } if let Some(leading_vert_span) = leading_vert_span { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index a6f702e542869..72994df6f9f46 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -11,6 +11,7 @@ use rustc_ast::{ self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind, }; +use rustc_ast_pretty::pprust; use rustc_errors::{pluralize, struct_span_err, Applicability, PResult}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, sym, Ident}; @@ -43,17 +44,24 @@ pub(super) enum AllowPlus { No, } -#[derive(PartialEq)] +#[derive(PartialEq, Clone, Copy)] pub(super) enum RecoverQPath { Yes, No, } +#[derive(PartialEq, Clone, Copy)] pub(super) enum RecoverQuestionMark { Yes, No, } +#[derive(PartialEq, Clone, Copy)] +pub(super) enum RecoverAnonEnum { + Yes, + No, +} + /// Signals whether parsing a type should recover `->`. /// /// More specifically, when parsing a function like: @@ -86,7 +94,7 @@ impl RecoverReturnSign { } // Is `...` (`CVarArgs`) legal at this level of type parsing? -#[derive(PartialEq)] +#[derive(PartialEq, Clone, Copy)] enum AllowCVariadic { Yes, No, @@ -111,6 +119,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, + RecoverAnonEnum::No, ) } @@ -125,6 +134,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, Some(ty_params), RecoverQuestionMark::Yes, + RecoverAnonEnum::No, ) } @@ -139,6 +149,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, + RecoverAnonEnum::Yes, ) } @@ -156,6 +167,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, + RecoverAnonEnum::No, ) } @@ -169,6 +181,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::No, + RecoverAnonEnum::No, ) } @@ -180,6 +193,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::No, + RecoverAnonEnum::No, ) } @@ -192,6 +206,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::OnlyFatArrow, None, RecoverQuestionMark::Yes, + RecoverAnonEnum::No, ) } @@ -211,6 +226,7 @@ impl<'a> Parser<'a> { recover_return_sign, None, RecoverQuestionMark::Yes, + RecoverAnonEnum::Yes, )?; FnRetTy::Ty(ty) } else if recover_return_sign.can_recover(&self.token.kind) { @@ -232,6 +248,7 @@ impl<'a> Parser<'a> { recover_return_sign, None, RecoverQuestionMark::Yes, + RecoverAnonEnum::Yes, )?; FnRetTy::Ty(ty) } else { @@ -247,6 +264,7 @@ impl<'a> Parser<'a> { recover_return_sign: RecoverReturnSign, ty_generics: Option<&Generics>, recover_question_mark: RecoverQuestionMark, + recover_anon_enum: RecoverAnonEnum, ) -> PResult<'a, P> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); @@ -325,14 +343,55 @@ impl<'a> Parser<'a> { let mut ty = self.mk_ty(span, kind); // Try to recover from use of `+` with incorrect priority. - if matches!(allow_plus, AllowPlus::Yes) { + if allow_plus == AllowPlus::Yes { self.maybe_recover_from_bad_type_plus(&ty)?; } else { self.maybe_report_ambiguous_plus(impl_dyn_multi, &ty); } - if let RecoverQuestionMark::Yes = recover_question_mark { + if RecoverQuestionMark::Yes == recover_question_mark { ty = self.maybe_recover_from_question_mark(ty); } + if recover_anon_enum == RecoverAnonEnum::Yes + && self.check_noexpect(&token::BinOp(token::Or)) + && self.look_ahead(1, |t| t.can_begin_type()) + { + let mut pipes = vec![self.token.span]; + let mut types = vec![ty]; + loop { + if !self.eat(&token::BinOp(token::Or)) { + break; + } + pipes.push(self.prev_token.span); + types.push(self.parse_ty_common( + allow_plus, + allow_c_variadic, + recover_qpath, + recover_return_sign, + ty_generics, + recover_question_mark, + RecoverAnonEnum::No, + )?); + } + let mut err = self.struct_span_err(pipes, "anonymous enums are not supported"); + for ty in &types { + err.span_label(ty.span, ""); + } + err.help(&format!( + "create a named `enum` and use it here instead:\nenum Name {{\n{}\n}}", + types + .iter() + .enumerate() + .map(|(i, t)| format!( + " Variant{}({}),", + i + 1, // Lets not confuse people with zero-indexing :) + pprust::to_string(|s| s.print_type(&t)), + )) + .collect::>() + .join("\n"), + )); + err.emit(); + return Ok(self.mk_ty(lo.to(self.prev_token.span), TyKind::AnonEnum(types))); + } if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) } } diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index b86d2316820ce..312bd8839e0d0 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -579,6 +579,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { [ Slice, Array, + AnonEnum, Ptr, Ref, BareFn, diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index c1991e8d2c808..f065ec680d724 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -839,7 +839,9 @@ impl Rewrite for ast::Ty { }) } ast::TyKind::CVarArgs => Some("...".to_owned()), - ast::TyKind::Err => Some(context.snippet(self.span).to_owned()), + ast::TyKind::AnonEnum(_) | ast::TyKind::Err => { + Some(context.snippet(self.span).to_owned()) + } ast::TyKind::Typeof(ref anon_const) => rewrite_call( context, "typeof", diff --git a/tests/ui/parser/anon-enums.rs b/tests/ui/parser/anon-enums.rs new file mode 100644 index 0000000000000..a8b5e8359d996 --- /dev/null +++ b/tests/ui/parser/anon-enums.rs @@ -0,0 +1,19 @@ +fn foo(x: bool | i32) -> i32 | f64 { +//~^ ERROR anonymous enums are not supported +//~| ERROR anonymous enums are not supported + match x { + x: i32 => x, //~ ERROR expected + //~^ ERROR failed to resolve + true => 42., + false => 0.333, + } +} + +fn main() { + match foo(true) { + 42: i32 => (), //~ ERROR expected + _: f64 => (), //~ ERROR expected + x: i32 => (), //~ ERROR expected + //~^ ERROR failed to resolve + } +} diff --git a/tests/ui/parser/anon-enums.stderr b/tests/ui/parser/anon-enums.stderr new file mode 100644 index 0000000000000..4febf2df0dc72 --- /dev/null +++ b/tests/ui/parser/anon-enums.stderr @@ -0,0 +1,81 @@ +error: anonymous enums are not supported + --> $DIR/anon-enums.rs:1:16 + | +LL | fn foo(x: bool | i32) -> i32 | f64 { + | ---- ^ --- + | + = help: create a named `enum` and use it here instead: + enum Name { + Variant1(bool), + Variant2(i32), + } + +error: anonymous enums are not supported + --> $DIR/anon-enums.rs:1:30 + | +LL | fn foo(x: bool | i32) -> i32 | f64 { + | --- ^ --- + | + = help: create a named `enum` and use it here instead: + enum Name { + Variant1(i32), + Variant2(f64), + } + +error: expected one of `@` or `|`, found `:` + --> $DIR/anon-enums.rs:5:10 + | +LL | x: i32 => x, + | ^ --- specifying the type of a pattern isn't supported + | | + | expected one of `@` or `|` + | +help: maybe write a path separator here + | +LL | x::i32 => x, + | ~~ + +error: expected one of `...`, `..=`, `..`, or `|`, found `:` + --> $DIR/anon-enums.rs:14:11 + | +LL | 42: i32 => (), + | ^ --- specifying the type of a pattern isn't supported + | | + | expected one of `...`, `..=`, `..`, or `|` + +error: expected `|`, found `:` + --> $DIR/anon-enums.rs:15:10 + | +LL | _: f64 => (), + | ^ --- specifying the type of a pattern isn't supported + | | + | expected `|` + +error: expected one of `@` or `|`, found `:` + --> $DIR/anon-enums.rs:16:10 + | +LL | x: i32 => (), + | ^ --- specifying the type of a pattern isn't supported + | | + | expected one of `@` or `|` + | +help: maybe write a path separator here + | +LL | x::i32 => (), + | ~~ + +error[E0433]: failed to resolve: use of undeclared crate or module `x` + --> $DIR/anon-enums.rs:5:9 + | +LL | x: i32 => x, + | ^ use of undeclared crate or module `x` + +error[E0433]: failed to resolve: use of undeclared crate or module `x` + --> $DIR/anon-enums.rs:16:9 + | +LL | x: i32 => (), + | ^ use of undeclared crate or module `x` + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr b/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr index 2050a16beb349..ed05bfe8b0a32 100644 --- a/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr +++ b/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr @@ -2,82 +2,118 @@ error: expected one of `@` or `|`, found `:` --> $DIR/issue-87086-colon-path-sep.rs:17:12 | LL | Foo:Bar => {} - | ^ + | ^--- specifying the type of a pattern isn't supported | | | expected one of `@` or `|` - | help: maybe write a path separator here: `::` + | +help: maybe write a path separator here + | +LL | Foo::Bar => {} + | ~~ error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `{`, or `|`, found `:` --> $DIR/issue-87086-colon-path-sep.rs:23:17 | LL | qux::Foo:Bar => {} - | ^ + | ^--- specifying the type of a pattern isn't supported | | | expected one of 8 possible tokens - | help: maybe write a path separator here: `::` + | +help: maybe write a path separator here + | +LL | qux::Foo::Bar => {} + | ~~ error: expected one of `@` or `|`, found `:` --> $DIR/issue-87086-colon-path-sep.rs:29:12 | LL | qux:Foo::Baz => {} - | ^ + | ^-------- specifying the type of a pattern isn't supported | | | expected one of `@` or `|` - | help: maybe write a path separator here: `::` + | +help: maybe write a path separator here + | +LL | qux::Foo::Baz => {} + | ~~ error: expected one of `@` or `|`, found `:` --> $DIR/issue-87086-colon-path-sep.rs:35:12 | LL | qux: Foo::Baz if true => {} - | ^ + | ^ -------- specifying the type of a pattern isn't supported | | | expected one of `@` or `|` - | help: maybe write a path separator here: `::` + | +help: maybe write a path separator here + | +LL | qux::Foo::Baz if true => {} + | ~~ error: expected one of `@` or `|`, found `:` --> $DIR/issue-87086-colon-path-sep.rs:40:15 | LL | if let Foo:Bar = f() { - | ^ + | ^--- specifying the type of a pattern isn't supported | | | expected one of `@` or `|` - | help: maybe write a path separator here: `::` + | +help: maybe write a path separator here + | +LL | if let Foo::Bar = f() { + | ~~ error: expected one of `@` or `|`, found `:` --> $DIR/issue-87086-colon-path-sep.rs:48:16 | LL | ref qux: Foo::Baz => {} - | ^ + | ^ -------- specifying the type of a pattern isn't supported | | | expected one of `@` or `|` - | help: maybe write a path separator here: `::` + | +help: maybe write a path separator here + | +LL | ref qux::Foo::Baz => {} + | ~~ error: expected one of `@` or `|`, found `:` --> $DIR/issue-87086-colon-path-sep.rs:57:16 | LL | mut qux: Foo::Baz => {} - | ^ + | ^ -------- specifying the type of a pattern isn't supported | | | expected one of `@` or `|` - | help: maybe write a path separator here: `::` + | +help: maybe write a path separator here + | +LL | mut qux::Foo::Baz => {} + | ~~ error: expected one of `@` or `|`, found `:` --> $DIR/issue-87086-colon-path-sep.rs:68:12 | LL | Foo:Bar::Baz => {} - | ^ + | ^-------- specifying the type of a pattern isn't supported | | | expected one of `@` or `|` - | help: maybe write a path separator here: `::` + | +help: maybe write a path separator here + | +LL | Foo::Bar::Baz => {} + | ~~ error: expected one of `@` or `|`, found `:` --> $DIR/issue-87086-colon-path-sep.rs:75:12 | LL | Foo:Bar => {} - | ^ + | ^--- specifying the type of a pattern isn't supported | | | expected one of `@` or `|` - | help: maybe write a path separator here: `::` + | +help: maybe write a path separator here + | +LL | Foo::Bar => {} + | ~~ error[E0433]: failed to resolve: `Bar` is a variant, not a module --> $DIR/issue-87086-colon-path-sep.rs:68:13 From c847a01a3b1f620c4fdb98c75805033e768975d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 17 Jan 2023 01:50:45 +0000 Subject: [PATCH 04/21] Emit fewer errors on patterns with possible type ascription --- .../rustc_parse/src/parser/diagnostics.rs | 2 ++ tests/ui/parser/anon-enums.rs | 2 -- tests/ui/parser/anon-enums.stderr | 21 ++++--------------- .../issues/issue-87086-colon-path-sep.rs | 1 - .../issues/issue-87086-colon-path-sep.stderr | 11 ++-------- 5 files changed, 8 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 07bd76dc39ba9..eda7046c748e5 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2500,6 +2500,8 @@ impl<'a> Parser<'a> { Ok(ty) => { err.span_label(ty.span, "specifying the type of a pattern isn't supported"); self.restore_snapshot(snapshot_type); + let new_span = first_pat.span.to(ty.span); + first_pat = self.mk_pat(new_span, PatKind::Wild); } } err.emit(); diff --git a/tests/ui/parser/anon-enums.rs b/tests/ui/parser/anon-enums.rs index a8b5e8359d996..56b8a3d43bedf 100644 --- a/tests/ui/parser/anon-enums.rs +++ b/tests/ui/parser/anon-enums.rs @@ -3,7 +3,6 @@ fn foo(x: bool | i32) -> i32 | f64 { //~| ERROR anonymous enums are not supported match x { x: i32 => x, //~ ERROR expected - //~^ ERROR failed to resolve true => 42., false => 0.333, } @@ -14,6 +13,5 @@ fn main() { 42: i32 => (), //~ ERROR expected _: f64 => (), //~ ERROR expected x: i32 => (), //~ ERROR expected - //~^ ERROR failed to resolve } } diff --git a/tests/ui/parser/anon-enums.stderr b/tests/ui/parser/anon-enums.stderr index 4febf2df0dc72..8415822566091 100644 --- a/tests/ui/parser/anon-enums.stderr +++ b/tests/ui/parser/anon-enums.stderr @@ -36,7 +36,7 @@ LL | x::i32 => x, | ~~ error: expected one of `...`, `..=`, `..`, or `|`, found `:` - --> $DIR/anon-enums.rs:14:11 + --> $DIR/anon-enums.rs:13:11 | LL | 42: i32 => (), | ^ --- specifying the type of a pattern isn't supported @@ -44,7 +44,7 @@ LL | 42: i32 => (), | expected one of `...`, `..=`, `..`, or `|` error: expected `|`, found `:` - --> $DIR/anon-enums.rs:15:10 + --> $DIR/anon-enums.rs:14:10 | LL | _: f64 => (), | ^ --- specifying the type of a pattern isn't supported @@ -52,7 +52,7 @@ LL | _: f64 => (), | expected `|` error: expected one of `@` or `|`, found `:` - --> $DIR/anon-enums.rs:16:10 + --> $DIR/anon-enums.rs:15:10 | LL | x: i32 => (), | ^ --- specifying the type of a pattern isn't supported @@ -64,18 +64,5 @@ help: maybe write a path separator here LL | x::i32 => (), | ~~ -error[E0433]: failed to resolve: use of undeclared crate or module `x` - --> $DIR/anon-enums.rs:5:9 - | -LL | x: i32 => x, - | ^ use of undeclared crate or module `x` - -error[E0433]: failed to resolve: use of undeclared crate or module `x` - --> $DIR/anon-enums.rs:16:9 - | -LL | x: i32 => (), - | ^ use of undeclared crate or module `x` - -error: aborting due to 8 previous errors +error: aborting due to 6 previous errors -For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/parser/issues/issue-87086-colon-path-sep.rs b/tests/ui/parser/issues/issue-87086-colon-path-sep.rs index 0b7b67496d6f3..e1ea38f2795df 100644 --- a/tests/ui/parser/issues/issue-87086-colon-path-sep.rs +++ b/tests/ui/parser/issues/issue-87086-colon-path-sep.rs @@ -68,7 +68,6 @@ fn main() { Foo:Bar::Baz => {} //~^ ERROR: expected one of //~| HELP: maybe write a path separator here - //~| ERROR: failed to resolve: `Bar` is a variant, not a module } match myfoo { Foo::Bar => {} diff --git a/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr b/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr index ed05bfe8b0a32..63b072ac4cdc6 100644 --- a/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr +++ b/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr @@ -103,7 +103,7 @@ LL | Foo::Bar::Baz => {} | ~~ error: expected one of `@` or `|`, found `:` - --> $DIR/issue-87086-colon-path-sep.rs:75:12 + --> $DIR/issue-87086-colon-path-sep.rs:74:12 | LL | Foo:Bar => {} | ^--- specifying the type of a pattern isn't supported @@ -115,12 +115,5 @@ help: maybe write a path separator here LL | Foo::Bar => {} | ~~ -error[E0433]: failed to resolve: `Bar` is a variant, not a module - --> $DIR/issue-87086-colon-path-sep.rs:68:13 - | -LL | Foo:Bar::Baz => {} - | ^^^ `Bar` is a variant, not a module - -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors -For more information about this error, try `rustc --explain E0433`. From 12d18e403139eeeeb339e8611b2bed4910864edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 17 Jan 2023 17:02:09 +0000 Subject: [PATCH 05/21] Ensure macros are not affected --- tests/ui/parser/fake-anon-enums-in-macros.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/ui/parser/fake-anon-enums-in-macros.rs diff --git a/tests/ui/parser/fake-anon-enums-in-macros.rs b/tests/ui/parser/fake-anon-enums-in-macros.rs new file mode 100644 index 0000000000000..38fe8dee23829 --- /dev/null +++ b/tests/ui/parser/fake-anon-enums-in-macros.rs @@ -0,0 +1,20 @@ +// build-pass +macro_rules! check_ty { + ($Z:ty) => { compile_error!("triggered"); }; + ($X:ty | $Y:ty) => { $X }; +} + +macro_rules! check { + ($Z:ty) => { compile_error!("triggered"); }; + ($X:ty | $Y:ty) => { }; +} + +check! { i32 | u8 } + +fn foo(x: check_ty! { i32 | u8 }) -> check_ty! { i32 | u8 } { + x +} +fn main() { + let x: check_ty! { i32 | u8 } = 42; + let _: check_ty! { i32 | u8 } = foo(x); +} From 8e43414bce77cfe2030b875e365c77691897b95f Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Thu, 19 Jan 2023 16:31:50 +0100 Subject: [PATCH 06/21] Fix proc macro tests --- tests/ui/proc-macro/allowed-signatures.rs | 2 ++ tests/ui/proc-macro/proc-macro-abi.rs | 3 +++ tests/ui/proc-macro/proc-macro-abi.stderr | 6 +++--- .../ui/proc-macro/signature-proc-macro-attribute.rs | 3 +++ .../proc-macro/signature-proc-macro-attribute.stderr | 12 ++++++------ tests/ui/proc-macro/signature-proc-macro-derive.rs | 3 +++ .../ui/proc-macro/signature-proc-macro-derive.stderr | 10 +++++----- tests/ui/proc-macro/signature-proc-macro.rs | 3 +++ tests/ui/proc-macro/signature-proc-macro.stderr | 10 +++++----- 9 files changed, 33 insertions(+), 19 deletions(-) diff --git a/tests/ui/proc-macro/allowed-signatures.rs b/tests/ui/proc-macro/allowed-signatures.rs index 03c6ef86632df..8685087611248 100644 --- a/tests/ui/proc-macro/allowed-signatures.rs +++ b/tests/ui/proc-macro/allowed-signatures.rs @@ -1,4 +1,6 @@ // check-pass +// force-host +// no-prefer-dynamic #![crate_type = "proc-macro"] #![allow(private_in_public)] diff --git a/tests/ui/proc-macro/proc-macro-abi.rs b/tests/ui/proc-macro/proc-macro-abi.rs index 8e9d50d7832d5..873660a5b3ab9 100644 --- a/tests/ui/proc-macro/proc-macro-abi.rs +++ b/tests/ui/proc-macro/proc-macro-abi.rs @@ -1,3 +1,6 @@ +// force-host +// no-prefer-dynamic + #![crate_type = "proc-macro"] #![allow(warnings)] diff --git a/tests/ui/proc-macro/proc-macro-abi.stderr b/tests/ui/proc-macro/proc-macro-abi.stderr index fa5f5dc098998..9a781be0996dd 100644 --- a/tests/ui/proc-macro/proc-macro-abi.stderr +++ b/tests/ui/proc-macro/proc-macro-abi.stderr @@ -1,17 +1,17 @@ error: proc macro functions may not be `extern "C"` - --> $DIR/proc-macro-abi.rs:8:1 + --> $DIR/proc-macro-abi.rs:11:1 | LL | pub extern "C" fn abi(a: TokenStream) -> TokenStream { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: proc macro functions may not be `extern "system"` - --> $DIR/proc-macro-abi.rs:14:1 + --> $DIR/proc-macro-abi.rs:17:1 | LL | pub extern "system" fn abi2(a: TokenStream) -> TokenStream { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: proc macro functions may not be `extern "C"` - --> $DIR/proc-macro-abi.rs:20:1 + --> $DIR/proc-macro-abi.rs:23:1 | LL | pub extern fn abi3(a: TokenStream) -> TokenStream { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/proc-macro/signature-proc-macro-attribute.rs b/tests/ui/proc-macro/signature-proc-macro-attribute.rs index fb17710950106..51abc8e7d3edb 100644 --- a/tests/ui/proc-macro/signature-proc-macro-attribute.rs +++ b/tests/ui/proc-macro/signature-proc-macro-attribute.rs @@ -1,3 +1,6 @@ +// force-host +// no-prefer-dynamic + #![crate_type = "proc-macro"] extern crate proc_macro; diff --git a/tests/ui/proc-macro/signature-proc-macro-attribute.stderr b/tests/ui/proc-macro/signature-proc-macro-attribute.stderr index ecfd2b06e109c..abf7a6f3ce922 100644 --- a/tests/ui/proc-macro/signature-proc-macro-attribute.stderr +++ b/tests/ui/proc-macro/signature-proc-macro-attribute.stderr @@ -1,11 +1,11 @@ error: mismatched attribute proc macro signature - --> $DIR/signature-proc-macro-attribute.rs:7:1 + --> $DIR/signature-proc-macro-attribute.rs:10:1 | LL | pub fn bad_input(input: String) -> TokenStream { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attribute proc macro must have two arguments of type `proc_macro::TokenStream` error: mismatched attribute proc macro signature - --> $DIR/signature-proc-macro-attribute.rs:13:42 + --> $DIR/signature-proc-macro-attribute.rs:16:42 | LL | pub fn bad_output(input: TokenStream) -> String { | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` @@ -13,13 +13,13 @@ LL | pub fn bad_output(input: TokenStream) -> String { = note: attribute proc macros must have a signature of `fn(TokenStream, TokenStream) -> TokenStream` error: mismatched attribute proc macro signature - --> $DIR/signature-proc-macro-attribute.rs:13:1 + --> $DIR/signature-proc-macro-attribute.rs:16:1 | LL | pub fn bad_output(input: TokenStream) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attribute proc macro must have two arguments of type `proc_macro::TokenStream` error: mismatched attribute proc macro signature - --> $DIR/signature-proc-macro-attribute.rs:20:41 + --> $DIR/signature-proc-macro-attribute.rs:23:41 | LL | pub fn bad_everything(input: String) -> String { | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` @@ -27,13 +27,13 @@ LL | pub fn bad_everything(input: String) -> String { = note: attribute proc macros must have a signature of `fn(TokenStream, TokenStream) -> TokenStream` error: mismatched attribute proc macro signature - --> $DIR/signature-proc-macro-attribute.rs:20:1 + --> $DIR/signature-proc-macro-attribute.rs:23:1 | LL | pub fn bad_everything(input: String) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attribute proc macro must have two arguments of type `proc_macro::TokenStream` error: mismatched attribute proc macro signature - --> $DIR/signature-proc-macro-attribute.rs:27:49 + --> $DIR/signature-proc-macro-attribute.rs:30:49 | LL | pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream { | ^^^^^^^^^ found unexpected argument diff --git a/tests/ui/proc-macro/signature-proc-macro-derive.rs b/tests/ui/proc-macro/signature-proc-macro-derive.rs index a079157538fb6..f2fd824b675b6 100644 --- a/tests/ui/proc-macro/signature-proc-macro-derive.rs +++ b/tests/ui/proc-macro/signature-proc-macro-derive.rs @@ -1,3 +1,6 @@ +// force-host +// no-prefer-dynamic + #![crate_type = "proc-macro"] extern crate proc_macro; diff --git a/tests/ui/proc-macro/signature-proc-macro-derive.stderr b/tests/ui/proc-macro/signature-proc-macro-derive.stderr index bb2bd9a93d24b..a358ae277037f 100644 --- a/tests/ui/proc-macro/signature-proc-macro-derive.stderr +++ b/tests/ui/proc-macro/signature-proc-macro-derive.stderr @@ -1,5 +1,5 @@ error: mismatched derive proc macro signature - --> $DIR/signature-proc-macro-derive.rs:7:25 + --> $DIR/signature-proc-macro-derive.rs:10:25 | LL | pub fn bad_input(input: String) -> TokenStream { | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` @@ -7,7 +7,7 @@ LL | pub fn bad_input(input: String) -> TokenStream { = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream` error: mismatched derive proc macro signature - --> $DIR/signature-proc-macro-derive.rs:13:42 + --> $DIR/signature-proc-macro-derive.rs:16:42 | LL | pub fn bad_output(input: TokenStream) -> String { | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` @@ -15,7 +15,7 @@ LL | pub fn bad_output(input: TokenStream) -> String { = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream` error: mismatched derive proc macro signature - --> $DIR/signature-proc-macro-derive.rs:19:41 + --> $DIR/signature-proc-macro-derive.rs:22:41 | LL | pub fn bad_everything(input: String) -> String { | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` @@ -23,7 +23,7 @@ LL | pub fn bad_everything(input: String) -> String { = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream` error: mismatched derive proc macro signature - --> $DIR/signature-proc-macro-derive.rs:19:30 + --> $DIR/signature-proc-macro-derive.rs:22:30 | LL | pub fn bad_everything(input: String) -> String { | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` @@ -31,7 +31,7 @@ LL | pub fn bad_everything(input: String) -> String { = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream` error: mismatched derive proc macro signature - --> $DIR/signature-proc-macro-derive.rs:26:33 + --> $DIR/signature-proc-macro-derive.rs:29:33 | LL | pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream { | ^^^^^^^^^^^^^^^^^^^^^^^^^ found unexpected arguments diff --git a/tests/ui/proc-macro/signature-proc-macro.rs b/tests/ui/proc-macro/signature-proc-macro.rs index 35d5be2171283..54770aacd1a98 100644 --- a/tests/ui/proc-macro/signature-proc-macro.rs +++ b/tests/ui/proc-macro/signature-proc-macro.rs @@ -1,3 +1,6 @@ +// force-host +// no-prefer-dynamic + #![crate_type = "proc-macro"] extern crate proc_macro; diff --git a/tests/ui/proc-macro/signature-proc-macro.stderr b/tests/ui/proc-macro/signature-proc-macro.stderr index 32241e1b9e00a..4b14a54e67503 100644 --- a/tests/ui/proc-macro/signature-proc-macro.stderr +++ b/tests/ui/proc-macro/signature-proc-macro.stderr @@ -1,5 +1,5 @@ error: mismatched function-like proc macro signature - --> $DIR/signature-proc-macro.rs:7:25 + --> $DIR/signature-proc-macro.rs:10:25 | LL | pub fn bad_input(input: String) -> TokenStream { | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` @@ -7,7 +7,7 @@ LL | pub fn bad_input(input: String) -> TokenStream { = note: function-like proc macros must have a signature of `fn(TokenStream) -> TokenStream` error: mismatched function-like proc macro signature - --> $DIR/signature-proc-macro.rs:13:42 + --> $DIR/signature-proc-macro.rs:16:42 | LL | pub fn bad_output(input: TokenStream) -> String { | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` @@ -15,7 +15,7 @@ LL | pub fn bad_output(input: TokenStream) -> String { = note: function-like proc macros must have a signature of `fn(TokenStream) -> TokenStream` error: mismatched function-like proc macro signature - --> $DIR/signature-proc-macro.rs:19:41 + --> $DIR/signature-proc-macro.rs:22:41 | LL | pub fn bad_everything(input: String) -> String { | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` @@ -23,7 +23,7 @@ LL | pub fn bad_everything(input: String) -> String { = note: function-like proc macros must have a signature of `fn(TokenStream) -> TokenStream` error: mismatched function-like proc macro signature - --> $DIR/signature-proc-macro.rs:19:30 + --> $DIR/signature-proc-macro.rs:22:30 | LL | pub fn bad_everything(input: String) -> String { | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` @@ -31,7 +31,7 @@ LL | pub fn bad_everything(input: String) -> String { = note: function-like proc macros must have a signature of `fn(TokenStream) -> TokenStream` error: mismatched function-like proc macro signature - --> $DIR/signature-proc-macro.rs:26:33 + --> $DIR/signature-proc-macro.rs:29:33 | LL | pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream { | ^^^^^^^^^^^^^^^^^^^^^^^^^ found unexpected arguments From e1f630f23de2cbce1870e4a226fa0cb8312f106c Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Mon, 16 Jan 2023 18:50:45 +0100 Subject: [PATCH 07/21] Add `OnceCell: !Sync` impl for diagnostics --- library/core/src/cell/once.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/core/src/cell/once.rs b/library/core/src/cell/once.rs index 7757068a4f2b9..f74e563f1b9c0 100644 --- a/library/core/src/cell/once.rs +++ b/library/core/src/cell/once.rs @@ -298,3 +298,7 @@ impl const From for OnceCell { OnceCell { inner: UnsafeCell::new(Some(value)) } } } + +// Just like for `Cell` this isn't needed, but results in nicer error messages. +#[unstable(feature = "once_cell", issue = "74465")] +impl !Sync for OnceCell {} From 6d0c91fda35f7a78ff688ea623d1d2ee9b16cad7 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Mon, 16 Jan 2023 18:51:31 +0100 Subject: [PATCH 08/21] Add `rustc_on_unimplemented` on `Sync` for cell types Suggest using a lock instead. --- library/core/src/marker.rs | 56 ++++++++++++++++++ .../issue-68112.drop_tracking.stderr | 3 + .../issue-68112.no_drop_tracking.stderr | 3 + tests/ui/generator/issue-68112.rs | 2 + tests/ui/generator/issue-68112.stderr | 12 ++-- tests/ui/generator/not-send-sync.stderr | 2 + .../print/generator-print-verbose-1.stderr | 2 + .../print/generator-print-verbose-2.stderr | 2 + tests/ui/issues/issue-7364.stderr | 1 + tests/ui/stdlib-unit-tests/not-sync.stderr | 2 + tests/ui/{ => sync}/mutexguard-sync.rs | 0 tests/ui/{ => sync}/mutexguard-sync.stderr | 1 + tests/ui/sync/suggest-cell.rs | 31 ++++++++++ tests/ui/sync/suggest-cell.stderr | 59 +++++++++++++++++++ tests/ui/sync/suggest-once-cell.rs | 12 ++++ tests/ui/sync/suggest-once-cell.stderr | 17 ++++++ tests/ui/sync/suggest-ref-cell.rs | 12 ++++ tests/ui/sync/suggest-ref-cell.stderr | 17 ++++++ 18 files changed, 229 insertions(+), 5 deletions(-) rename tests/ui/{ => sync}/mutexguard-sync.rs (100%) rename tests/ui/{ => sync}/mutexguard-sync.stderr (83%) create mode 100644 tests/ui/sync/suggest-cell.rs create mode 100644 tests/ui/sync/suggest-cell.stderr create mode 100644 tests/ui/sync/suggest-once-cell.rs create mode 100644 tests/ui/sync/suggest-once-cell.stderr create mode 100644 tests/ui/sync/suggest-ref-cell.rs create mode 100644 tests/ui/sync/suggest-ref-cell.stderr diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 1326fc9ab096f..74055602ec2e6 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -469,6 +469,62 @@ pub macro Copy($item:item) { #[cfg_attr(not(test), rustc_diagnostic_item = "Sync")] #[lang = "sync"] #[rustc_on_unimplemented( + on( + _Self = "std::cell::OnceCell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::OnceLock` instead" + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU8` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU16` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU32` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU64` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicUsize` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI8` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI16` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI64` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicIsize` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicBool` instead", + ), + on( + _Self = "std::cell::Cell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`", + ), + on( + _Self = "std::cell::RefCell", + note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead", + ), message = "`{Self}` cannot be shared between threads safely", label = "`{Self}` cannot be shared between threads safely" )] diff --git a/tests/ui/async-await/issue-68112.drop_tracking.stderr b/tests/ui/async-await/issue-68112.drop_tracking.stderr index f2802698fd5b6..bd648de30672d 100644 --- a/tests/ui/async-await/issue-68112.drop_tracking.stderr +++ b/tests/ui/async-await/issue-68112.drop_tracking.stderr @@ -5,6 +5,7 @@ LL | require_send(send_fut); | ^^^^^^^^ future created by async block is not `Send` | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead note: future is not `Send` as it awaits another future which is not `Send` --> $DIR/issue-68112.rs:34:17 | @@ -23,6 +24,7 @@ LL | require_send(send_fut); | ^^^^^^^^ future created by async block is not `Send` | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead note: future is not `Send` as it awaits another future which is not `Send` --> $DIR/issue-68112.rs:43:17 | @@ -43,6 +45,7 @@ LL | require_send(send_fut); | required by a bound introduced by this call | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead = note: required for `Arc>` to implement `Send` note: required because it's used within this `async fn` body --> $DIR/issue-68112.rs:50:31 diff --git a/tests/ui/async-await/issue-68112.no_drop_tracking.stderr b/tests/ui/async-await/issue-68112.no_drop_tracking.stderr index 38eb85b302fd5..35b7341f63a4d 100644 --- a/tests/ui/async-await/issue-68112.no_drop_tracking.stderr +++ b/tests/ui/async-await/issue-68112.no_drop_tracking.stderr @@ -5,6 +5,7 @@ LL | require_send(send_fut); | ^^^^^^^^ future created by async block is not `Send` | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead note: future is not `Send` as it awaits another future which is not `Send` --> $DIR/issue-68112.rs:34:17 | @@ -23,6 +24,7 @@ LL | require_send(send_fut); | ^^^^^^^^ future created by async block is not `Send` | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead note: future is not `Send` as it awaits another future which is not `Send` --> $DIR/issue-68112.rs:43:17 | @@ -43,6 +45,7 @@ LL | require_send(send_fut); | required by a bound introduced by this call | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead = note: required for `Arc>` to implement `Send` note: required because it's used within this `async fn` body --> $DIR/issue-68112.rs:50:31 diff --git a/tests/ui/generator/issue-68112.rs b/tests/ui/generator/issue-68112.rs index 21026f45cb823..9def544e3d25c 100644 --- a/tests/ui/generator/issue-68112.rs +++ b/tests/ui/generator/issue-68112.rs @@ -40,6 +40,7 @@ fn test1() { require_send(send_gen); //~^ ERROR generator cannot be sent between threads //~| NOTE not `Send` + //~| NOTE use `std::sync::RwLock` instead } pub fn make_gen2(t: T) -> impl Generator { @@ -66,6 +67,7 @@ fn test2() { //~| NOTE required for //~| NOTE required by a bound introduced by this call //~| NOTE captures the following types + //~| NOTE use `std::sync::RwLock` instead } fn main() {} diff --git a/tests/ui/generator/issue-68112.stderr b/tests/ui/generator/issue-68112.stderr index eb99d42c92068..b42bc93d01f66 100644 --- a/tests/ui/generator/issue-68112.stderr +++ b/tests/ui/generator/issue-68112.stderr @@ -5,6 +5,7 @@ LL | require_send(send_gen); | ^^^^^^^^ generator is not `Send` | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead note: generator is not `Send` as this value is used across a yield --> $DIR/issue-68112.rs:36:9 | @@ -23,7 +24,7 @@ LL | fn require_send(_: impl Send) {} | ^^^^ required by this bound in `require_send` error[E0277]: `RefCell` cannot be shared between threads safely - --> $DIR/issue-68112.rs:63:18 + --> $DIR/issue-68112.rs:64:18 | LL | require_send(send_gen); | ------------ ^^^^^^^^ `RefCell` cannot be shared between threads safely @@ -31,25 +32,26 @@ LL | require_send(send_gen); | required by a bound introduced by this call | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead = note: required for `Arc>` to implement `Send` note: required because it's used within this generator - --> $DIR/issue-68112.rs:48:5 + --> $DIR/issue-68112.rs:49:5 | LL | || { | ^^ note: required because it appears within the type `impl Generator>>` - --> $DIR/issue-68112.rs:45:30 + --> $DIR/issue-68112.rs:46:30 | LL | pub fn make_gen2(t: T) -> impl Generator { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ note: required because it appears within the type `impl Generator>>` - --> $DIR/issue-68112.rs:53:34 + --> $DIR/issue-68112.rs:54:34 | LL | fn make_non_send_generator2() -> impl Generator>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: required because it captures the following types: `impl Generator>>`, `()` note: required because it's used within this generator - --> $DIR/issue-68112.rs:59:20 + --> $DIR/issue-68112.rs:60:20 | LL | let send_gen = || { | ^^ diff --git a/tests/ui/generator/not-send-sync.stderr b/tests/ui/generator/not-send-sync.stderr index a821c57b923a0..1711df729b8c0 100644 --- a/tests/ui/generator/not-send-sync.stderr +++ b/tests/ui/generator/not-send-sync.stderr @@ -12,6 +12,7 @@ LL | | }); | |_____^ `Cell` cannot be shared between threads safely | = help: the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead = note: required for `&Cell` to implement `Send` note: required because it's used within this generator --> $DIR/not-send-sync.rs:16:17 @@ -36,6 +37,7 @@ LL | | }); | |_____^ generator is not `Sync` | = help: within `[generator@$DIR/not-send-sync.rs:9:17: 9:19]`, the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead note: generator is not `Sync` as this value is used across a yield --> $DIR/not-send-sync.rs:12:9 | diff --git a/tests/ui/generator/print/generator-print-verbose-1.stderr b/tests/ui/generator/print/generator-print-verbose-1.stderr index ebf35be581c60..45d018b8ebad5 100644 --- a/tests/ui/generator/print/generator-print-verbose-1.stderr +++ b/tests/ui/generator/print/generator-print-verbose-1.stderr @@ -5,6 +5,7 @@ LL | require_send(send_gen); | ^^^^^^^^ generator is not `Send` | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead note: generator is not `Send` as this value is used across a yield --> $DIR/generator-print-verbose-1.rs:35:9 | @@ -29,6 +30,7 @@ LL | require_send(send_gen); | required by a bound introduced by this call | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead = note: required for `Arc>` to implement `Send` note: required because it's used within this generator --> $DIR/generator-print-verbose-1.rs:42:5 diff --git a/tests/ui/generator/print/generator-print-verbose-2.stderr b/tests/ui/generator/print/generator-print-verbose-2.stderr index 909e49c38b8d1..59112ce0a79e6 100644 --- a/tests/ui/generator/print/generator-print-verbose-2.stderr +++ b/tests/ui/generator/print/generator-print-verbose-2.stderr @@ -12,6 +12,7 @@ LL | | }); | |_____^ `Cell` cannot be shared between threads safely | = help: the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead = note: required for `&'_#4r Cell` to implement `Send` note: required because it's used within this generator --> $DIR/generator-print-verbose-2.rs:19:17 @@ -36,6 +37,7 @@ LL | | }); | |_____^ generator is not `Sync` | = help: within `[main::{closure#0} upvar_tys=() {Cell, ()}]`, the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead note: generator is not `Sync` as this value is used across a yield --> $DIR/generator-print-verbose-2.rs:15:9 | diff --git a/tests/ui/issues/issue-7364.stderr b/tests/ui/issues/issue-7364.stderr index 5dc8c2b607e61..aee73380f15e7 100644 --- a/tests/ui/issues/issue-7364.stderr +++ b/tests/ui/issues/issue-7364.stderr @@ -5,6 +5,7 @@ LL | static boxed: Box> = Box::new(RefCell::new(0)); | ^^^^^^^^^^^^^^^^^^^ `RefCell` cannot be shared between threads safely | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead = note: required for `Unique>` to implement `Sync` = note: required because it appears within the type `Box>` = note: shared static variables must have a type that implements `Sync` diff --git a/tests/ui/stdlib-unit-tests/not-sync.stderr b/tests/ui/stdlib-unit-tests/not-sync.stderr index 1ee358ba8368e..4e34e10e37789 100644 --- a/tests/ui/stdlib-unit-tests/not-sync.stderr +++ b/tests/ui/stdlib-unit-tests/not-sync.stderr @@ -5,6 +5,7 @@ LL | test::>(); | ^^^^^^^^^ `Cell` cannot be shared between threads safely | = help: the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead note: required by a bound in `test` --> $DIR/not-sync.rs:5:12 | @@ -18,6 +19,7 @@ LL | test::>(); | ^^^^^^^^^^^^ `RefCell` cannot be shared between threads safely | = help: the trait `Sync` is not implemented for `RefCell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead note: required by a bound in `test` --> $DIR/not-sync.rs:5:12 | diff --git a/tests/ui/mutexguard-sync.rs b/tests/ui/sync/mutexguard-sync.rs similarity index 100% rename from tests/ui/mutexguard-sync.rs rename to tests/ui/sync/mutexguard-sync.rs diff --git a/tests/ui/mutexguard-sync.stderr b/tests/ui/sync/mutexguard-sync.stderr similarity index 83% rename from tests/ui/mutexguard-sync.stderr rename to tests/ui/sync/mutexguard-sync.stderr index 3fbb2ddf183d3..4dc5571196c14 100644 --- a/tests/ui/mutexguard-sync.stderr +++ b/tests/ui/sync/mutexguard-sync.stderr @@ -7,6 +7,7 @@ LL | test_sync(guard); | required by a bound introduced by this call | = help: the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead = note: required for `MutexGuard<'_, Cell>` to implement `Sync` note: required by a bound in `test_sync` --> $DIR/mutexguard-sync.rs:5:17 diff --git a/tests/ui/sync/suggest-cell.rs b/tests/ui/sync/suggest-cell.rs new file mode 100644 index 0000000000000..3284eae7be181 --- /dev/null +++ b/tests/ui/sync/suggest-cell.rs @@ -0,0 +1,31 @@ +fn require_sync() {} +//~^ NOTE required by this bound in `require_sync` +//~| NOTE required by this bound in `require_sync` +//~| NOTE required by this bound in `require_sync` +//~| NOTE required by this bound in `require_sync` +//~| NOTE required by a bound in `require_sync` +//~| NOTE required by a bound in `require_sync` +//~| NOTE required by a bound in `require_sync` +//~| NOTE required by a bound in `require_sync` + +fn main() { + require_sync::>(); + //~^ ERROR `Cell<()>` cannot be shared between threads safely + //~| NOTE `Cell<()>` cannot be shared between threads safely + //~| NOTE if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` + + require_sync::>(); + //~^ ERROR `Cell` cannot be shared between threads safely + //~| NOTE `Cell` cannot be shared between threads safely + //~| NOTE if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU8` instead + + require_sync::>(); + //~^ ERROR `Cell` cannot be shared between threads safely + //~| NOTE `Cell` cannot be shared between threads safely + //~| NOTE if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead + + require_sync::>(); + //~^ ERROR `Cell` cannot be shared between threads safely + //~| NOTE `Cell` cannot be shared between threads safely + //~| NOTE if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicBool` instead +} diff --git a/tests/ui/sync/suggest-cell.stderr b/tests/ui/sync/suggest-cell.stderr new file mode 100644 index 0000000000000..c232c1ccd53dd --- /dev/null +++ b/tests/ui/sync/suggest-cell.stderr @@ -0,0 +1,59 @@ +error[E0277]: `Cell<()>` cannot be shared between threads safely + --> $DIR/suggest-cell.rs:12:20 + | +LL | require_sync::>(); + | ^^^^^^^^^^^^^^^^^^^ `Cell<()>` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `Cell<()>` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` +note: required by a bound in `require_sync` + --> $DIR/suggest-cell.rs:1:20 + | +LL | fn require_sync() {} + | ^^^^ required by this bound in `require_sync` + +error[E0277]: `Cell` cannot be shared between threads safely + --> $DIR/suggest-cell.rs:17:20 + | +LL | require_sync::>(); + | ^^^^^^^^^^^^^^^^^^^ `Cell` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU8` instead +note: required by a bound in `require_sync` + --> $DIR/suggest-cell.rs:1:20 + | +LL | fn require_sync() {} + | ^^^^ required by this bound in `require_sync` + +error[E0277]: `Cell` cannot be shared between threads safely + --> $DIR/suggest-cell.rs:22:20 + | +LL | require_sync::>(); + | ^^^^^^^^^^^^^^^^^^^^ `Cell` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead +note: required by a bound in `require_sync` + --> $DIR/suggest-cell.rs:1:20 + | +LL | fn require_sync() {} + | ^^^^ required by this bound in `require_sync` + +error[E0277]: `Cell` cannot be shared between threads safely + --> $DIR/suggest-cell.rs:27:20 + | +LL | require_sync::>(); + | ^^^^^^^^^^^^^^^^^^^^^ `Cell` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicBool` instead +note: required by a bound in `require_sync` + --> $DIR/suggest-cell.rs:1:20 + | +LL | fn require_sync() {} + | ^^^^ required by this bound in `require_sync` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/sync/suggest-once-cell.rs b/tests/ui/sync/suggest-once-cell.rs new file mode 100644 index 0000000000000..82fca45b1a47f --- /dev/null +++ b/tests/ui/sync/suggest-once-cell.rs @@ -0,0 +1,12 @@ +#![feature(once_cell)] + +fn require_sync() {} +//~^ NOTE required by this bound in `require_sync` +//~| NOTE required by a bound in `require_sync` + +fn main() { + require_sync::>(); + //~^ ERROR `OnceCell<()>` cannot be shared between threads safely + //~| NOTE `OnceCell<()>` cannot be shared between threads safely + //~| NOTE use `std::sync::OnceLock` instead +} diff --git a/tests/ui/sync/suggest-once-cell.stderr b/tests/ui/sync/suggest-once-cell.stderr new file mode 100644 index 0000000000000..fadf05374d8ca --- /dev/null +++ b/tests/ui/sync/suggest-once-cell.stderr @@ -0,0 +1,17 @@ +error[E0277]: `OnceCell<()>` cannot be shared between threads safely + --> $DIR/suggest-once-cell.rs:8:20 + | +LL | require_sync::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ `OnceCell<()>` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `OnceCell<()>` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::OnceLock` instead +note: required by a bound in `require_sync` + --> $DIR/suggest-once-cell.rs:3:20 + | +LL | fn require_sync() {} + | ^^^^ required by this bound in `require_sync` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/sync/suggest-ref-cell.rs b/tests/ui/sync/suggest-ref-cell.rs new file mode 100644 index 0000000000000..6b972ae09622e --- /dev/null +++ b/tests/ui/sync/suggest-ref-cell.rs @@ -0,0 +1,12 @@ +#![feature(once_cell)] + +fn require_sync() {} +//~^ NOTE required by this bound in `require_sync` +//~| NOTE required by a bound in `require_sync` + +fn main() { + require_sync::>(); + //~^ ERROR `RefCell<()>` cannot be shared between threads safely + //~| NOTE `RefCell<()>` cannot be shared between threads safely + //~| NOTE use `std::sync::RwLock` instead +} diff --git a/tests/ui/sync/suggest-ref-cell.stderr b/tests/ui/sync/suggest-ref-cell.stderr new file mode 100644 index 0000000000000..9e8b8fcb42ed0 --- /dev/null +++ b/tests/ui/sync/suggest-ref-cell.stderr @@ -0,0 +1,17 @@ +error[E0277]: `RefCell<()>` cannot be shared between threads safely + --> $DIR/suggest-ref-cell.rs:8:20 + | +LL | require_sync::>(); + | ^^^^^^^^^^^^^^^^^^^^^^ `RefCell<()>` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `RefCell<()>` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead +note: required by a bound in `require_sync` + --> $DIR/suggest-ref-cell.rs:3:20 + | +LL | fn require_sync() {} + | ^^^^ required by this bound in `require_sync` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From d3cfe97a8a975511a631083d64aa6ae3ddf7470a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Thu, 19 Jan 2023 00:00:00 +0000 Subject: [PATCH 09/21] Custom MIR: Support binary and unary operations --- .../src/build/custom/parse/instruction.rs | 6 ++++ .../custom/operators.f.built.after.mir | 26 +++++++++++++++++ tests/mir-opt/building/custom/operators.rs | 28 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 tests/mir-opt/building/custom/operators.f.built.after.mir create mode 100644 tests/mir-opt/building/custom/operators.rs diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs index dca4906c07de5..2560eaee43372 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -141,6 +141,12 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { ExprKind::AddressOf { mutability, arg } => Ok( Rvalue::AddressOf(*mutability, self.parse_place(*arg)?) ), + ExprKind::Binary { op, lhs, rhs } => Ok( + Rvalue::BinaryOp(*op, Box::new((self.parse_operand(*lhs)?, self.parse_operand(*rhs)?))) + ), + ExprKind::Unary { op, arg } => Ok( + Rvalue::UnaryOp(*op, self.parse_operand(*arg)?) + ), _ => self.parse_operand(expr_id).map(Rvalue::Use), ) } diff --git a/tests/mir-opt/building/custom/operators.f.built.after.mir b/tests/mir-opt/building/custom/operators.f.built.after.mir new file mode 100644 index 0000000000000..a0c5f1b40dbb7 --- /dev/null +++ b/tests/mir-opt/building/custom/operators.f.built.after.mir @@ -0,0 +1,26 @@ +// MIR for `f` after built + +fn f(_1: i32, _2: bool) -> i32 { + let mut _0: i32; // return place in scope 0 at $DIR/operators.rs:+0:30: +0:33 + + bb0: { + _1 = Neg(_1); // scope 0 at $DIR/operators.rs:+2:9: +2:15 + _2 = Not(_2); // scope 0 at $DIR/operators.rs:+3:9: +3:15 + _1 = Add(_1, _1); // scope 0 at $DIR/operators.rs:+4:9: +4:18 + _1 = Sub(_1, _1); // scope 0 at $DIR/operators.rs:+5:9: +5:18 + _1 = Mul(_1, _1); // scope 0 at $DIR/operators.rs:+6:9: +6:18 + _1 = Div(_1, _1); // scope 0 at $DIR/operators.rs:+7:9: +7:18 + _1 = Rem(_1, _1); // scope 0 at $DIR/operators.rs:+8:9: +8:18 + _1 = BitXor(_1, _1); // scope 0 at $DIR/operators.rs:+9:9: +9:18 + _1 = BitAnd(_1, _1); // scope 0 at $DIR/operators.rs:+10:9: +10:18 + _1 = Shl(_1, _1); // scope 0 at $DIR/operators.rs:+11:9: +11:19 + _1 = Shr(_1, _1); // scope 0 at $DIR/operators.rs:+12:9: +12:19 + _2 = Eq(_1, _1); // scope 0 at $DIR/operators.rs:+13:9: +13:19 + _2 = Lt(_1, _1); // scope 0 at $DIR/operators.rs:+14:9: +14:18 + _2 = Le(_1, _1); // scope 0 at $DIR/operators.rs:+15:9: +15:19 + _2 = Ge(_1, _1); // scope 0 at $DIR/operators.rs:+16:9: +16:19 + _2 = Gt(_1, _1); // scope 0 at $DIR/operators.rs:+17:9: +17:18 + _0 = _1; // scope 0 at $DIR/operators.rs:+18:9: +18:16 + return; // scope 0 at $DIR/operators.rs:+19:9: +19:17 + } +} diff --git a/tests/mir-opt/building/custom/operators.rs b/tests/mir-opt/building/custom/operators.rs new file mode 100644 index 0000000000000..51f80c66392a1 --- /dev/null +++ b/tests/mir-opt/building/custom/operators.rs @@ -0,0 +1,28 @@ +// compile-flags: --crate-type=lib +#![feature(custom_mir, core_intrinsics, inline_const)] +use std::intrinsics::mir::*; + +// EMIT_MIR operators.f.built.after.mir +#[custom_mir(dialect = "built")] +pub fn f(a: i32, b: bool) -> i32 { + mir!({ + a = -a; + b = !b; + a = a + a; + a = a - a; + a = a * a; + a = a / a; + a = a % a; + a = a ^ a; + a = a & a; + a = a << a; + a = a >> a; + b = a == a; + b = a < a; + b = a <= a; + b = a >= a; + b = a > a; + RET = a; + Return() + }) +} From 9806a9ea05b6505e3f4b9417e03429d8ca35b158 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Thu, 19 Jan 2023 21:11:17 +0000 Subject: [PATCH 10/21] Print PID holding bootstrap build lock on Linux --- src/bootstrap/bin/main.rs | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/bin/main.rs b/src/bootstrap/bin/main.rs index be69f819c6428..db42212821018 100644 --- a/src/bootstrap/bin/main.rs +++ b/src/bootstrap/bin/main.rs @@ -6,6 +6,8 @@ //! directory in each respective module. use std::env; +use std::fs::File; +use std::io::{BufRead, BufReader}; use bootstrap::{t, Build, Config, Subcommand, VERSION}; @@ -16,12 +18,17 @@ fn main() { let mut build_lock; let _build_lock_guard; if cfg!(any(unix, windows)) { - build_lock = fd_lock::RwLock::new(t!(std::fs::File::create(config.out.join("lock")))); + let path = config.out.join("lock"); + build_lock = fd_lock::RwLock::new(t!(std::fs::File::create(&path))); _build_lock_guard = match build_lock.try_write() { Ok(lock) => lock, err => { - println!("warning: build directory locked, waiting for lock"); drop(err); + if let Some(pid) = get_lock_owner(&path) { + println!("warning: build directory locked by process {pid}, waiting for lock"); + } else { + println!("warning: build directory locked, waiting for lock"); + } t!(build_lock.write()) } }; @@ -98,3 +105,27 @@ fn check_version(config: &Config) -> Option { Some(msg) } + +/// Get the PID of the process which took the write lock by +/// parsing `/proc/locks`. +#[cfg(target_os = "linux")] +fn get_lock_owner(f: &std::path::Path) -> Option { + use std::os::unix::fs::MetadataExt; + let lock_inode = std::fs::metadata(f).ok()?.ino(); + let lockfile = File::open("/proc/locks").ok()?; + BufReader::new(lockfile).lines().find_map(|line| { + // pid--vvvvvv vvvvvvv--- inode + // 21: FLOCK ADVISORY WRITE 359238 08:02:3719774 0 EOF + let line = line.ok()?; + let parts = line.split_whitespace().collect::>(); + let (pid, inode) = (parts[4].parse::().ok()?, &parts[5]); + let inode = inode.rsplit_once(':')?.1.parse::().ok()?; + if inode == lock_inode { Some(pid) } else { None } + }) +} + +#[cfg(not(target_os = "linux"))] +fn get_lock_owner(f: &std::path::Path) -> Option { + // FIXME: Implement on other OS's + None +} From 236f8231a64241d3fb3b9d81a8cd3175367e691d Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 23 Jan 2023 14:42:32 +0200 Subject: [PATCH 11/21] `sub_ptr()` is equivalent to `usize::try_from().unwrap_unchecked()`, not `usize::from().unwrap_unchecked()`. `usize::from()` gives a `usize`, not `Result`, and `usize: From` is not implemented. --- library/core/src/ptr/const_ptr.rs | 2 +- library/core/src/ptr/mut_ptr.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 7b1cb5488bcac..16eb726f6f614 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -731,7 +731,7 @@ impl *const T { /// This computes the same value that [`offset_from`](#method.offset_from) /// would compute, but with the added precondition that the offset is /// guaranteed to be non-negative. This method is equivalent to - /// `usize::from(self.offset_from(origin)).unwrap_unchecked()`, + /// `usize::try_from(self.offset_from(origin)).unwrap_unchecked()`, /// but it provides slightly more information to the optimizer, which can /// sometimes allow it to optimize slightly better with some backends. /// diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index ed1e3bd481227..0a2f63e3ec6a5 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -904,7 +904,7 @@ impl *mut T { /// This computes the same value that [`offset_from`](#method.offset_from) /// would compute, but with the added precondition that the offset is /// guaranteed to be non-negative. This method is equivalent to - /// `usize::from(self.offset_from(origin)).unwrap_unchecked()`, + /// `usize::try_from(self.offset_from(origin)).unwrap_unchecked()`, /// but it provides slightly more information to the optimizer, which can /// sometimes allow it to optimize slightly better with some backends. /// From e477cf9475540bf7f5f71940b29fc065cb988982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 8 Jan 2023 02:54:59 +0000 Subject: [PATCH 12/21] Suggest coercion of `Result` using `?` Fix #47560. --- compiler/rustc_hir_typeck/src/coercion.rs | 49 ++++++++++++++++++- .../coerce-result-return-value.fixed | 16 ++++++ .../type-check/coerce-result-return-value.rs | 16 ++++++ .../coerce-result-return-value.stderr | 33 +++++++++++++ 4 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 tests/ui/type/type-check/coerce-result-return-value.fixed create mode 100644 tests/ui/type/type-check/coerce-result-return-value.rs create mode 100644 tests/ui/type/type-check/coerce-result-return-value.stderr diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index bbf7b81a2cc66..752e3f79d4abf 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -45,7 +45,7 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::Expr; use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{Coercion, InferOk, InferResult}; +use rustc_infer::infer::{Coercion, InferOk, InferResult, TyCtxtInferExt}; use rustc_infer::traits::Obligation; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::adjustment::{ @@ -1565,6 +1565,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { && let hir::ExprKind::Loop(loop_blk, ..) = expression.kind { intravisit::walk_block(& mut visitor, loop_blk); } + if let Some(expr) = expression { + self.note_result_coercion(fcx, &mut err, expr, expected, found); + } } ObligationCauseCode::ReturnValue(id) => { err = self.report_return_mismatched_types( @@ -1581,6 +1584,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { let id = fcx.tcx.hir().parent_id(id); unsized_return = self.is_return_ty_unsized(fcx, id); } + if let Some(expr) = expression { + self.note_result_coercion(fcx, &mut err, expr, expected, found); + } } _ => { err = fcx.err_ctxt().report_mismatched_types( @@ -1619,6 +1625,47 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } } } + + fn note_result_coercion( + &self, + fcx: &FnCtxt<'_, 'tcx>, + err: &mut Diagnostic, + expr: &hir::Expr<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + let ty::Adt(e, substs_e) = expected.kind() else { return; }; + let ty::Adt(f, substs_f) = found.kind() else { return; }; + if e.did() != f.did() { + return; + } + if Some(e.did()) != fcx.tcx.get_diagnostic_item(sym::Result) { + return; + } + let e = substs_e.type_at(1); + let f = substs_f.type_at(1); + if fcx + .tcx + .infer_ctxt() + .build() + .type_implements_trait( + fcx.tcx.get_diagnostic_item(sym::Into).unwrap(), + [fcx.tcx.erase_regions(f), fcx.tcx.erase_regions(e)], + fcx.param_env, + ) + .must_apply_modulo_regions() + { + err.multipart_suggestion( + "you can rely on the implicit conversion that `?` does to transform the error type", + vec![ + (expr.span.shrink_to_lo(), "Ok(".to_string()), + (expr.span.shrink_to_hi(), "?)".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } + } + fn note_unreachable_loop_return( &self, err: &mut Diagnostic, diff --git a/tests/ui/type/type-check/coerce-result-return-value.fixed b/tests/ui/type/type-check/coerce-result-return-value.fixed new file mode 100644 index 0000000000000..91066262303ec --- /dev/null +++ b/tests/ui/type/type-check/coerce-result-return-value.fixed @@ -0,0 +1,16 @@ +// run-rustfix +struct A; +struct B; +impl From for B { + fn from(_: A) -> Self { B } +} +fn foo1(x: Result<(), A>) -> Result<(), B> { + Ok(x?) //~ ERROR mismatched types +} +fn foo2(x: Result<(), A>) -> Result<(), B> { + return Ok(x?); //~ ERROR mismatched types +} +fn main() { + let _ = foo1(Ok(())); + let _ = foo2(Ok(())); +} diff --git a/tests/ui/type/type-check/coerce-result-return-value.rs b/tests/ui/type/type-check/coerce-result-return-value.rs new file mode 100644 index 0000000000000..9a71376f462dc --- /dev/null +++ b/tests/ui/type/type-check/coerce-result-return-value.rs @@ -0,0 +1,16 @@ +// run-rustfix +struct A; +struct B; +impl From for B { + fn from(_: A) -> Self { B } +} +fn foo1(x: Result<(), A>) -> Result<(), B> { + x //~ ERROR mismatched types +} +fn foo2(x: Result<(), A>) -> Result<(), B> { + return x; //~ ERROR mismatched types +} +fn main() { + let _ = foo1(Ok(())); + let _ = foo2(Ok(())); +} diff --git a/tests/ui/type/type-check/coerce-result-return-value.stderr b/tests/ui/type/type-check/coerce-result-return-value.stderr new file mode 100644 index 0000000000000..7aebc9dcc7ad8 --- /dev/null +++ b/tests/ui/type/type-check/coerce-result-return-value.stderr @@ -0,0 +1,33 @@ +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value.rs:8:5 + | +LL | fn foo1(x: Result<(), A>) -> Result<(), B> { + | ------------- expected `Result<(), B>` because of return type +LL | x + | ^ expected struct `B`, found struct `A` + | + = note: expected enum `Result<_, B>` + found enum `Result<_, A>` +help: you can rely on the implicit conversion that `?` does to transform the error type + | +LL | Ok(x?) + | +++ ++ + +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value.rs:11:12 + | +LL | fn foo2(x: Result<(), A>) -> Result<(), B> { + | ------------- expected `Result<(), B>` because of return type +LL | return x; + | ^ expected struct `B`, found struct `A` + | + = note: expected enum `Result<_, B>` + found enum `Result<_, A>` +help: you can rely on the implicit conversion that `?` does to transform the error type + | +LL | return Ok(x?); + | +++ ++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. From d5a1609ec450d624fd8bf3f88647e966cf83eee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 8 Jan 2023 07:05:23 +0000 Subject: [PATCH 13/21] review comment: use `fcx.infcx` --- compiler/rustc_hir_typeck/src/coercion.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 752e3f79d4abf..893d1e88bf8a2 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1645,9 +1645,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { let e = substs_e.type_at(1); let f = substs_f.type_at(1); if fcx - .tcx - .infer_ctxt() - .build() + .infcx .type_implements_trait( fcx.tcx.get_diagnostic_item(sym::Into).unwrap(), [fcx.tcx.erase_regions(f), fcx.tcx.erase_regions(e)], From ddd9a9fb4666401d4f1a92edd8a7d8b33bf0824f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 8 Jan 2023 07:14:17 +0000 Subject: [PATCH 14/21] Add call in `emit_type_mismatch_suggestions` --- compiler/rustc_hir_typeck/src/coercion.rs | 46 +---------------------- compiler/rustc_hir_typeck/src/demand.rs | 42 ++++++++++++++++++++- 2 files changed, 42 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 893d1e88bf8a2..d5f37abb8b455 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -45,7 +45,7 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::Expr; use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{Coercion, InferOk, InferResult, TyCtxtInferExt}; +use rustc_infer::infer::{Coercion, InferOk, InferResult}; use rustc_infer::traits::Obligation; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::adjustment::{ @@ -1565,9 +1565,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { && let hir::ExprKind::Loop(loop_blk, ..) = expression.kind { intravisit::walk_block(& mut visitor, loop_blk); } - if let Some(expr) = expression { - self.note_result_coercion(fcx, &mut err, expr, expected, found); - } } ObligationCauseCode::ReturnValue(id) => { err = self.report_return_mismatched_types( @@ -1584,9 +1581,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { let id = fcx.tcx.hir().parent_id(id); unsized_return = self.is_return_ty_unsized(fcx, id); } - if let Some(expr) = expression { - self.note_result_coercion(fcx, &mut err, expr, expected, found); - } } _ => { err = fcx.err_ctxt().report_mismatched_types( @@ -1626,44 +1620,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } } - fn note_result_coercion( - &self, - fcx: &FnCtxt<'_, 'tcx>, - err: &mut Diagnostic, - expr: &hir::Expr<'tcx>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - let ty::Adt(e, substs_e) = expected.kind() else { return; }; - let ty::Adt(f, substs_f) = found.kind() else { return; }; - if e.did() != f.did() { - return; - } - if Some(e.did()) != fcx.tcx.get_diagnostic_item(sym::Result) { - return; - } - let e = substs_e.type_at(1); - let f = substs_f.type_at(1); - if fcx - .infcx - .type_implements_trait( - fcx.tcx.get_diagnostic_item(sym::Into).unwrap(), - [fcx.tcx.erase_regions(f), fcx.tcx.erase_regions(e)], - fcx.param_env, - ) - .must_apply_modulo_regions() - { - err.multipart_suggestion( - "you can rely on the implicit conversion that `?` does to transform the error type", - vec![ - (expr.span.shrink_to_lo(), "Ok(".to_string()), - (expr.span.shrink_to_hi(), "?)".to_string()), - ], - Applicability::MaybeIncorrect, - ); - } - } - fn note_unreachable_loop_return( &self, err: &mut Diagnostic, diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 665dc8b6a2f2a..b10bb593ead4b 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -59,7 +59,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_copied_or_cloned(err, expr, expr_ty, expected) || self.suggest_clone_for_ref(err, expr, expr_ty, expected) || self.suggest_into(err, expr, expr_ty, expected) - || self.suggest_floating_point_literal(err, expr, expected); + || self.suggest_floating_point_literal(err, expr, expected) + || self.note_result_coercion(err, expr, expected, expr_ty); if !suggested { self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected); } @@ -697,6 +698,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } + pub(crate) fn note_result_coercion( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) -> bool { + let ty::Adt(e, substs_e) = expected.kind() else { return false; }; + let ty::Adt(f, substs_f) = found.kind() else { return false; }; + if e.did() != f.did() { + return false; + } + if Some(e.did()) != self.tcx.get_diagnostic_item(sym::Result) { + return false; + } + let e = substs_e.type_at(1); + let f = substs_f.type_at(1); + if self + .infcx + .type_implements_trait( + self.tcx.get_diagnostic_item(sym::Into).unwrap(), + [self.tcx.erase_regions(f), self.tcx.erase_regions(e)], + self.param_env, + ) + .must_apply_modulo_regions() + { + err.multipart_suggestion( + "you can rely on the implicit conversion that `?` does to transform the error type", + vec![ + (expr.span.shrink_to_lo(), "Ok(".to_string()), + (expr.span.shrink_to_hi(), "?)".to_string()), + ], + Applicability::MaybeIncorrect, + ); + return true; + } + false + } + /// If the expected type is an enum (Issue #55250) with any variants whose /// sole field is of the found type, suggest such variants. (Issue #42764) fn suggest_compatible_variants( From fcf0ed90187ae60739c76ef3e72e8232bf14746b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 8 Jan 2023 07:43:24 +0000 Subject: [PATCH 15/21] Do not erase regions --- compiler/rustc_hir_typeck/src/demand.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index b10bb593ead4b..d55ea52d16b02 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -719,7 +719,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .infcx .type_implements_trait( self.tcx.get_diagnostic_item(sym::Into).unwrap(), - [self.tcx.erase_regions(f), self.tcx.erase_regions(e)], + [f, e], self.param_env, ) .must_apply_modulo_regions() From df81147b51f95441c8db74eda92b5c5fadecb20e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 8 Jan 2023 19:12:15 +0000 Subject: [PATCH 16/21] Ensure suggestion correctness --- compiler/rustc_hir_typeck/src/demand.rs | 10 ++++ .../coerce-result-return-value-2.rs | 24 ++++++++++ .../coerce-result-return-value-2.stderr | 47 +++++++++++++++++++ .../coerce-result-return-value.fixed | 8 ++++ .../type-check/coerce-result-return-value.rs | 8 ++++ .../coerce-result-return-value.stderr | 34 +++++++++++++- 6 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 tests/ui/type/type-check/coerce-result-return-value-2.rs create mode 100644 tests/ui/type/type-check/coerce-result-return-value-2.stderr diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index d55ea52d16b02..97490194e2558 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -713,6 +713,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if Some(e.did()) != self.tcx.get_diagnostic_item(sym::Result) { return false; } + let map = self.tcx.hir(); + if let Some(hir::Node::Expr(expr)) = map.find_parent(expr.hir_id) + && let hir::ExprKind::Ret(_) = expr.kind + { + // `return foo;` + } else if map.get_return_block(expr.hir_id).is_some() { + // Function's tail expression. + } else { + return false; + } let e = substs_e.type_at(1); let f = substs_f.type_at(1); if self diff --git a/tests/ui/type/type-check/coerce-result-return-value-2.rs b/tests/ui/type/type-check/coerce-result-return-value-2.rs new file mode 100644 index 0000000000000..23bafa6c5c94c --- /dev/null +++ b/tests/ui/type/type-check/coerce-result-return-value-2.rs @@ -0,0 +1,24 @@ +struct A; +struct B; +impl From for B { + fn from(_: A) -> Self { B } +} +fn foo4(x: Result<(), A>) -> Result<(), B> { + match true { + true => x, //~ ERROR mismatched types + false => x, + } +} +fn foo5(x: Result<(), A>) -> Result<(), B> { + match true { + true => return x, //~ ERROR mismatched types + false => return x, + } +} +fn main() { + let _ = foo4(Ok(())); + let _ = foo5(Ok(())); + let _: Result<(), B> = { //~ ERROR mismatched types + Err(A); + }; +} diff --git a/tests/ui/type/type-check/coerce-result-return-value-2.stderr b/tests/ui/type/type-check/coerce-result-return-value-2.stderr new file mode 100644 index 0000000000000..64a8c779fce76 --- /dev/null +++ b/tests/ui/type/type-check/coerce-result-return-value-2.stderr @@ -0,0 +1,47 @@ +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value-2.rs:8:17 + | +LL | fn foo4(x: Result<(), A>) -> Result<(), B> { + | ------------- expected `Result<(), B>` because of return type +LL | match true { +LL | true => x, + | ^ expected struct `B`, found struct `A` + | + = note: expected enum `Result<_, B>` + found enum `Result<_, A>` +help: you can rely on the implicit conversion that `?` does to transform the error type + | +LL | true => Ok(x?), + | +++ ++ + +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value-2.rs:14:24 + | +LL | fn foo5(x: Result<(), A>) -> Result<(), B> { + | ------------- expected `Result<(), B>` because of return type +LL | match true { +LL | true => return x, + | ^ expected struct `B`, found struct `A` + | + = note: expected enum `Result<_, B>` + found enum `Result<_, A>` +help: you can rely on the implicit conversion that `?` does to transform the error type + | +LL | true => return Ok(x?), + | +++ ++ + +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value-2.rs:21:28 + | +LL | let _: Result<(), B> = { + | ____________________________^ +LL | | Err(A); +LL | | }; + | |_____^ expected enum `Result`, found `()` + | + = note: expected enum `Result<(), B>` + found unit type `()` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type/type-check/coerce-result-return-value.fixed b/tests/ui/type/type-check/coerce-result-return-value.fixed index 91066262303ec..8a05407070dad 100644 --- a/tests/ui/type/type-check/coerce-result-return-value.fixed +++ b/tests/ui/type/type-check/coerce-result-return-value.fixed @@ -10,7 +10,15 @@ fn foo1(x: Result<(), A>) -> Result<(), B> { fn foo2(x: Result<(), A>) -> Result<(), B> { return Ok(x?); //~ ERROR mismatched types } +fn foo3(x: Result<(), A>) -> Result<(), B> { + if true { + Ok(x?) //~ ERROR mismatched types + } else { + Ok(x?) //~ ERROR mismatched types + } +} fn main() { let _ = foo1(Ok(())); let _ = foo2(Ok(())); + let _ = foo3(Ok(())); } diff --git a/tests/ui/type/type-check/coerce-result-return-value.rs b/tests/ui/type/type-check/coerce-result-return-value.rs index 9a71376f462dc..442203addb787 100644 --- a/tests/ui/type/type-check/coerce-result-return-value.rs +++ b/tests/ui/type/type-check/coerce-result-return-value.rs @@ -10,7 +10,15 @@ fn foo1(x: Result<(), A>) -> Result<(), B> { fn foo2(x: Result<(), A>) -> Result<(), B> { return x; //~ ERROR mismatched types } +fn foo3(x: Result<(), A>) -> Result<(), B> { + if true { + x //~ ERROR mismatched types + } else { + x //~ ERROR mismatched types + } +} fn main() { let _ = foo1(Ok(())); let _ = foo2(Ok(())); + let _ = foo3(Ok(())); } diff --git a/tests/ui/type/type-check/coerce-result-return-value.stderr b/tests/ui/type/type-check/coerce-result-return-value.stderr index 7aebc9dcc7ad8..18993b3cef1b9 100644 --- a/tests/ui/type/type-check/coerce-result-return-value.stderr +++ b/tests/ui/type/type-check/coerce-result-return-value.stderr @@ -28,6 +28,38 @@ help: you can rely on the implicit conversion that `?` does to transform the err LL | return Ok(x?); | +++ ++ -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value.rs:15:9 + | +LL | fn foo3(x: Result<(), A>) -> Result<(), B> { + | ------------- expected `Result<(), B>` because of return type +LL | if true { +LL | x + | ^ expected struct `B`, found struct `A` + | + = note: expected enum `Result<_, B>` + found enum `Result<_, A>` +help: you can rely on the implicit conversion that `?` does to transform the error type + | +LL | Ok(x?) + | +++ ++ + +error[E0308]: mismatched types + --> $DIR/coerce-result-return-value.rs:17:9 + | +LL | fn foo3(x: Result<(), A>) -> Result<(), B> { + | ------------- expected `Result<(), B>` because of return type +... +LL | x + | ^ expected struct `B`, found struct `A` + | + = note: expected enum `Result<_, B>` + found enum `Result<_, A>` +help: you can rely on the implicit conversion that `?` does to transform the error type + | +LL | Ok(x?) + | +++ ++ + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`. From 62aff3bbc75468bedf751b01a746e52886be760c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 23 Jan 2023 14:46:30 +0000 Subject: [PATCH 17/21] tweak wording --- compiler/rustc_hir_typeck/src/demand.rs | 3 ++- .../type/type-check/coerce-result-return-value-2.stderr | 4 ++-- .../ui/type/type-check/coerce-result-return-value.stderr | 8 ++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 97490194e2558..3f185dfae0241 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -735,7 +735,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .must_apply_modulo_regions() { err.multipart_suggestion( - "you can rely on the implicit conversion that `?` does to transform the error type", + "use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \ + in `Ok` so the expression remains of type `Result`", vec![ (expr.span.shrink_to_lo(), "Ok(".to_string()), (expr.span.shrink_to_hi(), "?)".to_string()), diff --git a/tests/ui/type/type-check/coerce-result-return-value-2.stderr b/tests/ui/type/type-check/coerce-result-return-value-2.stderr index 64a8c779fce76..5992162341e6e 100644 --- a/tests/ui/type/type-check/coerce-result-return-value-2.stderr +++ b/tests/ui/type/type-check/coerce-result-return-value-2.stderr @@ -9,7 +9,7 @@ LL | true => x, | = note: expected enum `Result<_, B>` found enum `Result<_, A>` -help: you can rely on the implicit conversion that `?` does to transform the error type +help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result` | LL | true => Ok(x?), | +++ ++ @@ -25,7 +25,7 @@ LL | true => return x, | = note: expected enum `Result<_, B>` found enum `Result<_, A>` -help: you can rely on the implicit conversion that `?` does to transform the error type +help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result` | LL | true => return Ok(x?), | +++ ++ diff --git a/tests/ui/type/type-check/coerce-result-return-value.stderr b/tests/ui/type/type-check/coerce-result-return-value.stderr index 18993b3cef1b9..550153520782c 100644 --- a/tests/ui/type/type-check/coerce-result-return-value.stderr +++ b/tests/ui/type/type-check/coerce-result-return-value.stderr @@ -8,7 +8,7 @@ LL | x | = note: expected enum `Result<_, B>` found enum `Result<_, A>` -help: you can rely on the implicit conversion that `?` does to transform the error type +help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result` | LL | Ok(x?) | +++ ++ @@ -23,7 +23,7 @@ LL | return x; | = note: expected enum `Result<_, B>` found enum `Result<_, A>` -help: you can rely on the implicit conversion that `?` does to transform the error type +help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result` | LL | return Ok(x?); | +++ ++ @@ -39,7 +39,7 @@ LL | x | = note: expected enum `Result<_, B>` found enum `Result<_, A>` -help: you can rely on the implicit conversion that `?` does to transform the error type +help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result` | LL | Ok(x?) | +++ ++ @@ -55,7 +55,7 @@ LL | x | = note: expected enum `Result<_, B>` found enum `Result<_, A>` -help: you can rely on the implicit conversion that `?` does to transform the error type +help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result` | LL | Ok(x?) | +++ ++ From 033047a72cd3129831d4c0ae59024d77f17106ec Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 23 Jan 2023 15:34:11 +0100 Subject: [PATCH 18/21] `new_outside_solver` -> `evaluate_root_goal` --- .../src/solve/fulfill.rs | 7 +--- .../rustc_trait_selection/src/solve/mod.rs | 42 +++++++++++++------ 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 40b9bedc84fd3..d59fa71406c31 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -1,5 +1,6 @@ use std::mem; +use super::{Certainty, InferCtxtEvalExt}; use rustc_infer::{ infer::InferCtxt, traits::{ @@ -8,8 +9,6 @@ use rustc_infer::{ }, }; -use super::{search_graph, Certainty, EvalCtxt}; - /// A trait engine using the new trait solver. /// /// This is mostly identical to how `evaluate_all` works inside of the @@ -66,9 +65,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { let mut has_changed = false; for obligation in mem::take(&mut self.obligations) { let goal = obligation.clone().into(); - let search_graph = &mut search_graph::SearchGraph::new(infcx.tcx); - let mut ecx = EvalCtxt::new_outside_solver(infcx, search_graph); - let (changed, certainty) = match ecx.evaluate_goal(goal) { + let (changed, certainty) = match infcx.evaluate_root_goal(goal) { Ok(result) => result, Err(NoSolution) => { errors.push(FulfillmentError { diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index da2a1a19957e1..70f094014453e 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -152,6 +152,36 @@ impl<'tcx> TyCtxtExt<'tcx> for TyCtxt<'tcx> { } } +pub trait InferCtxtEvalExt<'tcx> { + /// Evaluates a goal from **outside** of the trait solver. + /// + /// Using this while inside of the solver is wrong as it uses a new + /// search graph which would break cycle detection. + fn evaluate_root_goal( + &self, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + ) -> Result<(bool, Certainty), NoSolution>; +} + +impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> { + fn evaluate_root_goal( + &self, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + ) -> Result<(bool, Certainty), NoSolution> { + let mut search_graph = search_graph::SearchGraph::new(self.tcx); + + let result = EvalCtxt { + search_graph: &mut search_graph, + infcx: self, + var_values: CanonicalVarValues::dummy(), + } + .evaluate_goal(goal); + + assert!(search_graph.is_empty()); + result + } +} + struct EvalCtxt<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, var_values: CanonicalVarValues<'tcx>, @@ -164,18 +194,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { self.infcx.tcx } - /// Creates a new evaluation context outside of the trait solver. - /// - /// With this solver making a canonical response doesn't make much sense. - /// The `search_graph` for this solver has to be completely empty. - fn new_outside_solver( - infcx: &'a InferCtxt<'tcx>, - search_graph: &'a mut search_graph::SearchGraph<'tcx>, - ) -> EvalCtxt<'a, 'tcx> { - assert!(search_graph.is_empty()); - EvalCtxt { infcx, var_values: CanonicalVarValues::dummy(), search_graph } - } - #[instrument(level = "debug", skip(tcx, search_graph), ret)] fn evaluate_canonical_goal( tcx: TyCtxt<'tcx>, From bed3bb53d207c1bf92c26833e8d3d4280550f83e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 21 Jan 2023 21:54:49 +0000 Subject: [PATCH 19/21] Don't resolve type var roots in point_at_expr_source_of_inferred_type --- compiler/rustc_hir_typeck/src/demand.rs | 4 +++- tests/ui/typeck/bad-type-in-vec-push.rs | 14 ++++++++++++++ tests/ui/typeck/bad-type-in-vec-push.stderr | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 tests/ui/typeck/bad-type-in-vec-push.rs create mode 100644 tests/ui/typeck/bad-type-in-vec-push.stderr diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index bd1626dff7951..d8cca55a8f892 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -270,7 +270,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lt_op: |_| self.tcx.lifetimes.re_erased, ct_op: |c| c, ty_op: |t| match *t.kind() { - ty::Infer(ty::TyVar(vid)) => self.tcx.mk_ty_infer(ty::TyVar(self.root_var(vid))), + ty::Infer(ty::TyVar(_)) => self.tcx.mk_ty_var(ty::TyVid::from_u32(0)), ty::Infer(ty::IntVar(_)) => { self.tcx.mk_ty_infer(ty::IntVar(ty::IntVid { index: 0 })) } @@ -333,6 +333,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // inferred in this method call. let arg = &args[i]; let arg_ty = self.node_ty(arg.hir_id); + if !arg.span.overlaps(mismatch_span) { err.span_label( arg.span, &format!( @@ -340,6 +341,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { inferred as `{ty}`", ), ); + } param_args.insert(param_ty, (arg, arg_ty)); } } diff --git a/tests/ui/typeck/bad-type-in-vec-push.rs b/tests/ui/typeck/bad-type-in-vec-push.rs new file mode 100644 index 0000000000000..397e07dc17b42 --- /dev/null +++ b/tests/ui/typeck/bad-type-in-vec-push.rs @@ -0,0 +1,14 @@ +// The error message here still is pretty confusing. + +fn main() { + let mut result = vec![1]; + // The type of `result` is constrained to be `Vec<{integer}>` here. + // But the logic we use to find what expression constrains a type + // is not sophisticated enough to know this. + + let mut vector = Vec::new(); + vector.sort(); + result.push(vector); + //~^ ERROR mismatched types + // So it thinks that the type of `result` is constrained here. +} diff --git a/tests/ui/typeck/bad-type-in-vec-push.stderr b/tests/ui/typeck/bad-type-in-vec-push.stderr new file mode 100644 index 0000000000000..a24c49f0e9a3c --- /dev/null +++ b/tests/ui/typeck/bad-type-in-vec-push.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/bad-type-in-vec-push.rs:11:17 + | +LL | vector.sort(); + | ------ here the type of `vector` is inferred to be `Vec<_>` +LL | result.push(vector); + | ---- ^^^^^^ + | | | + | | expected integer, found struct `Vec` + | | this is of type `Vec<_>`, which causes `result` to be inferred as `Vec<{integer}>` + | arguments to this method are incorrect + | + = note: expected type `{integer}` + found struct `Vec<_>` +note: associated function defined here + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From 9f933b5642c08e4241cbfed0f15270df552ce8e6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 21 Jan 2023 22:18:51 +0000 Subject: [PATCH 20/21] Hack to suppress bad labels in type mismatch inference deduction code --- compiler/rustc_hir_typeck/src/demand.rs | 36 ++++++++++--------- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 8 ++++- tests/ui/typeck/bad-type-in-vec-push.rs | 6 ++++ tests/ui/typeck/bad-type-in-vec-push.stderr | 19 +++++++--- 4 files changed, 47 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index d8cca55a8f892..8e23ded3c09b4 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -61,7 +61,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_into(err, expr, expr_ty, expected) || self.suggest_floating_point_literal(err, expr, expected); if !suggested { - self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected); + self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected, expr.span); } } @@ -222,6 +222,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, found: Ty<'tcx>, expected: Ty<'tcx>, + mismatch_span: Span, ) -> bool { let map = self.tcx.hir(); @@ -281,7 +282,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, }; let mut prev = eraser.fold_ty(ty); - let mut prev_span = None; + let mut prev_span: Option = None; for binding in expr_finder.uses { // In every expression where the binding is referenced, we will look at that @@ -334,13 +335,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let arg = &args[i]; let arg_ty = self.node_ty(arg.hir_id); if !arg.span.overlaps(mismatch_span) { - err.span_label( - arg.span, - &format!( - "this is of type `{arg_ty}`, which causes `{ident}` to be \ - inferred as `{ty}`", - ), - ); + err.span_label( + arg.span, + &format!( + "this is of type `{arg_ty}`, which causes `{ident}` to be \ + inferred as `{ty}`", + ), + ); } param_args.insert(param_ty, (arg, arg_ty)); } @@ -384,12 +385,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && self.can_eq(self.param_env, ty, found).is_ok() { // We only point at the first place where the found type was inferred. + if !segment.ident.span.overlaps(mismatch_span) { err.span_label( segment.ident.span, with_forced_trimmed_paths!(format!( "here the type of `{ident}` is inferred to be `{ty}`", )), - ); + );} break; } else if !param_args.is_empty() { break; @@ -408,12 +410,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We use the *previous* span because if the type is known *here* it means // it was *evaluated earlier*. We don't do this for method calls because we // evaluate the method's self type eagerly, but not in any other case. - err.span_label( - span, - with_forced_trimmed_paths!(format!( - "here the type of `{ident}` is inferred to be `{ty}`", - )), - ); + if !span.overlaps(mismatch_span) { + err.span_label( + span, + with_forced_trimmed_paths!(format!( + "here the type of `{ident}` is inferred to be `{ty}`", + )), + ); + } break; } prev = ty; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index c9609e6943981..677c80297b912 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -808,7 +808,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { kind: TypeVariableOriginKind::MiscVariable, span: full_call_span, }); - self.point_at_expr_source_of_inferred_type(&mut err, rcvr, expected, callee_ty); + self.point_at_expr_source_of_inferred_type( + &mut err, + rcvr, + expected, + callee_ty, + provided_arg_span, + ); } // Call out where the function is defined self.label_fn_like( diff --git a/tests/ui/typeck/bad-type-in-vec-push.rs b/tests/ui/typeck/bad-type-in-vec-push.rs index 397e07dc17b42..a807f030cfce4 100644 --- a/tests/ui/typeck/bad-type-in-vec-push.rs +++ b/tests/ui/typeck/bad-type-in-vec-push.rs @@ -12,3 +12,9 @@ fn main() { //~^ ERROR mismatched types // So it thinks that the type of `result` is constrained here. } + +fn example2() { + let mut x = vec![1]; + x.push(""); + //~^ ERROR mismatched types +} diff --git a/tests/ui/typeck/bad-type-in-vec-push.stderr b/tests/ui/typeck/bad-type-in-vec-push.stderr index a24c49f0e9a3c..e4c99ec8e701f 100644 --- a/tests/ui/typeck/bad-type-in-vec-push.stderr +++ b/tests/ui/typeck/bad-type-in-vec-push.stderr @@ -4,10 +4,8 @@ error[E0308]: mismatched types LL | vector.sort(); | ------ here the type of `vector` is inferred to be `Vec<_>` LL | result.push(vector); - | ---- ^^^^^^ - | | | - | | expected integer, found struct `Vec` - | | this is of type `Vec<_>`, which causes `result` to be inferred as `Vec<{integer}>` + | ---- ^^^^^^ expected integer, found struct `Vec` + | | | arguments to this method are incorrect | = note: expected type `{integer}` @@ -15,6 +13,17 @@ LL | result.push(vector); note: associated function defined here --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL -error: aborting due to previous error +error[E0308]: mismatched types + --> $DIR/bad-type-in-vec-push.rs:18:12 + | +LL | x.push(""); + | ---- ^^ expected integer, found `&str` + | | + | arguments to this method are incorrect + | +note: associated function defined here + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. From 020cca8d36cb678e3ddc2ead41364be314d19e93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 23 Jan 2023 14:29:53 +0000 Subject: [PATCH 21/21] review comment: Remove AST AnonTy --- compiler/rustc_ast/src/ast.rs | 3 --- compiler/rustc_ast/src/mut_visit.rs | 2 +- compiler/rustc_ast/src/visit.rs | 2 +- compiler/rustc_ast_lowering/src/lib.rs | 1 - compiler/rustc_ast_pretty/src/pprust/state.rs | 3 --- compiler/rustc_parse/src/parser/ty.rs | 2 +- compiler/rustc_passes/src/hir_stats.rs | 1 - src/tools/rustfmt/src/types.rs | 4 +--- 8 files changed, 4 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 3aad51325dcd6..7de594719ddc4 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2096,9 +2096,6 @@ pub enum TyKind { Err, /// Placeholder for a `va_list`. CVarArgs, - /// Placeholder for "anonymous enums", which don't exist, but keeping their - /// information around lets us produce better diagnostics. - AnonEnum(Vec>), } impl TyKind { diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 894885cf0fea7..77f342d1eb322 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -470,7 +470,7 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { vis.visit_fn_decl(decl); vis.visit_span(decl_span); } - TyKind::AnonEnum(tys) | TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)), + TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)), TyKind::Paren(ty) => vis.visit_ty(ty), TyKind::Path(qself, path) => { vis.visit_qself(qself); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 1ab70f0309c01..feb5187536ffa 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -400,7 +400,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) { walk_list!(visitor, visit_lifetime, opt_lifetime, LifetimeCtxt::Ref); visitor.visit_ty(&mutable_type.ty) } - TyKind::AnonEnum(tys) | TyKind::Tup(tys) => { + TyKind::Tup(tys) => { walk_list!(visitor, visit_ty, tys); } TyKind::BareFn(function_declaration) => { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index a60d02002da33..41d4a5679f1a0 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1235,7 +1235,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let kind = match &t.kind { TyKind::Infer => hir::TyKind::Infer, TyKind::Err => hir::TyKind::Err, - TyKind::AnonEnum(_) => hir::TyKind::Err, TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)), TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)), TyKind::Ref(region, mt) => { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 3f9b96a6158a1..6a8064b0e874e 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1041,9 +1041,6 @@ impl<'a> State<'a> { } self.pclose(); } - ast::TyKind::AnonEnum(elts) => { - self.strsep("|", false, Inconsistent, elts, |s, ty| s.print_type(ty)); - } ast::TyKind::Paren(typ) => { self.popen(); self.print_type(typ); diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 72994df6f9f46..43e6eac438ba5 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -390,7 +390,7 @@ impl<'a> Parser<'a> { .join("\n"), )); err.emit(); - return Ok(self.mk_ty(lo.to(self.prev_token.span), TyKind::AnonEnum(types))); + return Ok(self.mk_ty(lo.to(self.prev_token.span), TyKind::Err)); } if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) } } diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 312bd8839e0d0..b86d2316820ce 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -579,7 +579,6 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { [ Slice, Array, - AnonEnum, Ptr, Ref, BareFn, diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index f065ec680d724..c1991e8d2c808 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -839,9 +839,7 @@ impl Rewrite for ast::Ty { }) } ast::TyKind::CVarArgs => Some("...".to_owned()), - ast::TyKind::AnonEnum(_) | ast::TyKind::Err => { - Some(context.snippet(self.span).to_owned()) - } + ast::TyKind::Err => Some(context.snippet(self.span).to_owned()), ast::TyKind::Typeof(ref anon_const) => rewrite_call( context, "typeof",