From 6ccaea19895cc8c0861f3a8707f888a15f8be3a1 Mon Sep 17 00:00:00 2001 From: Andrew Zhogin Date: Fri, 7 Mar 2025 05:34:12 +0700 Subject: [PATCH 01/14] Target modifiers fix for bool flags without value --- compiler/rustc_metadata/messages.ftl | 17 ++++- compiler/rustc_metadata/src/creader.rs | 69 ++++++++++++++----- compiler/rustc_metadata/src/errors.rs | 36 +++++++++- compiler/rustc_session/src/options.rs | 52 +++++++------- .../auxiliary/enabled_reg_struct_return.rs | 7 ++ .../defaults_check.error.stderr | 4 +- .../no_value_bool.error.stderr | 13 ++++ .../no_value_bool.error_explicit.stderr | 13 ++++ tests/ui/target_modifiers/no_value_bool.rs | 22 ++++++ 9 files changed, 184 insertions(+), 49 deletions(-) create mode 100644 tests/ui/target_modifiers/auxiliary/enabled_reg_struct_return.rs create mode 100644 tests/ui/target_modifiers/no_value_bool.error.stderr create mode 100644 tests/ui/target_modifiers/no_value_bool.error_explicit.stderr create mode 100644 tests/ui/target_modifiers/no_value_bool.rs diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index 20f66fae5c0..9adbcabcf45 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -118,12 +118,23 @@ metadata_incompatible_rustc = metadata_incompatible_target_modifiers = mixing `{$flag_name_prefixed}` will cause an ABI mismatch in crate `{$local_crate}` - .note = `{$flag_name_prefixed}={$flag_local_value}` in this crate is incompatible with `{$flag_name_prefixed}={$flag_extern_value}` in dependency `{$extern_crate}` + .note = `{$flag_name_prefixed}={$local_value}` in this crate is incompatible with `{$flag_name_prefixed}={$extern_value}` in dependency `{$extern_crate}` .help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely - metadata_incompatible_target_modifiers_help_allow = if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch={$flag_name}` to silence this error -metadata_incompatible_target_modifiers_help_fix = set `{$flag_name_prefixed}={$flag_extern_value}` in this crate or `{$flag_name_prefixed}={$flag_local_value}` in `{$extern_crate}` +metadata_incompatible_target_modifiers_help_fix = set `{$flag_name_prefixed}={$extern_value}` in this crate or `{$flag_name_prefixed}={$local_value}` in `{$extern_crate}` +metadata_incompatible_target_modifiers_help_fix_l_missed = set `{$flag_name_prefixed}={$extern_value}` in this crate or unset `{$flag_name_prefixed}` in `{$extern_crate}` + +metadata_incompatible_target_modifiers_help_fix_r_missed = unset `{$flag_name_prefixed}` in this crate or set `{$flag_name_prefixed}={$local_value}` in `{$extern_crate}` + +metadata_incompatible_target_modifiers_l_missed = + mixing `{$flag_name_prefixed}` will cause an ABI mismatch in crate `{$local_crate}` + .note = unset `{$flag_name_prefixed}` in this crate is incompatible with `{$flag_name_prefixed}={$extern_value}` in dependency `{$extern_crate}` + .help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely +metadata_incompatible_target_modifiers_r_missed = + mixing `{$flag_name_prefixed}` will cause an ABI mismatch in crate `{$local_crate}` + .note = `{$flag_name_prefixed}={$local_value}` in this crate is incompatible with unset `{$flag_name_prefixed}` in dependency `{$extern_crate}` + .help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely metadata_incompatible_wasm_link = `wasm_import_module` is incompatible with other arguments in `#[link]` attributes diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 12503ffd1a6..b7f13e0afdc 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -358,30 +358,58 @@ impl CStore { ) { let span = krate.spans.inner_span.shrink_to_lo(); let allowed_flag_mismatches = &tcx.sess.opts.cg.unsafe_allow_abi_mismatch; - let name = tcx.crate_name(LOCAL_CRATE); + let local_crate = tcx.crate_name(LOCAL_CRATE); let tmod_extender = |tmod: &TargetModifier| (tmod.extend(), tmod.clone()); let report_diff = |prefix: &String, opt_name: &String, - flag_local_value: &String, - flag_extern_value: &String| { + flag_local_value: Option<&String>, + flag_extern_value: Option<&String>| { if allowed_flag_mismatches.contains(&opt_name) { return; } - tcx.dcx().emit_err(errors::IncompatibleTargetModifiers { - span, - extern_crate: data.name(), - local_crate: name, - flag_name: opt_name.clone(), - flag_name_prefixed: format!("-{}{}", prefix, opt_name), - flag_local_value: flag_local_value.to_string(), - flag_extern_value: flag_extern_value.to_string(), - }); + let extern_crate = data.name(); + let flag_name = opt_name.clone(); + let flag_name_prefixed = format!("-{}{}", prefix, opt_name); + + match (flag_local_value, flag_extern_value) { + (Some(local_value), Some(extern_value)) => { + tcx.dcx().emit_err(errors::IncompatibleTargetModifiers { + span, + extern_crate, + local_crate, + flag_name, + flag_name_prefixed, + local_value: local_value.to_string(), + extern_value: extern_value.to_string(), + }) + } + (None, Some(extern_value)) => { + tcx.dcx().emit_err(errors::IncompatibleTargetModifiersLMissed { + span, + extern_crate, + local_crate, + flag_name, + flag_name_prefixed, + extern_value: extern_value.to_string(), + }) + } + (Some(local_value), None) => { + tcx.dcx().emit_err(errors::IncompatibleTargetModifiersRMissed { + span, + extern_crate, + local_crate, + flag_name, + flag_name_prefixed, + local_value: local_value.to_string(), + }) + } + (None, None) => panic!("Incorrect target modifiers report_diff(None, None)"), + }; }; let mut it1 = mods.iter().map(tmod_extender); let mut it2 = dep_mods.iter().map(tmod_extender); let mut left_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None; let mut right_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None; - let no_val = "*".to_string(); loop { left_name_val = left_name_val.or_else(|| it1.next()); right_name_val = right_name_val.or_else(|| it2.next()); @@ -389,26 +417,31 @@ impl CStore { (Some(l), Some(r)) => match l.1.opt.cmp(&r.1.opt) { cmp::Ordering::Equal => { if l.0.tech_value != r.0.tech_value { - report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &r.1.value_name); + report_diff( + &l.0.prefix, + &l.0.name, + Some(&l.1.value_name), + Some(&r.1.value_name), + ); } left_name_val = None; right_name_val = None; } cmp::Ordering::Greater => { - report_diff(&r.0.prefix, &r.0.name, &no_val, &r.1.value_name); + report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name)); right_name_val = None; } cmp::Ordering::Less => { - report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &no_val); + report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None); left_name_val = None; } }, (Some(l), None) => { - report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &no_val); + report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None); left_name_val = None; } (None, Some(r)) => { - report_diff(&r.0.prefix, &r.0.name, &no_val, &r.1.value_name); + report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name)); right_name_val = None; } (None, None) => break, diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index 2ad6389c0b4..0c54628598c 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -759,8 +759,40 @@ pub struct IncompatibleTargetModifiers { pub local_crate: Symbol, pub flag_name: String, pub flag_name_prefixed: String, - pub flag_local_value: String, - pub flag_extern_value: String, + pub local_value: String, + pub extern_value: String, +} + +#[derive(Diagnostic)] +#[diag(metadata_incompatible_target_modifiers_l_missed)] +#[help] +#[note] +#[help(metadata_incompatible_target_modifiers_help_fix_l_missed)] +#[help(metadata_incompatible_target_modifiers_help_allow)] +pub struct IncompatibleTargetModifiersLMissed { + #[primary_span] + pub span: Span, + pub extern_crate: Symbol, + pub local_crate: Symbol, + pub flag_name: String, + pub flag_name_prefixed: String, + pub extern_value: String, +} + +#[derive(Diagnostic)] +#[diag(metadata_incompatible_target_modifiers_r_missed)] +#[help] +#[note] +#[help(metadata_incompatible_target_modifiers_help_fix_r_missed)] +#[help(metadata_incompatible_target_modifiers_help_allow)] +pub struct IncompatibleTargetModifiersRMissed { + #[primary_span] + pub span: Span, + pub extern_crate: Symbol, + pub local_crate: Symbol, + pub flag_name: String, + pub flag_name_prefixed: String, + pub local_value: String, } #[derive(Diagnostic)] diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 8977365ee73..599b4fa9109 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -94,45 +94,49 @@ fn tmod_push_impl( tmod_vals: &BTreeMap, tmods: &mut Vec, ) { - tmods.push(TargetModifier { opt, value_name: tmod_vals.get(&opt).cloned().unwrap_or_default() }) + if let Some(v) = tmod_vals.get(&opt) { + tmods.push(TargetModifier { opt, value_name: v.clone() }) + } } macro_rules! tmod_push { - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $mods:expr, $tmod_vals:expr) => { - tmod_push_impl( - OptionsTargetModifiers::$struct_name($tmod_enum_name::$opt_name), - $tmod_vals, - $mods, - ); + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr) => { + if *$opt_expr != $init { + tmod_push_impl( + OptionsTargetModifiers::$struct_name($tmod_enum_name::$opt_name), + $tmod_vals, + $mods, + ); + } }; } macro_rules! gather_tmods { - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, [SUBSTRUCT], [TARGET_MODIFIER]) => { compile_error!("SUBSTRUCT can't be target modifier"); }; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, [UNTRACKED], [TARGET_MODIFIER]) => { - tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals) + tmod_push!($struct_name, $tmod_enum_name, $opt_name, $opt_expr, $init, $mods, $tmod_vals) }; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, [TRACKED], [TARGET_MODIFIER]) => { - tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals) + tmod_push!($struct_name, $tmod_enum_name, $opt_name, $opt_expr, $init, $mods, $tmod_vals) }; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, [TRACKED_NO_CRATE_HASH], [TARGET_MODIFIER]) => { - tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals) + tmod_push!($struct_name, $tmod_enum_name, $opt_name, $opt_expr, $init, $mods, $tmod_vals) }; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, [SUBSTRUCT], []) => { $opt_expr.gather_target_modifiers($mods, $tmod_vals); }; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, [UNTRACKED], []) => {{}}; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, [TRACKED], []) => {{}}; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, [TRACKED_NO_CRATE_HASH], []) => {{}}; } @@ -474,7 +478,8 @@ macro_rules! tmod_enum { $($pout)* Self::$opt => { let mut parsed : $t = Default::default(); - parse::$parse(&mut parsed, Some($puser_value)); + let val = if $puser_value.is_empty() { None } else { Some($puser_value) }; + parse::$parse(&mut parsed, val); ExtendedTargetModifierInfo { prefix: $prefix.to_string(), name: stringify!($opt).to_string().replace('_', "-"), @@ -569,7 +574,7 @@ macro_rules! options { _tmod_vals: &BTreeMap, ) { $({ - gather_tmods!($struct_name, $tmod_enum_name, $opt, &self.$opt, _mods, _tmod_vals, + gather_tmods!($struct_name, $tmod_enum_name, $opt, &self.$opt, $init, _mods, _tmod_vals, [$dep_tracking_marker], [$($tmod),*]); })* } @@ -681,10 +686,9 @@ fn build_options( ), } } - if let Some(tmod) = *tmod - && let Some(value) = value - { - target_modifiers.insert(tmod, value.to_string()); + if let Some(tmod) = *tmod { + let v = value.map_or(String::new(), ToOwned::to_owned); + target_modifiers.insert(tmod, v); } } None => early_dcx.early_fatal(format!("unknown {outputname} option: `{key}`")), diff --git a/tests/ui/target_modifiers/auxiliary/enabled_reg_struct_return.rs b/tests/ui/target_modifiers/auxiliary/enabled_reg_struct_return.rs new file mode 100644 index 00000000000..4bda4ba24c5 --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/enabled_reg_struct_return.rs @@ -0,0 +1,7 @@ +//@ no-prefer-dynamic +//@ compile-flags: --target i686-unknown-linux-gnu -Zreg-struct-return=true +//@ needs-llvm-components: x86 + +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] diff --git a/tests/ui/target_modifiers/defaults_check.error.stderr b/tests/ui/target_modifiers/defaults_check.error.stderr index 4833fe90677..936fbbc94d6 100644 --- a/tests/ui/target_modifiers/defaults_check.error.stderr +++ b/tests/ui/target_modifiers/defaults_check.error.stderr @@ -5,8 +5,8 @@ LL | #![feature(no_core)] | ^ | = help: the `-Zreg-struct-return` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely - = note: `-Zreg-struct-return=true` in this crate is incompatible with `-Zreg-struct-return=` in dependency `default_reg_struct_return` - = help: set `-Zreg-struct-return=` in this crate or `-Zreg-struct-return=true` in `default_reg_struct_return` + = note: `-Zreg-struct-return=true` in this crate is incompatible with unset `-Zreg-struct-return` in dependency `default_reg_struct_return` + = help: unset `-Zreg-struct-return` in this crate or set `-Zreg-struct-return=true` in `default_reg_struct_return` = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=reg-struct-return` to silence this error error: aborting due to 1 previous error diff --git a/tests/ui/target_modifiers/no_value_bool.error.stderr b/tests/ui/target_modifiers/no_value_bool.error.stderr new file mode 100644 index 00000000000..0484960dc62 --- /dev/null +++ b/tests/ui/target_modifiers/no_value_bool.error.stderr @@ -0,0 +1,13 @@ +error: mixing `-Zreg-struct-return` will cause an ABI mismatch in crate `no_value_bool` + --> $DIR/no_value_bool.rs:16:1 + | +LL | #![feature(no_core)] + | ^ + | + = help: the `-Zreg-struct-return` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: unset `-Zreg-struct-return` in this crate is incompatible with `-Zreg-struct-return=true` in dependency `enabled_reg_struct_return` + = help: set `-Zreg-struct-return=true` in this crate or unset `-Zreg-struct-return` in `enabled_reg_struct_return` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=reg-struct-return` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/no_value_bool.error_explicit.stderr b/tests/ui/target_modifiers/no_value_bool.error_explicit.stderr new file mode 100644 index 00000000000..0484960dc62 --- /dev/null +++ b/tests/ui/target_modifiers/no_value_bool.error_explicit.stderr @@ -0,0 +1,13 @@ +error: mixing `-Zreg-struct-return` will cause an ABI mismatch in crate `no_value_bool` + --> $DIR/no_value_bool.rs:16:1 + | +LL | #![feature(no_core)] + | ^ + | + = help: the `-Zreg-struct-return` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: unset `-Zreg-struct-return` in this crate is incompatible with `-Zreg-struct-return=true` in dependency `enabled_reg_struct_return` + = help: set `-Zreg-struct-return=true` in this crate or unset `-Zreg-struct-return` in `enabled_reg_struct_return` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=reg-struct-return` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/no_value_bool.rs b/tests/ui/target_modifiers/no_value_bool.rs new file mode 100644 index 00000000000..ceba40afa89 --- /dev/null +++ b/tests/ui/target_modifiers/no_value_bool.rs @@ -0,0 +1,22 @@ +// Tests that bool target modifier value (true) in dependency crate is ok linked +// with the -Zflag specified without value (-Zflag=true is consistent with -Zflag) + +//@ aux-build:enabled_reg_struct_return.rs +//@ compile-flags: --target i686-unknown-linux-gnu -Cpanic=abort +//@ needs-llvm-components: x86 + +//@ revisions: ok ok_explicit error error_explicit +//@[ok] compile-flags: -Zreg-struct-return +//@[ok_explicit] compile-flags: -Zreg-struct-return=true +//@[error] compile-flags: +//@[error_explicit] compile-flags: -Zreg-struct-return=false +//@[ok] check-pass +//@[ok_explicit] check-pass + +#![feature(no_core)] +//[error]~^ ERROR mixing `-Zreg-struct-return` will cause an ABI mismatch in crate `no_value_bool` +//[error_explicit]~^^ ERROR mixing `-Zreg-struct-return` will cause an ABI mismatch in crate `no_value_bool` +#![crate_type = "rlib"] +#![no_core] + +extern crate enabled_reg_struct_return; From 8cab8e07bc94fb2fea8e1421d2d2e6de6398b69b Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Fri, 21 Mar 2025 17:34:45 -0700 Subject: [PATCH 02/14] Don't produce debug information for compiler-introduced-vars when desugaring assignments. An assignment such as (a, b) = (b, c); desugars to the HIR { let (lhs, lhs) = (b, c); a = lhs; b = lhs; }; The repeated `lhs` leads to multiple Locals assigned to the same DILocalVariable. Rather than attempting to fix that, get rid of the debug info for these bindings that don't even exist in the program to begin with. Fixes #138198 --- .../src/builder/matches/mod.rs | 58 ++++++++++++++----- tests/codegen/assign-desugar-debuginfo.rs | 18 ++++++ 2 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 tests/codegen/assign-desugar-debuginfo.rs diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index ea341b604e0..cf216ff73ca 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -13,13 +13,13 @@ use std::sync::Arc; use rustc_abi::VariantIdx; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_hir::{BindingMode, ByRef}; +use rustc_hir::{BindingMode, ByRef, LetStmt, LocalSource, Node}; use rustc_middle::bug; use rustc_middle::middle::region; use rustc_middle::mir::{self, *}; use rustc_middle::thir::{self, *}; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty}; -use rustc_span::{BytePos, Pos, Span, Symbol}; +use rustc_span::{BytePos, Pos, Span, Symbol, sym}; use tracing::{debug, instrument}; use crate::builder::ForGuard::{self, OutsideGuard, RefWithinGuard}; @@ -2796,13 +2796,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { )))), }; let for_arm_body = self.local_decls.push(local); - self.var_debug_info.push(VarDebugInfo { - name, - source_info: debug_source_info, - value: VarDebugInfoContents::Place(for_arm_body.into()), - composite: None, - argument_index: None, - }); + if self.should_emit_debug_info_for_binding(name, var_id) { + self.var_debug_info.push(VarDebugInfo { + name, + source_info: debug_source_info, + value: VarDebugInfoContents::Place(for_arm_body.into()), + composite: None, + argument_index: None, + }); + } let locals = if has_guard.0 { let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> { // This variable isn't mutated but has a name, so has to be @@ -2815,13 +2817,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { BindingForm::RefForGuard, ))), }); - self.var_debug_info.push(VarDebugInfo { - name, - source_info: debug_source_info, - value: VarDebugInfoContents::Place(ref_for_guard.into()), - composite: None, - argument_index: None, - }); + if self.should_emit_debug_info_for_binding(name, var_id) { + self.var_debug_info.push(VarDebugInfo { + name, + source_info: debug_source_info, + value: VarDebugInfoContents::Place(ref_for_guard.into()), + composite: None, + argument_index: None, + }); + } LocalsForNode::ForGuard { ref_for_guard, for_arm_body } } else { LocalsForNode::One(for_arm_body) @@ -2829,4 +2833,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug!(?locals); self.var_indices.insert(var_id, locals); } + + /// Some bindings are introduced when producing HIR from the AST and don't + /// actually exist in the source. Skip producing debug info for those when + /// we can recognize them. + fn should_emit_debug_info_for_binding(&self, name: Symbol, var_id: LocalVarId) -> bool { + // For now we only recognize the output of desugaring assigns. + if name != sym::lhs { + return true; + } + + let tcx = self.tcx; + for (_, node) in tcx.hir_parent_iter(var_id.0) { + // FIXME(khuey) at what point is it safe to bail on the iterator? + // Can we stop at the first non-Pat node? + if matches!(node, Node::LetStmt(&LetStmt { source: LocalSource::AssignDesugar(_), .. })) + { + return false; + } + } + + true + } } diff --git a/tests/codegen/assign-desugar-debuginfo.rs b/tests/codegen/assign-desugar-debuginfo.rs new file mode 100644 index 00000000000..77ee8758b3b --- /dev/null +++ b/tests/codegen/assign-desugar-debuginfo.rs @@ -0,0 +1,18 @@ +//@ compile-flags: -g -Zmir-opt-level=0 + +#![crate_type = "lib"] + +#[inline(never)] +fn swizzle(a: u32, b: u32, c: u32) -> (u32, (u32, u32)) { + (b, (c, a)) +} + +pub fn work() { + let mut a = 1; + let mut b = 2; + let mut c = 3; + (a, (b, c)) = swizzle(a, b, c); + println!("{a} {b} {c}"); +} + +// CHECK-NOT: !DILocalVariable(name: "lhs", From 848b0da34fa20d0ff5d12d1d4f506affc765534b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 23 Mar 2025 22:00:39 +0100 Subject: [PATCH 03/14] Remove fields that are dead since the removal of type ascription syntax Since `{ ident: ident }` is a parse error, these fields are dead. --- compiler/rustc_ast/src/ast.rs | 8 ------ compiler/rustc_ast/src/mut_visit.rs | 2 +- compiler/rustc_ast/src/visit.rs | 2 +- compiler/rustc_builtin_macros/src/autodiff.rs | 1 - .../rustc_builtin_macros/src/deriving/mod.rs | 1 - compiler/rustc_expand/src/build.rs | 1 - .../rustc_parse/src/parser/diagnostics.rs | 25 ++++++++----------- compiler/rustc_parse/src/parser/stmt.rs | 9 +------ compiler/rustc_resolve/src/late.rs | 13 ---------- .../rustc_resolve/src/late/diagnostics.rs | 14 ----------- src/tools/rustfmt/src/closures.rs | 1 - src/tools/rustfmt/src/macros.rs | 1 - tests/ui-fulldeps/pprust-expr-roundtrip.rs | 1 - 13 files changed, 13 insertions(+), 66 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 5b7545b3396..cc2fa8810d6 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -545,14 +545,6 @@ pub struct Block { pub rules: BlockCheckMode, pub span: Span, pub tokens: Option, - /// The following *isn't* a parse error, but will cause multiple errors in following stages. - /// ```compile_fail - /// let x = { - /// foo: var - /// }; - /// ``` - /// #34255 - pub could_be_bare_literal: bool, } /// A match pattern. diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 4edd0864300..c898a60fbe6 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1225,7 +1225,7 @@ fn walk_mt(vis: &mut T, MutTy { ty, mutbl: _ }: &mut MutTy) { } pub fn walk_block(vis: &mut T, block: &mut P) { - let Block { id, stmts, rules: _, span, tokens, could_be_bare_literal: _ } = block.deref_mut(); + let Block { id, stmts, rules: _, span, tokens } = block.deref_mut(); vis.visit_id(id); stmts.flat_map_in_place(|stmt| vis.flat_map_stmt(stmt)); visit_lazy_tts(vis, tokens); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index ce8d6df75af..50d8aa293c5 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -1035,7 +1035,7 @@ pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) } pub fn walk_block<'a, V: Visitor<'a>>(visitor: &mut V, block: &'a Block) -> V::Result { - let Block { stmts, id: _, rules: _, span: _, tokens: _, could_be_bare_literal: _ } = block; + let Block { stmts, id: _, rules: _, span: _, tokens: _ } = block; walk_list!(visitor, visit_stmt, stmts); V::Result::output() } diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 6591ed151cf..f3a40b8c682 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -395,7 +395,6 @@ mod llvm_enzyme { tokens: None, rules: unsf, span, - could_be_bare_literal: false, }; let unsf_expr = ecx.expr_block(P(unsf_block)); let blackbox_call_expr = ecx.expr_path(ecx.path(span, blackbox_path)); diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index c112589b131..50e7b989ed8 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -110,7 +110,6 @@ fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> P { rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated), span, tokens: None, - could_be_bare_literal: false, })) } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index ee7f68cc2f0..99b336969e4 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -286,7 +286,6 @@ impl<'a> ExtCtxt<'a> { rules: BlockCheckMode::Default, span, tokens: None, - could_be_bare_literal: false, }) } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index c1cca1186af..daa27b14669 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -971,18 +971,8 @@ impl<'a> Parser<'a> { // fn foo() -> Foo { // field: value, // } - // Suggest: - // fn foo() -> Foo { Path { - // field: value, - // } } let guar = err.delay_as_bug(); self.restore_snapshot(snapshot); - let mut tail = self.mk_block( - thin_vec![self.mk_stmt_err(expr.span, guar)], - s, - lo.to(self.prev_token.span), - ); - tail.could_be_bare_literal = true; if maybe_struct_name.is_ident() && can_be_struct_literal { // Account for `if Example { a: one(), }.is_pos() {}`. // expand `before` so that we take care of module path such as: @@ -1004,6 +994,10 @@ impl<'a> Parser<'a> { return None; } } else { + // Suggest: + // fn foo() -> Foo { Path { + // field: value, + // } } self.dcx().emit_err(StructLiteralBodyWithoutPath { span: expr.span, sugg: StructLiteralBodyWithoutPathSugg { @@ -1011,7 +1005,11 @@ impl<'a> Parser<'a> { after: expr.span.shrink_to_hi(), }, }); - Ok(tail) + Ok(self.mk_block( + thin_vec![self.mk_stmt_err(expr.span, guar)], + s, + lo.to(self.prev_token.span), + )) } } (Err(err), Ok(tail)) => { @@ -1025,10 +1023,7 @@ impl<'a> Parser<'a> { self.consume_block(exp!(OpenBrace), exp!(CloseBrace), ConsumeClosingDelim::Yes); Err(err) } - (Ok(_), Ok(mut tail)) => { - tail.could_be_bare_literal = true; - Ok(tail) - } + (Ok(_), Ok(tail)) => Ok(tail), }); } None diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 0fe247078d5..368366c60d6 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -1043,14 +1043,7 @@ impl<'a> Parser<'a> { rules: BlockCheckMode, span: Span, ) -> P { - P(Block { - stmts, - id: DUMMY_NODE_ID, - rules, - span, - tokens: None, - could_be_bare_literal: false, - }) + P(Block { stmts, id: DUMMY_NODE_ID, rules, span, tokens: None }) } pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 6056a69ee71..b4d9d5a8a0e 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -674,11 +674,6 @@ struct DiagMetadata<'ast> { /// they are used (in a `break` or `continue` statement) unused_labels: FxHashMap, - /// Only used for better errors on `let x = { foo: bar };`. - /// In the case of a parse error with `let x = { foo: bar, };`, this isn't needed, it's only - /// needed for cases where this parses as a correct type ascription. - current_block_could_be_bare_struct_literal: Option, - /// Only used for better errors on `let : ;`. current_let_binding: Option<(Span, Option, Option)>, @@ -4650,13 +4645,6 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.ribs[ValueNS].push(Rib::new(RibKind::Normal)); } - let prev = self.diag_metadata.current_block_could_be_bare_struct_literal.take(); - if let (true, [Stmt { kind: StmtKind::Expr(expr), .. }]) = - (block.could_be_bare_literal, &block.stmts[..]) - && let ExprKind::Type(..) = expr.kind - { - self.diag_metadata.current_block_could_be_bare_struct_literal = Some(block.span); - } // Descend into the block. for stmt in &block.stmts { if let StmtKind::Item(ref item) = stmt.kind @@ -4670,7 +4658,6 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.visit_stmt(stmt); } - self.diag_metadata.current_block_could_be_bare_struct_literal = prev; // Move back up. self.parent_scope.module = orig_module; diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 3d666055a94..dff9b478698 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -450,7 +450,6 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { err.span_suggestion_verbose(sugg.0, sugg.1, &sugg.2, Applicability::MaybeIncorrect); } - self.suggest_bare_struct_literal(&mut err); self.suggest_changing_type_to_const_param(&mut err, res, source, span); self.explain_functions_in_pattern(&mut err, res, source); @@ -1281,19 +1280,6 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } - fn suggest_bare_struct_literal(&mut self, err: &mut Diag<'_>) { - if let Some(span) = self.diag_metadata.current_block_could_be_bare_struct_literal { - err.multipart_suggestion( - "you might have meant to write a `struct` literal", - vec![ - (span.shrink_to_lo(), "{ SomeStruct ".to_string()), - (span.shrink_to_hi(), "}".to_string()), - ], - Applicability::HasPlaceholders, - ); - } - } - fn explain_functions_in_pattern( &mut self, err: &mut Diag<'_>, diff --git a/src/tools/rustfmt/src/closures.rs b/src/tools/rustfmt/src/closures.rs index a37b47e3bc9..61e148cdf18 100644 --- a/src/tools/rustfmt/src/closures.rs +++ b/src/tools/rustfmt/src/closures.rs @@ -176,7 +176,6 @@ fn rewrite_closure_with_block( .first() .map(|attr| attr.span.to(body.span)) .unwrap_or(body.span), - could_be_bare_literal: false, }; let block = crate::expr::rewrite_block_with_visitor( context, diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index 664c90b991a..e239ff47c04 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/src/macros.rs @@ -423,7 +423,6 @@ fn rewrite_empty_macro_def_body( rules: ast::BlockCheckMode::Default, span, tokens: None, - could_be_bare_literal: false, }; block.rewrite_result(context, shape) } diff --git a/tests/ui-fulldeps/pprust-expr-roundtrip.rs b/tests/ui-fulldeps/pprust-expr-roundtrip.rs index 37e328a315f..4a866560e79 100644 --- a/tests/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/tests/ui-fulldeps/pprust-expr-roundtrip.rs @@ -114,7 +114,6 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P)) { rules: BlockCheckMode::Default, span: DUMMY_SP, tokens: None, - could_be_bare_literal: false, }); iter_exprs(depth - 1, &mut |e| g(ExprKind::If(e, block.clone(), None))); } From 6df2d589338945e663a3a798a60db17db5f805b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 25 Mar 2025 12:37:52 +0100 Subject: [PATCH 04/14] Add function for linearizing `BuildStep` substeps --- src/build_helper/src/metrics.rs | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/build_helper/src/metrics.rs b/src/build_helper/src/metrics.rs index b6daac32a44..4ab61245c16 100644 --- a/src/build_helper/src/metrics.rs +++ b/src/build_helper/src/metrics.rs @@ -156,25 +156,30 @@ impl BuildStep { child.find_by_type(r#type, result); } } + + /// Returns a Vec with all substeps, ordered by their hierarchical order. + /// The first element of the tuple is the depth of a given step. + fn linearize_steps(&self) -> Vec<(u32, &BuildStep)> { + let mut substeps: Vec<(u32, &BuildStep)> = Vec::new(); + + fn visit<'a>(step: &'a BuildStep, level: u32, substeps: &mut Vec<(u32, &'a BuildStep)>) { + substeps.push((level, step)); + for child in &step.children { + visit(child, level + 1, substeps); + } + } + + visit(self, 0, &mut substeps); + substeps + } } /// Writes build steps into a nice indented table. pub fn format_build_steps(root: &BuildStep) -> String { use std::fmt::Write; - let mut substeps: Vec<(u32, &BuildStep)> = Vec::new(); - - fn visit<'a>(step: &'a BuildStep, level: u32, substeps: &mut Vec<(u32, &'a BuildStep)>) { - substeps.push((level, step)); - for child in &step.children { - visit(child, level + 1, substeps); - } - } - - visit(root, 0, &mut substeps); - let mut output = String::new(); - for (level, step) in substeps { + for (level, step) in root.linearize_steps() { let label = format!( "{}{}", ".".repeat(level as usize), From 82796dd8585c0c160fe2a50b12f77455a7e0f8d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 24 Mar 2025 00:01:30 +0100 Subject: [PATCH 05/14] Brace-ident-colon can certainly no longer start a block thanks to the removal of type ascription. --- compiler/rustc_parse/src/parser/expr.rs | 14 +--- tests/ui/parser/issues/issue-111692.rs | 10 ++- tests/ui/parser/issues/issue-111692.stderr | 30 +++---- ...-call-on-struct-literal-in-if-condition.rs | 2 +- ...l-on-struct-literal-in-if-condition.stderr | 4 +- tests/ui/parser/type-ascription-in-pattern.rs | 9 +-- .../parser/type-ascription-in-pattern.stderr | 80 ++++++++++++++----- 7 files changed, 88 insertions(+), 61 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index c48f91643e8..79d12ed4bbe 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3474,19 +3474,9 @@ impl<'a> Parser<'a> { } fn is_certainly_not_a_block(&self) -> bool { + // `{ ident, ` and `{ ident: ` cannot start a block. self.look_ahead(1, |t| t.is_ident()) - && ( - // `{ ident, ` cannot start a block. - self.look_ahead(2, |t| t == &token::Comma) - || self.look_ahead(2, |t| t == &token::Colon) - && ( - // `{ ident: token, ` cannot start a block. - self.look_ahead(4, |t| t == &token::Comma) - // `{ ident: ` cannot start a block unless it's a type ascription - // `ident: Type`. - || self.look_ahead(3, |t| !t.can_begin_type()) - ) - ) + && self.look_ahead(2, |t| t == &token::Comma || t == &token::Colon) } fn maybe_parse_struct_expr( diff --git a/tests/ui/parser/issues/issue-111692.rs b/tests/ui/parser/issues/issue-111692.rs index 56096f706a8..de6de222754 100644 --- a/tests/ui/parser/issues/issue-111692.rs +++ b/tests/ui/parser/issues/issue-111692.rs @@ -9,23 +9,25 @@ mod module { } fn test(x: module::Type) { - if x == module::Type { x: module::C, y: 1 } { //~ ERROR invalid struct literal + if x == module::Type { x: module::C, y: 1 } { //~ ERROR struct literals are not allowed here } } fn test2(x: module::Type) { - if x ==module::Type { x: module::C, y: 1 } { //~ ERROR invalid struct literal + if x ==module::Type { x: module::C, y: 1 } { //~ ERROR struct literals are not allowed here } } fn test3(x: module::Type) { - if x == Type { x: module::C, y: 1 } { //~ ERROR invalid struct literal + use module::Type; + if x == Type { x: module::C, y: 1 } { //~ ERROR struct literals are not allowed here } } fn test4(x: module::Type) { - if x == demo_module::Type { x: module::C, y: 1 } { //~ ERROR invalid struct literal + use module as demo_module; + if x == demo_module::Type { x: module::C, y: 1 } { //~ ERROR struct literals are not allowed here } } diff --git a/tests/ui/parser/issues/issue-111692.stderr b/tests/ui/parser/issues/issue-111692.stderr index 068b0483b0f..979dfade1ba 100644 --- a/tests/ui/parser/issues/issue-111692.stderr +++ b/tests/ui/parser/issues/issue-111692.stderr @@ -1,43 +1,43 @@ -error: invalid struct literal - --> $DIR/issue-111692.rs:12:21 +error: struct literals are not allowed here + --> $DIR/issue-111692.rs:12:13 | LL | if x == module::Type { x: module::C, y: 1 } { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: you might need to surround the struct literal with parentheses +help: surround the struct literal with parentheses | LL | if x == (module::Type { x: module::C, y: 1 }) { | + + -error: invalid struct literal - --> $DIR/issue-111692.rs:17:20 +error: struct literals are not allowed here + --> $DIR/issue-111692.rs:17:12 | LL | if x ==module::Type { x: module::C, y: 1 } { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: you might need to surround the struct literal with parentheses +help: surround the struct literal with parentheses | LL | if x ==(module::Type { x: module::C, y: 1 }) { | + + -error: invalid struct literal - --> $DIR/issue-111692.rs:23:13 +error: struct literals are not allowed here + --> $DIR/issue-111692.rs:24:13 | LL | if x == Type { x: module::C, y: 1 } { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: you might need to surround the struct literal with parentheses +help: surround the struct literal with parentheses | LL | if x == (Type { x: module::C, y: 1 }) { | + + -error: invalid struct literal - --> $DIR/issue-111692.rs:28:26 +error: struct literals are not allowed here + --> $DIR/issue-111692.rs:30:13 | LL | if x == demo_module::Type { x: module::C, y: 1 } { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: you might need to surround the struct literal with parentheses +help: surround the struct literal with parentheses | LL | if x == (demo_module::Type { x: module::C, y: 1 }) { | + + diff --git a/tests/ui/parser/method-call-on-struct-literal-in-if-condition.rs b/tests/ui/parser/method-call-on-struct-literal-in-if-condition.rs index 8be7c9ee8ac..3211b6c7bb9 100644 --- a/tests/ui/parser/method-call-on-struct-literal-in-if-condition.rs +++ b/tests/ui/parser/method-call-on-struct-literal-in-if-condition.rs @@ -7,7 +7,7 @@ impl Example { fn one() -> i32 { 1 } fn main() { - if Example { a: one(), }.is_pos() { //~ ERROR invalid struct literal + if Example { a: one(), }.is_pos() { //~ ERROR struct literals are not allowed here println!("Positive!"); } } diff --git a/tests/ui/parser/method-call-on-struct-literal-in-if-condition.stderr b/tests/ui/parser/method-call-on-struct-literal-in-if-condition.stderr index f7822ba1124..8eba2230f8f 100644 --- a/tests/ui/parser/method-call-on-struct-literal-in-if-condition.stderr +++ b/tests/ui/parser/method-call-on-struct-literal-in-if-condition.stderr @@ -1,10 +1,10 @@ -error: invalid struct literal +error: struct literals are not allowed here --> $DIR/method-call-on-struct-literal-in-if-condition.rs:10:8 | LL | if Example { a: one(), }.is_pos() { | ^^^^^^^^^^^^^^^^^^^^^ | -help: you might need to surround the struct literal with parentheses +help: surround the struct literal with parentheses | LL | if (Example { a: one(), }).is_pos() { | + + diff --git a/tests/ui/parser/type-ascription-in-pattern.rs b/tests/ui/parser/type-ascription-in-pattern.rs index fec168afba1..18d7061d69c 100644 --- a/tests/ui/parser/type-ascription-in-pattern.rs +++ b/tests/ui/parser/type-ascription-in-pattern.rs @@ -1,11 +1,10 @@ fn foo(x: bool) -> i32 { - match x { + match x { //~ ERROR struct literals are not allowed here x: i32 => x, //~ ERROR expected - //~^ ERROR mismatched types - true => 42., - false => 0.333, + true => 42., //~ ERROR expected identifier + false => 0.333, //~ ERROR expected identifier } -} +} //~ ERROR expected one of fn main() { match foo(true) { diff --git a/tests/ui/parser/type-ascription-in-pattern.stderr b/tests/ui/parser/type-ascription-in-pattern.stderr index 09190754993..135879f208b 100644 --- a/tests/ui/parser/type-ascription-in-pattern.stderr +++ b/tests/ui/parser/type-ascription-in-pattern.stderr @@ -1,18 +1,64 @@ -error: expected one of `@` or `|`, found `:` - --> $DIR/type-ascription-in-pattern.rs:3:10 +error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, `}`, or an operator, found `=>` + --> $DIR/type-ascription-in-pattern.rs:3:16 | +LL | match x { + | - while parsing this struct LL | x: i32 => x, - | ^ --- specifying the type of a pattern isn't supported - | | - | expected one of `@` or `|` + | -^^ expected one of 8 possible tokens + | | + | help: try adding a comma: `,` + +error: expected identifier, found keyword `true` + --> $DIR/type-ascription-in-pattern.rs:4:9 | -help: maybe write a path separator here +LL | match x { + | - while parsing this struct +LL | x: i32 => x, +LL | true => 42., + | ^^^^ expected identifier, found keyword + +error: expected identifier, found keyword `false` + --> $DIR/type-ascription-in-pattern.rs:5:9 | -LL | x::i32 => x, - | ~~ +LL | match x { + | - while parsing this struct +... +LL | false => 0.333, + | ^^^^^ expected identifier, found keyword + +error: struct literals are not allowed here + --> $DIR/type-ascription-in-pattern.rs:2:11 + | +LL | match x { + | ___________^ +LL | | x: i32 => x, +LL | | true => 42., +LL | | false => 0.333, +LL | | } + | |_____^ + | +help: surround the struct literal with parentheses + | +LL ~ match (x { +LL | x: i32 => x, +LL | true => 42., +LL | false => 0.333, +LL ~ }) + | + +error: expected one of `.`, `?`, `{`, or an operator, found `}` + --> $DIR/type-ascription-in-pattern.rs:7:1 + | +LL | match x { + | ----- while parsing this `match` expression +... +LL | } + | - expected one of `.`, `?`, `{`, or an operator +LL | } + | ^ unexpected token error: expected one of `...`, `..=`, `..`, or `|`, found `:` - --> $DIR/type-ascription-in-pattern.rs:12:11 + --> $DIR/type-ascription-in-pattern.rs:11:11 | LL | 42: i32 => (), | ^ --- specifying the type of a pattern isn't supported @@ -20,7 +66,7 @@ LL | 42: i32 => (), | expected one of `...`, `..=`, `..`, or `|` error: expected `|`, found `:` - --> $DIR/type-ascription-in-pattern.rs:13:10 + --> $DIR/type-ascription-in-pattern.rs:12:10 | LL | _: f64 => (), | ^ --- specifying the type of a pattern isn't supported @@ -28,7 +74,7 @@ LL | _: f64 => (), | expected `|` error: expected one of `@` or `|`, found `:` - --> $DIR/type-ascription-in-pattern.rs:14:10 + --> $DIR/type-ascription-in-pattern.rs:13:10 | LL | x: i32 => (), | ^ --- specifying the type of a pattern isn't supported @@ -40,15 +86,5 @@ help: maybe write a path separator here LL | x::i32 => (), | ~~ -error[E0308]: mismatched types - --> $DIR/type-ascription-in-pattern.rs:3:19 - | -LL | fn foo(x: bool) -> i32 { - | --- expected `i32` because of return type -LL | match x { -LL | x: i32 => x, - | ^ expected `i32`, found `bool` +error: aborting due to 8 previous errors -error: aborting due to 5 previous errors - -For more information about this error, try `rustc --explain E0308`. From 9f336ce2eb0ba04de7f1ddd2e1b0958e7df15c42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 24 Mar 2025 00:12:53 +0100 Subject: [PATCH 06/14] Remove now unreachable parse recovery code StructLiteralNeedingParens is no longer reachable always giving precedence to StructLiteralNotAllowedHere. As an aside: The former error struct shouldn't've existed in the first place. We should've just used the latter in this branch. --- compiler/rustc_parse/messages.ftl | 4 -- compiler/rustc_parse/src/errors.rs | 18 ------ .../rustc_parse/src/parser/diagnostics.rs | 64 ++++++------------- compiler/rustc_parse/src/parser/expr.rs | 2 +- compiler/rustc_parse/src/parser/item.rs | 2 +- compiler/rustc_parse/src/parser/stmt.rs | 10 +-- 6 files changed, 24 insertions(+), 76 deletions(-) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 6d4308cda1a..3253222b8f2 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -757,10 +757,6 @@ parse_struct_literal_body_without_path = struct literal body without path .suggestion = you might have forgotten to add the struct literal inside the block -parse_struct_literal_needing_parens = - invalid struct literal - .suggestion = you might need to surround the struct literal with parentheses - parse_struct_literal_not_allowed_here = struct literals are not allowed here .suggestion = surround the struct literal with parentheses diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index e090d9cf760..f813c3380fc 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1272,24 +1272,6 @@ pub(crate) struct StructLiteralBodyWithoutPathSugg { pub after: Span, } -#[derive(Diagnostic)] -#[diag(parse_struct_literal_needing_parens)] -pub(crate) struct StructLiteralNeedingParens { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub sugg: StructLiteralNeedingParensSugg, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] -pub(crate) struct StructLiteralNeedingParensSugg { - #[suggestion_part(code = "(")] - pub before: Span, - #[suggestion_part(code = ")")] - pub after: Span, -} - #[derive(Diagnostic)] #[diag(parse_unmatched_angle_brackets)] pub(crate) struct UnmatchedAngleBrackets { diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index daa27b14669..ef044fe9d63 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -40,9 +40,8 @@ use crate::errors::{ HelpIdentifierStartsWithNumber, HelpUseLatestEdition, InInTypo, IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath, - StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens, StructLiteralNeedingParensSugg, - SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator, - UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, + StructLiteralBodyWithoutPathSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, + TernaryOperator, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType, }; use crate::parser::attr::InnerAttrPolicy; @@ -949,7 +948,6 @@ impl<'a> Parser<'a> { lo: Span, s: BlockCheckMode, maybe_struct_name: token::Token, - can_be_struct_literal: bool, ) -> Option>> { if self.token.is_ident() && self.look_ahead(1, |t| t == &token::Colon) { // We might be having a struct literal where people forgot to include the path: @@ -971,49 +969,27 @@ impl<'a> Parser<'a> { // fn foo() -> Foo { // field: value, // } - let guar = err.delay_as_bug(); + // Suggest: + // fn foo() -> Foo { Path { + // field: value, + // } } + err.cancel(); self.restore_snapshot(snapshot); - if maybe_struct_name.is_ident() && can_be_struct_literal { - // Account for `if Example { a: one(), }.is_pos() {}`. - // expand `before` so that we take care of module path such as: - // `foo::Bar { ... } ` - // we expect to suggest `(foo::Bar { ... })` instead of `foo::(Bar { ... })` - let sm = self.psess.source_map(); - let before = maybe_struct_name.span.shrink_to_lo(); - if let Ok(extend_before) = sm.span_extend_prev_while(before, |t| { - t.is_alphanumeric() || t == ':' || t == '_' - }) { - Err(self.dcx().create_err(StructLiteralNeedingParens { - span: maybe_struct_name.span.to(expr.span), - sugg: StructLiteralNeedingParensSugg { - before: extend_before.shrink_to_lo(), - after: expr.span.shrink_to_hi(), - }, - })) - } else { - return None; - } - } else { - // Suggest: - // fn foo() -> Foo { Path { - // field: value, - // } } - self.dcx().emit_err(StructLiteralBodyWithoutPath { - span: expr.span, - sugg: StructLiteralBodyWithoutPathSugg { - before: expr.span.shrink_to_lo(), - after: expr.span.shrink_to_hi(), - }, - }); - Ok(self.mk_block( - thin_vec![self.mk_stmt_err(expr.span, guar)], - s, - lo.to(self.prev_token.span), - )) - } + let guar = self.dcx().emit_err(StructLiteralBodyWithoutPath { + span: expr.span, + sugg: StructLiteralBodyWithoutPathSugg { + before: expr.span.shrink_to_lo(), + after: expr.span.shrink_to_hi(), + }, + }); + Ok(self.mk_block( + thin_vec![self.mk_stmt_err(expr.span, guar)], + s, + lo.to(self.prev_token.span), + )) } (Err(err), Ok(tail)) => { - // We have a block tail that contains a somehow valid type ascription expr. + // We have a block tail that contains a somehow valid expr. err.cancel(); Ok(tail) } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 79d12ed4bbe..92e83577f1b 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2296,7 +2296,7 @@ impl<'a> Parser<'a> { }); } - let (attrs, blk) = self.parse_block_common(lo, blk_mode, true, None)?; + let (attrs, blk) = self.parse_block_common(lo, blk_mode, None)?; Ok(self.mk_expr_with_attrs(blk.span, ExprKind::Block(blk, opt_label), attrs)) } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index f4df4044dd2..77e675ea91f 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2529,7 +2529,7 @@ impl<'a> Parser<'a> { *sig_hi = self.prev_token.span; (AttrVec::new(), None) } else if self.check(exp!(OpenBrace)) || self.token.is_whole_block() { - self.parse_block_common(self.token.span, BlockCheckMode::Default, false, None) + self.parse_block_common(self.token.span, BlockCheckMode::Default, None) .map(|(attrs, body)| (attrs, Some(body)))? } else if self.token == token::Eq { // Recover `fn foo() = $expr;`. diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 368366c60d6..97cd4d2117f 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -668,7 +668,7 @@ impl<'a> Parser<'a> { &mut self, loop_header: Option, ) -> PResult<'a, (AttrVec, P)> { - self.parse_block_common(self.token.span, BlockCheckMode::Default, true, loop_header) + self.parse_block_common(self.token.span, BlockCheckMode::Default, loop_header) } /// Parses a block. Inner attributes are allowed, block labels are not. @@ -679,7 +679,6 @@ impl<'a> Parser<'a> { &mut self, lo: Span, blk_mode: BlockCheckMode, - can_be_struct_literal: bool, loop_header: Option, ) -> PResult<'a, (AttrVec, P)> { maybe_whole!(self, NtBlock, |block| (AttrVec::new(), block)); @@ -691,12 +690,7 @@ impl<'a> Parser<'a> { } let attrs = self.parse_inner_attributes()?; - let tail = match self.maybe_suggest_struct_literal( - lo, - blk_mode, - maybe_ident, - can_be_struct_literal, - ) { + let tail = match self.maybe_suggest_struct_literal(lo, blk_mode, maybe_ident) { Some(tail) => tail?, None => self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?, }; From 598f8658744db0dc0215545d2193accd3a1ce8c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 24 Mar 2025 17:16:26 +0100 Subject: [PATCH 07/14] Combine several test files into one This makes it a lot easier to add smaller regression tests related to "incorrectly placed" struct literals. --- ...-call-on-struct-literal-in-if-condition.rs | 13 -- ...l-on-struct-literal-in-if-condition.stderr | 13 -- tests/ui/parser/struct-literal-in-for.rs | 17 -- tests/ui/parser/struct-literal-in-for.stderr | 31 --- tests/ui/parser/struct-literal-in-if.rs | 22 -- tests/ui/parser/struct-literal-in-if.stderr | 34 ---- .../struct-literal-in-match-discriminant.rs | 13 -- ...truct-literal-in-match-discriminant.stderr | 18 -- tests/ui/parser/struct-literal-in-while.rs | 22 -- .../ui/parser/struct-literal-in-while.stderr | 34 ---- .../struct-literal-restrictions-in-lamda.rs | 17 -- ...truct-literal-restrictions-in-lamda.stderr | 37 ---- .../ui/parser/struct-literal-variant-in-if.rs | 25 --- .../struct-literal-variant-in-if.stderr | 76 ------- .../struct-literals-in-invalid-places.rs | 72 +++++++ .../struct-literals-in-invalid-places.stderr | 191 ++++++++++++++++++ 16 files changed, 263 insertions(+), 372 deletions(-) delete mode 100644 tests/ui/parser/method-call-on-struct-literal-in-if-condition.rs delete mode 100644 tests/ui/parser/method-call-on-struct-literal-in-if-condition.stderr delete mode 100644 tests/ui/parser/struct-literal-in-for.rs delete mode 100644 tests/ui/parser/struct-literal-in-for.stderr delete mode 100644 tests/ui/parser/struct-literal-in-if.rs delete mode 100644 tests/ui/parser/struct-literal-in-if.stderr delete mode 100644 tests/ui/parser/struct-literal-in-match-discriminant.rs delete mode 100644 tests/ui/parser/struct-literal-in-match-discriminant.stderr delete mode 100644 tests/ui/parser/struct-literal-in-while.rs delete mode 100644 tests/ui/parser/struct-literal-in-while.stderr delete mode 100644 tests/ui/parser/struct-literal-restrictions-in-lamda.rs delete mode 100644 tests/ui/parser/struct-literal-restrictions-in-lamda.stderr delete mode 100644 tests/ui/parser/struct-literal-variant-in-if.rs delete mode 100644 tests/ui/parser/struct-literal-variant-in-if.stderr create mode 100644 tests/ui/parser/struct-literals-in-invalid-places.rs create mode 100644 tests/ui/parser/struct-literals-in-invalid-places.stderr diff --git a/tests/ui/parser/method-call-on-struct-literal-in-if-condition.rs b/tests/ui/parser/method-call-on-struct-literal-in-if-condition.rs deleted file mode 100644 index 3211b6c7bb9..00000000000 --- a/tests/ui/parser/method-call-on-struct-literal-in-if-condition.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub struct Example { a: i32 } - -impl Example { - fn is_pos(&self) -> bool { self.a > 0 } -} - -fn one() -> i32 { 1 } - -fn main() { - if Example { a: one(), }.is_pos() { //~ ERROR struct literals are not allowed here - println!("Positive!"); - } -} diff --git a/tests/ui/parser/method-call-on-struct-literal-in-if-condition.stderr b/tests/ui/parser/method-call-on-struct-literal-in-if-condition.stderr deleted file mode 100644 index 8eba2230f8f..00000000000 --- a/tests/ui/parser/method-call-on-struct-literal-in-if-condition.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: struct literals are not allowed here - --> $DIR/method-call-on-struct-literal-in-if-condition.rs:10:8 - | -LL | if Example { a: one(), }.is_pos() { - | ^^^^^^^^^^^^^^^^^^^^^ - | -help: surround the struct literal with parentheses - | -LL | if (Example { a: one(), }).is_pos() { - | + + - -error: aborting due to 1 previous error - diff --git a/tests/ui/parser/struct-literal-in-for.rs b/tests/ui/parser/struct-literal-in-for.rs deleted file mode 100644 index 3227ae37bfd..00000000000 --- a/tests/ui/parser/struct-literal-in-for.rs +++ /dev/null @@ -1,17 +0,0 @@ -struct Foo { - x: isize, -} - -impl Foo { - fn hi(&self) -> bool { - true - } -} - -fn main() { - for x in Foo { //~ ERROR struct literals are not allowed here - x: 3 //~^ ERROR `bool` is not an iterator - }.hi() { - println!("yo"); - } -} diff --git a/tests/ui/parser/struct-literal-in-for.stderr b/tests/ui/parser/struct-literal-in-for.stderr deleted file mode 100644 index 1c91eba68e3..00000000000 --- a/tests/ui/parser/struct-literal-in-for.stderr +++ /dev/null @@ -1,31 +0,0 @@ -error: struct literals are not allowed here - --> $DIR/struct-literal-in-for.rs:12:14 - | -LL | for x in Foo { - | ______________^ -LL | | x: 3 -LL | | }.hi() { - | |_____^ - | -help: surround the struct literal with parentheses - | -LL ~ for x in (Foo { -LL | x: 3 -LL ~ }).hi() { - | - -error[E0277]: `bool` is not an iterator - --> $DIR/struct-literal-in-for.rs:12:14 - | -LL | for x in Foo { - | ______________^ -LL | | x: 3 -LL | | }.hi() { - | |__________^ `bool` is not an iterator - | - = help: the trait `Iterator` is not implemented for `bool` - = note: required for `bool` to implement `IntoIterator` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/parser/struct-literal-in-if.rs b/tests/ui/parser/struct-literal-in-if.rs deleted file mode 100644 index c4a253c3da2..00000000000 --- a/tests/ui/parser/struct-literal-in-if.rs +++ /dev/null @@ -1,22 +0,0 @@ -struct Foo { - x: isize, -} - -impl Foo { - fn hi(&self) -> bool { - true - } -} - -fn main() { - if Foo { //~ ERROR struct literals are not allowed here - x: 3 - }.hi() { - println!("yo"); - } - if let true = Foo { //~ ERROR struct literals are not allowed here - x: 3 - }.hi() { - println!("yo"); - } -} diff --git a/tests/ui/parser/struct-literal-in-if.stderr b/tests/ui/parser/struct-literal-in-if.stderr deleted file mode 100644 index 8b72469fcf5..00000000000 --- a/tests/ui/parser/struct-literal-in-if.stderr +++ /dev/null @@ -1,34 +0,0 @@ -error: struct literals are not allowed here - --> $DIR/struct-literal-in-if.rs:12:8 - | -LL | if Foo { - | ________^ -LL | | x: 3 -LL | | }.hi() { - | |_____^ - | -help: surround the struct literal with parentheses - | -LL ~ if (Foo { -LL | x: 3 -LL ~ }).hi() { - | - -error: struct literals are not allowed here - --> $DIR/struct-literal-in-if.rs:17:19 - | -LL | if let true = Foo { - | ___________________^ -LL | | x: 3 -LL | | }.hi() { - | |_____^ - | -help: surround the struct literal with parentheses - | -LL ~ if let true = (Foo { -LL | x: 3 -LL ~ }).hi() { - | - -error: aborting due to 2 previous errors - diff --git a/tests/ui/parser/struct-literal-in-match-discriminant.rs b/tests/ui/parser/struct-literal-in-match-discriminant.rs deleted file mode 100644 index ce132df5a88..00000000000 --- a/tests/ui/parser/struct-literal-in-match-discriminant.rs +++ /dev/null @@ -1,13 +0,0 @@ -struct Foo { - x: isize, -} - -fn main() { - match Foo { //~ ERROR struct literals are not allowed here - x: 3 - } { - Foo { - x: x - } => {} - } -} diff --git a/tests/ui/parser/struct-literal-in-match-discriminant.stderr b/tests/ui/parser/struct-literal-in-match-discriminant.stderr deleted file mode 100644 index 5177f5f126e..00000000000 --- a/tests/ui/parser/struct-literal-in-match-discriminant.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error: struct literals are not allowed here - --> $DIR/struct-literal-in-match-discriminant.rs:6:11 - | -LL | match Foo { - | ___________^ -LL | | x: 3 -LL | | } { - | |_____^ - | -help: surround the struct literal with parentheses - | -LL ~ match (Foo { -LL | x: 3 -LL ~ }) { - | - -error: aborting due to 1 previous error - diff --git a/tests/ui/parser/struct-literal-in-while.rs b/tests/ui/parser/struct-literal-in-while.rs deleted file mode 100644 index 86931f7888d..00000000000 --- a/tests/ui/parser/struct-literal-in-while.rs +++ /dev/null @@ -1,22 +0,0 @@ -struct Foo { - x: isize, -} - -impl Foo { - fn hi(&self) -> bool { - true - } -} - -fn main() { - while Foo { //~ ERROR struct literals are not allowed here - x: 3 - }.hi() { - println!("yo"); - } - while let true = Foo { //~ ERROR struct literals are not allowed here - x: 3 - }.hi() { - println!("yo"); - } -} diff --git a/tests/ui/parser/struct-literal-in-while.stderr b/tests/ui/parser/struct-literal-in-while.stderr deleted file mode 100644 index 13d003608a1..00000000000 --- a/tests/ui/parser/struct-literal-in-while.stderr +++ /dev/null @@ -1,34 +0,0 @@ -error: struct literals are not allowed here - --> $DIR/struct-literal-in-while.rs:12:11 - | -LL | while Foo { - | ___________^ -LL | | x: 3 -LL | | }.hi() { - | |_____^ - | -help: surround the struct literal with parentheses - | -LL ~ while (Foo { -LL | x: 3 -LL ~ }).hi() { - | - -error: struct literals are not allowed here - --> $DIR/struct-literal-in-while.rs:17:22 - | -LL | while let true = Foo { - | ______________________^ -LL | | x: 3 -LL | | }.hi() { - | |_____^ - | -help: surround the struct literal with parentheses - | -LL ~ while let true = (Foo { -LL | x: 3 -LL ~ }).hi() { - | - -error: aborting due to 2 previous errors - diff --git a/tests/ui/parser/struct-literal-restrictions-in-lamda.rs b/tests/ui/parser/struct-literal-restrictions-in-lamda.rs deleted file mode 100644 index e185153dcf6..00000000000 --- a/tests/ui/parser/struct-literal-restrictions-in-lamda.rs +++ /dev/null @@ -1,17 +0,0 @@ -struct Foo { - x: isize, -} - -impl Foo { - fn hi(&self) -> bool { - true - } -} - -fn main() { - while || Foo { //~ ERROR struct literals are not allowed here - x: 3 //~^ ERROR mismatched types - }.hi() { - println!("yo"); - } -} diff --git a/tests/ui/parser/struct-literal-restrictions-in-lamda.stderr b/tests/ui/parser/struct-literal-restrictions-in-lamda.stderr deleted file mode 100644 index c715486e2da..00000000000 --- a/tests/ui/parser/struct-literal-restrictions-in-lamda.stderr +++ /dev/null @@ -1,37 +0,0 @@ -error: struct literals are not allowed here - --> $DIR/struct-literal-restrictions-in-lamda.rs:12:14 - | -LL | while || Foo { - | ______________^ -LL | | x: 3 -LL | | }.hi() { - | |_____^ - | -help: surround the struct literal with parentheses - | -LL ~ while || (Foo { -LL | x: 3 -LL ~ }).hi() { - | - -error[E0308]: mismatched types - --> $DIR/struct-literal-restrictions-in-lamda.rs:12:11 - | -LL | while || Foo { - | ___________^ -LL | | x: 3 -LL | | }.hi() { - | |__________^ expected `bool`, found closure - | - = note: expected type `bool` - found closure `{closure@$DIR/struct-literal-restrictions-in-lamda.rs:12:11: 12:13}` -help: use parentheses to call this closure - | -LL ~ while (|| Foo { -LL | x: 3 -LL ~ }.hi())() { - | - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/struct-literal-variant-in-if.rs b/tests/ui/parser/struct-literal-variant-in-if.rs deleted file mode 100644 index 4ef8effaf1f..00000000000 --- a/tests/ui/parser/struct-literal-variant-in-if.rs +++ /dev/null @@ -1,25 +0,0 @@ -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -enum E { - V { field: bool }, - I { field1: bool, field2: usize }, - J { field: isize }, - K { field: &'static str}, -} -fn test_E(x: E) { - let field = true; - if x == E::V { field } {} - //~^ ERROR expected value, found struct variant `E::V` - //~| ERROR mismatched types - if x == E::I { field1: true, field2: 42 } {} - //~^ ERROR struct literals are not allowed here - if x == E::V { field: false } {} - //~^ ERROR struct literals are not allowed here - if x == E::J { field: -42 } {} - //~^ ERROR struct literals are not allowed here - if x == E::K { field: "" } {} - //~^ ERROR struct literals are not allowed here - let y: usize = (); - //~^ ERROR mismatched types -} - -fn main() {} diff --git a/tests/ui/parser/struct-literal-variant-in-if.stderr b/tests/ui/parser/struct-literal-variant-in-if.stderr deleted file mode 100644 index 15f059f145b..00000000000 --- a/tests/ui/parser/struct-literal-variant-in-if.stderr +++ /dev/null @@ -1,76 +0,0 @@ -error: struct literals are not allowed here - --> $DIR/struct-literal-variant-in-if.rs:13:13 - | -LL | if x == E::I { field1: true, field2: 42 } {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: surround the struct literal with parentheses - | -LL | if x == (E::I { field1: true, field2: 42 }) {} - | + + - -error: struct literals are not allowed here - --> $DIR/struct-literal-variant-in-if.rs:15:13 - | -LL | if x == E::V { field: false } {} - | ^^^^^^^^^^^^^^^^^^^^^ - | -help: surround the struct literal with parentheses - | -LL | if x == (E::V { field: false }) {} - | + + - -error: struct literals are not allowed here - --> $DIR/struct-literal-variant-in-if.rs:17:13 - | -LL | if x == E::J { field: -42 } {} - | ^^^^^^^^^^^^^^^^^^^ - | -help: surround the struct literal with parentheses - | -LL | if x == (E::J { field: -42 }) {} - | + + - -error: struct literals are not allowed here - --> $DIR/struct-literal-variant-in-if.rs:19:13 - | -LL | if x == E::K { field: "" } {} - | ^^^^^^^^^^^^^^^^^^ - | -help: surround the struct literal with parentheses - | -LL | if x == (E::K { field: "" }) {} - | + + - -error[E0533]: expected value, found struct variant `E::V` - --> $DIR/struct-literal-variant-in-if.rs:10:13 - | -LL | if x == E::V { field } {} - | ^^^^ not a value - | -help: you might have meant to create a new value of the struct - | -LL | if x == (E::V { field }) {} - | + + - -error[E0308]: mismatched types - --> $DIR/struct-literal-variant-in-if.rs:10:20 - | -LL | if x == E::V { field } {} - | ---------------^^^^^-- - | | | - | | expected `()`, found `bool` - | expected this to be `()` - -error[E0308]: mismatched types - --> $DIR/struct-literal-variant-in-if.rs:21:20 - | -LL | let y: usize = (); - | ----- ^^ expected `usize`, found `()` - | | - | expected due to this - -error: aborting due to 7 previous errors - -Some errors have detailed explanations: E0308, E0533. -For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/struct-literals-in-invalid-places.rs b/tests/ui/parser/struct-literals-in-invalid-places.rs new file mode 100644 index 00000000000..89cdb30fc04 --- /dev/null +++ b/tests/ui/parser/struct-literals-in-invalid-places.rs @@ -0,0 +1,72 @@ +fn main() { + if Foo { x: 3 }.hi() { //~ ERROR struct literals are not allowed here + println!("yo"); + } + if let true = Foo { x: 3 }.hi() { //~ ERROR struct literals are not allowed here + println!("yo"); + } + + for x in Foo { x: 3 }.hi() { //~ ERROR struct literals are not allowed here + //~^ ERROR `bool` is not an iterator + println!("yo"); + } + + while Foo { x: 3 }.hi() { //~ ERROR struct literals are not allowed here + println!("yo"); + } + while let true = Foo { x: 3 }.hi() { //~ ERROR struct literals are not allowed here + println!("yo"); + } + + match Foo { x: 3 } { //~ ERROR struct literals are not allowed here + Foo { x: x } => {} + } + + let _ = |x: E| { + let field = true; + if x == E::V { field } {} + //~^ ERROR expected value, found struct variant `E::V` + //~| ERROR mismatched types + if x == E::I { field1: true, field2: 42 } {} + //~^ ERROR struct literals are not allowed here + if x == E::V { field: false } {} + //~^ ERROR struct literals are not allowed here + if x == E::J { field: -42 } {} + //~^ ERROR struct literals are not allowed here + if x == E::K { field: "" } {} + //~^ ERROR struct literals are not allowed here + let y: usize = (); + //~^ ERROR mismatched types + }; + + // Regression test for . + while || Foo { x: 3 }.hi() { //~ ERROR struct literals are not allowed here + //~^ ERROR mismatched types + println!("yo"); + } + + // Regression test for . + if Foo { x: one(), }.hi() { //~ ERROR struct literals are not allowed here + println!("Positive!"); + } +} + +struct Foo { + x: isize, +} + +impl Foo { + fn hi(&self) -> bool { + true + } +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +enum E { + V { field: bool }, + I { field1: bool, field2: usize }, + J { field: isize }, + K { field: &'static str}, +} + +fn one() -> isize { 1 } diff --git a/tests/ui/parser/struct-literals-in-invalid-places.stderr b/tests/ui/parser/struct-literals-in-invalid-places.stderr new file mode 100644 index 00000000000..ed094fc3e15 --- /dev/null +++ b/tests/ui/parser/struct-literals-in-invalid-places.stderr @@ -0,0 +1,191 @@ +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:2:8 + | +LL | if Foo { x: 3 }.hi() { + | ^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if (Foo { x: 3 }).hi() { + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:5:19 + | +LL | if let true = Foo { x: 3 }.hi() { + | ^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if let true = (Foo { x: 3 }).hi() { + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:9:14 + | +LL | for x in Foo { x: 3 }.hi() { + | ^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | for x in (Foo { x: 3 }).hi() { + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:14:11 + | +LL | while Foo { x: 3 }.hi() { + | ^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | while (Foo { x: 3 }).hi() { + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:17:22 + | +LL | while let true = Foo { x: 3 }.hi() { + | ^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | while let true = (Foo { x: 3 }).hi() { + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:21:11 + | +LL | match Foo { x: 3 } { + | ^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | match (Foo { x: 3 }) { + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:30:17 + | +LL | if x == E::I { field1: true, field2: 42 } {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if x == (E::I { field1: true, field2: 42 }) {} + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:32:17 + | +LL | if x == E::V { field: false } {} + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if x == (E::V { field: false }) {} + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:34:17 + | +LL | if x == E::J { field: -42 } {} + | ^^^^^^^^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if x == (E::J { field: -42 }) {} + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:36:17 + | +LL | if x == E::K { field: "" } {} + | ^^^^^^^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if x == (E::K { field: "" }) {} + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:43:14 + | +LL | while || Foo { x: 3 }.hi() { + | ^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | while || (Foo { x: 3 }).hi() { + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:49:8 + | +LL | if Foo { x: one(), }.hi() { + | ^^^^^^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if (Foo { x: one(), }).hi() { + | + + + +error[E0277]: `bool` is not an iterator + --> $DIR/struct-literals-in-invalid-places.rs:9:14 + | +LL | for x in Foo { x: 3 }.hi() { + | ^^^^^^^^^^^^^^^^^ `bool` is not an iterator + | + = help: the trait `Iterator` is not implemented for `bool` + = note: required for `bool` to implement `IntoIterator` + +error[E0533]: expected value, found struct variant `E::V` + --> $DIR/struct-literals-in-invalid-places.rs:27:17 + | +LL | if x == E::V { field } {} + | ^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | if x == (E::V { field }) {} + | + + + +error[E0308]: mismatched types + --> $DIR/struct-literals-in-invalid-places.rs:27:24 + | +LL | if x == E::V { field } {} + | ---------------^^^^^-- + | | | + | | expected `()`, found `bool` + | expected this to be `()` + | +help: you might have meant to return this value + | +LL | if x == E::V { return field; } {} + | ++++++ + + +error[E0308]: mismatched types + --> $DIR/struct-literals-in-invalid-places.rs:38:24 + | +LL | let y: usize = (); + | ----- ^^ expected `usize`, found `()` + | | + | expected due to this + +error[E0308]: mismatched types + --> $DIR/struct-literals-in-invalid-places.rs:43:11 + | +LL | while || Foo { x: 3 }.hi() { + | ^^^^^^^^^^^^^^^^^^^^ expected `bool`, found closure + | + = note: expected type `bool` + found closure `{closure@$DIR/struct-literals-in-invalid-places.rs:43:11: 43:13}` +help: use parentheses to call this closure + | +LL | while (|| Foo { x: 3 }.hi())() { + | + +++ + +error: aborting due to 17 previous errors + +Some errors have detailed explanations: E0277, E0308, E0533. +For more information about an error, try `rustc --explain E0277`. From b501e58c2e8aa42c0b6f4f568c90f70e34a11170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 24 Mar 2025 17:39:38 +0100 Subject: [PATCH 08/14] Incorporate issue-111692.rs into the larger test file and add more test cases Note that issue-111692.rs was incorrectly named: It's a regression test for issue [#]112278, not for [#]111692. That's been addressed, too. --- src/tools/tidy/src/issues.txt | 1 - tests/ui/parser/issues/issue-111692.rs | 34 ------------- tests/ui/parser/issues/issue-111692.stderr | 46 ----------------- .../struct-literals-in-invalid-places.rs | 20 ++++++++ .../struct-literals-in-invalid-places.stderr | 49 +++++++++++++++++-- 5 files changed, 66 insertions(+), 84 deletions(-) delete mode 100644 tests/ui/parser/issues/issue-111692.rs delete mode 100644 tests/ui/parser/issues/issue-111692.stderr diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 4a929a376d7..a33e03d5861 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -3189,7 +3189,6 @@ ui/parser/issues/issue-108495-dec.rs ui/parser/issues/issue-110014.rs ui/parser/issues/issue-111148.rs ui/parser/issues/issue-111416.rs -ui/parser/issues/issue-111692.rs ui/parser/issues/issue-112188.rs ui/parser/issues/issue-112458.rs ui/parser/issues/issue-113110-non-item-at-module-root.rs diff --git a/tests/ui/parser/issues/issue-111692.rs b/tests/ui/parser/issues/issue-111692.rs deleted file mode 100644 index de6de222754..00000000000 --- a/tests/ui/parser/issues/issue-111692.rs +++ /dev/null @@ -1,34 +0,0 @@ -mod module { - #[derive(Eq, PartialEq)] - pub struct Type { - pub x: u8, - pub y: u8, - } - - pub const C: u8 = 32u8; -} - -fn test(x: module::Type) { - if x == module::Type { x: module::C, y: 1 } { //~ ERROR struct literals are not allowed here - } -} - -fn test2(x: module::Type) { - if x ==module::Type { x: module::C, y: 1 } { //~ ERROR struct literals are not allowed here - } -} - - -fn test3(x: module::Type) { - use module::Type; - if x == Type { x: module::C, y: 1 } { //~ ERROR struct literals are not allowed here - } -} - -fn test4(x: module::Type) { - use module as demo_module; - if x == demo_module::Type { x: module::C, y: 1 } { //~ ERROR struct literals are not allowed here - } -} - -fn main() { } diff --git a/tests/ui/parser/issues/issue-111692.stderr b/tests/ui/parser/issues/issue-111692.stderr deleted file mode 100644 index 979dfade1ba..00000000000 --- a/tests/ui/parser/issues/issue-111692.stderr +++ /dev/null @@ -1,46 +0,0 @@ -error: struct literals are not allowed here - --> $DIR/issue-111692.rs:12:13 - | -LL | if x == module::Type { x: module::C, y: 1 } { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: surround the struct literal with parentheses - | -LL | if x == (module::Type { x: module::C, y: 1 }) { - | + + - -error: struct literals are not allowed here - --> $DIR/issue-111692.rs:17:12 - | -LL | if x ==module::Type { x: module::C, y: 1 } { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: surround the struct literal with parentheses - | -LL | if x ==(module::Type { x: module::C, y: 1 }) { - | + + - -error: struct literals are not allowed here - --> $DIR/issue-111692.rs:24:13 - | -LL | if x == Type { x: module::C, y: 1 } { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: surround the struct literal with parentheses - | -LL | if x == (Type { x: module::C, y: 1 }) { - | + + - -error: struct literals are not allowed here - --> $DIR/issue-111692.rs:30:13 - | -LL | if x == demo_module::Type { x: module::C, y: 1 } { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: surround the struct literal with parentheses - | -LL | if x == (demo_module::Type { x: module::C, y: 1 }) { - | + + - -error: aborting due to 4 previous errors - diff --git a/tests/ui/parser/struct-literals-in-invalid-places.rs b/tests/ui/parser/struct-literals-in-invalid-places.rs index 89cdb30fc04..eed51b94583 100644 --- a/tests/ui/parser/struct-literals-in-invalid-places.rs +++ b/tests/ui/parser/struct-literals-in-invalid-places.rs @@ -45,12 +45,30 @@ fn main() { println!("yo"); } + // This uses `one()` over `1` as token `one` may begin a type and thus back when type ascription + // `$expr : $ty` still existed, `{ x: one` could've been the start of a block expr which used to + // make the compiler take a different execution path. Now it no longer makes a difference tho. + // Regression test for . if Foo { x: one(), }.hi() { //~ ERROR struct literals are not allowed here println!("Positive!"); } + + const FOO: Foo = Foo { x: 1 }; + // Below, test that we correctly parenthesize the struct literals. + + // Regression test for . + if FOO == self::Foo { x: one() } {} //~ ERROR struct literals are not allowed here + + if FOO == Foo::<> { x: one() } {} //~ ERROR struct literals are not allowed here + + fn env>() { + if FOO == ::Out { x: one() } {} //~ ERROR struct literals are not allowed here + //~^ ERROR usage of qualified paths in this context is experimental + } } +#[derive(PartialEq, Eq)] struct Foo { x: isize, } @@ -70,3 +88,5 @@ enum E { } fn one() -> isize { 1 } + +trait Trait { type Out; } diff --git a/tests/ui/parser/struct-literals-in-invalid-places.stderr b/tests/ui/parser/struct-literals-in-invalid-places.stderr index ed094fc3e15..39dc2d2efb7 100644 --- a/tests/ui/parser/struct-literals-in-invalid-places.stderr +++ b/tests/ui/parser/struct-literals-in-invalid-places.stderr @@ -120,7 +120,7 @@ LL | while || (Foo { x: 3 }).hi() { | + + error: struct literals are not allowed here - --> $DIR/struct-literals-in-invalid-places.rs:49:8 + --> $DIR/struct-literals-in-invalid-places.rs:53:8 | LL | if Foo { x: one(), }.hi() { | ^^^^^^^^^^^^^^^^^ @@ -130,6 +130,49 @@ help: surround the struct literal with parentheses LL | if (Foo { x: one(), }).hi() { | + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:61:15 + | +LL | if FOO == self::Foo { x: one() } {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if FOO == (self::Foo { x: one() }) {} + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:63:15 + | +LL | if FOO == Foo::<> { x: one() } {} + | ^^^^^^^^^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if FOO == (Foo::<> { x: one() }) {} + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:66:19 + | +LL | if FOO == ::Out { x: one() } {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if FOO == (::Out { x: one() }) {} + | + + + +error[E0658]: usage of qualified paths in this context is experimental + --> $DIR/struct-literals-in-invalid-places.rs:66:19 + | +LL | if FOO == ::Out { x: one() } {} + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #86935 for more information + = help: add `#![feature(more_qualified_paths)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0277]: `bool` is not an iterator --> $DIR/struct-literals-in-invalid-places.rs:9:14 | @@ -185,7 +228,7 @@ help: use parentheses to call this closure LL | while (|| Foo { x: 3 }.hi())() { | + +++ -error: aborting due to 17 previous errors +error: aborting due to 21 previous errors -Some errors have detailed explanations: E0277, E0308, E0533. +Some errors have detailed explanations: E0277, E0308, E0533, E0658. For more information about an error, try `rustc --explain E0277`. From 813783e7115c58b5e70034f31be02201dac55407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 25 Mar 2025 13:43:25 +0100 Subject: [PATCH 09/14] Add diff of bootstrap steps --- src/build_helper/src/metrics.rs | 34 ++++++---- src/ci/citool/Cargo.lock | 7 ++ src/ci/citool/Cargo.toml | 1 + src/ci/citool/src/analysis.rs | 114 ++++++++++++++++++++++++++++++-- src/ci/citool/src/main.rs | 44 ++++++------ 5 files changed, 161 insertions(+), 39 deletions(-) diff --git a/src/build_helper/src/metrics.rs b/src/build_helper/src/metrics.rs index 4ab61245c16..fdff9cd18ce 100644 --- a/src/build_helper/src/metrics.rs +++ b/src/build_helper/src/metrics.rs @@ -109,6 +109,8 @@ pub struct BuildStep { pub r#type: String, pub children: Vec, pub duration: Duration, + // Full name of the step, including all parent names + pub full_name: String, } impl BuildStep { @@ -116,7 +118,7 @@ impl BuildStep { /// The most important thing is that the build step aggregates the /// durations of all children, so that it can be easily accessed. pub fn from_invocation(invocation: &JsonInvocation) -> Self { - fn parse(node: &JsonNode) -> Option { + fn parse(node: &JsonNode, parent_name: &str) -> Option { match node { JsonNode::RustbuildStep { type_: kind, @@ -124,11 +126,14 @@ impl BuildStep { duration_excluding_children_sec, .. } => { - let children: Vec<_> = children.into_iter().filter_map(parse).collect(); + let full_name = format!("{parent_name}-{kind}"); + let children: Vec<_> = + children.into_iter().filter_map(|s| parse(s, &full_name)).collect(); let children_duration = children.iter().map(|c| c.duration).sum::(); Some(BuildStep { r#type: kind.to_string(), children, + full_name, duration: children_duration + Duration::from_secs_f64(*duration_excluding_children_sec), }) @@ -138,8 +143,13 @@ impl BuildStep { } let duration = Duration::from_secs_f64(invocation.duration_including_children_sec); - let children: Vec<_> = invocation.children.iter().filter_map(parse).collect(); - Self { r#type: "total".to_string(), children, duration } + + // The root "total" step is kind of a virtual step that encompasses all other steps, + // but it is not a real parent of the other steps. + // We thus start the parent name hierarchy here and use an empty string + // as the parent name of the top-level steps. + let children: Vec<_> = invocation.children.iter().filter_map(|s| parse(s, "")).collect(); + Self { r#type: "total".to_string(), children, duration, full_name: "total".to_string() } } pub fn find_all_by_type(&self, r#type: &str) -> Vec<&Self> { @@ -159,7 +169,7 @@ impl BuildStep { /// Returns a Vec with all substeps, ordered by their hierarchical order. /// The first element of the tuple is the depth of a given step. - fn linearize_steps(&self) -> Vec<(u32, &BuildStep)> { + pub fn linearize_steps(&self) -> Vec<(u32, &BuildStep)> { let mut substeps: Vec<(u32, &BuildStep)> = Vec::new(); fn visit<'a>(step: &'a BuildStep, level: u32, substeps: &mut Vec<(u32, &'a BuildStep)>) { @@ -180,14 +190,14 @@ pub fn format_build_steps(root: &BuildStep) -> String { let mut output = String::new(); for (level, step) in root.linearize_steps() { - let label = format!( - "{}{}", - ".".repeat(level as usize), - // Bootstrap steps can be generic and thus contain angle brackets (<...>). - // However, Markdown interprets these as HTML, so we need to escap ethem. - step.r#type.replace('<', "<").replace('>', ">") - ); + let label = format!("{}{}", ".".repeat(level as usize), escape_step_name(step)); writeln!(output, "{label:.<65}{:>8.2}s", step.duration.as_secs_f64()).unwrap(); } output } + +/// Bootstrap steps can be generic and thus contain angle brackets (<...>). +/// However, Markdown interprets these as HTML, so we need to escap ethem. +pub fn escape_step_name(step: &BuildStep) -> String { + step.r#type.replace('<', "<").replace('>', ">") +} diff --git a/src/ci/citool/Cargo.lock b/src/ci/citool/Cargo.lock index c061ec6ebdc..800eaae0766 100644 --- a/src/ci/citool/Cargo.lock +++ b/src/ci/citool/Cargo.lock @@ -107,6 +107,7 @@ dependencies = [ "build_helper", "clap", "csv", + "diff", "glob-match", "insta", "serde", @@ -241,6 +242,12 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "displaydoc" version = "0.2.5" diff --git a/src/ci/citool/Cargo.toml b/src/ci/citool/Cargo.toml index dde09224afe..f18436a1263 100644 --- a/src/ci/citool/Cargo.toml +++ b/src/ci/citool/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" anyhow = "1" clap = { version = "4.5", features = ["derive"] } csv = "1" +diff = "0.1" glob-match = "0.2" serde = { version = "1", features = ["derive"] } serde_yaml = "0.9" diff --git a/src/ci/citool/src/analysis.rs b/src/ci/citool/src/analysis.rs index 2088ce29620..ccc030511cb 100644 --- a/src/ci/citool/src/analysis.rs +++ b/src/ci/citool/src/analysis.rs @@ -1,33 +1,134 @@ use std::collections::{BTreeMap, HashMap, HashSet}; +use std::fmt::Debug; use build_helper::metrics::{ - BuildStep, JsonRoot, TestOutcome, TestSuite, TestSuiteMetadata, format_build_steps, + BuildStep, JsonRoot, TestOutcome, TestSuite, TestSuiteMetadata, escape_step_name, + format_build_steps, }; use crate::metrics; use crate::metrics::{JobMetrics, JobName, get_test_suites}; use crate::utils::{output_details, pluralize}; -pub fn output_bootstrap_stats(metrics: &JsonRoot) { +/// Outputs durations of individual bootstrap steps from the gathered bootstrap invocations, +/// and also a table with summarized information about executed tests. +pub fn output_bootstrap_stats(metrics: &JsonRoot, parent_metrics: Option<&JsonRoot>) { if !metrics.invocations.is_empty() { println!("# Bootstrap steps"); - record_bootstrap_step_durations(&metrics); + record_bootstrap_step_durations(&metrics, parent_metrics); record_test_suites(&metrics); } } -fn record_bootstrap_step_durations(metrics: &JsonRoot) { +fn record_bootstrap_step_durations(metrics: &JsonRoot, parent_metrics: Option<&JsonRoot>) { + let parent_steps: HashMap = parent_metrics + .map(|metrics| { + metrics + .invocations + .iter() + .map(|invocation| { + (invocation.cmdline.clone(), BuildStep::from_invocation(invocation)) + }) + .collect() + }) + .unwrap_or_default(); + for invocation in &metrics.invocations { let step = BuildStep::from_invocation(invocation); let table = format_build_steps(&step); eprintln!("Step `{}`\n{table}\n", invocation.cmdline); - output_details(&invocation.cmdline, || { + output_details(&format!("{} (steps)", invocation.cmdline), || { println!("
{table}
"); }); + + // If there was a parent bootstrap invocation with the same cmdline, diff it + if let Some(parent_step) = parent_steps.get(&invocation.cmdline) { + let table = format_build_step_diffs(&step, parent_step); + + let duration_before = parent_step.duration.as_secs(); + let duration_after = step.duration.as_secs(); + output_details( + &format!("{} (diff) ({duration_before}s -> {duration_after}s)", invocation.cmdline), + || { + println!("{table}"); + }, + ); + } } eprintln!("Recorded {} bootstrap invocation(s)", metrics.invocations.len()); } +/// Creates a table that displays a diff between the durations of steps between +/// two bootstrap invocations. +/// It also shows steps that were missing before/after. +fn format_build_step_diffs(current: &BuildStep, parent: &BuildStep) -> String { + use std::fmt::Write; + + // Helper struct that compares steps by their full name + struct StepByName<'a>((u32, &'a BuildStep)); + + impl<'a> PartialEq for StepByName<'a> { + fn eq(&self, other: &Self) -> bool { + self.0.1.full_name.eq(&other.0.1.full_name) + } + } + + fn get_steps(step: &BuildStep) -> Vec { + step.linearize_steps().into_iter().map(|v| StepByName(v)).collect() + } + + let steps_before = get_steps(parent); + let steps_after = get_steps(current); + + let mut table = "| Step | Before | After | Change |\n".to_string(); + writeln!(table, "|:-----|-------:|------:|-------:|").unwrap(); + + // Try to match removed, added and same steps using a classic diff algorithm + for result in diff::slice(&steps_before, &steps_after) { + let (step, before, after, change) = match result { + // The step was found both before and after + diff::Result::Both(before, after) => { + let duration_before = before.0.1.duration; + let duration_after = after.0.1.duration; + let pct_change = duration_after.as_secs_f64() / duration_before.as_secs_f64(); + let pct_change = pct_change * 100.0; + // Normalize around 100, to get + for regression and - for improvements + let pct_change = pct_change - 100.0; + ( + before, + format!("{:.2}s", duration_before.as_secs_f64()), + format!("{:.2}s", duration_after.as_secs_f64()), + format!("{pct_change:.1}%"), + ) + } + // The step was only found in the parent, so it was likely removed + diff::Result::Left(removed) => ( + removed, + format!("{:.2}s", removed.0.1.duration.as_secs_f64()), + "".to_string(), + "(removed)".to_string(), + ), + // The step was only found in the current commit, so it was likely added + diff::Result::Right(added) => ( + added, + "".to_string(), + format!("{:.2}s", added.0.1.duration.as_secs_f64()), + "(added)".to_string(), + ), + }; + + let prefix = ".".repeat(step.0.0 as usize); + writeln!( + table, + "| {prefix}{} | {before} | {after} | {change} |", + escape_step_name(step.0.1), + ) + .unwrap(); + } + + table +} + fn record_test_suites(metrics: &JsonRoot) { let suites = metrics::get_test_suites(&metrics); @@ -82,8 +183,7 @@ fn render_table(suites: BTreeMap) -> String { table } -/// Computes a post merge CI analysis report of test differences -/// between the `parent` and `current` commits. +/// Outputs a report of test differences between the `parent` and `current` commits. pub fn output_test_diffs(job_metrics: HashMap) { let aggregated_test_diffs = aggregate_test_diffs(&job_metrics); report_test_diffs(aggregated_test_diffs); diff --git a/src/ci/citool/src/main.rs b/src/ci/citool/src/main.rs index 5a84ecb5e47..5f5c50dc43a 100644 --- a/src/ci/citool/src/main.rs +++ b/src/ci/citool/src/main.rs @@ -144,31 +144,35 @@ fn postprocess_metrics( job_name: Option, ) -> anyhow::Result<()> { let metrics = load_metrics(&metrics_path)?; - output_bootstrap_stats(&metrics); - let (Some(parent), Some(job_name)) = (parent, job_name) else { - return Ok(()); - }; + if let (Some(parent), Some(job_name)) = (parent, job_name) { + // This command is executed also on PR builds, which might not have parent metrics + // available, because some PR jobs don't run on auto builds, and PR jobs do not upload metrics + // due to missing permissions. + // To avoid having to detect if this is a PR job, and to avoid having failed steps in PR jobs, + // we simply print an error if the parent metrics were not found, but otherwise exit + // successfully. + match download_job_metrics(&job_name, &parent).context("cannot download parent metrics") { + Ok(parent_metrics) => { + output_bootstrap_stats(&metrics, Some(&parent_metrics)); - // This command is executed also on PR builds, which might not have parent metrics - // available, because some PR jobs don't run on auto builds, and PR jobs do not upload metrics - // due to missing permissions. - // To avoid having to detect if this is a PR job, and to avoid having failed steps in PR jobs, - // we simply print an error if the parent metrics were not found, but otherwise exit - // successfully. - match download_job_metrics(&job_name, &parent).context("cannot download parent metrics") { - Ok(parent_metrics) => { - let job_metrics = HashMap::from([( - job_name, - JobMetrics { parent: Some(parent_metrics), current: metrics }, - )]); - output_test_diffs(job_metrics); - } - Err(error) => { - eprintln!("Metrics for job `{job_name}` and commit `{parent}` not found: {error:?}"); + let job_metrics = HashMap::from([( + job_name, + JobMetrics { parent: Some(parent_metrics), current: metrics }, + )]); + output_test_diffs(job_metrics); + return Ok(()); + } + Err(error) => { + eprintln!( + "Metrics for job `{job_name}` and commit `{parent}` not found: {error:?}" + ); + } } } + output_bootstrap_stats(&metrics, None); + Ok(()) } From 4b22ac5296a7a40aebfc997eec122fbfe50164da Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 26 Mar 2025 00:15:24 +0000 Subject: [PATCH 10/14] Ensure define_opaque is accounted for in HIR hash --- compiler/rustc_ast_lowering/src/lib.rs | 2 +- compiler/rustc_middle/src/hir/mod.rs | 7 ++++++- compiler/rustc_middle/src/ty/context.rs | 3 ++- tests/incremental/define-opaques.rs | 19 +++++++++++++++++++ 4 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 tests/incremental/define-opaques.rs diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 6c380d77b49..a946f956225 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -625,7 +625,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Don't hash unless necessary, because it's expensive. let (opt_hash_including_bodies, attrs_hash) = - self.tcx.hash_owner_nodes(node, &bodies, &attrs); + self.tcx.hash_owner_nodes(node, &bodies, &attrs, define_opaque); let num_nodes = self.item_local_id_counter.as_usize(); let (nodes, parenting) = index::index_hir(self.tcx, node, &bodies, num_nodes); let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies }; diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 68b9a4f56b9..347bc5ea312 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -14,7 +14,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::*; use rustc_macros::{Decodable, Encodable, HashStable}; -use rustc_span::{ErrorGuaranteed, ExpnId}; +use rustc_span::{ErrorGuaranteed, ExpnId, Span}; use crate::query::Providers; use crate::ty::{EarlyBinder, ImplSubject, TyCtxt}; @@ -157,6 +157,7 @@ impl<'tcx> TyCtxt<'tcx> { node: OwnerNode<'_>, bodies: &SortedMap>, attrs: &SortedMap, + define_opaque: Option<&[(Span, LocalDefId)]>, ) -> (Option, Option) { if self.needs_crate_hash() { self.with_stable_hashing_context(|mut hcx| { @@ -168,6 +169,10 @@ impl<'tcx> TyCtxt<'tcx> { let mut stable_hasher = StableHasher::new(); attrs.hash_stable(&mut hcx, &mut stable_hasher); + + // Hash the defined opaque types, which are not present in the attrs. + define_opaque.hash_stable(&mut hcx, &mut stable_hasher); + let h2 = stable_hasher.finish(); (Some(h1), Some(h2)) }) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index f54dd2b0040..834d1f2a4a8 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1288,7 +1288,8 @@ impl<'tcx> TyCtxtFeed<'tcx, LocalDefId> { let bodies = Default::default(); let attrs = hir::AttributeMap::EMPTY; - let (opt_hash_including_bodies, _) = self.tcx.hash_owner_nodes(node, &bodies, &attrs.map); + let (opt_hash_including_bodies, _) = + self.tcx.hash_owner_nodes(node, &bodies, &attrs.map, attrs.define_opaque); let node = node.into(); self.opt_hir_owner_nodes(Some(self.tcx.arena.alloc(hir::OwnerNodes { opt_hash_including_bodies, diff --git a/tests/incremental/define-opaques.rs b/tests/incremental/define-opaques.rs new file mode 100644 index 00000000000..d6eae238341 --- /dev/null +++ b/tests/incremental/define-opaques.rs @@ -0,0 +1,19 @@ +//@ revisions: rpass1 cfail2 + +#![feature(type_alias_impl_trait)] + +pub type Foo = impl Sized; + +#[cfg_attr(rpass1, define_opaque())] +#[cfg_attr(cfail2, define_opaque(Foo))] +fn a() { + //[cfail2]~^ ERROR item does not constrain `Foo::{opaque#0}` + let _: Foo = b(); +} + +#[define_opaque(Foo)] +fn b() -> Foo { + () +} + +fn main() {} From 0c162b19ec0bcf1488a2f797404de594e347e053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maja=20K=C4=85dzio=C5=82ka?= Date: Wed, 26 Mar 2025 02:09:13 +0100 Subject: [PATCH 11/14] Revert "Make `MatchPairTree::place` non-optional" This reverts commit e3e74bc89a958e36c658fa809d98b4dd66d53cf8. The comment that was used to justify the change was outdated. --- .../src/builder/matches/match_pair.rs | 3 +-- compiler/rustc_mir_build/src/builder/matches/mod.rs | 12 ++++++++++-- compiler/rustc_mir_build/src/builder/matches/test.rs | 9 ++++++--- compiler/rustc_mir_build/src/builder/matches/util.rs | 8 ++++++-- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index f2ce0c46dd3..29d400a957b 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -115,7 +115,6 @@ impl<'tcx> MatchPairTree<'tcx> { place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty)); } - // Place can be none if the pattern refers to a non-captured place in a closure. let place = place_builder.try_to_place(cx); let mut subpairs = Vec::new(); let test_case = match pattern.kind { @@ -321,7 +320,7 @@ impl<'tcx> MatchPairTree<'tcx> { if let Some(test_case) = test_case { // This pattern is refutable, so push a new match-pair node. match_pairs.push(MatchPairTree { - place: place.expect("refutable patterns should always have a place to inspect"), + place, test_case, subpairs, pattern_ty: pattern.ty, diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index ea341b604e0..56671763977 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -1279,7 +1279,13 @@ impl<'tcx> TestCase<'tcx> { #[derive(Debug, Clone)] pub(crate) struct MatchPairTree<'tcx> { /// This place... - place: Place<'tcx>, + /// + /// --- + /// This can be `None` if it referred to a non-captured place in a closure. + /// + /// Invariant: Can only be `None` when `test_case` is `Irrefutable`. + /// Therefore this must be `Some(_)` after simplification. + place: Option>, /// ... must pass this test... test_case: TestCase<'tcx>, @@ -2099,9 +2105,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Extract the match-pair from the highest priority candidate let match_pair = &candidates[0].match_pairs[0]; let test = self.pick_test_for_match_pair(match_pair); + // Unwrap is ok after simplification. + let match_place = match_pair.place.unwrap(); debug!(?test, ?match_pair); - (match_pair.place, test) + (match_place, test) } /// Given a test, we partition the input candidates into several buckets. diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index e5d61bc9e55..f92036a83e1 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -540,8 +540,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // than one, but it'd be very unusual to have two sides that // both require tests; you'd expect one side to be simplified // away.) - let (match_pair_index, match_pair) = - candidate.match_pairs.iter().enumerate().find(|&(_, mp)| mp.place == test_place)?; + let (match_pair_index, match_pair) = candidate + .match_pairs + .iter() + .enumerate() + .find(|&(_, mp)| mp.place == Some(test_place))?; // If true, the match pair is completely entailed by its corresponding test // branch, so it can be removed. If false, the match pair is _compatible_ @@ -584,7 +587,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate .match_pairs .iter() - .any(|mp| mp.place == test_place && is_covering_range(&mp.test_case)) + .any(|mp| mp.place == Some(test_place) && is_covering_range(&mp.test_case)) }; if sorted_candidates .get(&TestBranch::Failure) diff --git a/compiler/rustc_mir_build/src/builder/matches/util.rs b/compiler/rustc_mir_build/src/builder/matches/util.rs index ed3b652e4ef..589e350a03f 100644 --- a/compiler/rustc_mir_build/src/builder/matches/util.rs +++ b/compiler/rustc_mir_build/src/builder/matches/util.rs @@ -173,10 +173,14 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { // } // ``` // Hence we fake borrow using a deep borrow. - self.fake_borrow(match_pair.place, FakeBorrowKind::Deep); + if let Some(place) = match_pair.place { + self.fake_borrow(place, FakeBorrowKind::Deep); + } } else { // Insert a Shallow borrow of any place that is switched on. - self.fake_borrow(match_pair.place, FakeBorrowKind::Shallow); + if let Some(place) = match_pair.place { + self.fake_borrow(place, FakeBorrowKind::Shallow); + } for subpair in &match_pair.subpairs { self.visit_match_pair(subpair); From c8a5b3677be3749b9f3eb9fafd912f8c4be5912e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maja=20K=C4=85dzio=C5=82ka?= Date: Wed, 26 Mar 2025 02:18:13 +0100 Subject: [PATCH 12/14] MatchPairTree: update invariant comment --- compiler/rustc_mir_build/src/builder/matches/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 56671763977..fb1d067865c 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -1283,8 +1283,8 @@ pub(crate) struct MatchPairTree<'tcx> { /// --- /// This can be `None` if it referred to a non-captured place in a closure. /// - /// Invariant: Can only be `None` when `test_case` is `Irrefutable`. - /// Therefore this must be `Some(_)` after simplification. + /// Invariant: Can only be `None` when `test_case` is `Or`. + /// Therefore this must be `Some(_)` after or-pattern expansion. place: Option>, /// ... must pass this test... From 1524060da6f65332917b6c62f7b68eadd0be2032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maja=20K=C4=85dzio=C5=82ka?= Date: Wed, 26 Mar 2025 02:23:13 +0100 Subject: [PATCH 13/14] Add a regression test for TestCase::Or without place --- tests/ui/closures/upvar-or-pattern-issue-138958.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/ui/closures/upvar-or-pattern-issue-138958.rs diff --git a/tests/ui/closures/upvar-or-pattern-issue-138958.rs b/tests/ui/closures/upvar-or-pattern-issue-138958.rs new file mode 100644 index 00000000000..fe9ebc0a7e6 --- /dev/null +++ b/tests/ui/closures/upvar-or-pattern-issue-138958.rs @@ -0,0 +1,11 @@ +//@ edition:2024 +//@ check-pass + +pub fn f(x: (u32, u32)) { + let _ = || { + let ((0, a) | (a, _)) = x; + a + }; +} + +fn main() {} From a475f5d18169c5333a97d9a5583be61f249b8f22 Mon Sep 17 00:00:00 2001 From: Thalia Archibald Date: Sun, 16 Mar 2025 16:47:26 -0700 Subject: [PATCH 14/14] Fix typo in error message --- library/std/src/sys/net/connection/xous/tcpstream.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/net/connection/xous/tcpstream.rs b/library/std/src/sys/net/connection/xous/tcpstream.rs index 283b1fe9a33..e8aea8b706a 100644 --- a/library/std/src/sys/net/connection/xous/tcpstream.rs +++ b/library/std/src/sys/net/connection/xous/tcpstream.rs @@ -324,7 +324,7 @@ impl TcpStream { } Ok(SocketAddr::V6(SocketAddrV6::new(new_addr.into(), self.local_port, 0, 0))) } - _ => Err(io::const_error!(io::ErrorKind::InvalidInput, "tnternal error")), + _ => Err(io::const_error!(io::ErrorKind::InvalidInput, "internal error")), } }