diff options
Diffstat (limited to 'compiler')
20 files changed, 372 insertions, 235 deletions
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 627a0ebb4e5..b014fc2dc58 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -51,7 +51,8 @@ use rustc_metadata::creader::MetadataLoader; use rustc_metadata::locator; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_session::config::{ - nightly_options, ErrorOutputType, Input, OutFileName, OutputType, CG_OPTIONS, Z_OPTIONS, + nightly_options, ErrorOutputType, Input, OutFileName, OutputType, UnstableOptions, CG_OPTIONS, + Z_OPTIONS, }; use rustc_session::getopts::{self, Matches}; use rustc_session::lint::{Lint, LintId}; @@ -301,6 +302,8 @@ fn run_compiler( let Some(matches) = handle_options(&default_early_dcx, &args) else { return Ok(()) }; let sopts = config::build_session_options(&mut default_early_dcx, &matches); + // fully initialize ice path static once unstable options are available as context + let ice_file = ice_path_with_config(Some(&sopts.unstable_opts)).clone(); if let Some(ref code) = matches.opt_str("explain") { handle_explain(&default_early_dcx, diagnostics_registry(), code, sopts.color); @@ -315,7 +318,7 @@ fn run_compiler( input: Input::File(PathBuf::new()), output_file: ofile, output_dir: odir, - ice_file: ice_path().clone(), + ice_file, file_loader, locale_resources: DEFAULT_LOCALE_RESOURCES, lint_caps: Default::default(), @@ -1306,25 +1309,43 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 { static ICE_PATH: OnceLock<Option<PathBuf>> = OnceLock::new(); +// This function should only be called from the ICE hook. +// +// The intended behavior is that `run_compiler` will invoke `ice_path_with_config` early in the +// initialization process to properly initialize the ICE_PATH static based on parsed CLI flags. +// +// Subsequent calls to either function will then return the proper ICE path as configured by +// the environment and cli flags fn ice_path() -> &'static Option<PathBuf> { + ice_path_with_config(None) +} + +fn ice_path_with_config(config: Option<&UnstableOptions>) -> &'static Option<PathBuf> { + if ICE_PATH.get().is_some() && config.is_some() && cfg!(debug_assertions) { + tracing::warn!( + "ICE_PATH has already been initialized -- files may be emitted at unintended paths" + ) + } + ICE_PATH.get_or_init(|| { if !rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build() { return None; } - if let Some(s) = std::env::var_os("RUST_BACKTRACE") - && s == "0" - { - return None; - } let mut path = match std::env::var_os("RUSTC_ICE") { Some(s) => { if s == "0" { // Explicitly opting out of writing ICEs to disk. return None; } + if let Some(unstable_opts) = config && unstable_opts.metrics_dir.is_some() { + tracing::warn!("ignoring -Zerror-metrics in favor of RUSTC_ICE for destination of ICE report files"); + } PathBuf::from(s) } - None => std::env::current_dir().unwrap_or_default(), + None => config + .and_then(|unstable_opts| unstable_opts.metrics_dir.to_owned()) + .or_else(|| std::env::current_dir().ok()) + .unwrap_or_default(), }; let now: OffsetDateTime = SystemTime::now().into(); let file_now = now diff --git a/compiler/rustc_error_codes/src/error_codes/E0517.md b/compiler/rustc_error_codes/src/error_codes/E0517.md index ae802245bd1..5354a08bf31 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0517.md +++ b/compiler/rustc_error_codes/src/error_codes/E0517.md @@ -25,14 +25,17 @@ impl Foo { These attributes do not work on typedefs, since typedefs are just aliases. Representations like `#[repr(u8)]`, `#[repr(i64)]` are for selecting the -discriminant size for enums with no data fields on any of the variants, e.g. -`enum Color {Red, Blue, Green}`, effectively setting the size of the enum to -the size of the provided type. Such an enum can be cast to a value of the same -type as well. In short, `#[repr(u8)]` makes the enum behave like an integer -with a constrained set of allowed values. +discriminant size for enums. For enums with no data fields on any of the +variants, e.g. `enum Color {Red, Blue, Green}`, this effectively sets the size +of the enum to the size of the provided type. Such an enum can be cast to a +value of the same type as well. In short, `#[repr(u8)]` makes a field-less enum +behave like an integer with a constrained set of allowed values. -Only field-less enums can be cast to numerical primitives, so this attribute -will not apply to structs. +For a description of how `#[repr(C)]` and representations like `#[repr(u8)]` +affect the layout of enums with data fields, see [RFC 2195][rfc2195]. + +Only field-less enums can be cast to numerical primitives. Representations like +`#[repr(u8)]` will not apply to structs. `#[repr(packed)]` reduces padding to make the struct size smaller. The representation of enums isn't strictly defined in Rust, and this attribute @@ -42,3 +45,5 @@ won't work on enums. types (i.e., `u8`, `i32`, etc) a representation that permits vectorization via SIMD. This doesn't make much sense for enums since they don't consist of a single list of data. + +[rfc2195]: https://github.com/rust-lang/rfcs/blob/master/text/2195-really-tagged-unions.md diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 88ed3128164..b8813ea4125 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -43,19 +43,14 @@ const DEFAULT_COLUMN_WIDTH: usize = 140; /// Describes the way the content of the `rendered` field of the json output is generated #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum HumanReadableErrorType { - Default(ColorConfig), - AnnotateSnippet(ColorConfig), - Short(ColorConfig), + Default, + AnnotateSnippet, + Short, } impl HumanReadableErrorType { - /// Returns a (`short`, `color`) tuple - pub fn unzip(self) -> (bool, ColorConfig) { - match self { - HumanReadableErrorType::Default(cc) => (false, cc), - HumanReadableErrorType::Short(cc) => (true, cc), - HumanReadableErrorType::AnnotateSnippet(cc) => (false, cc), - } + pub fn short(&self) -> bool { + *self == HumanReadableErrorType::Short } } @@ -2595,9 +2590,7 @@ fn num_decimal_digits(num: usize) -> usize { // We replace some characters so the CLI output is always consistent and underlines aligned. // Keep the following list in sync with `rustc_span::char_width`. -// ATTENTION: keep lexicografically sorted so that the binary search will work const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[ - // tidy-alphabetical-start // In terminals without Unicode support the following will be garbled, but in *all* terminals // the underlying codepoint will be as well. We could gate this replacement behind a "unicode // support" gate. @@ -2610,7 +2603,7 @@ const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[ ('\u{0006}', "␆"), ('\u{0007}', "␇"), ('\u{0008}', "␈"), - ('\u{0009}', " "), // We do our own tab replacement + ('\t', " "), // We do our own tab replacement ('\u{000b}', "␋"), ('\u{000c}', "␌"), ('\u{000d}', "␍"), @@ -2643,13 +2636,23 @@ const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[ ('\u{2067}', "�"), ('\u{2068}', "�"), ('\u{2069}', "�"), - // tidy-alphabetical-end ]; fn normalize_whitespace(s: &str) -> String { - // Scan the input string for a character in the ordered table above. If it's present, replace - // it with it's alternative string (it can be more than 1 char!). Otherwise, retain the input - // char. At the end, allocate all chars into a string in one operation. + const { + let mut i = 1; + while i < OUTPUT_REPLACEMENTS.len() { + assert!( + OUTPUT_REPLACEMENTS[i - 1].0 < OUTPUT_REPLACEMENTS[i].0, + "The OUTPUT_REPLACEMENTS array must be sorted (for binary search to work) \ + and must contain no duplicate entries" + ); + i += 1; + } + } + // Scan the input string for a character in the ordered table above. + // If it's present, replace it with its alternative string (it can be more than 1 char!). + // Otherwise, retain the input char. s.chars().fold(String::with_capacity(s.len()), |mut s, c| { match OUTPUT_REPLACEMENTS.binary_search_by_key(&c, |(k, _)| *k) { Ok(i) => s.push_str(OUTPUT_REPLACEMENTS[i].1), diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 42a28bc7890..32e59f9ab03 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -55,6 +55,7 @@ pub struct JsonEmitter { ignored_directories_in_source_blocks: Vec<String>, #[setters(skip)] json_rendered: HumanReadableErrorType, + color_config: ColorConfig, diagnostic_width: Option<usize>, macro_backtrace: bool, track_diagnostics: bool, @@ -68,6 +69,7 @@ impl JsonEmitter { fallback_bundle: LazyFallbackBundle, pretty: bool, json_rendered: HumanReadableErrorType, + color_config: ColorConfig, ) -> JsonEmitter { JsonEmitter { dst: IntoDynSyncSend(dst), @@ -79,6 +81,7 @@ impl JsonEmitter { ui_testing: false, ignored_directories_in_source_blocks: Vec::new(), json_rendered, + color_config, diagnostic_width: None, macro_backtrace: false, track_diagnostics: false, @@ -173,7 +176,7 @@ impl Emitter for JsonEmitter { } fn should_show_explain(&self) -> bool { - !matches!(self.json_rendered, HumanReadableErrorType::Short(_)) + !self.json_rendered.short() } } @@ -353,8 +356,8 @@ impl Diagnostic { let buf = BufWriter::default(); let mut dst: Destination = Box::new(buf.clone()); - let (short, color_config) = je.json_rendered.unzip(); - match color_config { + let short = je.json_rendered.short(); + match je.color_config { ColorConfig::Always | ColorConfig::Auto => dst = Box::new(termcolor::Ansi::new(dst)), ColorConfig::Never => {} } diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index e3549fc3aa5..6af376d7afd 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -50,7 +50,8 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { sm, fallback_bundle, true, // pretty - HumanReadableErrorType::Short(ColorConfig::Never), + HumanReadableErrorType::Short, + ColorConfig::Never, ); let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1)); diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index 6ce9ff18c27..628c6bfeb79 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -3,34 +3,32 @@ use std::borrow::Cow; use rustc_ast::token::{self, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast_pretty::pprust; -use rustc_errors::{Applicability, Diag, DiagMessage}; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage}; use rustc_macros::Subdiagnostic; use rustc_parse::parser::{Parser, Recovery}; +use rustc_session::parse::ParseSess; use rustc_span::source_map::SourceMap; use rustc_span::symbol::Ident; use rustc_span::{ErrorGuaranteed, Span}; use tracing::debug; use super::macro_rules::{parser_from_cx, NoopTracker}; -use crate::base::{DummyResult, ExtCtxt, MacResult}; use crate::expand::{parse_ast_fragment, AstFragmentKind}; use crate::mbe::macro_parser::ParseResult::*; use crate::mbe::macro_parser::{MatcherLoc, NamedParseResult, TtParser}; use crate::mbe::macro_rules::{try_match_macro, Tracker}; -pub(super) fn failed_to_match_macro<'cx>( - cx: &'cx mut ExtCtxt<'_>, +pub(super) fn failed_to_match_macro( + psess: &ParseSess, sp: Span, def_span: Span, name: Ident, arg: TokenStream, lhses: &[Vec<MatcherLoc>], -) -> Box<dyn MacResult + 'cx> { - let psess = &cx.sess.psess; - +) -> (Span, ErrorGuaranteed) { // An error occurred, try the expansion again, tracking the expansion closely for better // diagnostics. - let mut tracker = CollectTrackerAndEmitter::new(cx, sp); + let mut tracker = CollectTrackerAndEmitter::new(psess.dcx(), sp); let try_success_result = try_match_macro(psess, name, &arg, lhses, &mut tracker); @@ -38,7 +36,7 @@ pub(super) fn failed_to_match_macro<'cx>( // Nonterminal parser recovery might turn failed matches into successful ones, // but for that it must have emitted an error already assert!( - tracker.cx.dcx().has_errors().is_some(), + tracker.dcx.has_errors().is_some(), "Macro matching returned a success on the second try" ); } @@ -50,15 +48,15 @@ pub(super) fn failed_to_match_macro<'cx>( let Some(BestFailure { token, msg: label, remaining_matcher, .. }) = tracker.best_failure else { - return DummyResult::any(sp, cx.dcx().span_delayed_bug(sp, "failed to match a macro")); + return (sp, psess.dcx().span_delayed_bug(sp, "failed to match a macro")); }; let span = token.span.substitute_dummy(sp); - let mut err = cx.dcx().struct_span_err(span, parse_failure_msg(&token, None)); + let mut err = psess.dcx().struct_span_err(span, parse_failure_msg(&token, None)); err.span_label(span, label); - if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) { - err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro"); + if !def_span.is_dummy() && !psess.source_map().is_imported(def_span) { + err.span_label(psess.source_map().guess_head_span(def_span), "when calling this macro"); } annotate_doc_comment(&mut err, psess.source_map(), span); @@ -76,7 +74,7 @@ pub(super) fn failed_to_match_macro<'cx>( err.note("captured metavariables except for `:tt`, `:ident` and `:lifetime` cannot be compared to other tokens"); err.note("see <https://doc.rust-lang.org/nightly/reference/macros-by-example.html#forwarding-a-matched-fragment> for more information"); - if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) { + if !def_span.is_dummy() && !psess.source_map().is_imported(def_span) { err.help("try using `:tt` instead in the macro definition"); } } @@ -104,18 +102,17 @@ pub(super) fn failed_to_match_macro<'cx>( } } let guar = err.emit(); - cx.trace_macros_diag(); - DummyResult::any(sp, guar) + (sp, guar) } /// The tracker used for the slow error path that collects useful info for diagnostics. -struct CollectTrackerAndEmitter<'a, 'cx, 'matcher> { - cx: &'a mut ExtCtxt<'cx>, +struct CollectTrackerAndEmitter<'dcx, 'matcher> { + dcx: DiagCtxtHandle<'dcx>, remaining_matcher: Option<&'matcher MatcherLoc>, /// Which arm's failure should we report? (the one furthest along) best_failure: Option<BestFailure>, root_span: Span, - result: Option<Box<dyn MacResult + 'cx>>, + result: Option<(Span, ErrorGuaranteed)>, } struct BestFailure { @@ -131,7 +128,7 @@ impl BestFailure { } } -impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, 'matcher> { +impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'matcher> { type Failure = (Token, u32, &'static str); fn build_failure(tok: Token, position: u32, msg: &'static str) -> Self::Failure { @@ -151,7 +148,7 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, Success(_) => { // Nonterminal parser recovery might turn failed matches into successful ones, // but for that it must have emitted an error already - self.cx.dcx().span_delayed_bug( + self.dcx.span_delayed_bug( self.root_span, "should not collect detailed info for successful macro match", ); @@ -177,10 +174,10 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, } Error(err_sp, msg) => { let span = err_sp.substitute_dummy(self.root_span); - let guar = self.cx.dcx().span_err(span, msg.clone()); - self.result = Some(DummyResult::any(span, guar)); + let guar = self.dcx.span_err(span, msg.clone()); + self.result = Some((span, guar)); } - ErrorReported(guar) => self.result = Some(DummyResult::any(self.root_span, *guar)), + ErrorReported(guar) => self.result = Some((self.root_span, *guar)), } } @@ -193,9 +190,9 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, } } -impl<'a, 'cx> CollectTrackerAndEmitter<'a, 'cx, '_> { - fn new(cx: &'a mut ExtCtxt<'cx>, root_span: Span) -> Self { - Self { cx, remaining_matcher: None, best_failure: None, root_span, result: None } +impl<'dcx> CollectTrackerAndEmitter<'dcx, '_> { + fn new(dcx: DiagCtxtHandle<'dcx>, root_span: Span) -> Self { + Self { dcx, remaining_matcher: None, best_failure: None, root_span, result: None } } } diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 1502177563d..6f177107e70 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -268,7 +268,10 @@ fn expand_macro<'cx>( } Err(CanRetry::Yes) => { // Retry and emit a better error. - diagnostics::failed_to_match_macro(cx, sp, def_span, name, arg, lhses) + let (span, guar) = + diagnostics::failed_to_match_macro(cx.psess(), sp, def_span, name, arg, lhses); + cx.trace_macros_diag(); + DummyResult::any(span, guar) } } } diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index e64f4ebf45d..55f002291f0 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -219,7 +219,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `async-await/async-closures/force-move-due-to-inferred-kind.rs`. // // 2. If the coroutine-closure is forced to be `FnOnce` due to the way it - // uses its upvars, but not *all* upvars would force the closure to `FnOnce`. + // uses its upvars (e.g. it consumes a non-copy value), but not *all* upvars + // would force the closure to `FnOnce`. // See the test: `async-await/async-closures/force-move-due-to-actually-fnonce.rs`. // // This would lead to an impossible to satisfy situation, since `AsyncFnOnce` @@ -227,11 +228,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // we force the inner coroutine to also be `move`. This only matters for // coroutine-closures that are `move` since otherwise they themselves will // be borrowing from the outer environment, so there's no self-borrows occuring. - // - // One *important* note is that we do a call to `process_collected_capture_information` - // to eagerly test whether the coroutine would end up `FnOnce`, but we do this - // *before* capturing all the closure args by-value below, since that would always - // cause the analysis to return `FnOnce`. if let UpvarArgs::Coroutine(..) = args && let hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) = self.tcx.coroutine_kind(closure_def_id).expect("coroutine should have kind") @@ -246,19 +242,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { capture_clause = hir::CaptureBy::Value { move_kw }; } // (2.) The way that the closure uses its upvars means it's `FnOnce`. - else if let (_, ty::ClosureKind::FnOnce, _) = self - .process_collected_capture_information( - capture_clause, - &delegate.capture_information, - ) - { + else if self.coroutine_body_consumes_upvars(closure_def_id, body) { capture_clause = hir::CaptureBy::Value { move_kw }; } } // As noted in `lower_coroutine_body_with_moved_arguments`, we default the capture mode // to `ByRef` for the `async {}` block internal to async fns/closure. This means - // that we would *not* be moving all of the parameters into the async block by default. + // that we would *not* be moving all of the parameters into the async block in all cases. + // For example, when one of the arguments is `Copy`, we turn a consuming use into a copy of + // a reference, so for `async fn x(t: i32) {}`, we'd only take a reference to `t`. // // We force all of these arguments to be captured by move before we do expr use analysis. // @@ -535,6 +528,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Determines whether the body of the coroutine uses its upvars in a way that + /// consumes (i.e. moves) the value, which would force the coroutine to `FnOnce`. + /// In a more detailed comment above, we care whether this happens, since if + /// this happens, we want to force the coroutine to move all of the upvars it + /// would've borrowed from the parent coroutine-closure. + /// + /// This only really makes sense to be called on the child coroutine of a + /// coroutine-closure. + fn coroutine_body_consumes_upvars( + &self, + coroutine_def_id: LocalDefId, + body: &'tcx hir::Body<'tcx>, + ) -> bool { + // This block contains argument capturing details. Since arguments + // aren't upvars, we do not care about them for determining if the + // coroutine body actually consumes its upvars. + let hir::ExprKind::Block(&hir::Block { expr: Some(body), .. }, None) = body.value.kind + else { + bug!(); + }; + // Specifically, we only care about the *real* body of the coroutine. + // We skip out into the drop-temps within the block of the body in order + // to skip over the args of the desugaring. + let hir::ExprKind::DropTemps(body) = body.kind else { + bug!(); + }; + + let mut delegate = InferBorrowKind { + closure_def_id: coroutine_def_id, + capture_information: Default::default(), + fake_reads: Default::default(), + }; + + let _ = euv::ExprUseVisitor::new( + &FnCtxt::new(self, self.tcx.param_env(coroutine_def_id), coroutine_def_id), + &mut delegate, + ) + .consume_expr(body); + + let (_, kind, _) = self.process_collected_capture_information( + hir::CaptureBy::Ref, + &delegate.capture_information, + ); + + matches!(kind, ty::ClosureKind::FnOnce) + } + // Returns a list of `Ty`s for each upvar. fn final_upvar_tys(&self, closure_id: LocalDefId) -> Vec<Ty<'tcx>> { self.typeck_results diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 8c99b1f4447..023352156eb 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -818,6 +818,13 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { }); sess.time("layout_testing", || layout_test::test_layout(tcx)); sess.time("abi_testing", || abi_test::test_abi(tcx)); + if tcx.sess.opts.unstable_opts.validate_mir { + sess.time("ensuring_optimized_MIR_is_computable", || { + tcx.hir().par_body_owners(|def_id| { + tcx.instance_mir(ty::InstanceKind::Item(def_id.into())); + }); + }); + } } /// Runs the type-checking, region checking and other miscellaneous analysis diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index ce3b2f77f21..34f2dca7c42 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -315,7 +315,8 @@ fn test_search_paths_tracking_hash_different_order() { let early_dcx = EarlyDiagCtxt::new(JSON); const JSON: ErrorOutputType = ErrorOutputType::Json { pretty: false, - json_rendered: HumanReadableErrorType::Default(ColorConfig::Never), + json_rendered: HumanReadableErrorType::Default, + color_config: ColorConfig::Never, }; let push = |opts: &mut Options, search_path| { diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 6a9e67f74da..8c27cac1ea8 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -228,9 +228,11 @@ pub fn explain_lint_level_source( err.note_once(format!( "`{flag} {hyphen_case_lint_name}` implied by `{flag} {hyphen_case_flag_val}`" )); - err.help_once(format!( - "to override `{flag} {hyphen_case_flag_val}` add `#[allow({name})]`" - )); + if matches!(orig_level, Level::Warn | Level::Deny) { + err.help_once(format!( + "to override `{flag} {hyphen_case_flag_val}` add `#[allow({name})]`" + )); + } } } LintLevelSource::Node { name: lint_attr_name, span, reason, .. } => { diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index e69d8d84d7d..00837f7cdd8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -458,28 +458,23 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I: )) } - ty::FnDef(..) | ty::FnPtr(..) => { - let bound_sig = self_ty.fn_sig(cx); - let sig = bound_sig.skip_binder(); - let future_trait_def_id = cx.require_lang_item(TraitSolverLangItem::Future); - // `FnDef` and `FnPtr` only implement `AsyncFn*` when their - // return type implements `Future`. - let nested = vec![ - bound_sig - .rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()])) - .upcast(cx), - ]; - let future_output_def_id = cx.require_lang_item(TraitSolverLangItem::FutureOutput); - let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]); - Ok(( - bound_sig.rebind(AsyncCallableRelevantTypes { - tupled_inputs_ty: Ty::new_tup(cx, sig.inputs().as_slice()), - output_coroutine_ty: sig.output(), - coroutine_return_ty: future_output_ty, - }), - nested, - )) + ty::FnDef(def_id, _) => { + let sig = self_ty.fn_sig(cx); + if sig.skip_binder().is_fn_trait_compatible() && !cx.has_target_features(def_id) { + fn_item_to_async_callable(cx, sig) + } else { + Err(NoSolution) + } + } + ty::FnPtr(..) => { + let sig = self_ty.fn_sig(cx); + if sig.skip_binder().is_fn_trait_compatible() { + fn_item_to_async_callable(cx, sig) + } else { + Err(NoSolution) + } } + ty::Closure(_, args) => { let args = args.as_closure(); let bound_sig = args.sig(); @@ -563,6 +558,29 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I: } } +fn fn_item_to_async_callable<I: Interner>( + cx: I, + bound_sig: ty::Binder<I, ty::FnSig<I>>, +) -> Result<(ty::Binder<I, AsyncCallableRelevantTypes<I>>, Vec<I::Predicate>), NoSolution> { + let sig = bound_sig.skip_binder(); + let future_trait_def_id = cx.require_lang_item(TraitSolverLangItem::Future); + // `FnDef` and `FnPtr` only implement `AsyncFn*` when their + // return type implements `Future`. + let nested = vec![ + bound_sig.rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()])).upcast(cx), + ]; + let future_output_def_id = cx.require_lang_item(TraitSolverLangItem::FutureOutput); + let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]); + Ok(( + bound_sig.rebind(AsyncCallableRelevantTypes { + tupled_inputs_ty: Ty::new_tup(cx, sig.inputs().as_slice()), + output_coroutine_ty: sig.output(), + coroutine_return_ty: future_output_ty, + }), + nested, + )) +} + /// Given a coroutine-closure, project to its returned coroutine when we are *certain* /// that the closure's kind is compatible with the goal. fn coroutine_closure_to_certain_coroutine<I: Interner>( diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index cf7d724ab63..c8d4c190113 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -125,6 +125,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::inline, ..] => self.check_inline(hir_id, attr, span, target), [sym::coverage, ..] => self.check_coverage(attr, span, target), [sym::optimize, ..] => self.check_optimize(hir_id, attr, target), + [sym::no_sanitize, ..] => self.check_no_sanitize(hir_id, attr, span, target), [sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target), [sym::marker, ..] => self.check_marker(hir_id, attr, span, target), [sym::target_feature, ..] => { @@ -256,7 +257,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::may_dangle // FIXME(dropck_eyepatch) | sym::pointee // FIXME(derive_smart_pointer) | sym::linkage // FIXME(linkage) - | sym::no_sanitize // FIXME(no_sanitize) | sym::omit_gdb_pretty_printer_section // FIXME(omit_gdb_pretty_printer_section) | sym::used // handled elsewhere to restrict to static items | sym::repr // handled elsewhere to restrict to type decls items @@ -451,6 +451,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + /// Checks that `#[no_sanitize(..)]` is applied to a function or method. + fn check_no_sanitize(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { + self.check_applied_to_fn_or_method(hir_id, attr, span, target) + } + fn check_generic_attr( &self, hir_id: HirId, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 4d28c0a3ef1..a5bd2fdd8f3 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -2668,119 +2668,128 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { let mut function_type_rib = Rib::new(kind); let mut function_value_rib = Rib::new(kind); let mut function_lifetime_rib = LifetimeRib::new(lifetime_kind); - let mut seen_bindings = FxHashMap::default(); - // Store all seen lifetimes names from outer scopes. - let mut seen_lifetimes = FxHashSet::default(); - - // We also can't shadow bindings from associated parent items. - for ns in [ValueNS, TypeNS] { - for parent_rib in self.ribs[ns].iter().rev() { - seen_bindings.extend(parent_rib.bindings.keys().map(|ident| (*ident, ident.span))); - - // Break at mod level, to account for nested items which are - // allowed to shadow generic param names. - if matches!(parent_rib.kind, RibKind::Module(..)) { - break; - } - } - } - // Forbid shadowing lifetime bindings - for rib in self.lifetime_ribs.iter().rev() { - seen_lifetimes.extend(rib.bindings.iter().map(|(ident, _)| *ident)); - if let LifetimeRibKind::Item = rib.kind { - break; + // Only check for shadowed bindings if we're declaring new params. + if !params.is_empty() { + let mut seen_bindings = FxHashMap::default(); + // Store all seen lifetimes names from outer scopes. + let mut seen_lifetimes = FxHashSet::default(); + + // We also can't shadow bindings from associated parent items. + for ns in [ValueNS, TypeNS] { + for parent_rib in self.ribs[ns].iter().rev() { + seen_bindings + .extend(parent_rib.bindings.keys().map(|ident| (*ident, ident.span))); + + // Break at mod level, to account for nested items which are + // allowed to shadow generic param names. + if matches!(parent_rib.kind, RibKind::Module(..)) { + break; + } + } } - } - - for param in params { - let ident = param.ident.normalize_to_macros_2_0(); - debug!("with_generic_param_rib: {}", param.id); - if let GenericParamKind::Lifetime = param.kind - && let Some(&original) = seen_lifetimes.get(&ident) - { - diagnostics::signal_lifetime_shadowing(self.r.tcx.sess, original, param.ident); - // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_param(param.id, LifetimeRes::Error); - continue; + // Forbid shadowing lifetime bindings + for rib in self.lifetime_ribs.iter().rev() { + seen_lifetimes.extend(rib.bindings.iter().map(|(ident, _)| *ident)); + if let LifetimeRibKind::Item = rib.kind { + break; + } } - match seen_bindings.entry(ident) { - Entry::Occupied(entry) => { - let span = *entry.get(); - let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, span); - self.report_error(param.ident.span, err); - let rib = match param.kind { - GenericParamKind::Lifetime => { - // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_param(param.id, LifetimeRes::Error); - continue; - } - GenericParamKind::Type { .. } => &mut function_type_rib, - GenericParamKind::Const { .. } => &mut function_value_rib, - }; + for param in params { + let ident = param.ident.normalize_to_macros_2_0(); + debug!("with_generic_param_rib: {}", param.id); - // Taint the resolution in case of errors to prevent follow up errors in typeck - self.r.record_partial_res(param.id, PartialRes::new(Res::Err)); - rib.bindings.insert(ident, Res::Err); + if let GenericParamKind::Lifetime = param.kind + && let Some(&original) = seen_lifetimes.get(&ident) + { + diagnostics::signal_lifetime_shadowing(self.r.tcx.sess, original, param.ident); + // Record lifetime res, so lowering knows there is something fishy. + self.record_lifetime_param(param.id, LifetimeRes::Error); continue; } - Entry::Vacant(entry) => { - entry.insert(param.ident.span); - } - } - if param.ident.name == kw::UnderscoreLifetime { - self.r - .dcx() - .emit_err(errors::UnderscoreLifetimeIsReserved { span: param.ident.span }); - // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_param(param.id, LifetimeRes::Error); - continue; - } + match seen_bindings.entry(ident) { + Entry::Occupied(entry) => { + let span = *entry.get(); + let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, span); + self.report_error(param.ident.span, err); + let rib = match param.kind { + GenericParamKind::Lifetime => { + // Record lifetime res, so lowering knows there is something fishy. + self.record_lifetime_param(param.id, LifetimeRes::Error); + continue; + } + GenericParamKind::Type { .. } => &mut function_type_rib, + GenericParamKind::Const { .. } => &mut function_value_rib, + }; - if param.ident.name == kw::StaticLifetime { - self.r.dcx().emit_err(errors::StaticLifetimeIsReserved { - span: param.ident.span, - lifetime: param.ident, - }); - // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_param(param.id, LifetimeRes::Error); - continue; - } + // Taint the resolution in case of errors to prevent follow up errors in typeck + self.r.record_partial_res(param.id, PartialRes::new(Res::Err)); + rib.bindings.insert(ident, Res::Err); + continue; + } + Entry::Vacant(entry) => { + entry.insert(param.ident.span); + } + } - let def_id = self.r.local_def_id(param.id); + if param.ident.name == kw::UnderscoreLifetime { + self.r + .dcx() + .emit_err(errors::UnderscoreLifetimeIsReserved { span: param.ident.span }); + // Record lifetime res, so lowering knows there is something fishy. + self.record_lifetime_param(param.id, LifetimeRes::Error); + continue; + } - // Plain insert (no renaming). - let (rib, def_kind) = match param.kind { - GenericParamKind::Type { .. } => (&mut function_type_rib, DefKind::TyParam), - GenericParamKind::Const { .. } => (&mut function_value_rib, DefKind::ConstParam), - GenericParamKind::Lifetime => { - let res = LifetimeRes::Param { param: def_id, binder }; - self.record_lifetime_param(param.id, res); - function_lifetime_rib.bindings.insert(ident, (param.id, res)); + if param.ident.name == kw::StaticLifetime { + self.r.dcx().emit_err(errors::StaticLifetimeIsReserved { + span: param.ident.span, + lifetime: param.ident, + }); + // Record lifetime res, so lowering knows there is something fishy. + self.record_lifetime_param(param.id, LifetimeRes::Error); continue; } - }; - let res = match kind { - RibKind::Item(..) | RibKind::AssocItem => Res::Def(def_kind, def_id.to_def_id()), - RibKind::Normal => { - // FIXME(non_lifetime_binders): Stop special-casing - // const params to error out here. - if self.r.tcx.features().non_lifetime_binders - && matches!(param.kind, GenericParamKind::Type { .. }) - { + let def_id = self.r.local_def_id(param.id); + + // Plain insert (no renaming). + let (rib, def_kind) = match param.kind { + GenericParamKind::Type { .. } => (&mut function_type_rib, DefKind::TyParam), + GenericParamKind::Const { .. } => { + (&mut function_value_rib, DefKind::ConstParam) + } + GenericParamKind::Lifetime => { + let res = LifetimeRes::Param { param: def_id, binder }; + self.record_lifetime_param(param.id, res); + function_lifetime_rib.bindings.insert(ident, (param.id, res)); + continue; + } + }; + + let res = match kind { + RibKind::Item(..) | RibKind::AssocItem => { Res::Def(def_kind, def_id.to_def_id()) - } else { - Res::Err } - } - _ => span_bug!(param.ident.span, "Unexpected rib kind {:?}", kind), - }; - self.r.record_partial_res(param.id, PartialRes::new(res)); - rib.bindings.insert(ident, res); + RibKind::Normal => { + // FIXME(non_lifetime_binders): Stop special-casing + // const params to error out here. + if self.r.tcx.features().non_lifetime_binders + && matches!(param.kind, GenericParamKind::Type { .. }) + { + Res::Def(def_kind, def_id.to_def_id()) + } else { + Res::Err + } + } + _ => span_bug!(param.ident.span, "Unexpected rib kind {:?}", kind), + }; + self.r.record_partial_res(param.id, PartialRes::new(res)); + rib.bindings.insert(ident, res); + } } self.lifetime_ribs.push(function_lifetime_rib); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 023e428dc1b..89ac839651f 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1129,9 +1129,6 @@ pub struct Resolver<'a, 'tcx> { /// Also includes of list of each fields visibility struct_constructors: LocalDefIdMap<(Res, ty::Visibility<DefId>, Vec<ty::Visibility<DefId>>)>, - /// Features declared for this crate. - declared_features: FxHashSet<Symbol>, - lint_buffer: LintBuffer, next_node_id: NodeId, @@ -1402,7 +1399,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let registered_tools = tcx.registered_tools(()); - let features = tcx.features(); let pub_vis = ty::Visibility::<DefId>::Public; let edition = tcx.sess.edition(); @@ -1506,7 +1502,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { multi_segment_macro_resolutions: Default::default(), builtin_attrs: Default::default(), containers_deriving_copy: Default::default(), - declared_features: features.declared_features.clone(), lint_buffer: LintBuffer::default(), next_node_id: CRATE_NODE_ID, node_id_to_def_id, diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index a6301a367a4..da7278175e9 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -1001,7 +1001,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let feature = stability.feature; let is_allowed = |feature| { - self.declared_features.contains(&feature) || span.allows_unstable(feature) + self.tcx.features().declared_features.contains(&feature) + || span.allows_unstable(feature) }; let allowed_by_implication = implied_by.is_some_and(|feature| is_allowed(feature)); if !is_allowed(feature) && !allowed_by_implication { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 20c14a98502..95d171409d8 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -602,7 +602,7 @@ impl OutputType { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ErrorOutputType { /// Output meant for the consumption of humans. - HumanReadable(HumanReadableErrorType), + HumanReadable(HumanReadableErrorType, ColorConfig), /// Output that's consumed by other tools such as `rustfix` or the `RLS`. Json { /// Render the JSON in a human readable way (with indents and newlines). @@ -610,12 +610,13 @@ pub enum ErrorOutputType { /// The JSON output includes a `rendered` field that includes the rendered /// human output. json_rendered: HumanReadableErrorType, + color_config: ColorConfig, }, } impl Default for ErrorOutputType { fn default() -> Self { - Self::HumanReadable(HumanReadableErrorType::Default(ColorConfig::Auto)) + Self::HumanReadable(HumanReadableErrorType::Default, ColorConfig::Auto) } } @@ -1631,6 +1632,7 @@ pub fn parse_color(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Col /// Possible json config files pub struct JsonConfig { pub json_rendered: HumanReadableErrorType, + pub json_color: ColorConfig, json_artifact_notifications: bool, pub json_unused_externs: JsonUnusedExterns, json_future_incompat: bool, @@ -1668,8 +1670,7 @@ impl JsonUnusedExterns { /// The first value returned is how to render JSON diagnostics, and the second /// is whether or not artifact notifications are enabled. pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> JsonConfig { - let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType = - HumanReadableErrorType::Default; + let mut json_rendered = HumanReadableErrorType::Default; let mut json_color = ColorConfig::Never; let mut json_artifact_notifications = false; let mut json_unused_externs = JsonUnusedExterns::No; @@ -1696,7 +1697,8 @@ pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Json } JsonConfig { - json_rendered: json_rendered(json_color), + json_rendered, + json_color, json_artifact_notifications, json_unused_externs, json_future_incompat, @@ -1708,6 +1710,7 @@ pub fn parse_error_format( early_dcx: &mut EarlyDiagCtxt, matches: &getopts::Matches, color: ColorConfig, + json_color: ColorConfig, json_rendered: HumanReadableErrorType, ) -> ErrorOutputType { // We need the `opts_present` check because the driver will send us Matches @@ -1717,18 +1720,22 @@ pub fn parse_error_format( let error_format = if matches.opts_present(&["error-format".to_owned()]) { match matches.opt_str("error-format").as_deref() { None | Some("human") => { - ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) + ErrorOutputType::HumanReadable(HumanReadableErrorType::Default, color) } Some("human-annotate-rs") => { - ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(color)) + ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet, color) } - Some("json") => ErrorOutputType::Json { pretty: false, json_rendered }, - Some("pretty-json") => ErrorOutputType::Json { pretty: true, json_rendered }, - Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)), - + Some("json") => { + ErrorOutputType::Json { pretty: false, json_rendered, color_config: json_color } + } + Some("pretty-json") => { + ErrorOutputType::Json { pretty: true, json_rendered, color_config: json_color } + } + Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short, color), Some(arg) => { early_dcx.abort_if_error_and_set_error_format(ErrorOutputType::HumanReadable( - HumanReadableErrorType::Default(color), + HumanReadableErrorType::Default, + color, )); early_dcx.early_fatal(format!( "argument for `--error-format` must be `human`, `json` or \ @@ -1737,7 +1744,7 @@ pub fn parse_error_format( } } } else { - ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) + ErrorOutputType::HumanReadable(HumanReadableErrorType::Default, color) }; match error_format { @@ -1791,7 +1798,7 @@ fn check_error_format_stability( if let ErrorOutputType::Json { pretty: true, .. } = error_format { early_dcx.early_fatal("`--error-format=pretty-json` is unstable"); } - if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) = + if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet, _) = error_format { early_dcx.early_fatal("`--error-format=human-annotate-rs` is unstable"); @@ -2392,12 +2399,13 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let JsonConfig { json_rendered, + json_color, json_artifact_notifications, json_unused_externs, json_future_incompat, } = parse_json(early_dcx, matches); - let error_format = parse_error_format(early_dcx, matches, color, json_rendered); + let error_format = parse_error_format(early_dcx, matches, color, json_color, json_rendered); early_dcx.abort_if_error_and_set_error_format(error_format); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index bf54aae1cfe..4b87f5d62b2 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1827,6 +1827,8 @@ options! { the same values as the target option of the same name"), meta_stats: bool = (false, parse_bool, [UNTRACKED], "gather metadata statistics (default: no)"), + metrics_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED], + "stores metrics about the errors being emitted by rustc to disk"), mir_emit_retag: bool = (false, parse_bool, [TRACKED], "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \ (default: no)"), diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index be67baf57f6..672dddf871e 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -950,10 +950,10 @@ fn default_emitter( t => t, }; match sopts.error_format { - config::ErrorOutputType::HumanReadable(kind) => { - let (short, color_config) = kind.unzip(); + config::ErrorOutputType::HumanReadable(kind, color_config) => { + let short = kind.short(); - if let HumanReadableErrorType::AnnotateSnippet(_) = kind { + if let HumanReadableErrorType::AnnotateSnippet = kind { let emitter = AnnotateSnippetEmitter::new( Some(source_map), bundle, @@ -978,13 +978,14 @@ fn default_emitter( Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing)) } } - config::ErrorOutputType::Json { pretty, json_rendered } => Box::new( + config::ErrorOutputType::Json { pretty, json_rendered, color_config } => Box::new( JsonEmitter::new( Box::new(io::BufWriter::new(io::stderr())), source_map, fallback_bundle, pretty, json_rendered, + color_config, ) .registry(Some(registry)) .fluent_bundle(bundle) @@ -1425,20 +1426,23 @@ fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> { let fallback_bundle = fallback_fluent_bundle(vec![rustc_errors::DEFAULT_LOCALE_RESOURCE], false); let emitter: Box<DynEmitter> = match output { - config::ErrorOutputType::HumanReadable(kind) => { - let (short, color_config) = kind.unzip(); + config::ErrorOutputType::HumanReadable(kind, color_config) => { + let short = kind.short(); Box::new( HumanEmitter::new(stderr_destination(color_config), fallback_bundle) .short_message(short), ) } - config::ErrorOutputType::Json { pretty, json_rendered } => Box::new(JsonEmitter::new( - Box::new(io::BufWriter::new(io::stderr())), - Lrc::new(SourceMap::new(FilePathMapping::empty())), - fallback_bundle, - pretty, - json_rendered, - )), + config::ErrorOutputType::Json { pretty, json_rendered, color_config } => { + Box::new(JsonEmitter::new( + Box::new(io::BufWriter::new(io::stderr())), + Lrc::new(SourceMap::new(FilePathMapping::empty())), + fallback_bundle, + pretty, + json_rendered, + color_config, + )) + } }; emitter } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 2085d3da443..9de62031311 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -467,8 +467,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } candidates.vec.push(AsyncClosureCandidate); } - ty::FnDef(..) | ty::FnPtr(..) => { - candidates.vec.push(AsyncClosureCandidate); + // Provide an impl, but only for suitable `fn` pointers. + ty::FnPtr(sig) => { + if sig.is_fn_trait_compatible() { + candidates.vec.push(AsyncClosureCandidate); + } + } + // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396). + ty::FnDef(def_id, _) => { + let tcx = self.tcx(); + if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible() + && tcx.codegen_fn_attrs(def_id).target_features.is_empty() + { + candidates.vec.push(AsyncClosureCandidate); + } } _ => {} } |
