From ee7413b94cd0f953518ad73f37cac28feb6e8d52 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Sat, 19 Mar 2022 12:04:59 -0400 Subject: [PATCH 1/8] Implement `apply_switch_int_edge_effects` for backward analyses --- .../src/framework/direction.rs | 60 +++++++++++++++++-- .../rustc_mir_dataflow/src/framework/mod.rs | 2 - compiler/rustc_mir_dataflow/src/lib.rs | 2 +- 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 102e7439772..1ad2722d91b 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -248,6 +248,7 @@ impl Direction for Backward { ); propagate(pred, &tmp); } + mir::TerminatorKind::InlineAsm { destination: Some(dest), ref operands, .. } if dest == bb => { @@ -266,6 +267,25 @@ impl Direction for Backward { propagate(pred, &tmp); } + mir::TerminatorKind::SwitchInt { ref targets, ref discr, switch_ty: _ } => { + let mut applier = BackwardSwitchIntEdgeEffectsApplier { + pred, + exit_state, + targets, + bb, + propagate: &mut propagate, + effects_applied: false, + }; + + analysis.apply_switch_int_edge_effects(pred, discr, &mut applier); + + let BackwardSwitchIntEdgeEffectsApplier { effects_applied, .. } = applier; + + if !effects_applied { + propagate(pred, exit_state) + } + } + // Ignore dead unwinds. mir::TerminatorKind::Call { cleanup: Some(unwind), .. } | mir::TerminatorKind::Assert { cleanup: Some(unwind), .. } @@ -286,6 +306,33 @@ impl Direction for Backward { } } +struct BackwardSwitchIntEdgeEffectsApplier<'a, D, F> { + pred: BasicBlock, + exit_state: &'a mut D, + targets: &'a SwitchTargets, + bb: BasicBlock, + propagate: &'a mut F, + + effects_applied: bool, +} + +impl super::SwitchIntEdgeEffects for BackwardSwitchIntEdgeEffectsApplier<'_, D, F> +where + D: Clone, + F: FnMut(BasicBlock, &D), +{ + fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) { + assert!(!self.effects_applied); + + let value = + self.targets.iter().find_map(|(value, target)| (target == self.bb).then_some(value)); + apply_edge_effect(self.exit_state, SwitchIntTarget { value, target: self.bb }); + (self.propagate)(self.pred, self.exit_state); + + self.effects_applied = true; + } +} + /// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator). pub struct Forward; @@ -528,7 +575,7 @@ impl Direction for Forward { } SwitchInt { ref targets, ref discr, switch_ty: _ } => { - let mut applier = SwitchIntEdgeEffectApplier { + let mut applier = ForwardSwitchIntEdgeEffectsApplier { exit_state, targets, propagate, @@ -537,8 +584,11 @@ impl Direction for Forward { analysis.apply_switch_int_edge_effects(bb, discr, &mut applier); - let SwitchIntEdgeEffectApplier { - exit_state, mut propagate, effects_applied, .. + let ForwardSwitchIntEdgeEffectsApplier { + exit_state, + mut propagate, + effects_applied, + .. } = applier; if !effects_applied { @@ -551,7 +601,7 @@ impl Direction for Forward { } } -struct SwitchIntEdgeEffectApplier<'a, D, F> { +struct ForwardSwitchIntEdgeEffectsApplier<'a, D, F> { exit_state: &'a mut D, targets: &'a SwitchTargets, propagate: F, @@ -559,7 +609,7 @@ struct SwitchIntEdgeEffectApplier<'a, D, F> { effects_applied: bool, } -impl super::SwitchIntEdgeEffects for SwitchIntEdgeEffectApplier<'_, D, F> +impl super::SwitchIntEdgeEffects for ForwardSwitchIntEdgeEffectsApplier<'_, D, F> where D: Clone, F: FnMut(BasicBlock, &D), diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index c51dd06de25..67c16e6c084 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -234,8 +234,6 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// about a given `SwitchInt` terminator for each one of its edges—and more efficient—the /// engine doesn't need to clone the exit state for a block unless /// `SwitchIntEdgeEffects::apply` is actually called. - /// - /// FIXME: This class of effects is not supported for backward dataflow analyses. fn apply_switch_int_edge_effects( &self, _block: BasicBlock, diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index 6c2d1b85646..c221b358670 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -28,7 +28,7 @@ pub use self::drop_flag_effects::{ pub use self::framework::{ fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, Backward, CallReturnPlaces, Direction, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, ResultsCursor, - ResultsRefCursor, ResultsVisitable, ResultsVisitor, + ResultsRefCursor, ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects, }; use self::move_paths::MoveData; From 37ebd47ddb1398c7b0f19aeb888417fbcf2d5992 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Fri, 25 Mar 2022 21:11:49 -0400 Subject: [PATCH 2/8] Address review comments * Add lazily computed `switch_sources` data structure * Don't assume a target has only one associated value --- compiler/rustc_middle/src/mir/mod.rs | 13 +++ .../rustc_middle/src/mir/switch_sources.rs | 82 +++++++++++++++++++ .../src/framework/direction.rs | 18 ++-- 3 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 compiler/rustc_middle/src/mir/switch_sources.rs diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index a5468b3f4f2..455387901b6 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -45,6 +45,7 @@ use std::{iter, mem, option}; use self::graph_cyclic_cache::GraphIsCyclicCache; use self::predecessors::{PredecessorCache, Predecessors}; pub use self::query::*; +use self::switch_sources::{SwitchSourceCache, SwitchSources}; pub mod coverage; mod generic_graph; @@ -58,6 +59,7 @@ mod predecessors; pub mod pretty; mod query; pub mod spanview; +mod switch_sources; pub mod tcx; pub mod terminator; pub use terminator::*; @@ -284,6 +286,7 @@ pub struct Body<'tcx> { pub is_polymorphic: bool, predecessor_cache: PredecessorCache, + switch_source_cache: SwitchSourceCache, is_cyclic: GraphIsCyclicCache, pub tainted_by_errors: Option, @@ -332,6 +335,7 @@ impl<'tcx> Body<'tcx> { required_consts: Vec::new(), is_polymorphic: false, predecessor_cache: PredecessorCache::new(), + switch_source_cache: SwitchSourceCache::new(), is_cyclic: GraphIsCyclicCache::new(), tainted_by_errors, }; @@ -360,6 +364,7 @@ impl<'tcx> Body<'tcx> { var_debug_info: Vec::new(), is_polymorphic: false, predecessor_cache: PredecessorCache::new(), + switch_source_cache: SwitchSourceCache::new(), is_cyclic: GraphIsCyclicCache::new(), tainted_by_errors: None, }; @@ -380,6 +385,7 @@ impl<'tcx> Body<'tcx> { // FIXME: Use a finer-grained API for this, so only transformations that alter terminators // invalidate the caches. self.predecessor_cache.invalidate(); + self.switch_source_cache.invalidate(); self.is_cyclic.invalidate(); &mut self.basic_blocks } @@ -389,6 +395,7 @@ impl<'tcx> Body<'tcx> { &mut self, ) -> (&mut IndexVec>, &mut LocalDecls<'tcx>) { self.predecessor_cache.invalidate(); + self.switch_source_cache.invalidate(); self.is_cyclic.invalidate(); (&mut self.basic_blocks, &mut self.local_decls) } @@ -402,6 +409,7 @@ impl<'tcx> Body<'tcx> { &mut Vec>, ) { self.predecessor_cache.invalidate(); + self.switch_source_cache.invalidate(); self.is_cyclic.invalidate(); (&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info) } @@ -529,6 +537,11 @@ impl<'tcx> Body<'tcx> { self.predecessor_cache.compute(&self.basic_blocks) } + #[inline] + pub fn switch_sources(&self) -> &SwitchSources { + self.switch_source_cache.compute(&self.basic_blocks) + } + #[inline] pub fn dominators(&self) -> Dominators { dominators(self) diff --git a/compiler/rustc_middle/src/mir/switch_sources.rs b/compiler/rustc_middle/src/mir/switch_sources.rs new file mode 100644 index 00000000000..7f62b4d0dba --- /dev/null +++ b/compiler/rustc_middle/src/mir/switch_sources.rs @@ -0,0 +1,82 @@ +//! Lazily compute the inverse of each `SwitchInt`'s switch targets. Modeled after +//! `Predecessors`/`PredecessorCache`. + +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::OnceCell; +use rustc_index::vec::IndexVec; +use rustc_serialize as serialize; +use smallvec::SmallVec; + +use crate::mir::{BasicBlock, BasicBlockData, Terminator, TerminatorKind}; + +pub type SwitchSources = IndexVec; 1]>>>; + +#[derive(Clone, Debug)] +pub(super) struct SwitchSourceCache { + cache: OnceCell, +} + +impl SwitchSourceCache { + #[inline] + pub(super) fn new() -> Self { + SwitchSourceCache { cache: OnceCell::new() } + } + + /// Invalidates the switch source cache. + #[inline] + pub(super) fn invalidate(&mut self) { + self.cache = OnceCell::new(); + } + + /// Returns the switch sources for this MIR. + #[inline] + pub(super) fn compute( + &self, + basic_blocks: &IndexVec>, + ) -> &SwitchSources { + self.cache.get_or_init(|| { + let mut switch_sources = IndexVec::from_elem( + IndexVec::from_elem(SmallVec::new(), basic_blocks), + basic_blocks, + ); + for (bb, data) in basic_blocks.iter_enumerated() { + if let Some(Terminator { + kind: TerminatorKind::SwitchInt { targets, .. }, .. + }) = &data.terminator + { + for (value, target) in targets.iter() { + switch_sources[target][bb].push(Some(value)); + } + switch_sources[targets.otherwise()][bb].push(None); + } + } + + switch_sources + }) + } +} + +impl serialize::Encodable for SwitchSourceCache { + #[inline] + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_unit() + } +} + +impl serialize::Decodable for SwitchSourceCache { + #[inline] + fn decode(_: &mut D) -> Self { + Self::new() + } +} + +impl HashStable for SwitchSourceCache { + #[inline] + fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) { + // do nothing + } +} + +TrivialTypeFoldableAndLiftImpls! { + SwitchSourceCache, +} diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 1ad2722d91b..d7f531f7de7 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -267,11 +267,11 @@ impl Direction for Backward { propagate(pred, &tmp); } - mir::TerminatorKind::SwitchInt { ref targets, ref discr, switch_ty: _ } => { + mir::TerminatorKind::SwitchInt { targets: _, ref discr, switch_ty: _ } => { let mut applier = BackwardSwitchIntEdgeEffectsApplier { pred, exit_state, - targets, + values: &body.switch_sources()[bb][pred], bb, propagate: &mut propagate, effects_applied: false, @@ -309,7 +309,7 @@ impl Direction for Backward { struct BackwardSwitchIntEdgeEffectsApplier<'a, D, F> { pred: BasicBlock, exit_state: &'a mut D, - targets: &'a SwitchTargets, + values: &'a [Option], bb: BasicBlock, propagate: &'a mut F, @@ -324,10 +324,14 @@ where fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) { assert!(!self.effects_applied); - let value = - self.targets.iter().find_map(|(value, target)| (target == self.bb).then_some(value)); - apply_edge_effect(self.exit_state, SwitchIntTarget { value, target: self.bb }); - (self.propagate)(self.pred, self.exit_state); + let targets = self.values.iter().map(|&value| SwitchIntTarget { value, target: self.bb }); + + let mut tmp = None; + for target in targets { + let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state); + apply_edge_effect(tmp, target); + (self.propagate)(self.pred, tmp); + } self.effects_applied = true; } From eae29031504d62769b94dffca4aba3d78a6fafae Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 27 Mar 2022 13:58:06 +0200 Subject: [PATCH 3/8] Add long error explanation for E0667 --- compiler/rustc_error_codes/src/error_codes.rs | 2 +- .../rustc_error_codes/src/error_codes/E0667.md | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 compiler/rustc_error_codes/src/error_codes/E0667.md diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index a185902123d..61a177f291b 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -394,6 +394,7 @@ E0663: include_str!("./error_codes/E0663.md"), E0664: include_str!("./error_codes/E0664.md"), E0665: include_str!("./error_codes/E0665.md"), E0666: include_str!("./error_codes/E0666.md"), +E0667: include_str!("./error_codes/E0667.md"), E0668: include_str!("./error_codes/E0668.md"), E0669: include_str!("./error_codes/E0669.md"), E0670: include_str!("./error_codes/E0670.md"), @@ -633,7 +634,6 @@ E0787: include_str!("./error_codes/E0787.md"), // attribute E0640, // infer outlives requirements // E0645, // trait aliases not finished - E0667, // `impl Trait` in projections // E0694, // an unknown tool name found in scoped attributes // E0702, // replaced with a generic attribute input check // E0707, // multiple elided lifetimes used in arguments of `async fn` diff --git a/compiler/rustc_error_codes/src/error_codes/E0667.md b/compiler/rustc_error_codes/src/error_codes/E0667.md new file mode 100644 index 00000000000..0709a24c433 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0667.md @@ -0,0 +1,18 @@ +`impl Trait` is not allowed in path parameters. + +Erroneous code example: + +```compile_fail,E0667 +fn some_fn(mut x: impl Iterator) -> ::Item { // error! + x.next().unwrap() +} +``` + +You cannot use `impl Trait` in path parameters. If you want something +equivalent, you can do this instead: + +``` +fn some_fn(mut x: T) -> T::Item { // ok! + x.next().unwrap() +} +``` From 3a5102986d2e4fcd70e0f03f75bd8793775c29e3 Mon Sep 17 00:00:00 2001 From: Caio Date: Sun, 27 Mar 2022 09:19:34 -0300 Subject: [PATCH 4/8] Remove duplicated test files --- .../anon-extern-mod-cross-crate-1.rs | 9 ----- src/test/ui/abi/auxiliary/foreign_lib.rs | 37 ------------------- .../anon-extern-mod-cross-crate-1.rs | 9 ----- .../duplicated-external-mods.rs | 0 .../{ => foreign}/invoke-external-foreign.rs | 0 .../ui/consts/auxiliary/cci_borrow_lib.rs | 3 -- src/test/ui/consts/auxiliary/cci_const.rs | 6 --- .../const-cross-crate-const.rs | 0 .../const-cross-crate-extern.rs | 0 9 files changed, 64 deletions(-) delete mode 100644 src/test/ui/abi/auxiliary/anon-extern-mod-cross-crate-1.rs delete mode 100644 src/test/ui/abi/auxiliary/foreign_lib.rs delete mode 100644 src/test/ui/abi/consts/auxiliary/anon-extern-mod-cross-crate-1.rs rename src/test/ui/abi/{ => cross-crate}/duplicated-external-mods.rs (100%) rename src/test/ui/abi/{ => foreign}/invoke-external-foreign.rs (100%) delete mode 100644 src/test/ui/consts/auxiliary/cci_borrow_lib.rs delete mode 100644 src/test/ui/consts/auxiliary/cci_const.rs rename src/test/ui/{consts => cross-crate}/const-cross-crate-const.rs (100%) rename src/test/ui/{consts => cross-crate}/const-cross-crate-extern.rs (100%) diff --git a/src/test/ui/abi/auxiliary/anon-extern-mod-cross-crate-1.rs b/src/test/ui/abi/auxiliary/anon-extern-mod-cross-crate-1.rs deleted file mode 100644 index 5cbf8093c5c..00000000000 --- a/src/test/ui/abi/auxiliary/anon-extern-mod-cross-crate-1.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![crate_name = "anonexternmod"] -#![feature(rustc_private)] - -extern crate libc; - -#[link(name = "rust_test_helpers", kind = "static")] -extern "C" { - pub fn rust_get_test_int() -> libc::intptr_t; -} diff --git a/src/test/ui/abi/auxiliary/foreign_lib.rs b/src/test/ui/abi/auxiliary/foreign_lib.rs deleted file mode 100644 index 3c649b778bd..00000000000 --- a/src/test/ui/abi/auxiliary/foreign_lib.rs +++ /dev/null @@ -1,37 +0,0 @@ -#![crate_name = "foreign_lib"] -#![feature(rustc_private)] - -pub mod rustrt { - extern crate libc; - - #[link(name = "rust_test_helpers", kind = "static")] - extern "C" { - pub fn rust_get_test_int() -> libc::intptr_t; - } -} - -pub mod rustrt2 { - extern crate libc; - - extern "C" { - pub fn rust_get_test_int() -> libc::intptr_t; - } -} - -pub mod rustrt3 { - // Different type, but same ABI (on all supported platforms). - // Ensures that we don't ICE or trigger LLVM asserts when - // importing the same symbol under different types. - // See https://github.com/rust-lang/rust/issues/32740. - extern "C" { - pub fn rust_get_test_int() -> *const u8; - } -} - -pub fn local_uses() { - unsafe { - let x = rustrt::rust_get_test_int(); - assert_eq!(x, rustrt2::rust_get_test_int()); - assert_eq!(x as *const _, rustrt3::rust_get_test_int()); - } -} diff --git a/src/test/ui/abi/consts/auxiliary/anon-extern-mod-cross-crate-1.rs b/src/test/ui/abi/consts/auxiliary/anon-extern-mod-cross-crate-1.rs deleted file mode 100644 index 5cbf8093c5c..00000000000 --- a/src/test/ui/abi/consts/auxiliary/anon-extern-mod-cross-crate-1.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![crate_name = "anonexternmod"] -#![feature(rustc_private)] - -extern crate libc; - -#[link(name = "rust_test_helpers", kind = "static")] -extern "C" { - pub fn rust_get_test_int() -> libc::intptr_t; -} diff --git a/src/test/ui/abi/duplicated-external-mods.rs b/src/test/ui/abi/cross-crate/duplicated-external-mods.rs similarity index 100% rename from src/test/ui/abi/duplicated-external-mods.rs rename to src/test/ui/abi/cross-crate/duplicated-external-mods.rs diff --git a/src/test/ui/abi/invoke-external-foreign.rs b/src/test/ui/abi/foreign/invoke-external-foreign.rs similarity index 100% rename from src/test/ui/abi/invoke-external-foreign.rs rename to src/test/ui/abi/foreign/invoke-external-foreign.rs diff --git a/src/test/ui/consts/auxiliary/cci_borrow_lib.rs b/src/test/ui/consts/auxiliary/cci_borrow_lib.rs deleted file mode 100644 index 7c57a1c6678..00000000000 --- a/src/test/ui/consts/auxiliary/cci_borrow_lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub fn foo(x: &usize) -> usize { - *x -} diff --git a/src/test/ui/consts/auxiliary/cci_const.rs b/src/test/ui/consts/auxiliary/cci_const.rs deleted file mode 100644 index c83b3f4a5bb..00000000000 --- a/src/test/ui/consts/auxiliary/cci_const.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub extern "C" fn bar() { -} - -pub const foopy: &'static str = "hi there"; -pub const uint_val: usize = 12; -pub const uint_expr: usize = (1 << uint_val) - 1; diff --git a/src/test/ui/consts/const-cross-crate-const.rs b/src/test/ui/cross-crate/const-cross-crate-const.rs similarity index 100% rename from src/test/ui/consts/const-cross-crate-const.rs rename to src/test/ui/cross-crate/const-cross-crate-const.rs diff --git a/src/test/ui/consts/const-cross-crate-extern.rs b/src/test/ui/cross-crate/const-cross-crate-extern.rs similarity index 100% rename from src/test/ui/consts/const-cross-crate-extern.rs rename to src/test/ui/cross-crate/const-cross-crate-extern.rs From 1ba885113af527a3034c8414c288da845ac5c84d Mon Sep 17 00:00:00 2001 From: Ryan Lopopolo Date: Sun, 27 Mar 2022 06:53:55 -0700 Subject: [PATCH 5/8] Fix typo in `String::try_reserve_exact` docs Copying the pattern from `Vec::try_reserve_exact` and `String::try_reserve`, it looks like this doc comment is intending to refer to the currently-being-documented function. --- library/alloc/src/string.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 7c0faf0659a..30eabcb95ec 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1038,7 +1038,7 @@ impl String { } /// Tries to reserve the minimum capacity for exactly `additional` more elements to - /// be inserted in the given `String`. After calling `reserve_exact`, + /// be inserted in the given `String`. After calling `try_reserve_exact`, /// capacity will be greater than or equal to `self.len() + additional`. /// Does nothing if the capacity is already sufficient. /// From 301fc070ff4506df463332b45066fa5e2ad45803 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 14 Mar 2022 11:46:12 -0700 Subject: [PATCH 6/8] diagnostics: suggest missing comma in bad FRU syntax Fixes #51103 --- compiler/rustc_typeck/src/check/expr.rs | 60 +++++++++++++++++-- .../ui/structs/struct-record-suggestion.fixed | 16 +++++ .../ui/structs/struct-record-suggestion.rs | 16 +++++ .../structs/struct-record-suggestion.stderr | 24 ++++++++ 4 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 src/test/ui/structs/struct-record-suggestion.fixed create mode 100644 src/test/ui/structs/struct-record-suggestion.rs create mode 100644 src/test/ui/structs/struct-record-suggestion.stderr diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 0a720f15025..e5784259ce8 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -32,6 +32,7 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; +use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, HirId, QPath}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -1556,7 +1557,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if inaccessible_remaining_fields { self.report_inaccessible_fields(adt_ty, span); } else { - self.report_missing_fields(adt_ty, span, remaining_fields); + self.report_missing_fields( + adt_ty, + span, + remaining_fields, + variant, + ast_fields, + substs, + ); } } } @@ -1590,6 +1598,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { adt_ty: Ty<'tcx>, span: Span, remaining_fields: FxHashMap, + variant: &'tcx ty::VariantDef, + ast_fields: &'tcx [hir::ExprField<'tcx>], + substs: SubstsRef<'tcx>, ) { let len = remaining_fields.len(); @@ -1615,7 +1626,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - struct_span_err!( + let mut err = struct_span_err!( self.tcx.sess, span, E0063, @@ -1624,9 +1635,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { remaining_fields_names, truncated_fields_error, adt_ty - ) - .span_label(span, format!("missing {}{}", remaining_fields_names, truncated_fields_error)) - .emit(); + ); + err.span_label( + span, + format!("missing {}{}", remaining_fields_names, truncated_fields_error), + ); + + // If the last field is a range literal, but it isn't supposed to be, then they probably + // meant to use functional update syntax. + // + // I don't use 'is_range_literal' because only double-sided, half-open ranges count. + if let Some(( + last, + ExprKind::Struct( + QPath::LangItem(LangItem::Range, ..), + &[ref range_start, ref range_end], + _, + ), + )) = ast_fields.last().map(|last| (last, &last.expr.kind)) && + let variant_field = + variant.fields.iter().find(|field| field.ident(self.tcx) == last.ident) && + let range_def_id = self.tcx.lang_items().range_struct() && + variant_field + .and_then(|field| field.ty(self.tcx, substs).ty_adt_def()) + .map(|adt| adt.did()) + != range_def_id + { + let instead = self + .tcx + .sess + .source_map() + .span_to_snippet(range_end.expr.span) + .map(|s| format!(" from `{s}`")) + .unwrap_or(String::new()); + err.span_suggestion( + range_start.span.shrink_to_hi(), + &format!("to set the remaining fields{instead}, separate the last named field with a comma"), + ",".to_string(), + Applicability::MaybeIncorrect, + ); + } + + err.emit(); } /// Report an error for a struct field expression when there are invisible fields. diff --git a/src/test/ui/structs/struct-record-suggestion.fixed b/src/test/ui/structs/struct-record-suggestion.fixed new file mode 100644 index 00000000000..48144cd1ce2 --- /dev/null +++ b/src/test/ui/structs/struct-record-suggestion.fixed @@ -0,0 +1,16 @@ +// run-rustfix +#[derive(Debug, Default, Eq, PartialEq)] +struct A { + b: u32, + c: u64, + d: usize, +} + +fn main() { + let q = A { c: 5, .. Default::default() }; + //~^ ERROR mismatched types + //~| ERROR missing fields + //~| HELP separate the last named field with a comma + let r = A { c: 5, .. Default::default() }; + assert_eq!(q, r); +} diff --git a/src/test/ui/structs/struct-record-suggestion.rs b/src/test/ui/structs/struct-record-suggestion.rs new file mode 100644 index 00000000000..6d169d5c6db --- /dev/null +++ b/src/test/ui/structs/struct-record-suggestion.rs @@ -0,0 +1,16 @@ +// run-rustfix +#[derive(Debug, Default, Eq, PartialEq)] +struct A { + b: u32, + c: u64, + d: usize, +} + +fn main() { + let q = A { c: 5 .. Default::default() }; + //~^ ERROR mismatched types + //~| ERROR missing fields + //~| HELP separate the last named field with a comma + let r = A { c: 5, .. Default::default() }; + assert_eq!(q, r); +} diff --git a/src/test/ui/structs/struct-record-suggestion.stderr b/src/test/ui/structs/struct-record-suggestion.stderr new file mode 100644 index 00000000000..e5bd03117b9 --- /dev/null +++ b/src/test/ui/structs/struct-record-suggestion.stderr @@ -0,0 +1,24 @@ +error[E0308]: mismatched types + --> $DIR/struct-record-suggestion.rs:10:20 + | +LL | let q = A { c: 5 .. Default::default() }; + | ^^^^^^^^^^^^^^^^^^^^^^^ expected `u64`, found struct `std::ops::Range` + | + = note: expected type `u64` + found struct `std::ops::Range<{integer}>` + +error[E0063]: missing fields `b` and `d` in initializer of `A` + --> $DIR/struct-record-suggestion.rs:10:13 + | +LL | let q = A { c: 5 .. Default::default() }; + | ^ missing `b` and `d` + | +help: to set the remaining fields from `Default::default()`, separate the last named field with a comma + | +LL | let q = A { c: 5, .. Default::default() }; + | + + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0063, E0308. +For more information about an error, try `rustc --explain E0063`. From 81f24c1f776c6546289d3c5936dfa3862f9b0c7e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 27 Mar 2022 19:21:50 +0200 Subject: [PATCH 7/8] Update ui test output --- src/test/ui/impl-trait/impl_trait_projections.stderr | 3 ++- .../ui/impl-trait/issues/issue-57979-impl-trait-in-path.stderr | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/ui/impl-trait/impl_trait_projections.stderr b/src/test/ui/impl-trait/impl_trait_projections.stderr index e85ed0eda52..82d2422c407 100644 --- a/src/test/ui/impl-trait/impl_trait_projections.stderr +++ b/src/test/ui/impl-trait/impl_trait_projections.stderr @@ -30,4 +30,5 @@ LL | fn projection_is_disallowed(x: impl Iterator) -> ::Item { error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0223`. +Some errors have detailed explanations: E0223, E0667. +For more information about an error, try `rustc --explain E0223`. diff --git a/src/test/ui/impl-trait/issues/issue-57979-impl-trait-in-path.stderr b/src/test/ui/impl-trait/issues/issue-57979-impl-trait-in-path.stderr index f64545d83b8..e31393181d7 100644 --- a/src/test/ui/impl-trait/issues/issue-57979-impl-trait-in-path.stderr +++ b/src/test/ui/impl-trait/issues/issue-57979-impl-trait-in-path.stderr @@ -6,3 +6,4 @@ LL | pub fn demo(_: impl Quux<(), Assoc=<() as Quux>::Assoc>) { } error: aborting due to previous error +For more information about this error, try `rustc --explain E0667`. From 241ec5b3b35900cf5a9becf7da3e3ea49448d8fb Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Sun, 27 Mar 2022 10:58:55 -0700 Subject: [PATCH 8/8] Nit --- compiler/rustc_mir_dataflow/src/framework/direction.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index d7f531f7de7..93118dfeb77 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -279,9 +279,7 @@ impl Direction for Backward { analysis.apply_switch_int_edge_effects(pred, discr, &mut applier); - let BackwardSwitchIntEdgeEffectsApplier { effects_applied, .. } = applier; - - if !effects_applied { + if !applier.effects_applied { propagate(pred, exit_state) } }