diff options
| author | Ralf Jung <post@ralfj.de> | 2024-02-14 19:08:48 +0100 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2024-02-14 19:08:48 +0100 |
| commit | 7a086ac8875b45adee3d2cff0f024205eb568d8d (patch) | |
| tree | 138995ac33229aa6af4c1e84d891314800d71afa /compiler | |
| parent | 6539a64d73849176ff327b28a55d7d6787f293ef (diff) | |
| parent | 533e3f0a840cc7d803c508a9732f411ef8682188 (diff) | |
| download | rust-7a086ac8875b45adee3d2cff0f024205eb568d8d.tar.gz rust-7a086ac8875b45adee3d2cff0f024205eb568d8d.zip | |
Merge from rustc
Diffstat (limited to 'compiler')
103 files changed, 1638 insertions, 1300 deletions
diff --git a/compiler/rustc_ast/Cargo.toml b/compiler/rustc_ast/Cargo.toml index 937ee4bcd42..087f6a192b5 100644 --- a/compiler/rustc_ast/Cargo.toml +++ b/compiler/rustc_ast/Cargo.toml @@ -4,9 +4,10 @@ version = "0.0.0" edition = "2021" [dependencies] +# FIXME: bumping memchr to 2.7.1 causes linker errors in MSVC thin-lto # tidy-alphabetical-start bitflags = "2.4.1" -memchr = "2.5.0" +memchr = "=2.5.0" rustc_data_structures = { path = "../rustc_data_structures" } rustc_index = { path = "../rustc_index" } rustc_lexer = { path = "../rustc_lexer" } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 942aae3d536..73d1d891bb9 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -323,9 +323,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) } ExprKind::Yield(opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()), - ExprKind::Err => { - hir::ExprKind::Err(self.dcx().span_delayed_bug(e.span, "lowered ExprKind::Err")) - } + ExprKind::Err => hir::ExprKind::Err(self.dcx().has_errors().unwrap()), ExprKind::Try(sub_expr) => self.lower_expr_try(e.span, sub_expr), ExprKind::Paren(_) | ExprKind::ForLoop { .. } => { diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 933372fae4e..87ed47648c8 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1068,7 +1068,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_block_expr_opt(&mut self, span: Span, block: Option<&Block>) -> hir::Expr<'hir> { match block { Some(block) => self.lower_block_expr(block), - None => self.expr_err(span, self.dcx().span_delayed_bug(span, "no block")), + None => self.expr_err(span, self.dcx().has_errors().unwrap()), } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 480072ce705..6b5fc014240 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1285,9 +1285,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_ty_direct(&mut self, t: &Ty, itctx: ImplTraitContext) -> hir::Ty<'hir> { let kind = match &t.kind { TyKind::Infer => hir::TyKind::Infer, - TyKind::Err => { - hir::TyKind::Err(self.dcx().span_delayed_bug(t.span, "TyKind::Err lowered")) - } + TyKind::Err => hir::TyKind::Err(self.dcx().has_errors().unwrap()), // Lower the anonymous structs or unions in a nested lowering context. // // ``` diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index d24dc44d2c5..ebc5c17a4c4 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1593,44 +1593,98 @@ fn deny_equality_constraints( } } } - // Given `A: Foo, A::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`. - if let TyKind::Path(None, full_path) = &predicate.lhs_ty.kind { - if let [potential_param, potential_assoc] = &full_path.segments[..] { - for param in &generics.params { - if param.ident == potential_param.ident { - for bound in ¶m.bounds { - if let ast::GenericBound::Trait(trait_ref, TraitBoundModifiers::NONE) = - bound + + let mut suggest = + |poly: &PolyTraitRef, potential_assoc: &PathSegment, predicate: &WhereEqPredicate| { + if let [trait_segment] = &poly.trait_ref.path.segments[..] { + let assoc = pprust::path_to_string(&ast::Path::from_ident(potential_assoc.ident)); + let ty = pprust::ty_to_string(&predicate.rhs_ty); + let (args, span) = match &trait_segment.args { + Some(args) => match args.deref() { + ast::GenericArgs::AngleBracketed(args) => { + let Some(arg) = args.args.last() else { + return; + }; + (format!(", {assoc} = {ty}"), arg.span().shrink_to_hi()) + } + _ => return, + }, + None => (format!("<{assoc} = {ty}>"), trait_segment.span().shrink_to_hi()), + }; + let removal_span = if generics.where_clause.predicates.len() == 1 { + // We're removing th eonly where bound left, remove the whole thing. + generics.where_clause.span + } else { + let mut span = predicate.span; + let mut prev: Option<Span> = None; + let mut preds = generics.where_clause.predicates.iter().peekable(); + // Find the predicate that shouldn't have been in the where bound list. + while let Some(pred) = preds.next() { + if let WherePredicate::EqPredicate(pred) = pred + && pred.span == predicate.span { - if let [trait_segment] = &trait_ref.trait_ref.path.segments[..] { - let assoc = pprust::path_to_string(&ast::Path::from_ident( - potential_assoc.ident, - )); - let ty = pprust::ty_to_string(&predicate.rhs_ty); - let (args, span) = match &trait_segment.args { - Some(args) => match args.deref() { - ast::GenericArgs::AngleBracketed(args) => { - let Some(arg) = args.args.last() else { - continue; - }; - (format!(", {assoc} = {ty}"), arg.span().shrink_to_hi()) - } - _ => continue, - }, - None => ( - format!("<{assoc} = {ty}>"), - trait_segment.span().shrink_to_hi(), - ), - }; - err.assoc2 = Some(errors::AssociatedSuggestion2 { - span, - args, - predicate: predicate.span, - trait_segment: trait_segment.ident, - potential_assoc: potential_assoc.ident, - }); + if let Some(next) = preds.peek() { + // This is the first predicate, remove the trailing comma as well. + span = span.with_hi(next.span().lo()); + } else if let Some(prev) = prev { + // Remove the previous comma as well. + span = span.with_lo(prev.hi()); } } + prev = Some(pred.span()); + } + span + }; + err.assoc2 = Some(errors::AssociatedSuggestion2 { + span, + args, + predicate: removal_span, + trait_segment: trait_segment.ident, + potential_assoc: potential_assoc.ident, + }); + } + }; + + if let TyKind::Path(None, full_path) = &predicate.lhs_ty.kind { + // Given `A: Foo, Foo::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`. + for bounds in generics.params.iter().map(|p| &p.bounds).chain( + generics.where_clause.predicates.iter().filter_map(|pred| match pred { + WherePredicate::BoundPredicate(p) => Some(&p.bounds), + _ => None, + }), + ) { + for bound in bounds { + if let GenericBound::Trait(poly, TraitBoundModifiers::NONE) = bound { + if full_path.segments[..full_path.segments.len() - 1] + .iter() + .map(|segment| segment.ident.name) + .zip(poly.trait_ref.path.segments.iter().map(|segment| segment.ident.name)) + .all(|(a, b)| a == b) + && let Some(potential_assoc) = full_path.segments.iter().last() + { + suggest(poly, potential_assoc, predicate); + } + } + } + } + // Given `A: Foo, A::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`. + if let [potential_param, potential_assoc] = &full_path.segments[..] { + for (ident, bounds) in generics.params.iter().map(|p| (p.ident, &p.bounds)).chain( + generics.where_clause.predicates.iter().filter_map(|pred| match pred { + WherePredicate::BoundPredicate(p) + if let ast::TyKind::Path(None, path) = &p.bounded_ty.kind + && let [segment] = &path.segments[..] => + { + Some((segment.ident, &p.bounds)) + } + _ => None, + }), + ) { + if ident == potential_param.ident { + for bound in bounds { + if let ast::GenericBound::Trait(poly, TraitBoundModifiers::NONE) = bound { + suggest(poly, potential_assoc, predicate); + } } } } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index e8ffab9307e..6c2a511538d 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2479,7 +2479,8 @@ mod diags { &mut self, span: Span, ) -> Option<(DiagnosticBuilder<'tcx>, usize)> { - self.diags.buffered_mut_errors.remove(&span) + // FIXME(#120456) - is `swap_remove` correct? + self.diags.buffered_mut_errors.swap_remove(&span) } pub fn buffer_mut_error(&mut self, span: Span, t: DiagnosticBuilder<'tcx>, count: usize) { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index cfb46f3ac8a..ae4000f02a7 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1432,7 +1432,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { return; } }; - let (sig, map) = tcx.instantiate_bound_regions(sig, |br| { + let (unnormalized_sig, map) = tcx.instantiate_bound_regions(sig, |br| { use crate::renumber::RegionCtxt; let region_ctxt_fn = || { @@ -1454,7 +1454,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { region_ctxt_fn, ) }); - debug!(?sig); + debug!(?unnormalized_sig); // IMPORTANT: We have to prove well formed for the function signature before // we normalize it, as otherwise types like `<&'a &'b () as Trait>::Assoc` // get normalized away, causing us to ignore the `'b: 'a` bound used by the function. @@ -1464,7 +1464,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // // See #91068 for an example. self.prove_predicates( - sig.inputs_and_output.iter().map(|ty| { + unnormalized_sig.inputs_and_output.iter().map(|ty| { ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( ty.into(), ))) @@ -1472,7 +1472,23 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { term_location.to_locations(), ConstraintCategory::Boring, ); - let sig = self.normalize(sig, term_location); + + let sig = self.normalize(unnormalized_sig, term_location); + // HACK(#114936): `WF(sig)` does not imply `WF(normalized(sig))` + // with built-in `Fn` implementations, since the impl may not be + // well-formed itself. + if sig != unnormalized_sig { + self.prove_predicates( + sig.inputs_and_output.iter().map(|ty| { + ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::WellFormed(ty.into()), + )) + }), + term_location.to_locations(), + ConstraintCategory::Boring, + ); + } + self.check_call_dest(body, term, &sig, *destination, *target, term_location); // The ordinary liveness rules will ensure that all diff --git a/compiler/rustc_borrowck/src/used_muts.rs b/compiler/rustc_borrowck/src/used_muts.rs index 81757a62e5b..dea1c7823a5 100644 --- a/compiler/rustc_borrowck/src/used_muts.rs +++ b/compiler/rustc_borrowck/src/used_muts.rs @@ -58,7 +58,8 @@ impl GatherUsedMutsVisitor<'_, '_, '_> { // be those that were never initialized - we will consider those as being used as // they will either have been removed by unreachable code optimizations; or linted // as unused variables. - self.never_initialized_mut_locals.remove(&into.local); + // FIXME(#120456) - is `swap_remove` correct? + self.never_initialized_mut_locals.swap_remove(&into.local); } } diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index ee1d548b231..241b0a15f78 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -106,7 +106,8 @@ fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxIndexSet<Symbol> { match attrs.instruction_set { None => {} Some(InstructionSetAttr::ArmA32) => { - target_features.remove(&sym::thumb_mode); + // FIXME(#120456) - is `swap_remove` correct? + target_features.swap_remove(&sym::thumb_mode); } Some(InstructionSetAttr::ArmT32) => { target_features.insert(sym::thumb_mode); diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index d08985edb76..5019bec388c 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -122,7 +122,8 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxIndexMap<K, V> { where K: Borrow<Q>, { - FxIndexMap::remove(self, k) + // FIXME(#120456) - is `swap_remove` correct? + FxIndexMap::swap_remove(self, k) } #[inline(always)] diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 33e96e7faa8..8af69220e03 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -288,7 +288,7 @@ impl<'tcx> fmt::Display for FrameInfo<'tcx> { if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure { write!(f, "inside closure") } else { - // Note: this triggers a `good_path_delayed_bug` state, which means that if we ever + // Note: this triggers a `must_produce_diag` state, which means that if we ever // get here we must emit a diagnostic. We should never display a `FrameInfo` unless // we actually want to emit a warning or error to the user. write!(f, "inside `{}`", self.instance) @@ -304,7 +304,7 @@ impl<'tcx> FrameInfo<'tcx> { errors::FrameNote { where_: "closure", span, instance: String::new(), times: 0 } } else { let instance = format!("{}", self.instance); - // Note: this triggers a `good_path_delayed_bug` state, which means that if we ever get + // Note: this triggers a `must_produce_diag` state, which means that if we ever get // here we must emit a diagnostic. We should never display a `FrameInfo` unless we // actually want to emit a warning or error to the user. errors::FrameNote { where_: "instance", span, instance, times: 0 } diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 38e7843761b..7feac6156bc 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -49,7 +49,8 @@ fn intern_shallow<'rt, 'mir, 'tcx, T, M: CompileTimeMachine<'mir, 'tcx, T>>( ) -> Result<impl Iterator<Item = CtfeProvenance> + 'tcx, ()> { trace!("intern_shallow {:?}", alloc_id); // remove allocation - let Some((_kind, mut alloc)) = ecx.memory.alloc_map.remove(&alloc_id) else { + // FIXME(#120456) - is `swap_remove` correct? + let Some((_kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else { return Err(()); }; // Set allocation mutability as appropriate. This is used by LLVM to put things into diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 5ce6a71c4bd..b5c70538c52 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -117,18 +117,14 @@ struct CfgChecker<'a, 'tcx> { impl<'a, 'tcx> CfgChecker<'a, 'tcx> { #[track_caller] fn fail(&self, location: Location, msg: impl AsRef<str>) { - let span = self.body.source_info(location).span; - // We use `span_delayed_bug` as we might see broken MIR when other errors have already - // occurred. - self.tcx.dcx().span_delayed_bug( - span, - format!( - "broken MIR in {:?} ({}) at {:?}:\n{}", - self.body.source.instance, - self.when, - location, - msg.as_ref() - ), + // We might see broken MIR when other errors have already occurred. + assert!( + self.tcx.dcx().has_errors().is_some(), + "broken MIR in {:?} ({}) at {:?}:\n{}", + self.body.source.instance, + self.when, + location, + msg.as_ref(), ); } diff --git a/compiler/rustc_data_structures/src/small_c_str.rs b/compiler/rustc_data_structures/src/small_c_str.rs index 349fd7f9769..809ce3d4483 100644 --- a/compiler/rustc_data_structures/src/small_c_str.rs +++ b/compiler/rustc_data_structures/src/small_c_str.rs @@ -82,6 +82,6 @@ impl<'a> FromIterator<&'a str> for SmallCStr { impl From<&ffi::CStr> for SmallCStr { fn from(s: &ffi::CStr) -> Self { - Self { data: SmallVec::from_slice(s.to_bytes()) } + Self { data: SmallVec::from_slice(s.to_bytes_with_nul()) } } } diff --git a/compiler/rustc_data_structures/src/small_c_str/tests.rs b/compiler/rustc_data_structures/src/small_c_str/tests.rs index 47277604b2b..7b975dadcb7 100644 --- a/compiler/rustc_data_structures/src/small_c_str/tests.rs +++ b/compiler/rustc_data_structures/src/small_c_str/tests.rs @@ -43,3 +43,11 @@ fn long() { fn internal_nul() { let _ = SmallCStr::new("abcd\0def"); } + +#[test] +fn from_cstr() { + let c = c"foo"; + let s: SmallCStr = c.into(); + assert_eq!(s.len_with_nul(), 4); + assert_eq!(s.as_c_str(), c"foo"); +} diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index e174cba7813..a1abe8fd4f3 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -376,7 +376,7 @@ impl From<Cow<'static, str>> for DiagnosticMessage { } } -/// A workaround for good_path_delayed_bug ICEs when formatting types in disabled lints. +/// A workaround for must_produce_diag ICEs when formatting types in disabled lints. /// /// Delays formatting until `.into(): DiagnosticMessage` is used. pub struct DelayDm<F>(pub F); diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 37f568f12a7..1a34a83c1a4 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -85,11 +85,7 @@ fn source_string(file: Lrc<SourceFile>, line: &Line) -> String { /// Maps `Diagnostic::Level` to `snippet::AnnotationType` fn annotation_type_for_level(level: Level) -> AnnotationType { match level { - Level::Bug - | Level::Fatal - | Level::Error - | Level::DelayedBug - | Level::GoodPathDelayedBug => AnnotationType::Error, + Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => AnnotationType::Error, Level::ForceWarning(_) | Level::Warning => AnnotationType::Warning, Level::Note | Level::OnceNote => AnnotationType::Note, Level::Help | Level::OnceHelp => AnnotationType::Help, diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 2deb18484ec..b14a12175c7 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -237,8 +237,7 @@ impl Diagnostic { match self.level { Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => true, - Level::GoodPathDelayedBug - | Level::ForceWarning(_) + Level::ForceWarning(_) | Level::Warning | Level::Note | Level::OnceNote diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index b9e92dbb31c..38c6661377b 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -1635,7 +1635,8 @@ impl HumanEmitter { let mut to_add = FxHashMap::default(); for (depth, style) in depths { - if multilines.remove(&depth).is_none() { + // FIXME(#120456) - is `swap_remove` correct? + if multilines.swap_remove(&depth).is_none() { to_add.insert(depth, style); } } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index da9ef6627be..d876f28040d 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -78,6 +78,7 @@ use std::fmt; use std::hash::Hash; use std::io::Write; use std::num::NonZeroUsize; +use std::ops::DerefMut; use std::panic; use std::path::{Path, PathBuf}; @@ -435,7 +436,6 @@ struct DiagCtxtInner { lint_err_guars: Vec<ErrorGuaranteed>, /// The delayed bugs and their error guarantees. delayed_bugs: Vec<(DelayedDiagnostic, ErrorGuaranteed)>, - good_path_delayed_bugs: Vec<DelayedDiagnostic>, /// The number of stashed errors. Unlike the other counts, this can go up /// and down, so it doesn't guarantee anything. @@ -446,13 +446,18 @@ struct DiagCtxtInner { /// The warning count shown to the user at the end. deduplicated_warn_count: usize, + emitter: Box<DynEmitter>, + + /// Must we produce a diagnostic to justify the use of the expensive + /// `trimmed_def_paths` function? + must_produce_diag: bool, + /// Has this diagnostic context printed any diagnostics? (I.e. has /// `self.emitter.emit_diagnostic()` been called? has_printed: bool, - emitter: Box<DynEmitter>, /// This flag indicates that an expected diagnostic was emitted and suppressed. - /// This is used for the `good_path_delayed_bugs` check. + /// This is used for the `must_produce_diag` check. suppressed_expected_diag: bool, /// This set contains the code of all emitted diagnostics to avoid @@ -533,11 +538,6 @@ fn default_track_diagnostic(diag: Diagnostic, f: &mut dyn FnMut(Diagnostic)) { pub static TRACK_DIAGNOSTIC: AtomicRef<fn(Diagnostic, &mut dyn FnMut(Diagnostic))> = AtomicRef::new(&(default_track_diagnostic as _)); -enum DelayedBugKind { - Normal, - GoodPath, -} - #[derive(Copy, Clone, Default)] pub struct DiagCtxtFlags { /// If false, warning-level lints are suppressed. @@ -563,11 +563,16 @@ impl Drop for DiagCtxtInner { self.emit_stashed_diagnostics(); if self.err_guars.is_empty() { - self.flush_delayed(DelayedBugKind::Normal) + self.flush_delayed() } if !self.has_printed && !self.suppressed_expected_diag && !std::thread::panicking() { - self.flush_delayed(DelayedBugKind::GoodPath); + if self.must_produce_diag { + panic!( + "must_produce_diag: trimmed_def_paths called but no diagnostics emitted; \ + use `DelayDm` for lints or `with_no_trimmed_paths` for debugging" + ); + } } if self.check_unstable_expect_diagnostics { @@ -609,12 +614,12 @@ impl DiagCtxt { err_guars: Vec::new(), lint_err_guars: Vec::new(), delayed_bugs: Vec::new(), - good_path_delayed_bugs: Vec::new(), stashed_err_count: 0, deduplicated_err_count: 0, deduplicated_warn_count: 0, - has_printed: false, emitter, + must_produce_diag: false, + has_printed: false, suppressed_expected_diag: false, taught_diagnostics: Default::default(), emitted_diagnostic_codes: Default::default(), @@ -662,21 +667,51 @@ impl DiagCtxt { /// tools that want to reuse a `Parser` cleaning the previously emitted diagnostics as well as /// the overall count of emitted error diagnostics. pub fn reset_err_count(&self) { + // Use destructuring so that if a field gets added to `DiagCtxtInner`, it's impossible to + // fail to update this method as well. let mut inner = self.inner.borrow_mut(); - inner.stashed_err_count = 0; - inner.deduplicated_err_count = 0; - inner.deduplicated_warn_count = 0; - inner.has_printed = false; - - // actually free the underlying memory (which `clear` would not do) - inner.err_guars = Default::default(); - inner.lint_err_guars = Default::default(); - inner.delayed_bugs = Default::default(); - inner.good_path_delayed_bugs = Default::default(); - inner.taught_diagnostics = Default::default(); - inner.emitted_diagnostic_codes = Default::default(); - inner.emitted_diagnostics = Default::default(); - inner.stashed_diagnostics = Default::default(); + let DiagCtxtInner { + flags: _, + err_guars, + lint_err_guars, + delayed_bugs, + stashed_err_count, + deduplicated_err_count, + deduplicated_warn_count, + emitter: _, + must_produce_diag, + has_printed, + suppressed_expected_diag, + taught_diagnostics, + emitted_diagnostic_codes, + emitted_diagnostics, + stashed_diagnostics, + future_breakage_diagnostics, + check_unstable_expect_diagnostics, + unstable_expect_diagnostics, + fulfilled_expectations, + ice_file: _, + } = inner.deref_mut(); + + // For the `Vec`s and `HashMap`s, we overwrite with an empty container to free the + // underlying memory (which `clear` would not do). + *err_guars = Default::default(); + *lint_err_guars = Default::default(); + *delayed_bugs = Default::default(); + *stashed_err_count = 0; + *deduplicated_err_count = 0; + *deduplicated_warn_count = 0; + *must_produce_diag = false; + *has_printed = false; + *suppressed_expected_diag = false; + *taught_diagnostics = Default::default(); + *emitted_diagnostic_codes = Default::default(); + *emitted_diagnostics = Default::default(); + *stashed_diagnostics = Default::default(); + *future_breakage_diagnostics = Default::default(); + *check_unstable_expect_diagnostics = false; + *unstable_expect_diagnostics = Default::default(); + *fulfilled_expectations = Default::default(); } /// Stash a given diagnostic with the given `Span` and [`StashKey`] as the key. @@ -702,7 +737,8 @@ impl DiagCtxt { pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<DiagnosticBuilder<'_, ()>> { let mut inner = self.inner.borrow_mut(); let key = (span.with_parent(None), key); - let diag = inner.stashed_diagnostics.remove(&key)?; + // FIXME(#120456) - is `swap_remove` correct? + let diag = inner.stashed_diagnostics.swap_remove(&key)?; if diag.is_error() { if diag.is_lint.is_none() { inner.stashed_err_count -= 1; @@ -775,11 +811,12 @@ impl DiagCtxt { match (errors.len(), warnings.len()) { (0, 0) => return, (0, _) => { - // Use `inner.emitter` directly, otherwise the warning might not be emitted, e.g. - // with a configuration like `--cap-lints allow --force-warn bare_trait_objects`. - inner - .emitter - .emit_diagnostic(Diagnostic::new(Warning, DiagnosticMessage::Str(warnings))); + // Use `ForceWarning` rather than `Warning` to guarantee emission, e.g. with a + // configuration like `--cap-lints allow --force-warn bare_trait_objects`. + inner.emit_diagnostic(Diagnostic::new( + ForceWarning(None), + DiagnosticMessage::Str(warnings), + )); } (_, 0) => { inner.emit_diagnostic(Diagnostic::new(Error, errors)); @@ -807,20 +844,23 @@ impl DiagCtxt { error_codes.sort(); if error_codes.len() > 1 { let limit = if error_codes.len() > 9 { 9 } else { error_codes.len() }; - inner.failure_note(format!( + let msg1 = format!( "Some errors have detailed explanations: {}{}", error_codes[..limit].join(", "), if error_codes.len() > 9 { "..." } else { "." } - )); - inner.failure_note(format!( + ); + let msg2 = format!( "For more information about an error, try `rustc --explain {}`.", &error_codes[0] - )); + ); + inner.emit_diagnostic(Diagnostic::new(FailureNote, msg1)); + inner.emit_diagnostic(Diagnostic::new(FailureNote, msg2)); } else { - inner.failure_note(format!( + let msg = format!( "For more information about this error, try `rustc --explain {}`.", &error_codes[0] - )); + ); + inner.emit_diagnostic(Diagnostic::new(FailureNote, msg)); } } } @@ -843,10 +883,6 @@ impl DiagCtxt { self.inner.borrow_mut().taught_diagnostics.insert(code) } - pub fn force_print_diagnostic(&self, db: Diagnostic) { - self.inner.borrow_mut().emitter.emit_diagnostic(db); - } - pub fn emit_diagnostic(&self, diagnostic: Diagnostic) -> Option<ErrorGuaranteed> { self.inner.borrow_mut().emit_diagnostic(diagnostic) } @@ -934,7 +970,13 @@ impl DiagCtxt { } pub fn flush_delayed(&self) { - self.inner.borrow_mut().flush_delayed(DelayedBugKind::Normal); + self.inner.borrow_mut().flush_delayed(); + } + + /// Used when trimmed_def_paths is called and we must produce a diagnostic + /// to justify its cost. + pub fn set_must_produce_diag(&self) { + self.inner.borrow_mut().must_produce_diag = true; } } @@ -1108,13 +1150,6 @@ impl DiagCtxt { DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug, msg).with_span(sp).emit() } - /// Ensures that a diagnostic is printed. See `Level::GoodPathDelayedBug`. - // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. - #[track_caller] - pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) { - DiagnosticBuilder::<()>::new(self, GoodPathDelayedBug, msg).emit() - } - #[rustc_lint_diagnostics] #[track_caller] pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { @@ -1175,7 +1210,7 @@ impl DiagCtxt { span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, ()> { - DiagnosticBuilder::new(self, Note, msg).with_span(span) + self.struct_note(msg).with_span(span) } #[rustc_lint_diagnostics] @@ -1205,6 +1240,15 @@ impl DiagCtxt { #[rustc_lint_diagnostics] #[track_caller] + pub fn struct_failure_note( + &self, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, ()> { + DiagnosticBuilder::new(self, FailureNote, msg) + } + + #[rustc_lint_diagnostics] + #[track_caller] pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Allow, msg) } @@ -1266,42 +1310,39 @@ impl DiagCtxtInner { if diagnostic.has_future_breakage() { // Future breakages aren't emitted if they're Level::Allow, // but they still need to be constructed and stashed below, - // so they'll trigger the good-path bug check. + // so they'll trigger the must_produce_diag check. self.suppressed_expected_diag = true; self.future_breakage_diagnostics.push(diagnostic.clone()); } - if matches!(diagnostic.level, DelayedBug | GoodPathDelayedBug) - && self.flags.eagerly_emit_delayed_bugs - { + // Note that because this comes before the `match` below, + // `-Zeagerly-emit-delayed-bugs` continues to work even after we've + // issued an error and stopped recording new delayed bugs. + if diagnostic.level == DelayedBug && self.flags.eagerly_emit_delayed_bugs { diagnostic.level = Error; } match diagnostic.level { - // This must come after the possible promotion of `DelayedBug`/`GoodPathDelayedBug` to + // This must come after the possible promotion of `DelayedBug` to // `Error` above. Fatal | Error if self.treat_next_err_as_bug() => { diagnostic.level = Bug; } DelayedBug => { - // FIXME(eddyb) this should check for `has_errors` and stop pushing - // once *any* errors were emitted (and truncate `delayed_bugs` - // when an error is first emitted, also), but maybe there's a case - // in which that's not sound? otherwise this is really inefficient. - let backtrace = std::backtrace::Backtrace::capture(); - // This `unchecked_error_guaranteed` is valid. It is where the - // `ErrorGuaranteed` for delayed bugs originates. - #[allow(deprecated)] - let guar = ErrorGuaranteed::unchecked_error_guaranteed(); - self.delayed_bugs - .push((DelayedDiagnostic::with_backtrace(diagnostic, backtrace), guar)); - return Some(guar); - } - GoodPathDelayedBug => { - let backtrace = std::backtrace::Backtrace::capture(); - self.good_path_delayed_bugs - .push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace)); - return None; + // If we have already emitted at least one error, we don't need + // to record the delayed bug, because it'll never be used. + return if let Some(guar) = self.has_errors_or_lint_errors() { + Some(guar) + } else { + let backtrace = std::backtrace::Backtrace::capture(); + // This `unchecked_error_guaranteed` is valid. It is where the + // `ErrorGuaranteed` for delayed bugs originates. + #[allow(deprecated)] + let guar = ErrorGuaranteed::unchecked_error_guaranteed(); + self.delayed_bugs + .push((DelayedDiagnostic::with_backtrace(diagnostic, backtrace), guar)); + Some(guar) + }; } Warning if !self.flags.can_emit_warnings => { if diagnostic.has_future_breakage() { @@ -1367,6 +1408,16 @@ impl DiagCtxtInner { } if is_error { + // If we have any delayed bugs recorded, we can discard them + // because they won't be used. (This should only occur if there + // have been no errors previously emitted, because we don't add + // new delayed bugs once the first error is emitted.) + if !self.delayed_bugs.is_empty() { + assert_eq!(self.lint_err_guars.len() + self.err_guars.len(), 0); + self.delayed_bugs.clear(); + self.delayed_bugs.shrink_to_fit(); + } + // This `unchecked_error_guaranteed` is valid. It is where the // `ErrorGuaranteed` for errors and lint errors originates. #[allow(deprecated)] @@ -1410,27 +1461,14 @@ impl DiagCtxtInner { .or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied()) } - fn failure_note(&mut self, msg: impl Into<DiagnosticMessage>) { - self.emit_diagnostic(Diagnostic::new(FailureNote, msg)); - } - - fn flush_delayed(&mut self, kind: DelayedBugKind) { - let (bugs, note1) = match kind { - DelayedBugKind::Normal => ( - std::mem::take(&mut self.delayed_bugs).into_iter().map(|(b, _)| b).collect(), - "no errors encountered even though delayed bugs were created", - ), - DelayedBugKind::GoodPath => ( - std::mem::take(&mut self.good_path_delayed_bugs), - "no warnings or errors encountered even though good path delayed bugs were created", - ), - }; - let note2 = "those delayed bugs will now be shown as internal compiler errors"; - - if bugs.is_empty() { + fn flush_delayed(&mut self) { + if self.delayed_bugs.is_empty() { return; } + let bugs: Vec<_> = + std::mem::take(&mut self.delayed_bugs).into_iter().map(|(b, _)| b).collect(); + // If backtraces are enabled, also print the query stack let backtrace = std::env::var_os("RUST_BACKTRACE").map_or(true, |x| &x != "0"); for (i, bug) in bugs.into_iter().enumerate() { @@ -1454,6 +1492,8 @@ impl DiagCtxtInner { // frame them better (e.g. separate warnings from them). Also, // make it a note so it doesn't count as an error, because that // could trigger `-Ztreat-err-as-bug`, which we don't want. + let note1 = "no errors encountered even though delayed bugs were created"; + let note2 = "those delayed bugs will now be shown as internal compiler errors"; self.emit_diagnostic(Diagnostic::new(Note, note1)); self.emit_diagnostic(Diagnostic::new(Note, note2)); } @@ -1462,7 +1502,7 @@ impl DiagCtxtInner { if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner }; // "Undelay" the delayed bugs (into plain `Bug`s). - if !matches!(bug.level, DelayedBug | GoodPathDelayedBug) { + if bug.level != DelayedBug { // NOTE(eddyb) not panicking here because we're already producing // an ICE, and the more information the merrier. bug.subdiagnostic(InvalidFlushedDelayedDiagnosticLevel { @@ -1534,7 +1574,6 @@ impl DelayedDiagnostic { /// Fatal yes FatalAbort/FatalError(*) yes - - /// Error yes ErrorGuaranteed yes - yes /// DelayedBug yes ErrorGuaranteed yes - - -/// GoodPathDelayedBug - () yes - - /// ForceWarning - () yes - lint-only /// Warning - () yes yes yes /// Note - () rare yes - @@ -1567,20 +1606,6 @@ pub enum Level { /// that should only be reached when compiling erroneous code. DelayedBug, - /// Like `DelayedBug`, but weaker: lets you register an error without emitting it. If - /// compilation ends without any other diagnostics being emitted (and without an expected lint - /// being suppressed), this will be emitted as a bug. Otherwise, it will be silently dropped. - /// I.e. "expect other diagnostics are emitted (or suppressed)" semantics. Useful on code paths - /// that should only be reached when emitting diagnostics, e.g. for expensive one-time - /// diagnostic formatting operations. - /// - /// FIXME(nnethercote) good path delayed bugs are semantically strange: if printed they produce - /// an ICE, but they don't satisfy `is_error` and they don't guarantee an error is emitted. - /// Plus there's the extra complication with expected (suppressed) lints. They have limited - /// use, and are used in very few places, and "good path" isn't a good name. It would be good - /// to remove them. - GoodPathDelayedBug, - /// A `force-warn` lint warning about the code being compiled. Does not prevent compilation /// from finishing. /// @@ -1625,7 +1650,7 @@ impl Level { fn color(self) -> ColorSpec { let mut spec = ColorSpec::new(); match self { - Bug | Fatal | Error | DelayedBug | GoodPathDelayedBug => { + Bug | Fatal | Error | DelayedBug => { spec.set_fg(Some(Color::Red)).set_intense(true); } ForceWarning(_) | Warning => { @@ -1645,7 +1670,7 @@ impl Level { pub fn to_str(self) -> &'static str { match self { - Bug | DelayedBug | GoodPathDelayedBug => "error: internal compiler error", + Bug | DelayedBug => "error: internal compiler error", Fatal | Error => "error", ForceWarning(_) | Warning => "warning", Note | OnceNote => "note", @@ -1670,8 +1695,8 @@ impl Level { // subdiagnostic message? fn can_be_top_or_sub(&self) -> (bool, bool) { match self { - Bug | DelayedBug | Fatal | Error | GoodPathDelayedBug | ForceWarning(_) - | FailureNote | Allow | Expect(_) => (true, false), + Bug | DelayedBug | Fatal | Error | ForceWarning(_) | FailureNote | Allow + | Expect(_) => (true, false), Warning | Note | Help => (true, true), diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index eec86c36aed..be4b6399e12 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -34,10 +34,10 @@ pub(super) fn failed_to_match_macro<'cx>( if try_success_result.is_ok() { // Nonterminal parser recovery might turn failed matches into successful ones, // but for that it must have emitted an error already - tracker - .cx - .dcx() - .span_delayed_bug(sp, "Macro matching returned a success on the second try"); + assert!( + tracker.cx.dcx().has_errors().is_some(), + "Macro matching returned a success on the second try" + ); } if let Some(result) = tracker.result { diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index a001044c3e5..98a27c5ed20 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -758,8 +758,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // since we should have emitten an error for them earlier, and they will // not be well-formed! if polarity == ty::ImplPolarity::Negative { - self.tcx().dcx().span_delayed_bug( - binding.span, + assert!( + self.tcx().dcx().has_errors().is_some(), "negative trait bounds should not have bindings", ); continue; diff --git a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs index cbbf560076e..7705445ffaa 100644 --- a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs +++ b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs @@ -218,7 +218,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { for def_ids in associated_types.values_mut() { for (projection_bound, span) in &projection_bounds { let def_id = projection_bound.projection_def_id(); - def_ids.remove(&def_id); + // FIXME(#120456) - is `swap_remove` correct? + def_ids.swap_remove(&def_id); if tcx.generics_require_sized_self(def_id) { tcx.emit_node_span_lint( UNUSED_ASSOCIATED_TYPE_BOUNDS, diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 7f674a1e7e4..e7506cee60e 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1865,13 +1865,13 @@ fn check_variances_for_type_defn<'tcx>( let hir_param = &hir_generics.params[index]; if ty_param.def_id != hir_param.def_id.into() { - // valid programs always have lifetimes before types in the generic parameter list + // Valid programs always have lifetimes before types in the generic parameter list. // ty_generics are normalized to be in this required order, and variances are built // from ty generics, not from hir generics. but we need hir generics to get - // a span out + // a span out. // - // if they aren't in the same order, then the user has written invalid code, and already - // got an error about it (or I'm wrong about this) + // If they aren't in the same order, then the user has written invalid code, and already + // got an error about it (or I'm wrong about this). tcx.dcx().span_delayed_bug( hir_param.span, "hir generics and ty generics in different order", diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 370c6c607d7..6c3a9b747ef 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -15,7 +15,7 @@ use rustc_infer::infer::{DefineOpaqueTypes, TyCtxtInferExt}; use rustc_infer::traits::Obligation; use rustc_middle::ty::adjustment::CoerceUnsizedInfo; use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::misc::{ type_allowed_to_implement_const_param_ty, type_allowed_to_implement_copy, @@ -25,13 +25,14 @@ use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::{self, ObligationCause}; use std::collections::BTreeMap; -pub fn check_trait( - tcx: TyCtxt<'_>, +pub fn check_trait<'tcx>( + tcx: TyCtxt<'tcx>, trait_def_id: DefId, impl_def_id: LocalDefId, + impl_header: ty::ImplTraitHeader<'tcx>, ) -> Result<(), ErrorGuaranteed> { let lang_items = tcx.lang_items(); - let checker = Checker { tcx, trait_def_id, impl_def_id }; + let checker = Checker { tcx, trait_def_id, impl_def_id, impl_header }; let mut res = checker.check(lang_items.drop_trait(), visit_implementation_of_drop); res = res.and(checker.check(lang_items.copy_trait(), visit_implementation_of_copy)); res = res.and( @@ -50,24 +51,25 @@ struct Checker<'tcx> { tcx: TyCtxt<'tcx>, trait_def_id: DefId, impl_def_id: LocalDefId, + impl_header: ty::ImplTraitHeader<'tcx>, } impl<'tcx> Checker<'tcx> { fn check( &self, trait_def_id: Option<DefId>, - f: impl FnOnce(TyCtxt<'tcx>, LocalDefId) -> Result<(), ErrorGuaranteed>, + f: impl FnOnce(&Self) -> Result<(), ErrorGuaranteed>, ) -> Result<(), ErrorGuaranteed> { - if Some(self.trait_def_id) == trait_def_id { f(self.tcx, self.impl_def_id) } else { Ok(()) } + if Some(self.trait_def_id) == trait_def_id { f(self) } else { Ok(()) } } } -fn visit_implementation_of_drop( - tcx: TyCtxt<'_>, - impl_did: LocalDefId, -) -> Result<(), ErrorGuaranteed> { +fn visit_implementation_of_drop(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { + let tcx = checker.tcx; + let header = checker.impl_header; + let impl_did = checker.impl_def_id; // Destructors only work on local ADT types. - match tcx.type_of(impl_did).instantiate_identity().kind() { + match header.trait_ref.self_ty().kind() { ty::Adt(def, _) if def.did().is_local() => return Ok(()), ty::Error(_) => return Ok(()), _ => {} @@ -78,13 +80,13 @@ fn visit_implementation_of_drop( Err(tcx.dcx().emit_err(errors::DropImplOnWrongItem { span: impl_.self_ty.span })) } -fn visit_implementation_of_copy( - tcx: TyCtxt<'_>, - impl_did: LocalDefId, -) -> Result<(), ErrorGuaranteed> { +fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { + let tcx = checker.tcx; + let impl_header = checker.impl_header; + let impl_did = checker.impl_def_id; debug!("visit_implementation_of_copy: impl_did={:?}", impl_did); - let self_type = tcx.type_of(impl_did).instantiate_identity(); + let self_type = impl_header.trait_ref.self_ty(); debug!("visit_implementation_of_copy: self_type={:?} (bound)", self_type); let param_env = tcx.param_env(impl_did); @@ -92,56 +94,58 @@ fn visit_implementation_of_copy( debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type); - if let ty::ImplPolarity::Negative = tcx.impl_polarity(impl_did) { + if let ty::ImplPolarity::Negative = impl_header.polarity { return Ok(()); } - let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; - let cause = traits::ObligationCause::misc(span, impl_did); + let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did); match type_allowed_to_implement_copy(tcx, param_env, self_type, cause) { Ok(()) => Ok(()), Err(CopyImplementationError::InfringingFields(fields)) => { + let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; Err(infringing_fields_error(tcx, fields, LangItem::Copy, impl_did, span)) } Err(CopyImplementationError::NotAnAdt) => { + let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; Err(tcx.dcx().emit_err(errors::CopyImplOnNonAdt { span })) } Err(CopyImplementationError::HasDestructor) => { + let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; Err(tcx.dcx().emit_err(errors::CopyImplOnTypeWithDtor { span })) } } } -fn visit_implementation_of_const_param_ty( - tcx: TyCtxt<'_>, - impl_did: LocalDefId, -) -> Result<(), ErrorGuaranteed> { - let self_type = tcx.type_of(impl_did).instantiate_identity(); +fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { + let tcx = checker.tcx; + let header = checker.impl_header; + let impl_did = checker.impl_def_id; + let self_type = header.trait_ref.self_ty(); assert!(!self_type.has_escaping_bound_vars()); let param_env = tcx.param_env(impl_did); - if let ty::ImplPolarity::Negative = tcx.impl_polarity(impl_did) { + if let ty::ImplPolarity::Negative = header.polarity { return Ok(()); } - let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; - let cause = traits::ObligationCause::misc(span, impl_did); + let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did); match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, cause) { Ok(()) => Ok(()), Err(ConstParamTyImplementationError::InfrigingFields(fields)) => { + let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; Err(infringing_fields_error(tcx, fields, LangItem::ConstParamTy, impl_did, span)) } Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => { + let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnNonAdt { span })) } } } -fn visit_implementation_of_coerce_unsized( - tcx: TyCtxt<'_>, - impl_did: LocalDefId, -) -> Result<(), ErrorGuaranteed> { +fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { + let tcx = checker.tcx; + let impl_did = checker.impl_def_id; debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did); // Just compute this for the side-effects, in particular reporting @@ -151,20 +155,20 @@ fn visit_implementation_of_coerce_unsized( tcx.at(span).ensure().coerce_unsized_info(impl_did) } -fn visit_implementation_of_dispatch_from_dyn( - tcx: TyCtxt<'_>, - impl_did: LocalDefId, -) -> Result<(), ErrorGuaranteed> { +fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { + let tcx = checker.tcx; + let header = checker.impl_header; + let impl_did = checker.impl_def_id; + let trait_ref = header.trait_ref; debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did); let span = tcx.def_span(impl_did); let dispatch_from_dyn_trait = tcx.require_lang_item(LangItem::DispatchFromDyn, Some(span)); - let source = tcx.type_of(impl_did).instantiate_identity(); + let source = trait_ref.self_ty(); assert!(!source.has_escaping_bound_vars()); let target = { - let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait); trait_ref.args.type_at(1) diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 7f59763f2a0..d6281fa08f7 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -23,6 +23,7 @@ fn check_impl( tcx: TyCtxt<'_>, impl_def_id: LocalDefId, trait_ref: ty::TraitRef<'_>, + trait_def: &ty::TraitDef, ) -> Result<(), ErrorGuaranteed> { debug!( "(checking implementation) adding impl for trait '{:?}', item '{}'", @@ -36,19 +37,20 @@ fn check_impl( return Ok(()); } - enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id) - .and(enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id)) + enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id, trait_def) + .and(enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id, trait_def)) } fn enforce_trait_manually_implementable( tcx: TyCtxt<'_>, impl_def_id: LocalDefId, trait_def_id: DefId, + trait_def: &ty::TraitDef, ) -> Result<(), ErrorGuaranteed> { let impl_header_span = tcx.def_span(impl_def_id); // Disallow *all* explicit impls of traits marked `#[rustc_deny_explicit_impl]` - if tcx.trait_def(trait_def_id).deny_explicit_impl { + if trait_def.deny_explicit_impl { let trait_name = tcx.item_name(trait_def_id); let mut err = struct_span_code_err!( tcx.dcx(), @@ -67,8 +69,7 @@ fn enforce_trait_manually_implementable( return Err(err.emit()); } - if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable = - tcx.trait_def(trait_def_id).specialization_kind + if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable = trait_def.specialization_kind { if !tcx.features().specialization && !tcx.features().min_specialization @@ -87,8 +88,9 @@ fn enforce_empty_impls_for_marker_traits( tcx: TyCtxt<'_>, impl_def_id: LocalDefId, trait_def_id: DefId, + trait_def: &ty::TraitDef, ) -> Result<(), ErrorGuaranteed> { - if !tcx.trait_def(trait_def_id).is_marker { + if !trait_def.is_marker { return Ok(()); } @@ -132,14 +134,15 @@ fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Result<(), ErrorGuaranteed> let mut res = tcx.ensure().specialization_graph_of(def_id); for &impl_def_id in impls { - let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(); + let trait_header = tcx.impl_trait_header(impl_def_id).unwrap().instantiate_identity(); + let trait_def = tcx.trait_def(trait_header.trait_ref.def_id); - res = res.and(check_impl(tcx, impl_def_id, trait_ref)); - res = res.and(check_object_overlap(tcx, impl_def_id, trait_ref)); + res = res.and(check_impl(tcx, impl_def_id, trait_header.trait_ref, trait_def)); + res = res.and(check_object_overlap(tcx, impl_def_id, trait_header.trait_ref)); - res = res.and(unsafety::check_item(tcx, impl_def_id, trait_ref)); + res = res.and(unsafety::check_item(tcx, impl_def_id, trait_header, trait_def)); res = res.and(tcx.ensure().orphan_check_impl(impl_def_id)); - res = res.and(builtin::check_trait(tcx, def_id, impl_def_id)); + res = res.and(builtin::check_trait(tcx, def_id, impl_def_id, trait_header)); } res diff --git a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs index d217d53587d..53a5ada4105 100644 --- a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs +++ b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs @@ -2,23 +2,23 @@ //! crate or pertains to a type defined in this crate. use rustc_errors::{codes::*, struct_span_code_err}; -use rustc_hir as hir; use rustc_hir::Unsafety; -use rustc_middle::ty::{TraitRef, TyCtxt}; +use rustc_middle::ty::{ImplPolarity::*, ImplTraitHeader, TraitDef, TyCtxt}; use rustc_span::def_id::LocalDefId; use rustc_span::ErrorGuaranteed; pub(super) fn check_item( tcx: TyCtxt<'_>, def_id: LocalDefId, - trait_ref: TraitRef<'_>, + trait_header: ImplTraitHeader<'_>, + trait_def: &TraitDef, ) -> Result<(), ErrorGuaranteed> { - let item = tcx.hir().expect_item(def_id); - let impl_ = item.expect_impl(); - let trait_def = tcx.trait_def(trait_ref.def_id); - let unsafe_attr = impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle"); - match (trait_def.unsafety, unsafe_attr, impl_.unsafety, impl_.polarity) { - (Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => { + let trait_ref = trait_header.trait_ref; + let unsafe_attr = + tcx.generics_of(def_id).params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle"); + match (trait_def.unsafety, unsafe_attr, trait_header.unsafety, trait_header.polarity) { + (Unsafety::Normal, None, Unsafety::Unsafe, Positive | Reservation) => { + let span = tcx.def_span(def_id); return Err(struct_span_code_err!( tcx.dcx(), tcx.def_span(def_id), @@ -27,7 +27,7 @@ pub(super) fn check_item( trait_ref.print_trait_sugared() ) .with_span_suggestion_verbose( - item.span.with_hi(item.span.lo() + rustc_span::BytePos(7)), + span.with_hi(span.lo() + rustc_span::BytePos(7)), "remove `unsafe` from this trait implementation", "", rustc_errors::Applicability::MachineApplicable, @@ -35,10 +35,11 @@ pub(super) fn check_item( .emit()); } - (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => { + (Unsafety::Unsafe, _, Unsafety::Normal, Positive | Reservation) => { + let span = tcx.def_span(def_id); return Err(struct_span_code_err!( tcx.dcx(), - tcx.def_span(def_id), + span, E0200, "the trait `{}` requires an `unsafe impl` declaration", trait_ref.print_trait_sugared() @@ -50,7 +51,7 @@ pub(super) fn check_item( trait_ref.print_trait_sugared() )) .with_span_suggestion_verbose( - item.span.shrink_to_lo(), + span.shrink_to_lo(), "add `unsafe` to this trait implementation", "unsafe ", rustc_errors::Applicability::MaybeIncorrect, @@ -58,10 +59,11 @@ pub(super) fn check_item( .emit()); } - (Unsafety::Normal, Some(attr_name), Unsafety::Normal, hir::ImplPolarity::Positive) => { + (Unsafety::Normal, Some(attr_name), Unsafety::Normal, Positive | Reservation) => { + let span = tcx.def_span(def_id); return Err(struct_span_code_err!( tcx.dcx(), - tcx.def_span(def_id), + span, E0569, "requires an `unsafe impl` declaration due to `#[{}]` attribute", attr_name @@ -73,7 +75,7 @@ pub(super) fn check_item( trait_ref.print_trait_sugared() )) .with_span_suggestion_verbose( - item.span.shrink_to_lo(), + span.shrink_to_lo(), "add `unsafe` to this trait implementation", "unsafe ", rustc_errors::Applicability::MaybeIncorrect, @@ -81,14 +83,14 @@ pub(super) fn check_item( .emit()); } - (_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => { + (_, _, Unsafety::Unsafe, Negative) => { // Reported in AST validation - tcx.dcx().span_delayed_bug(item.span, "unsafe negative impl"); + assert!(tcx.dcx().has_errors().is_some(), "unsafe negative impl"); Ok(()) } - (_, _, Unsafety::Normal, hir::ImplPolarity::Negative(_)) - | (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive) - | (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive) + (_, _, Unsafety::Normal, Negative) + | (Unsafety::Unsafe, _, Unsafety::Unsafe, Positive | Reservation) + | (Unsafety::Normal, Some(_), Unsafety::Unsafe, Positive | Reservation) | (Unsafety::Normal, None, Unsafety::Normal, _) => Ok(()), } } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index e8787d159ae..4891dae47c6 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1539,6 +1539,7 @@ fn impl_trait_header( }; ty::EarlyBinder::bind(ty::ImplTraitHeader { trait_ref, + unsafety: impl_.unsafety, polarity: polarity_of_impl(tcx, def_id, impl_, item.span) }) }) diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index c9cf43ddfc8..287cb880908 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1873,7 +1873,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { lifetime_ref: &'tcx hir::Lifetime, bad_def: ResolvedArg, ) { - let old_value = self.map.defs.remove(&lifetime_ref.hir_id); + // FIXME(#120456) - is `swap_remove` correct? + let old_value = self.map.defs.swap_remove(&lifetime_ref.hir_id); assert_eq!(old_value, Some(bad_def)); } } diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 1aaefc5b520..1cd77050217 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -187,8 +187,10 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { } tcx.sess.time("wf_checking", || { - tcx.hir().try_par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module)) - })?; + tcx.hir().par_for_each_module(|module| { + let _ = tcx.ensure().check_mod_type_wf(module); + }) + }); if tcx.features().rustc_attrs { collect::test_opaque_hidden_types(tcx)?; diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index f21de1609cb..9a8f287ec14 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -139,10 +139,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Never | ty::Dynamic(_, _, ty::DynStar) | ty::Error(_) => { - let reported = self + let guar = self .dcx() .span_delayed_bug(span, format!("`{t:?}` should be sized but is not?")); - return Err(reported); + return Err(guar); } }) } diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index e98ace948f9..e6319747e78 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -98,8 +98,11 @@ struct CollectRetsVisitor<'tcx> { impl<'tcx> Visitor<'tcx> for CollectRetsVisitor<'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if let hir::ExprKind::Ret(_) = expr.kind { - self.ret_exprs.push(expr); + match expr.kind { + hir::ExprKind::Ret(_) => self.ret_exprs.push(expr), + // `return` in closures does not return from the outer function + hir::ExprKind::Closure(_) => return, + _ => {} } intravisit::walk_expr(self, expr); } @@ -1845,13 +1848,31 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } let parent_id = fcx.tcx.hir().get_parent_item(id); - let parent_item = fcx.tcx.hir_node_by_def_id(parent_id.def_id); + let mut parent_item = fcx.tcx.hir_node_by_def_id(parent_id.def_id); + // When suggesting return, we need to account for closures and async blocks, not just items. + for (_, node) in fcx.tcx.hir().parent_iter(id) { + match node { + hir::Node::Expr(&hir::Expr { + kind: hir::ExprKind::Closure(hir::Closure { .. }), + .. + }) => { + parent_item = node; + break; + } + hir::Node::Item(_) | hir::Node::TraitItem(_) | hir::Node::ImplItem(_) => break, + _ => {} + } + } - if let (Some(expr), Some(_), Some((fn_id, fn_decl, _, _))) = - (expression, blk_id, fcx.get_node_fn_decl(parent_item)) - { + if let (Some(expr), Some(_), Some(fn_decl)) = (expression, blk_id, parent_item.fn_decl()) { fcx.suggest_missing_break_or_return_expr( - &mut err, expr, fn_decl, expected, found, id, fn_id, + &mut err, + expr, + fn_decl, + expected, + found, + id, + parent_id.into(), ); } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index f7ff9f8224a..ce8b62d19b4 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -963,14 +963,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { owner_id, .. }) => Some((hir::HirId::make_owner(owner_id.def_id), &sig.decl, ident, false)), - Node::Expr(&hir::Expr { hir_id, kind: hir::ExprKind::Closure(..), .. }) - if let Node::Item(&hir::Item { - ident, - kind: hir::ItemKind::Fn(ref sig, ..), - owner_id, - .. - }) = self.tcx.parent_hir_node(hir_id) => - { + Node::Expr(&hir::Expr { + hir_id, + kind: + hir::ExprKind::Closure(hir::Closure { + kind: hir::ClosureKind::Coroutine(..), .. + }), + .. + }) => { + let (ident, sig, owner_id) = match self.tcx.parent_hir_node(hir_id) { + Node::Item(&hir::Item { + ident, + kind: hir::ItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => (ident, sig, owner_id), + Node::TraitItem(&hir::TraitItem { + ident, + kind: hir::TraitItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => (ident, sig, owner_id), + Node::ImplItem(&hir::ImplItem { + ident, + kind: hir::ImplItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => (ident, sig, owner_id), + _ => return None, + }; Some(( hir::HirId::make_owner(owner_id.def_id), &sig.decl, @@ -1558,7 +1579,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ctxt = { let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); debug_assert!(enclosing_breakables.stack.len() == index + 1); - enclosing_breakables.by_id.remove(&id).expect("missing breakable context"); + // FIXME(#120456) - is `swap_remove` correct? + enclosing_breakables.by_id.swap_remove(&id).expect("missing breakable context"); enclosing_breakables.stack.pop().expect("missing breakable context") }; (ctxt, result) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 35b3f27d791..65b8505c090 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1726,7 +1726,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise. - fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> { + pub(crate) fn get_parent_fn_decl( + &self, + blk_id: hir::HirId, + ) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> { let parent = self.tcx.hir_node_by_def_id(self.tcx.hir().get_parent_item(blk_id).def_id); self.get_node_fn_decl(parent).map(|(_, fn_decl, ident, _)| (fn_decl, ident)) } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index d68e20541ef..8b6f263b1a7 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -827,6 +827,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } hir::FnRetTy::Return(hir_ty) => { if let hir::TyKind::OpaqueDef(item_id, ..) = hir_ty.kind + // FIXME: account for RPITIT. && let hir::Node::Item(hir::Item { kind: hir::ItemKind::OpaqueTy(op_ty), .. }) = self.tcx.hir_node(item_id.hir_id()) @@ -1038,33 +1039,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } - if let hir::FnRetTy::Return(ty) = fn_decl.output { - let ty = self.astconv().ast_ty_to_ty(ty); - let bound_vars = self.tcx.late_bound_vars(fn_id); - let ty = self - .tcx - .instantiate_bound_regions_with_erased(Binder::bind_with_vars(ty, bound_vars)); - let ty = match self.tcx.asyncness(fn_id.owner) { - ty::Asyncness::Yes => self.get_impl_future_output_ty(ty).unwrap_or_else(|| { - span_bug!(fn_decl.output.span(), "failed to get output type of async function") - }), - ty::Asyncness::No => ty, - }; - let ty = self.normalize(expr.span, ty); - if self.can_coerce(found, ty) { - if let Some(owner_node) = self.tcx.hir_node(fn_id).as_owner() - && let Some(span) = expr.span.find_ancestor_inside(*owner_node.span()) - { - err.multipart_suggestion( - "you might have meant to return this value", - vec![ - (span.shrink_to_lo(), "return ".to_string()), - (span.shrink_to_hi(), ";".to_string()), - ], - Applicability::MaybeIncorrect, - ); - } + let scope = self + .tcx + .hir() + .parent_iter(id) + .filter(|(_, node)| { + matches!( + node, + Node::Expr(Expr { kind: ExprKind::Closure(..), .. }) + | Node::Item(_) + | Node::TraitItem(_) + | Node::ImplItem(_) + ) + }) + .next(); + let in_closure = + matches!(scope, Some((_, Node::Expr(Expr { kind: ExprKind::Closure(..), .. })))); + + let can_return = match fn_decl.output { + hir::FnRetTy::Return(ty) => { + let ty = self.astconv().ast_ty_to_ty(ty); + let bound_vars = self.tcx.late_bound_vars(fn_id); + let ty = self + .tcx + .instantiate_bound_regions_with_erased(Binder::bind_with_vars(ty, bound_vars)); + let ty = match self.tcx.asyncness(fn_id.owner) { + ty::Asyncness::Yes => self.get_impl_future_output_ty(ty).unwrap_or_else(|| { + span_bug!( + fn_decl.output.span(), + "failed to get output type of async function" + ) + }), + ty::Asyncness::No => ty, + }; + let ty = self.normalize(expr.span, ty); + self.can_coerce(found, ty) + } + hir::FnRetTy::DefaultReturn(_) if in_closure => { + self.ret_coercion.as_ref().map_or(false, |ret| { + let ret_ty = ret.borrow().expected_ty(); + self.can_coerce(found, ret_ty) + }) } + _ => false, + }; + if can_return + && let Some(owner_node) = self.tcx.hir_node(fn_id).as_owner() + && let Some(span) = expr.span.find_ancestor_inside(*owner_node.span()) + { + err.multipart_suggestion( + "you might have meant to return this value", + vec![ + (span.shrink_to_lo(), "return ".to_string()), + (span.shrink_to_hi(), ";".to_string()), + ], + Applicability::MaybeIncorrect, + ); } } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index c1af4b5983e..629c2f2a971 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -304,6 +304,10 @@ fn typeck_with_fallback<'tcx>( let typeck_results = fcx.resolve_type_vars_in_body(body); + // We clone the defined opaque types during writeback in the new solver + // because we have to use them during normalization. + let _ = fcx.infcx.take_opaque_types(); + // Consistency check our TypeckResults instance can hold all ItemLocalIds // it will need to hold. assert_eq!(typeck_results.hir_owner, id.owner); diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 4e9cb92919a..a580c114f26 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -518,12 +518,9 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { .report_mismatched_types(&cause, method_self_ty, self_ty, terr) .emit(); } else { - span_bug!( - self.span, - "{} was a subtype of {} but now is not?", - self_ty, - method_self_ty - ); + error!("{self_ty} was a subtype of {method_self_ty} but now is not?"); + // This must already have errored elsewhere. + self.dcx().has_errors().unwrap(); } } } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index ab5c0f32705..729ce1f00cd 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1073,12 +1073,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // for instance self.tcx.at(span).type_of(*def_id).instantiate_identity() != rcvr_ty - && self - .tcx - .at(span) - .type_of(*def_id) - .instantiate_identity() - != rcvr_ty } (Mode::Path, false, _) => true, _ => false, @@ -1092,7 +1086,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { inherent_impls_candidate.sort(); inherent_impls_candidate.dedup(); - // number of type to shows at most. + // number of types to show at most let limit = if inherent_impls_candidate.len() == 5 { 5 } else { 4 }; let type_candidates = inherent_impls_candidate .iter() diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 5ce80ef5c10..b83a0f893f5 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -221,8 +221,8 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { if base_ty.is_none() { // When encountering `return [0][0]` outside of a `fn` body we can encounter a base // that isn't in the type table. We assume more relevant errors have already been - // emitted, so we delay an ICE if none have. (#64638) - self.tcx().dcx().span_delayed_bug(e.span, format!("bad base: `{base:?}`")); + // emitted. (#64638) + assert!(self.tcx().dcx().has_errors().is_some(), "bad base: `{base:?}`"); } if let Some(base_ty) = base_ty && let ty::Ref(_, base_ty_inner, _) = *base_ty.kind() @@ -562,7 +562,15 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { #[instrument(skip(self), level = "debug")] fn visit_opaque_types(&mut self) { - let opaque_types = self.fcx.infcx.take_opaque_types(); + // We clone the opaques instead of stealing them here as they are still used for + // normalization in the next generation trait solver. + // + // FIXME(-Znext-solver): Opaque types defined after this would simply get dropped + // at the end of typeck. While this seems unlikely to happen in practice this + // should still get fixed. Either by preventing writeback from defining new opaque + // types or by using this function at the end of writeback and running it as a + // fixpoint. + let opaque_types = self.fcx.infcx.clone_opaque_types(); for (opaque_type_key, decl) in opaque_types { let hidden_type = self.resolve(decl.hidden_type, &decl.hidden_type.span); let opaque_type_key = self.resolve(opaque_type_key, &decl.hidden_type.span); diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 859593e1194..05b9479c7b0 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -285,13 +285,11 @@ impl<'a, 'tcx> Trace<'a, 'tcx> { T: Relate<'tcx>, { let Trace { at, trace, a_is_expected } = self; - at.infcx.commit_if_ok(|_| { - let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types); - fields - .sub(a_is_expected) - .relate(a, b) - .map(move |_| InferOk { value: (), obligations: fields.obligations }) - }) + let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types); + fields + .sub(a_is_expected) + .relate(a, b) + .map(move |_| InferOk { value: (), obligations: fields.obligations }) } /// Makes `a == b`; the expectation is set by the call to @@ -302,13 +300,11 @@ impl<'a, 'tcx> Trace<'a, 'tcx> { T: Relate<'tcx>, { let Trace { at, trace, a_is_expected } = self; - at.infcx.commit_if_ok(|_| { - let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types); - fields - .equate(a_is_expected) - .relate(a, b) - .map(move |_| InferOk { value: (), obligations: fields.obligations }) - }) + let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types); + fields + .equate(a_is_expected) + .relate(a, b) + .map(move |_| InferOk { value: (), obligations: fields.obligations }) } #[instrument(skip(self), level = "debug")] @@ -317,13 +313,11 @@ impl<'a, 'tcx> Trace<'a, 'tcx> { T: Relate<'tcx>, { let Trace { at, trace, a_is_expected } = self; - at.infcx.commit_if_ok(|_| { - let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types); - fields - .lub(a_is_expected) - .relate(a, b) - .map(move |t| InferOk { value: t, obligations: fields.obligations }) - }) + let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types); + fields + .lub(a_is_expected) + .relate(a, b) + .map(move |t| InferOk { value: t, obligations: fields.obligations }) } #[instrument(skip(self), level = "debug")] @@ -332,13 +326,11 @@ impl<'a, 'tcx> Trace<'a, 'tcx> { T: Relate<'tcx>, { let Trace { at, trace, a_is_expected } = self; - at.infcx.commit_if_ok(|_| { - let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types); - fields - .glb(a_is_expected) - .relate(a, b) - .map(move |t| InferOk { value: t, obligations: fields.obligations }) - }) + let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types); + fields + .glb(a_is_expected) + .relate(a, b) + .map(move |t| InferOk { value: t, obligations: fields.obligations }) } } diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 216b2e70abf..56a45586c9d 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -620,42 +620,40 @@ impl<'tcx> InferCtxt<'tcx> { variables1: &OriginalQueryValues<'tcx>, variables2: impl Fn(BoundVar) -> GenericArg<'tcx>, ) -> InferResult<'tcx, ()> { - self.commit_if_ok(|_| { - let mut obligations = vec![]; - for (index, value1) in variables1.var_values.iter().enumerate() { - let value2 = variables2(BoundVar::new(index)); - - match (value1.unpack(), value2.unpack()) { - (GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => { - obligations.extend( - self.at(cause, param_env) - .eq(DefineOpaqueTypes::Yes, v1, v2)? - .into_obligations(), - ); - } - (GenericArgKind::Lifetime(re1), GenericArgKind::Lifetime(re2)) - if re1.is_erased() && re2.is_erased() => - { - // no action needed - } - (GenericArgKind::Lifetime(v1), GenericArgKind::Lifetime(v2)) => { - obligations.extend( - self.at(cause, param_env) - .eq(DefineOpaqueTypes::Yes, v1, v2)? - .into_obligations(), - ); - } - (GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => { - let ok = self.at(cause, param_env).eq(DefineOpaqueTypes::Yes, v1, v2)?; - obligations.extend(ok.into_obligations()); - } - _ => { - bug!("kind mismatch, cannot unify {:?} and {:?}", value1, value2,); - } + let mut obligations = vec![]; + for (index, value1) in variables1.var_values.iter().enumerate() { + let value2 = variables2(BoundVar::new(index)); + + match (value1.unpack(), value2.unpack()) { + (GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => { + obligations.extend( + self.at(cause, param_env) + .eq(DefineOpaqueTypes::Yes, v1, v2)? + .into_obligations(), + ); + } + (GenericArgKind::Lifetime(re1), GenericArgKind::Lifetime(re2)) + if re1.is_erased() && re2.is_erased() => + { + // no action needed + } + (GenericArgKind::Lifetime(v1), GenericArgKind::Lifetime(v2)) => { + obligations.extend( + self.at(cause, param_env) + .eq(DefineOpaqueTypes::Yes, v1, v2)? + .into_obligations(), + ); + } + (GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => { + let ok = self.at(cause, param_env).eq(DefineOpaqueTypes::Yes, v1, v2)?; + obligations.extend(ok.into_obligations()); + } + _ => { + bug!("kind mismatch, cannot unify {:?} and {:?}", value1, value2,); } } - Ok(InferOk { value: (), obligations }) - }) + } + Ok(InferOk { value: (), obligations }) } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 4d2d19b51e2..b953b25d6c4 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -132,20 +132,6 @@ pub struct TypeErrCtxt<'a, 'tcx> { Box<dyn Fn(Ty<'tcx>) -> Vec<(Ty<'tcx>, Vec<PredicateObligation<'tcx>>)> + 'a>, } -impl Drop for TypeErrCtxt<'_, '_> { - fn drop(&mut self) { - if self.dcx().has_errors().is_some() { - // Ok, emitted an error. - } else { - // Didn't emit an error; maybe it was created but not yet emitted. - self.infcx - .tcx - .sess - .good_path_delayed_bug("used a `TypeErrCtxt` without raising an error or lint"); - } - } -} - impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { pub fn dcx(&self) -> &'tcx DiagCtxt { self.infcx.tcx.dcx() diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 6137506d4a9..c39d0425f7e 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -802,14 +802,12 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } // Errors in earlier passes can yield error variables without - // resolution errors here; delay ICE in favor of those errors. - self.tcx().dcx().span_delayed_bug( - self.var_infos[node_idx].origin.span(), - format!( - "collect_error_for_expanding_node() could not find \ - error for var {node_idx:?} in universe {node_universe:?}, lower_bounds={lower_bounds:#?}, \ - upper_bounds={upper_bounds:#?}" - ), + // resolution errors here; ICE if no errors have been emitted yet. + assert!( + self.tcx().dcx().has_errors().is_some(), + "collect_error_for_expanding_node() could not find error for var {node_idx:?} in \ + universe {node_universe:?}, lower_bounds={lower_bounds:#?}, \ + upper_bounds={upper_bounds:#?}", ); } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 0bf4598608f..2caf3b3cc93 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1325,6 +1325,12 @@ impl<'tcx> InferCtxt<'tcx> { std::mem::take(&mut self.inner.borrow_mut().opaque_type_storage.opaque_types) } + #[instrument(level = "debug", skip(self), ret)] + pub fn clone_opaque_types(&self) -> opaque_types::OpaqueTypeMap<'tcx> { + debug_assert_ne!(self.defining_use_anchor, DefiningAnchor::Error); + self.inner.borrow().opaque_type_storage.opaque_types.clone() + } + pub fn ty_to_string(&self, t: Ty<'tcx>) -> String { self.resolve_vars_if_possible(t).to_string() } diff --git a/compiler/rustc_infer/src/infer/opaque_types/table.rs b/compiler/rustc_infer/src/infer/opaque_types/table.rs index 6a684dba8de..9f49ed00219 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/table.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/table.rs @@ -20,7 +20,8 @@ impl<'tcx> OpaqueTypeStorage<'tcx> { if let Some(idx) = idx { self.opaque_types.get_mut(&key).unwrap().hidden_type = idx; } else { - match self.opaque_types.remove(&key) { + // FIXME(#120456) - is `swap_remove` correct? + match self.opaque_types.swap_remove(&key) { None => bug!("reverted opaque type inference that was never registered: {:?}", key), Some(_) => {} } diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 7208f17fb34..c0a99e5cc41 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -300,9 +300,9 @@ where self.components_must_outlive(origin, subcomponents, region, category); } Component::UnresolvedInferenceVariable(v) => { - // ignore this, we presume it will yield an error - // later, since if a type variable is not resolved by - // this point it never will be + // Ignore this, we presume it will yield an error later, + // since if a type variable is not resolved by this point + // it never will be. self.tcx.dcx().span_delayed_bug( origin.span(), format!("unresolved inference variable in outlives: {v:?}"), diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 24b351988bf..3ef37bf3466 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -172,13 +172,13 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { self.bound_from_components(components, visited) } Component::UnresolvedInferenceVariable(v) => { - // ignore this, we presume it will yield an error - // later, since if a type variable is not resolved by - // this point it never will be + // Ignore this, we presume it will yield an error later, since + // if a type variable is not resolved by this point it never + // will be. self.tcx .dcx() .delayed_bug(format!("unresolved inference variable in outlives: {v:?}")); - // add a bound that never holds + // Add a bound that never holds. VerifyBound::AnyBound(vec![]) } } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 5ae080d4702..0862204d88e 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -431,14 +431,13 @@ pub fn check_ast_node_inner<'a, T: EarlyLintPass>( // If not, that means that we somehow buffered a lint for a node id // that was not lint-checked (perhaps it doesn't exist?). This is a bug. for (id, lints) in cx.context.buffered.map { - for early_lint in lints { - sess.dcx().span_delayed_bug( - early_lint.span, - format!( - "failed to process buffered lint here (dummy = {})", - id == ast::DUMMY_NODE_ID - ), + if !lints.is_empty() { + assert!( + sess.dcx().has_errors().is_some(), + "failed to process buffered lint here (dummy = {})", + id == ast::DUMMY_NODE_ID ); + break; } } } diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 9f670893b27..35ee0c53046 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -651,9 +651,11 @@ trait UnusedDelimLint { fn is_expr_delims_necessary( inner: &ast::Expr, + ctx: UnusedDelimsCtx, followed_by_block: bool, - followed_by_else: bool, ) -> bool { + let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse; + if followed_by_else { match inner.kind { ast::ExprKind::Binary(op, ..) if op.node.is_lazy() => return true, @@ -662,6 +664,13 @@ trait UnusedDelimLint { } } + // Check it's range in LetScrutineeExpr + if let ast::ExprKind::Range(..) = inner.kind + && matches!(ctx, UnusedDelimsCtx::LetScrutineeExpr) + { + return true; + } + // Check if LHS needs parens to prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }`. { let mut innermost = inner; @@ -1007,8 +1016,7 @@ impl UnusedDelimLint for UnusedParens { ) { match value.kind { ast::ExprKind::Paren(ref inner) => { - let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse; - if !Self::is_expr_delims_necessary(inner, followed_by_block, followed_by_else) + if !Self::is_expr_delims_necessary(inner, ctx, followed_by_block) && value.attrs.is_empty() && !value.span.from_expansion() && (ctx != UnusedDelimsCtx::LetScrutineeExpr @@ -1334,7 +1342,7 @@ impl UnusedDelimLint for UnusedBraces { // FIXME(const_generics): handle paths when #67075 is fixed. if let [stmt] = inner.stmts.as_slice() { if let ast::StmtKind::Expr(ref expr) = stmt.kind { - if !Self::is_expr_delims_necessary(expr, followed_by_block, false) + if !Self::is_expr_delims_necessary(expr, ctx, followed_by_block) && (ctx != UnusedDelimsCtx::AnonConst || (matches!(expr.kind, ast::ExprKind::Lit(_)) && !expr.span.from_expansion())) diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 7ed78a2ffc8..09b1f03f151 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -708,7 +708,8 @@ impl LintBuffer { } pub fn take(&mut self, id: NodeId) -> Vec<BufferedEarlyLint> { - self.map.remove(&id).unwrap_or_default() + // FIXME(#120456) - is `swap_remove` correct? + self.map.swap_remove(&id).unwrap_or_default() } pub fn buffer_lint( diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml index 58e219e5a46..6598f1db86d 100644 --- a/compiler/rustc_llvm/Cargo.toml +++ b/compiler/rustc_llvm/Cargo.toml @@ -8,7 +8,12 @@ edition = "2021" libc = "0.2.73" # tidy-alphabetical-end +# FIXME: updating cc past 1.0.79 breaks libstd bootstrapping, pin +# to the last working version here so `cargo update` doesn't cause the +# a higher version to be selected +# https://github.com/rust-lang/cc-rs/issues/913 +# 1.0.{84, 85} fix this but have been yanked [build-dependencies] # tidy-alphabetical-start -cc = "1.0.69" +cc = "=1.0.79" # tidy-alphabetical-end diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp index 373bc5cc581..627be997513 100644 --- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp @@ -139,7 +139,7 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer( RustMappingRegions, NumMappingRegions)) { MappingRegions.emplace_back( fromRust(Region.Count), fromRust(Region.FalseCount), -#if LLVM_VERSION_GE(18, 0) +#if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0) coverage::CounterMappingRegion::MCDCParameters{}, #endif Region.FileID, Region.ExpandedFileID, diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 3b6bf03686b..5b1cc52f1f6 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -43,6 +43,9 @@ #include "llvm/Transforms/Instrumentation.h" #include "llvm/Transforms/Instrumentation/AddressSanitizer.h" #include "llvm/Support/TimeProfiler.h" +#if LLVM_VERSION_GE(19, 0) +#include "llvm/Support/PGOOptions.h" +#endif #include "llvm/Transforms/Instrumentation/GCOVProfiler.h" #include "llvm/Transforms/Instrumentation/InstrProfiling.h" #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" @@ -368,10 +371,10 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, } extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) { -#ifdef LLVM_RUSTLLVM +#if LLVM_VERSION_GE(18, 0) const TargetMachine *Target = unwrap(TM); const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); - const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getFeatureTable(); + const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getAllProcessorFeatures(); return FeatTable.size(); #else return 0; @@ -380,10 +383,10 @@ extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) { extern "C" void LLVMRustGetTargetFeature(LLVMTargetMachineRef TM, size_t Index, const char** Feature, const char** Desc) { -#ifdef LLVM_RUSTLLVM +#if LLVM_VERSION_GE(18, 0) const TargetMachine *Target = unwrap(TM); const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); - const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getFeatureTable(); + const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getAllProcessorFeatures(); const SubtargetFeatureKV Feat = FeatTable[Index]; *Feature = Feat.Key; *Desc = Feat.Desc; @@ -749,6 +752,9 @@ LLVMRustOptimize( FS, #endif PGOOptions::IRInstr, PGOOptions::NoCSAction, +#if LLVM_VERSION_GE(19, 0) + PGOOptions::ColdFuncOpt::Default, +#endif DebugInfoForProfiling); } else if (PGOUsePath) { assert(!PGOSampleUsePath); @@ -758,6 +764,9 @@ LLVMRustOptimize( FS, #endif PGOOptions::IRUse, PGOOptions::NoCSAction, +#if LLVM_VERSION_GE(19, 0) + PGOOptions::ColdFuncOpt::Default, +#endif DebugInfoForProfiling); } else if (PGOSampleUsePath) { PGOOpt = PGOOptions(PGOSampleUsePath, "", "", @@ -766,6 +775,9 @@ LLVMRustOptimize( FS, #endif PGOOptions::SampleUse, PGOOptions::NoCSAction, +#if LLVM_VERSION_GE(19, 0) + PGOOptions::ColdFuncOpt::Default, +#endif DebugInfoForProfiling); } else if (DebugInfoForProfiling) { PGOOpt = PGOOptions("", "", "", @@ -774,6 +786,9 @@ LLVMRustOptimize( FS, #endif PGOOptions::NoAction, PGOOptions::NoCSAction, +#if LLVM_VERSION_GE(19, 0) + PGOOptions::ColdFuncOpt::Default, +#endif DebugInfoForProfiling); } diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index a2dfebec594..b45706fd1e5 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1801,6 +1801,9 @@ extern "C" LLVMRustResult LLVMRustWriteImportLibrary( std::string{}, // ExtName std::string{}, // SymbolName std::string{}, // AliasTarget +#if LLVM_VERSION_GE(19, 0) + std::string{}, // ExportAs +#endif ordinal, // Ordinal ordinal_present, // Noname false, // Data diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 8e1cb6a514f..e7d9dc04886 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -617,7 +617,7 @@ impl<'hir> Map<'hir> { Node::Item(_) | Node::ForeignItem(_) | Node::TraitItem(_) - | Node::Expr(Expr { kind: ExprKind::Closure { .. }, .. }) + | Node::Expr(Expr { kind: ExprKind::Closure(_), .. }) | Node::ImplItem(_) // The input node `id` must be enclosed in the method's body as opposed // to some other place such as its return type (fixes #114918). diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 6ffa0819f35..2a6f473cd32 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -207,6 +207,11 @@ pub fn explain_lint_level_source( err: &mut Diagnostic, ) { let name = lint.name_lower(); + if let Level::Allow = level { + // Do not point at `#[allow(compat_lint)]` as the reason for a compatibility lint + // triggering. (#121009) + return; + } match src { LintLevelSource::Default => { err.note_once(format!("`#[{}({})]` on by default", level.as_str(), name)); diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 23771073745..5e4d899f517 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -35,6 +35,16 @@ impl<'tcx> IntoKind for Const<'tcx> { } } +impl<'tcx> rustc_type_ir::visit::Flags for Const<'tcx> { + fn flags(&self) -> TypeFlags { + self.0.flags + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + self.0.outer_exclusive_binder + } +} + impl<'tcx> ConstTy<TyCtxt<'tcx>> for Const<'tcx> { fn ty(self) -> Ty<'tcx> { self.ty() @@ -63,11 +73,13 @@ impl<'tcx> Const<'tcx> { self.0.kind } + // FIXME(compiler-errors): Think about removing this. #[inline] pub fn flags(self) -> TypeFlags { self.0.flags } + // FIXME(compiler-errors): Think about removing this. #[inline] pub fn outer_exclusive_binder(self) -> ty::DebruijnIndex { self.0.outer_exclusive_binder diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index bd86c1c284e..7033a4a833d 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -28,7 +28,7 @@ use crate::ty::{ self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Const, ConstData, GenericParamDefKind, ImplPolarity, List, ParamConst, ParamTy, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, - Visibility, + TypeVisitable, Visibility, }; use crate::ty::{GenericArg, GenericArgs, GenericArgsRef}; use rustc_ast::{self as ast, attr}; @@ -87,7 +87,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type GenericArg = ty::GenericArg<'tcx>; type Term = ty::Term<'tcx>; - type Binder<T> = Binder<'tcx, T>; + type Binder<T: TypeVisitable<TyCtxt<'tcx>>> = Binder<'tcx, T>; + type BoundVars = &'tcx List<ty::BoundVariableKind>; + type BoundVar = ty::BoundVariableKind; type CanonicalVars = CanonicalVarInfos<'tcx>; type Ty = Ty<'tcx>; @@ -151,6 +153,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ) -> Self::Const { Const::new_bound(self, debruijn, var, ty) } + + fn expect_error_or_delayed_bug() { + let has_errors = ty::tls::with(|tcx| tcx.dcx().has_errors_or_lint_errors_or_delayed_bugs()); + assert!(has_errors.is_some()); + } } type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>; @@ -1046,12 +1053,6 @@ impl<'tcx> TyCtxtAt<'tcx> { name: Symbol, def_kind: DefKind, ) -> TyCtxtFeed<'tcx, LocalDefId> { - // This function modifies `self.definitions` using a side-effect. - // We need to ensure that these side effects are re-run by the incr. comp. engine. - // Depending on the forever-red node will tell the graph that the calling query - // needs to be re-evaluated. - self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE); - // The following call has the side effect of modifying the tables inside `definitions`. // These very tables are relied on by the incr. comp. engine to decode DepNodes and to // decode the on-disk cache. @@ -1080,6 +1081,12 @@ impl<'tcx> TyCtxt<'tcx> { let data = def_kind.def_path_data(name); let def_id = self.untracked.definitions.write().create_def(parent, data); + // This function modifies `self.definitions` using a side-effect. + // We need to ensure that these side effects are re-run by the incr. comp. engine. + // Depending on the forever-red node will tell the graph that the calling query + // needs to be re-evaluated. + self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE); + let feed = self.feed_local_def_id(def_id); feed.def_kind(def_kind); // Unique types created for closures participate in type privacy checking. diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 6ee74ef2fb6..15bddb2a64f 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -252,6 +252,7 @@ pub struct ImplHeader<'tcx> { pub struct ImplTraitHeader<'tcx> { pub trait_ref: ty::TraitRef<'tcx>, pub polarity: ImplPolarity, + pub unsafety: hir::Unsafety, } #[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)] @@ -503,6 +504,16 @@ impl<'tcx> IntoKind for Ty<'tcx> { } } +impl<'tcx> rustc_type_ir::visit::Flags for Ty<'tcx> { + fn flags(&self) -> TypeFlags { + self.0.flags + } + + fn outer_exclusive_binder(&self) -> DebruijnIndex { + self.0.outer_exclusive_binder + } +} + impl EarlyParamRegion { /// Does this early bound region have a name? Early bound regions normally /// always have names except when using anonymous lifetimes (`'_`). diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 200811940ed..b63f9c6dfa0 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -29,6 +29,16 @@ pub struct Predicate<'tcx>( pub(super) Interned<'tcx, WithCachedTypeInfo<ty::Binder<'tcx, PredicateKind<'tcx>>>>, ); +impl<'tcx> rustc_type_ir::visit::Flags for Predicate<'tcx> { + fn flags(&self) -> TypeFlags { + self.0.flags + } + + fn outer_exclusive_binder(&self) -> ty::DebruijnIndex { + self.0.outer_exclusive_binder + } +} + impl<'tcx> Predicate<'tcx> { /// Gets the inner `ty::Binder<'tcx, PredicateKind<'tcx>>`. #[inline] @@ -36,11 +46,13 @@ impl<'tcx> Predicate<'tcx> { self.0.internee } + // FIXME(compiler-errors): Think about removing this. #[inline(always)] pub fn flags(self) -> TypeFlags { self.0.flags } + // FIXME(compiler-errors): Think about removing this. #[inline(always)] pub fn outer_exclusive_binder(self) -> DebruijnIndex { self.0.outer_exclusive_binder diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 5cf90e94907..92ec1a83bee 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -3156,13 +3156,12 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N // this is pub to be able to intra-doc-link it pub fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> DefIdMap<Symbol> { // Trimming paths is expensive and not optimized, since we expect it to only be used for error - // reporting. + // reporting. Record the fact that we did it, so we can abort if we later found it was + // unnecessary. // - // For good paths causing this bug, the `rustc_middle::ty::print::with_no_trimmed_paths` - // wrapper can be used to suppress this query, in exchange for full paths being formatted. - tcx.sess.good_path_delayed_bug( - "trimmed_def_paths constructed but no error emitted; use `DelayDm` for lints or `with_no_trimmed_paths` for debugging", - ); + // The `rustc_middle::ty::print::with_no_trimmed_paths` wrapper can be used to suppress this + // checking, in exchange for full paths being formatted. + tcx.sess.record_trimmed_def_paths(); // Once constructed, unique namespace+symbol pairs will have a `Some(_)` entry, while // non-unique pairs will have a `None` entry. diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs index 1191d7fca32..b206727f051 100644 --- a/compiler/rustc_middle/src/ty/region.rs +++ b/compiler/rustc_middle/src/ty/region.rs @@ -26,6 +26,19 @@ impl<'tcx> rustc_type_ir::IntoKind for Region<'tcx> { } } +impl<'tcx> rustc_type_ir::visit::Flags for Region<'tcx> { + fn flags(&self) -> TypeFlags { + self.type_flags() + } + + fn outer_exclusive_binder(&self) -> ty::DebruijnIndex { + match **self { + ty::ReBound(debruijn, _) => debruijn.shifted_in(1), + _ => ty::INNERMOST, + } + } +} + impl<'tcx> Region<'tcx> { #[inline] pub fn new_early_param( diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index a3d5f1f1955..66086ac87f1 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -942,6 +942,16 @@ where } } +impl<'tcx, T> rustc_type_ir::BoundVars<TyCtxt<'tcx>> for ty::Binder<'tcx, T> { + fn bound_vars(&self) -> &'tcx List<ty::BoundVariableKind> { + self.bound_vars + } + + fn has_no_bound_vars(&self) -> bool { + self.bound_vars.is_empty() + } +} + impl<'tcx, T> Binder<'tcx, T> { /// Skips the binder and returns the "bound" value. This is a /// risky thing to do because it's easy to get confused about @@ -1808,6 +1818,7 @@ impl<'tcx> Ty<'tcx> { self.0.0 } + // FIXME(compiler-errors): Think about removing this. #[inline(always)] pub fn flags(self) -> TypeFlags { self.0.0.flags @@ -2362,6 +2373,20 @@ impl<'tcx> Ty<'tcx> { /// to represent the closure kind, because it has not yet been /// inferred. Once upvar inference (in `rustc_hir_analysis/src/check/upvar.rs`) /// is complete, that type variable will be unified. + /// + /// To be noted that you can use [`ClosureArgs::kind()`] or [`CoroutineClosureArgs::kind()`] + /// to get the same information, which you can get by calling [`GenericArgs::as_closure()`] + /// or [`GenericArgs::as_coroutine_closure()`], depending on the type of the closure. + /// + /// Otherwise, this method can be used as follows: + /// + /// ```rust,ignore (snippet of compiler code) + /// let TyKind::Closure(def_id, [closure_fn_kind_ty, ..]) = closure_ty.kind() + /// && let Some(closure_kind) = closure_fn_kind_ty.expect_ty().to_opt_closure_kind() + /// { + /// // your code + /// } + /// ``` pub fn to_opt_closure_kind(self) -> Option<ty::ClosureKind> { match self.kind() { Int(int_ty) => match int_ty { diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index c674a868d9f..09bb06de483 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1320,6 +1320,7 @@ impl<'tcx> Ty<'tcx> { ty } + // FIXME(compiler-errors): Think about removing this. #[inline] pub fn outer_exclusive_binder(self) -> ty::DebruijnIndex { self.0.outer_exclusive_binder diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 7acdb931f1a..59292a281ed 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -1,140 +1,10 @@ use crate::ty::{self, Binder, Ty, TyCtxt, TypeFlags}; -use rustc_errors::ErrorGuaranteed; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sso::SsoHashSet; use std::ops::ControlFlow; -pub use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; - -pub trait TypeVisitableExt<'tcx>: TypeVisitable<TyCtxt<'tcx>> { - /// Returns `true` if `self` has any late-bound regions that are either - /// bound by `binder` or bound by some binder outside of `binder`. - /// If `binder` is `ty::INNERMOST`, this indicates whether - /// there are any late-bound regions that appear free. - fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool { - self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }).is_break() - } - - /// Returns `true` if this type has any regions that escape `binder` (and - /// hence are not bound by it). - fn has_vars_bound_above(&self, binder: ty::DebruijnIndex) -> bool { - self.has_vars_bound_at_or_above(binder.shifted_in(1)) - } - - /// Return `true` if this type has regions that are not a part of the type. - /// For example, `for<'a> fn(&'a i32)` return `false`, while `fn(&'a i32)` - /// would return `true`. The latter can occur when traversing through the - /// former. - /// - /// See [`HasEscapingVarsVisitor`] for more information. - fn has_escaping_bound_vars(&self) -> bool { - self.has_vars_bound_at_or_above(ty::INNERMOST) - } - - fn has_type_flags(&self, flags: TypeFlags) -> bool { - let res = - self.visit_with(&mut HasTypeFlagsVisitor { flags }).break_value() == Some(FoundFlags); - trace!(?self, ?flags, ?res, "has_type_flags"); - res - } - fn has_projections(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_PROJECTION) - } - fn has_inherent_projections(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_INHERENT) - } - fn has_opaque_types(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_OPAQUE) - } - fn has_coroutines(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_COROUTINE) - } - fn references_error(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_ERROR) - } - fn error_reported(&self) -> Result<(), ErrorGuaranteed> { - if self.references_error() { - // We must include lint errors and delayed bugs here. - if let Some(reported) = - ty::tls::with(|tcx| tcx.dcx().has_errors_or_lint_errors_or_delayed_bugs()) - { - Err(reported) - } else { - bug!("expected some kind of error in `error_reported`"); - } - } else { - Ok(()) - } - } - fn has_non_region_param(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_PARAM - TypeFlags::HAS_RE_PARAM) - } - fn has_infer_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_RE_INFER) - } - fn has_infer_types(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_INFER) - } - fn has_non_region_infer(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_INFER - TypeFlags::HAS_RE_INFER) - } - fn has_infer(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_INFER) - } - fn has_placeholders(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_PLACEHOLDER) - } - fn has_non_region_placeholders(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_PLACEHOLDER - TypeFlags::HAS_RE_PLACEHOLDER) - } - fn has_param(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_PARAM) - } - /// "Free" regions in this context means that it has any region - /// that is not (a) erased or (b) late-bound. - fn has_free_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) - } - - fn has_erased_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_RE_ERASED) - } - - /// True if there are any un-erased free regions. - fn has_erasable_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) - } - - /// Indicates whether this value references only 'global' - /// generic parameters that are the same regardless of what fn we are - /// in. This is used for caching. - fn is_global(&self) -> bool { - !self.has_type_flags(TypeFlags::HAS_FREE_LOCAL_NAMES) - } - - /// True if there are any late-bound regions - fn has_bound_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_RE_BOUND) - } - /// True if there are any late-bound non-region variables - fn has_non_region_bound_vars(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_BOUND_VARS - TypeFlags::HAS_RE_BOUND) - } - /// True if there are any bound variables - fn has_bound_vars(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_BOUND_VARS) - } - - /// Indicates whether this value still has parameters/placeholders/inference variables - /// which could be replaced later, in a way that would change the results of `impl` - /// specialization. - fn still_further_specializable(&self) -> bool { - self.has_type_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE) - } -} - -impl<'tcx, T: TypeVisitable<TyCtxt<'tcx>>> TypeVisitableExt<'tcx> for T {} +pub use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; /////////////////////////////////////////////////////////////////////////// // Region folder @@ -370,185 +240,6 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ValidateBoundVars<'tcx> { } } -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -struct FoundEscapingVars; - -/// An "escaping var" is a bound var whose binder is not part of `t`. A bound var can be a -/// bound region or a bound type. -/// -/// So, for example, consider a type like the following, which has two binders: -/// -/// for<'a> fn(x: for<'b> fn(&'a isize, &'b isize)) -/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope -/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ inner scope -/// -/// This type has *bound regions* (`'a`, `'b`), but it does not have escaping regions, because the -/// binders of both `'a` and `'b` are part of the type itself. However, if we consider the *inner -/// fn type*, that type has an escaping region: `'a`. -/// -/// Note that what I'm calling an "escaping var" is often just called a "free var". However, -/// we already use the term "free var". It refers to the regions or types that we use to represent -/// bound regions or type params on a fn definition while we are type checking its body. -/// -/// To clarify, conceptually there is no particular difference between -/// an "escaping" var and a "free" var. However, there is a big -/// difference in practice. Basically, when "entering" a binding -/// level, one is generally required to do some sort of processing to -/// a bound var, such as replacing it with a fresh/placeholder -/// var, or making an entry in the environment to represent the -/// scope to which it is attached, etc. An escaping var represents -/// a bound var for which this processing has not yet been done. -struct HasEscapingVarsVisitor { - /// Anything bound by `outer_index` or "above" is escaping. - outer_index: ty::DebruijnIndex, -} - -impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasEscapingVarsVisitor { - type BreakTy = FoundEscapingVars; - - fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>( - &mut self, - t: &Binder<'tcx, T>, - ) -> ControlFlow<Self::BreakTy> { - self.outer_index.shift_in(1); - let result = t.super_visit_with(self); - self.outer_index.shift_out(1); - result - } - - #[inline] - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - // If the outer-exclusive-binder is *strictly greater* than - // `outer_index`, that means that `t` contains some content - // bound at `outer_index` or above (because - // `outer_exclusive_binder` is always 1 higher than the - // content in `t`). Therefore, `t` has some escaping vars. - if t.outer_exclusive_binder() > self.outer_index { - ControlFlow::Break(FoundEscapingVars) - } else { - ControlFlow::Continue(()) - } - } - - #[inline] - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - // If the region is bound by `outer_index` or anything outside - // of outer index, then it escapes the binders we have - // visited. - if r.bound_at_or_above_binder(self.outer_index) { - ControlFlow::Break(FoundEscapingVars) - } else { - ControlFlow::Continue(()) - } - } - - fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { - // If the outer-exclusive-binder is *strictly greater* than - // `outer_index`, that means that `ct` contains some content - // bound at `outer_index` or above (because - // `outer_exclusive_binder` is always 1 higher than the - // content in `t`). Therefore, `t` has some escaping vars. - if ct.outer_exclusive_binder() > self.outer_index { - ControlFlow::Break(FoundEscapingVars) - } else { - ControlFlow::Continue(()) - } - } - - #[inline] - fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> { - if predicate.outer_exclusive_binder() > self.outer_index { - ControlFlow::Break(FoundEscapingVars) - } else { - ControlFlow::Continue(()) - } - } -} - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -struct FoundFlags; - -// FIXME: Optimize for checking for infer flags -struct HasTypeFlagsVisitor { - flags: ty::TypeFlags, -} - -impl std::fmt::Debug for HasTypeFlagsVisitor { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.flags.fmt(fmt) - } -} - -// Note: this visitor traverses values down to the level of -// `Ty`/`Const`/`Predicate`, but not within those types. This is because the -// type flags at the outer layer are enough. So it's faster than it first -// looks, particular for `Ty`/`Predicate` where it's just a field access. -// -// N.B. The only case where this isn't totally true is binders, which also -// add `HAS_{RE,TY,CT}_LATE_BOUND` flag depending on the *bound variables* that -// are present, regardless of whether those bound variables are used. This -// is important for anonymization of binders in `TyCtxt::erase_regions`. We -// specifically detect this case in `visit_binder`. -impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasTypeFlagsVisitor { - type BreakTy = FoundFlags; - - fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>( - &mut self, - t: &Binder<'tcx, T>, - ) -> ControlFlow<Self::BreakTy> { - // If we're looking for the HAS_BINDER_VARS flag, check if the - // binder has vars. This won't be present in the binder's bound - // value, so we need to check here too. - if self.flags.intersects(TypeFlags::HAS_BINDER_VARS) && !t.bound_vars().is_empty() { - return ControlFlow::Break(FoundFlags); - } - - t.super_visit_with(self) - } - - #[inline] - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - // Note: no `super_visit_with` call. - let flags = t.flags(); - if flags.intersects(self.flags) { - ControlFlow::Break(FoundFlags) - } else { - ControlFlow::Continue(()) - } - } - - #[inline] - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - // Note: no `super_visit_with` call, as usual for `Region`. - let flags = r.type_flags(); - if flags.intersects(self.flags) { - ControlFlow::Break(FoundFlags) - } else { - ControlFlow::Continue(()) - } - } - - #[inline] - fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { - // Note: no `super_visit_with` call. - if c.flags().intersects(self.flags) { - ControlFlow::Break(FoundFlags) - } else { - ControlFlow::Continue(()) - } - } - - #[inline] - fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> { - // Note: no `super_visit_with` call. - if predicate.flags().intersects(self.flags) { - ControlFlow::Break(FoundFlags) - } else { - ControlFlow::Continue(()) - } - } -} - /// Collects all the late-bound regions at the innermost binding level /// into a hash set. struct LateBoundRegionsCollector { diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 906b3205ca7..35f5a6bfac5 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -319,7 +319,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // them. let mut fake_borrows = match_has_guard.then(FxIndexSet::default); - let mut otherwise = None; + let otherwise_block = self.cfg.start_new_block(); // This will generate code to test scrutinee_place and // branch to the appropriate arm block @@ -327,46 +327,44 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match_start_span, scrutinee_span, block, - &mut otherwise, + otherwise_block, candidates, &mut fake_borrows, ); - if let Some(otherwise_block) = otherwise { - // See the doc comment on `match_candidates` for why we may have an - // otherwise block. Match checking will ensure this is actually - // unreachable. - let source_info = self.source_info(scrutinee_span); - - // Matching on a `scrutinee_place` with an uninhabited type doesn't - // generate any memory reads by itself, and so if the place "expression" - // contains unsafe operations like raw pointer dereferences or union - // field projections, we wouldn't know to require an `unsafe` block - // around a `match` equivalent to `std::intrinsics::unreachable()`. - // See issue #47412 for this hole being discovered in the wild. - // - // HACK(eddyb) Work around the above issue by adding a dummy inspection - // of `scrutinee_place`, specifically by applying `ReadForMatch`. - // - // NOTE: ReadForMatch also checks that the scrutinee is initialized. - // This is currently needed to not allow matching on an uninitialized, - // uninhabited value. If we get never patterns, those will check that - // the place is initialized, and so this read would only be used to - // check safety. - let cause_matched_place = FakeReadCause::ForMatchedPlace(None); - - if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) { - self.cfg.push_fake_read( - otherwise_block, - source_info, - cause_matched_place, - scrutinee_place, - ); - } + // See the doc comment on `match_candidates` for why we may have an + // otherwise block. Match checking will ensure this is actually + // unreachable. + let source_info = self.source_info(scrutinee_span); + + // Matching on a `scrutinee_place` with an uninhabited type doesn't + // generate any memory reads by itself, and so if the place "expression" + // contains unsafe operations like raw pointer dereferences or union + // field projections, we wouldn't know to require an `unsafe` block + // around a `match` equivalent to `std::intrinsics::unreachable()`. + // See issue #47412 for this hole being discovered in the wild. + // + // HACK(eddyb) Work around the above issue by adding a dummy inspection + // of `scrutinee_place`, specifically by applying `ReadForMatch`. + // + // NOTE: ReadForMatch also checks that the scrutinee is initialized. + // This is currently needed to not allow matching on an uninitialized, + // uninhabited value. If we get never patterns, those will check that + // the place is initialized, and so this read would only be used to + // check safety. + let cause_matched_place = FakeReadCause::ForMatchedPlace(None); - self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable); + if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) { + self.cfg.push_fake_read( + otherwise_block, + source_info, + cause_matched_place, + scrutinee_place, + ); } + self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable); + // Link each leaf candidate to the `pre_binding_block` of the next one. let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None; @@ -1163,7 +1161,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, scrutinee_span: Span, start_block: BasicBlock, - otherwise_block: &mut Option<BasicBlock>, + otherwise_block: BasicBlock, candidates: &mut [&mut Candidate<'pat, 'tcx>], fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>, ) { @@ -1210,7 +1208,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, scrutinee_span: Span, start_block: BasicBlock, - otherwise_block: &mut Option<BasicBlock>, + otherwise_block: BasicBlock, candidates: &mut [&mut Candidate<'_, 'tcx>], fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>, ) { @@ -1243,11 +1241,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // never reach this point. if unmatched_candidates.is_empty() { let source_info = self.source_info(span); - if let Some(otherwise) = *otherwise_block { - self.cfg.goto(block, source_info, otherwise); - } else { - *otherwise_block = Some(block); - } + self.cfg.goto(block, source_info, otherwise_block); return; } @@ -1428,7 +1422,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { scrutinee_span: Span, candidates: &mut [&mut Candidate<'_, 'tcx>], block: BasicBlock, - otherwise_block: &mut Option<BasicBlock>, + otherwise_block: BasicBlock, fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>, ) { let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap(); @@ -1453,7 +1447,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let match_pairs = mem::take(&mut first_candidate.match_pairs); first_candidate.pre_binding_block = Some(block); - let mut otherwise = None; + let remainder_start = self.cfg.start_new_block(); for match_pair in match_pairs { let PatKind::Or { ref pats } = &match_pair.pattern.kind else { bug!("Or-patterns should have been sorted to the end"); @@ -1463,7 +1457,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { first_candidate.visit_leaves(|leaf_candidate| { self.test_or_pattern( leaf_candidate, - &mut otherwise, + remainder_start, pats, or_span, &match_pair.place, @@ -1472,8 +1466,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }); } - let remainder_start = otherwise.unwrap_or_else(|| self.cfg.start_new_block()); - self.match_candidates( span, scrutinee_span, @@ -1491,7 +1483,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn test_or_pattern<'pat>( &mut self, candidate: &mut Candidate<'pat, 'tcx>, - otherwise: &mut Option<BasicBlock>, + otherwise: BasicBlock, pats: &'pat [Box<Pat<'tcx>>], or_span: Span, place: &PlaceBuilder<'tcx>, @@ -1503,8 +1495,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard, self)) .collect(); let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect(); - let otherwise = if candidate.otherwise_block.is_some() { - &mut candidate.otherwise_block + let otherwise = if let Some(otherwise_block) = candidate.otherwise_block { + otherwise_block } else { otherwise }; @@ -1680,8 +1672,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, scrutinee_span: Span, mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>], - block: BasicBlock, - otherwise_block: &mut Option<BasicBlock>, + start_block: BasicBlock, + otherwise_block: BasicBlock, fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>, ) { // extract the match-pair from the highest priority candidate @@ -1749,12 +1741,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug!("untested_candidates: {}", candidates.len()); // The block that we should branch to if none of the - // `target_candidates` match. This is either the block where we - // start matching the untested candidates if there are any, - // otherwise it's the `otherwise_block`. - let remainder_start = &mut None; - let remainder_start = - if candidates.is_empty() { &mut *otherwise_block } else { remainder_start }; + // `target_candidates` match. + let remainder_start = if !candidates.is_empty() { + let remainder_start = self.cfg.start_new_block(); + self.match_candidates( + span, + scrutinee_span, + remainder_start, + otherwise_block, + candidates, + fake_borrows, + ); + remainder_start + } else { + otherwise_block + }; // For each outcome of test, process the candidates that still // apply. Collect a list of blocks where control flow will @@ -1775,24 +1776,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); candidate_start } else { - *remainder_start.get_or_insert_with(|| self.cfg.start_new_block()) + remainder_start } }) .collect(); - if !candidates.is_empty() { - let remainder_start = remainder_start.unwrap_or_else(|| self.cfg.start_new_block()); - self.match_candidates( - span, - scrutinee_span, - remainder_start, - otherwise_block, - candidates, - fake_borrows, - ); - } - - self.perform_test(span, scrutinee_span, block, &match_place, &test, target_blocks); + // Perform the test, branching to one of N blocks. + self.perform_test(span, scrutinee_span, start_block, &match_place, &test, target_blocks); } /// Determine the fake borrows that are needed from a set of places that diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 8688c589063..241b6c6cb2c 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -33,7 +33,7 @@ struct UnsafetyVisitor<'a, 'tcx> { body_target_features: &'tcx [Symbol], /// When inside the LHS of an assignment to a field, this is the type /// of the LHS and the span of the assignment expression. - assignment_info: Option<(Ty<'tcx>, Span)>, + assignment_info: Option<Ty<'tcx>>, in_union_destructure: bool, param_env: ParamEnv<'tcx>, inside_adt: bool, @@ -473,10 +473,15 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { if let ty::Adt(adt_def, _) = lhs.ty.kind() && adt_def.is_union() { - if let Some((assigned_ty, assignment_span)) = self.assignment_info { + if let Some(assigned_ty) = self.assignment_info { if assigned_ty.needs_drop(self.tcx, self.param_env) { - // This would be unsafe, but should be outright impossible since we reject such unions. - self.tcx.dcx().span_delayed_bug(assignment_span, format!("union fields that need dropping should be impossible: {assigned_ty}")); + // This would be unsafe, but should be outright impossible since we + // reject such unions. + assert!( + self.tcx.dcx().has_errors().is_some(), + "union fields that need dropping should be impossible: \ + {assigned_ty}" + ); } } else { self.requires_unsafe(expr.span, AccessToUnionField); @@ -492,14 +497,15 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField); } - // Second, check for accesses to union fields - // don't have any special handling for AssignOp since it causes a read *and* write to lhs + // Second, check for accesses to union fields. Don't have any + // special handling for AssignOp since it causes a read *and* + // write to lhs. if matches!(expr.kind, ExprKind::Assign { .. }) { - self.assignment_info = Some((lhs.ty, expr.span)); + self.assignment_info = Some(lhs.ty); visit::walk_expr(self, lhs); self.assignment_info = None; visit::walk_expr(self, &self.thir()[rhs]); - return; // we have already visited everything by now + return; // We have already visited everything by now. } } ExprKind::Borrow { borrow_kind, arg } => { diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index c24b24d6f01..99952e1c178 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -3,6 +3,7 @@ use crate::thir::cx::region::Scope; use crate::thir::cx::Cx; use crate::thir::util::UserAnnotatedTyHelpers; use itertools::Itertools; +use rustc_ast::LitKind; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; @@ -20,7 +21,8 @@ use rustc_middle::ty::GenericArgs; use rustc_middle::ty::{ self, AdtKind, InlineConstArgs, InlineConstArgsParts, ScalarInt, Ty, UpvarArgs, UserType, }; -use rustc_span::{sym, Span}; +use rustc_span::source_map::Spanned; +use rustc_span::{sym, Span, DUMMY_SP}; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; impl<'tcx> Cx<'tcx> { @@ -894,7 +896,14 @@ impl<'tcx> Cx<'tcx> { Res::Def(DefKind::ConstParam, def_id) => { let hir_id = self.tcx.local_def_id_to_hir_id(def_id.expect_local()); let generics = self.tcx.generics_of(hir_id.owner); - let index = generics.param_def_id_to_index[&def_id]; + let Some(&index) = generics.param_def_id_to_index.get(&def_id) else { + self.tcx.dcx().has_errors().unwrap(); + // We already errored about a late bound const + return ExprKind::Literal { + lit: &Spanned { span: DUMMY_SP, node: LitKind::Err }, + neg: false, + }; + }; let name = self.tcx.hir().name(hir_id); let param = ty::ParamConst::new(index, name); diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 18a00724c3d..c77c80d9f4b 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -153,8 +153,7 @@ impl<'tcx> ConstToPat<'tcx> { // a hard error when we don't have a valtree or when we find something in // the valtree that is not structural; then this can all be made a lot simpler. - let structural = - traits::search_for_structural_match_violation(self.span, self.tcx(), cv.ty()); + let structural = traits::search_for_structural_match_violation(self.tcx(), cv.ty()); debug!( "search_for_structural_match_violation cv.ty: {:?} returned: {:?}", cv.ty(), diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 80e0d0baf57..c6ec1b5aee4 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -1,7 +1,7 @@ use rustc_index::IndexVec; use rustc_middle::mir::tcx::{PlaceTy, RvalueInitializationState}; use rustc_middle::mir::*; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use smallvec::{smallvec, SmallVec}; use std::mem; @@ -132,6 +132,9 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { let body = self.builder.body; let tcx = self.builder.tcx; let place_ty = place_ref.ty(body, tcx).ty; + if place_ty.references_error() { + return MovePathResult::Error; + } match elem { ProjectionElem::Deref => match place_ty.kind() { ty::Ref(..) | ty::RawPtr(..) => { diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs index fbb62695383..a0c3de3af58 100644 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ b/compiler/rustc_mir_transform/src/check_unsafety.rs @@ -243,10 +243,11 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> { // old value is being dropped. let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty; if assigned_ty.needs_drop(self.tcx, self.param_env) { - // This would be unsafe, but should be outright impossible since we reject such unions. - self.tcx.dcx().span_delayed_bug( - self.source_info.span, - format!("union fields that need dropping should be impossible: {assigned_ty}") + // This would be unsafe, but should be outright impossible since we reject + // such unions. + assert!( + self.tcx.dcx().has_errors().is_some(), + "union fields that need dropping should be impossible: {assigned_ty}" ); } } else { diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index d3d0c7bcc95..934e77e7deb 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -1,9 +1,10 @@ use rustc_data_structures::graph::WithNumNodes; use rustc_index::bit_set::BitSet; use rustc_middle::mir; -use rustc_span::{BytePos, Span, DUMMY_SP}; +use rustc_span::{BytePos, Span}; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB}; +use crate::coverage::spans::from_mir::SpanFromMir; use crate::coverage::ExtractedHirInfo; mod from_mir; @@ -61,7 +62,7 @@ pub(super) fn generate_coverage_spans( basic_coverage_blocks, ); let coverage_spans = SpansRefiner::refine_sorted_spans(basic_coverage_blocks, sorted_spans); - mappings.extend(coverage_spans.into_iter().map(|CoverageSpan { bcb, span, .. }| { + mappings.extend(coverage_spans.into_iter().map(|RefinedCovspan { bcb, span, .. }| { // Each span produced by the generator represents an ordinary code region. BcbMapping { kind: BcbMappingKind::Code(bcb), span } })); @@ -85,18 +86,36 @@ pub(super) fn generate_coverage_spans( Some(CoverageSpans { bcb_has_mappings, mappings }) } -/// A BCB is deconstructed into one or more `Span`s. Each `Span` maps to a `CoverageSpan` that -/// references the originating BCB and one or more MIR `Statement`s and/or `Terminator`s. -/// Initially, the `Span`s come from the `Statement`s and `Terminator`s, but subsequent -/// transforms can combine adjacent `Span`s and `CoverageSpan` from the same BCB, merging the -/// `merged_spans` vectors, and the `Span`s to cover the extent of the combined `Span`s. -/// -/// Note: A span merged into another CoverageSpan may come from a `BasicBlock` that -/// is not part of the `CoverageSpan` bcb if the statement was included because it's `Span` matches -/// or is subsumed by the `Span` associated with this `CoverageSpan`, and it's `BasicBlock` -/// `dominates()` the `BasicBlock`s in this `CoverageSpan`. -#[derive(Debug, Clone)] -struct CoverageSpan { +#[derive(Debug)] +struct CurrCovspan { + /// This is used as the basis for [`PrevCovspan::original_span`], so it must + /// not be modified. + span: Span, + bcb: BasicCoverageBlock, + is_closure: bool, +} + +impl CurrCovspan { + fn new(span: Span, bcb: BasicCoverageBlock, is_closure: bool) -> Self { + Self { span, bcb, is_closure } + } + + fn into_prev(self) -> PrevCovspan { + let Self { span, bcb, is_closure } = self; + PrevCovspan { original_span: span, span, bcb, merged_spans: vec![span], is_closure } + } + + fn into_refined(self) -> RefinedCovspan { + // This is only called in cases where `curr` is a closure span that has + // been carved out of `prev`. + debug_assert!(self.is_closure); + self.into_prev().into_refined() + } +} + +#[derive(Debug)] +struct PrevCovspan { + original_span: Span, span: Span, bcb: BasicCoverageBlock, /// List of all the original spans from MIR that have been merged into this @@ -105,37 +124,82 @@ struct CoverageSpan { is_closure: bool, } -impl CoverageSpan { - fn new(span: Span, bcb: BasicCoverageBlock, is_closure: bool) -> Self { - Self { span, bcb, merged_spans: vec![span], is_closure } +impl PrevCovspan { + fn is_mergeable(&self, other: &CurrCovspan) -> bool { + self.bcb == other.bcb && !self.is_closure && !other.is_closure } - pub fn merge_from(&mut self, other: &Self) { + fn merge_from(&mut self, other: &CurrCovspan) { debug_assert!(self.is_mergeable(other)); self.span = self.span.to(other.span); - self.merged_spans.extend_from_slice(&other.merged_spans); + self.merged_spans.push(other.span); } - pub fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) { + fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) { self.merged_spans.retain(|span| span.hi() <= cutoff_pos); if let Some(max_hi) = self.merged_spans.iter().map(|span| span.hi()).max() { self.span = self.span.with_hi(max_hi); } } - #[inline] - pub fn is_mergeable(&self, other: &Self) -> bool { - self.is_in_same_bcb(other) && !(self.is_closure || other.is_closure) + fn into_dup(self) -> DuplicateCovspan { + let Self { original_span, span, bcb, merged_spans: _, is_closure } = self; + // Only unmodified spans end up in `pending_dups`. + debug_assert_eq!(original_span, span); + DuplicateCovspan { span, bcb, is_closure } } - #[inline] - pub fn is_in_same_bcb(&self, other: &Self) -> bool { - self.bcb == other.bcb + fn refined_copy(&self) -> RefinedCovspan { + let &Self { original_span: _, span, bcb, merged_spans: _, is_closure } = self; + RefinedCovspan { span, bcb, is_closure } + } + + fn into_refined(self) -> RefinedCovspan { + self.refined_copy() } } -/// Converts the initial set of `CoverageSpan`s (one per MIR `Statement` or `Terminator`) into a -/// minimal set of `CoverageSpan`s, using the BCB CFG to determine where it is safe and useful to: +#[derive(Debug)] +struct DuplicateCovspan { + span: Span, + bcb: BasicCoverageBlock, + is_closure: bool, +} + +impl DuplicateCovspan { + /// Returns a copy of this covspan, as a [`RefinedCovspan`]. + /// Should only be called in places that would otherwise clone this covspan. + fn refined_copy(&self) -> RefinedCovspan { + let &Self { span, bcb, is_closure } = self; + RefinedCovspan { span, bcb, is_closure } + } + + fn into_refined(self) -> RefinedCovspan { + // Even though we consume self, we can just reuse the copying impl. + self.refined_copy() + } +} + +#[derive(Debug)] +struct RefinedCovspan { + span: Span, + bcb: BasicCoverageBlock, + is_closure: bool, +} + +impl RefinedCovspan { + fn is_mergeable(&self, other: &Self) -> bool { + self.bcb == other.bcb && !self.is_closure && !other.is_closure + } + + fn merge_from(&mut self, other: &Self) { + debug_assert!(self.is_mergeable(other)); + self.span = self.span.to(other.span); + } +} + +/// Converts the initial set of coverage spans (one per MIR `Statement` or `Terminator`) into a +/// minimal set of coverage spans, using the BCB CFG to determine where it is safe and useful to: /// /// * Remove duplicate source code coverage regions /// * Merge spans that represent continuous (both in source code and control flow), non-branching @@ -145,43 +209,33 @@ struct SpansRefiner<'a> { /// The BasicCoverageBlock Control Flow Graph (BCB CFG). basic_coverage_blocks: &'a CoverageGraph, - /// The initial set of `CoverageSpan`s, sorted by `Span` (`lo` and `hi`) and by relative + /// The initial set of coverage spans, sorted by `Span` (`lo` and `hi`) and by relative /// dominance between the `BasicCoverageBlock`s of equal `Span`s. - sorted_spans_iter: std::vec::IntoIter<CoverageSpan>, + sorted_spans_iter: std::vec::IntoIter<SpanFromMir>, - /// The current `CoverageSpan` to compare to its `prev`, to possibly merge, discard, force the + /// The current coverage span to compare to its `prev`, to possibly merge, discard, force the /// discard of the `prev` (and or `pending_dups`), or keep both (with `prev` moved to /// `pending_dups`). If `curr` is not discarded or merged, it becomes `prev` for the next /// iteration. - some_curr: Option<CoverageSpan>, - - /// The original `span` for `curr`, in case `curr.span()` is modified. The `curr_original_span` - /// **must not be mutated** (except when advancing to the next `curr`), even if `curr.span()` - /// is mutated. - curr_original_span: Span, + some_curr: Option<CurrCovspan>, - /// The CoverageSpan from a prior iteration; typically assigned from that iteration's `curr`. + /// The coverage span from a prior iteration; typically assigned from that iteration's `curr`. /// If that `curr` was discarded, `prev` retains its value from the previous iteration. - some_prev: Option<CoverageSpan>, + some_prev: Option<PrevCovspan>, - /// Assigned from `curr_original_span` from the previous iteration. The `prev_original_span` - /// **must not be mutated** (except when advancing to the next `prev`), even if `prev.span()` - /// is mutated. - prev_original_span: Span, - - /// One or more `CoverageSpan`s with the same `Span` but different `BasicCoverageBlock`s, and + /// One or more coverage spans with the same `Span` but different `BasicCoverageBlock`s, and /// no `BasicCoverageBlock` in this list dominates another `BasicCoverageBlock` in the list. /// If a new `curr` span also fits this criteria (compared to an existing list of - /// `pending_dups`), that `curr` `CoverageSpan` moves to `prev` before possibly being added to + /// `pending_dups`), that `curr` moves to `prev` before possibly being added to /// the `pending_dups` list, on the next iteration. As a result, if `prev` and `pending_dups` /// have the same `Span`, the criteria for `pending_dups` holds for `prev` as well: a `prev` /// with a matching `Span` does not dominate any `pending_dup` and no `pending_dup` dominates a /// `prev` with a matching `Span`) - pending_dups: Vec<CoverageSpan>, + pending_dups: Vec<DuplicateCovspan>, - /// The final `CoverageSpan`s to add to the coverage map. A `Counter` or `Expression` - /// will also be injected into the MIR for each `CoverageSpan`. - refined_spans: Vec<CoverageSpan>, + /// The final coverage spans to add to the coverage map. A `Counter` or `Expression` + /// will also be injected into the MIR for each BCB that has associated spans. + refined_spans: Vec<RefinedCovspan>, } impl<'a> SpansRefiner<'a> { @@ -190,15 +244,13 @@ impl<'a> SpansRefiner<'a> { /// and carving holes in spans when they overlap in unwanted ways. fn refine_sorted_spans( basic_coverage_blocks: &'a CoverageGraph, - sorted_spans: Vec<CoverageSpan>, - ) -> Vec<CoverageSpan> { + sorted_spans: Vec<SpanFromMir>, + ) -> Vec<RefinedCovspan> { let this = Self { basic_coverage_blocks, sorted_spans_iter: sorted_spans.into_iter(), some_curr: None, - curr_original_span: DUMMY_SP, some_prev: None, - prev_original_span: DUMMY_SP, pending_dups: Vec::new(), refined_spans: Vec::with_capacity(basic_coverage_blocks.num_nodes() * 2), }; @@ -206,9 +258,9 @@ impl<'a> SpansRefiner<'a> { this.to_refined_spans() } - /// Iterate through the sorted `CoverageSpan`s, and return the refined list of merged and - /// de-duplicated `CoverageSpan`s. - fn to_refined_spans(mut self) -> Vec<CoverageSpan> { + /// Iterate through the sorted coverage spans, and return the refined list of merged and + /// de-duplicated spans. + fn to_refined_spans(mut self) -> Vec<RefinedCovspan> { while self.next_coverage_span() { // For the first span we don't have `prev` set, so most of the // span-processing steps don't make sense yet. @@ -221,16 +273,15 @@ impl<'a> SpansRefiner<'a> { let prev = self.prev(); let curr = self.curr(); - if curr.is_mergeable(prev) { + if prev.is_mergeable(curr) { debug!(" same bcb (and neither is a closure), merge with prev={prev:?}"); - let prev = self.take_prev(); - self.curr_mut().merge_from(&prev); - // Note that curr.span may now differ from curr_original_span + let curr = self.take_curr(); + self.prev_mut().merge_from(&curr); } else if prev.span.hi() <= curr.span.lo() { debug!( " different bcbs and disjoint spans, so keep curr for next iter, and add prev={prev:?}", ); - let prev = self.take_prev(); + let prev = self.take_prev().into_refined(); self.refined_spans.push(prev); } else if prev.is_closure { // drop any equal or overlapping span (`curr`) and keep `prev` to test again in the @@ -241,9 +292,9 @@ impl<'a> SpansRefiner<'a> { self.take_curr(); // Discards curr. } else if curr.is_closure { self.carve_out_span_for_closure(); - } else if self.prev_original_span == curr.span { - // `prev` and `curr` have the same span, or would have had the - // same span before `prev` was modified by other spans. + } else if prev.original_span == prev.span && prev.span == curr.span { + // Prev and curr have the same span, and prev's span hasn't + // been modified by other spans. self.update_pending_dups(); } else { self.cutoff_prev_at_overlapping_curr(); @@ -253,14 +304,14 @@ impl<'a> SpansRefiner<'a> { // Drain any remaining dups into the output. for dup in self.pending_dups.drain(..) { debug!(" ...adding at least one pending dup={:?}", dup); - self.refined_spans.push(dup); + self.refined_spans.push(dup.into_refined()); } // There is usually a final span remaining in `prev` after the loop ends, // so add it to the output as well. if let Some(prev) = self.some_prev.take() { debug!(" AT END, adding last prev={prev:?}"); - self.refined_spans.push(prev); + self.refined_spans.push(prev.into_refined()); } // Do one last merge pass, to simplify the output. @@ -274,7 +325,7 @@ impl<'a> SpansRefiner<'a> { } }); - // Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage + // Remove spans derived from closures, originally added to ensure the coverage // regions for the current function leave room for the closure's own coverage regions // (injected separately, from the closure's own MIR). self.refined_spans.retain(|covspan| !covspan.is_closure); @@ -282,34 +333,29 @@ impl<'a> SpansRefiner<'a> { } #[track_caller] - fn curr(&self) -> &CoverageSpan { + fn curr(&self) -> &CurrCovspan { self.some_curr.as_ref().unwrap_or_else(|| bug!("some_curr is None (curr)")) } - #[track_caller] - fn curr_mut(&mut self) -> &mut CoverageSpan { - self.some_curr.as_mut().unwrap_or_else(|| bug!("some_curr is None (curr_mut)")) - } - /// If called, then the next call to `next_coverage_span()` will *not* update `prev` with the /// `curr` coverage span. #[track_caller] - fn take_curr(&mut self) -> CoverageSpan { + fn take_curr(&mut self) -> CurrCovspan { self.some_curr.take().unwrap_or_else(|| bug!("some_curr is None (take_curr)")) } #[track_caller] - fn prev(&self) -> &CoverageSpan { + fn prev(&self) -> &PrevCovspan { self.some_prev.as_ref().unwrap_or_else(|| bug!("some_prev is None (prev)")) } #[track_caller] - fn prev_mut(&mut self) -> &mut CoverageSpan { + fn prev_mut(&mut self) -> &mut PrevCovspan { self.some_prev.as_mut().unwrap_or_else(|| bug!("some_prev is None (prev_mut)")) } #[track_caller] - fn take_prev(&mut self) -> CoverageSpan { + fn take_prev(&mut self) -> PrevCovspan { self.some_prev.take().unwrap_or_else(|| bug!("some_prev is None (take_prev)")) } @@ -335,7 +381,7 @@ impl<'a> SpansRefiner<'a> { if last_dup.span.hi() <= self.curr().span.lo() { for dup in self.pending_dups.drain(..) { debug!(" ...adding at least one pending={:?}", dup); - self.refined_spans.push(dup); + self.refined_spans.push(dup.into_refined()); } } else { self.pending_dups.clear(); @@ -343,11 +389,10 @@ impl<'a> SpansRefiner<'a> { assert!(self.pending_dups.is_empty()); } - /// Advance `prev` to `curr` (if any), and `curr` to the next `CoverageSpan` in sorted order. + /// Advance `prev` to `curr` (if any), and `curr` to the next coverage span in sorted order. fn next_coverage_span(&mut self) -> bool { if let Some(curr) = self.some_curr.take() { - self.some_prev = Some(curr); - self.prev_original_span = self.curr_original_span; + self.some_prev = Some(curr.into_prev()); } while let Some(curr) = self.sorted_spans_iter.next() { debug!("FOR curr={:?}", curr); @@ -362,10 +407,7 @@ impl<'a> SpansRefiner<'a> { closure?); prev={prev:?}", ); } else { - // Save a copy of the original span for `curr` in case the `CoverageSpan` is changed - // by `self.curr_mut().merge_from(prev)`. - self.curr_original_span = curr.span; - self.some_curr.replace(curr); + self.some_curr = Some(CurrCovspan::new(curr.span, curr.bcb, curr.is_closure)); self.maybe_flush_pending_dups(); return true; } @@ -388,11 +430,11 @@ impl<'a> SpansRefiner<'a> { let has_post_closure_span = prev.span.hi() > right_cutoff; if has_pre_closure_span { - let mut pre_closure = self.prev().clone(); + let mut pre_closure = self.prev().refined_copy(); pre_closure.span = pre_closure.span.with_hi(left_cutoff); debug!(" prev overlaps a closure. Adding span for pre_closure={:?}", pre_closure); - for mut dup in self.pending_dups.iter().cloned() { + for mut dup in self.pending_dups.iter().map(DuplicateCovspan::refined_copy) { dup.span = dup.span.with_hi(left_cutoff); debug!(" ...and at least one pre_closure dup={:?}", dup); self.refined_spans.push(dup); @@ -402,9 +444,7 @@ impl<'a> SpansRefiner<'a> { } if has_post_closure_span { - // Mutate `prev.span()` to start after the closure (and discard curr). - // (**NEVER** update `prev_original_span` because it affects the assumptions - // about how the `CoverageSpan`s are ordered.) + // Mutate `prev.span` to start after the closure (and discard curr). self.prev_mut().span = self.prev().span.with_lo(right_cutoff); debug!(" Mutated prev.span to start after the closure. prev={:?}", self.prev()); @@ -413,25 +453,26 @@ impl<'a> SpansRefiner<'a> { dup.span = dup.span.with_lo(right_cutoff); } - let closure_covspan = self.take_curr(); // Prevent this curr from becoming prev. + // Prevent this curr from becoming prev. + let closure_covspan = self.take_curr().into_refined(); self.refined_spans.push(closure_covspan); // since self.prev() was already updated } else { self.pending_dups.clear(); } } - /// Called if `curr.span` equals `prev_original_span` (and potentially equal to all + /// Called if `curr.span` equals `prev.original_span` (and potentially equal to all /// `pending_dups` spans, if any). Keep in mind, `prev.span()` may have been changed. /// If prev.span() was merged into other spans (with matching BCB, for instance), - /// `prev.span.hi()` will be greater than (further right of) `prev_original_span.hi()`. + /// `prev.span.hi()` will be greater than (further right of) `prev.original_span.hi()`. /// If prev.span() was split off to the right of a closure, prev.span().lo() will be - /// greater than prev_original_span.lo(). The actual span of `prev_original_span` is + /// greater than prev.original_span.lo(). The actual span of `prev.original_span` is /// not as important as knowing that `prev()` **used to have the same span** as `curr()`, /// which means their sort order is still meaningful for determining the dominator /// relationship. /// - /// When two `CoverageSpan`s have the same `Span`, dominated spans can be discarded; but if - /// neither `CoverageSpan` dominates the other, both (or possibly more than two) are held, + /// When two coverage spans have the same `Span`, dominated spans can be discarded; but if + /// neither coverage span dominates the other, both (or possibly more than two) are held, /// until their disposition is determined. In this latter case, the `prev` dup is moved into /// `pending_dups` so the new `curr` dup can be moved to `prev` for the next iteration. fn update_pending_dups(&mut self) { @@ -439,9 +480,15 @@ impl<'a> SpansRefiner<'a> { let curr_bcb = self.curr().bcb; // Equal coverage spans are ordered by dominators before dominated (if any), so it should be - // impossible for `curr` to dominate any previous `CoverageSpan`. + // impossible for `curr` to dominate any previous coverage span. debug_assert!(!self.basic_coverage_blocks.dominates(curr_bcb, prev_bcb)); + // `prev` is a duplicate of `curr`, so add it to the list of pending dups. + // If it dominates `curr`, it will be removed by the subsequent discard step. + let prev = self.take_prev().into_dup(); + debug!(?prev, "adding prev to pending dups"); + self.pending_dups.push(prev); + let initial_pending_count = self.pending_dups.len(); if initial_pending_count > 0 { self.pending_dups @@ -454,42 +501,6 @@ impl<'a> SpansRefiner<'a> { ); } } - - if self.basic_coverage_blocks.dominates(prev_bcb, curr_bcb) { - debug!( - " different bcbs but SAME spans, and prev dominates curr. Discard prev={:?}", - self.prev() - ); - self.cutoff_prev_at_overlapping_curr(); - // If one span dominates the other, associate the span with the code from the dominated - // block only (`curr`), and discard the overlapping portion of the `prev` span. (Note - // that if `prev.span` is wider than `prev_original_span`, a `CoverageSpan` will still - // be created for `prev`s block, for the non-overlapping portion, left of `curr.span`.) - // - // For example: - // match somenum { - // x if x < 1 => { ... } - // }... - // - // The span for the first `x` is referenced by both the pattern block (every time it is - // evaluated) and the arm code (only when matched). The counter will be applied only to - // the dominated block. This allows coverage to track and highlight things like the - // assignment of `x` above, if the branch is matched, making `x` available to the arm - // code; and to track and highlight the question mark `?` "try" operator at the end of - // a function call returning a `Result`, so the `?` is covered when the function returns - // an `Err`, and not counted as covered if the function always returns `Ok`. - } else { - // Save `prev` in `pending_dups`. (`curr` will become `prev` in the next iteration.) - // If the `curr` CoverageSpan is later discarded, `pending_dups` can be discarded as - // well; but if `curr` is added to refined_spans, the `pending_dups` will also be added. - debug!( - " different bcbs but SAME spans, and neither dominates, so keep curr for \ - next iter, and, pending upcoming spans (unless overlapping) add prev={:?}", - self.prev() - ); - let prev = self.take_prev(); - self.pending_dups.push(prev); - } } /// `curr` overlaps `prev`. If `prev`s span extends left of `curr`s span, keep _only_ @@ -512,7 +523,7 @@ impl<'a> SpansRefiner<'a> { debug!(" ... no non-overlapping statements to add"); } else { debug!(" ... adding modified prev={:?}", self.prev()); - let prev = self.take_prev(); + let prev = self.take_prev().into_refined(); self.refined_spans.push(prev); } } else { diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 4ac8dde03a6..9517ede288f 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -9,7 +9,6 @@ use rustc_span::{ExpnKind, MacroKind, Span, Symbol}; use crate::coverage::graph::{ BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB, }; -use crate::coverage::spans::CoverageSpan; use crate::coverage::ExtractedHirInfo; /// Traverses the MIR body to produce an initial collection of coverage-relevant @@ -22,7 +21,7 @@ pub(super) fn mir_to_initial_sorted_coverage_spans( mir_body: &mir::Body<'_>, hir_info: &ExtractedHirInfo, basic_coverage_blocks: &CoverageGraph, -) -> Vec<CoverageSpan> { +) -> Vec<SpanFromMir> { let &ExtractedHirInfo { body_span, .. } = hir_info; let mut initial_spans = vec![]; @@ -61,7 +60,7 @@ pub(super) fn mir_to_initial_sorted_coverage_spans( .then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse()) }); - initial_spans.into_iter().map(SpanFromMir::into_coverage_span).collect::<Vec<_>>() + initial_spans } /// Macros that expand into branches (e.g. `assert!`, `trace!`) tend to generate @@ -119,10 +118,10 @@ fn split_visible_macro_spans(initial_spans: &mut Vec<SpanFromMir>) { initial_spans.extend(extra_spans); } -// Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of -// the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One `CoverageSpan` is generated +// Generate a set of coverage spans from the filtered set of `Statement`s and `Terminator`s of +// the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One coverage span is generated // for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will -// merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple +// merge some coverage spans, at which point a coverage span may represent multiple // `Statement`s and/or `Terminator`s.) fn bcb_to_initial_coverage_spans<'a, 'tcx>( mir_body: &'a mir::Body<'tcx>, @@ -316,7 +315,7 @@ fn unexpand_into_body_span_with_prev( } #[derive(Debug)] -struct SpanFromMir { +pub(super) struct SpanFromMir { /// A span that has been extracted from MIR and then "un-expanded" back to /// within the current function's `body_span`. After various intermediate /// processing steps, this span is emitted as part of the final coverage @@ -324,10 +323,10 @@ struct SpanFromMir { /// /// With the exception of `fn_sig_span`, this should always be contained /// within `body_span`. - span: Span, + pub(super) span: Span, visible_macro: Option<Symbol>, - bcb: BasicCoverageBlock, - is_closure: bool, + pub(super) bcb: BasicCoverageBlock, + pub(super) is_closure: bool, } impl SpanFromMir { @@ -343,9 +342,4 @@ impl SpanFromMir { ) -> Self { Self { span, visible_macro, bcb, is_closure } } - - fn into_coverage_span(self) -> CoverageSpan { - let Self { span, visible_macro: _, bcb, is_closure } = self; - CoverageSpan::new(span, bcb, is_closure) - } } diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 0ac4ab61d40..2c8201b1903 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -398,7 +398,8 @@ impl<'alloc> Candidates<'alloc> { let candidates = entry.get_mut(); Self::vec_filter_candidates(p, candidates, f, at); if candidates.len() == 0 { - entry.remove(); + // FIXME(#120456) - is `swap_remove` correct? + entry.swap_remove(); } } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 8d4afd5b5dc..370f0db72cb 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -561,9 +561,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { .ok()?; dest.into() } - CastKind::FnPtrToPtr - | CastKind::PtrToPtr - | CastKind::PointerCoercion( + CastKind::FnPtrToPtr | CastKind::PtrToPtr => { + let src = self.evaluated[value].as_ref()?; + let src = self.ecx.read_immediate(src).ok()?; + let to = self.ecx.layout_of(to).ok()?; + let ret = self.ecx.ptr_to_ptr(&src, to).ok()?; + ret.into() + } + CastKind::PointerCoercion( ty::adjustment::PointerCoercion::MutToConstPointer | ty::adjustment::PointerCoercion::ArrayToPointer | ty::adjustment::PointerCoercion::UnsafeFnPointer, @@ -571,8 +576,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let src = self.evaluated[value].as_ref()?; let src = self.ecx.read_immediate(src).ok()?; let to = self.ecx.layout_of(to).ok()?; - let ret = self.ecx.ptr_to_ptr(&src, to).ok()?; - ret.into() + ImmTy::from_immediate(*src, to).into() } _ => return None, }, diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 9f30f2836f1..c11fd5fcc90 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -265,7 +265,7 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs { let body = &tcx.mir_const(def).borrow(); if body.return_ty().references_error() { - tcx.dcx().span_delayed_bug(body.span, "mir_const_qualif: MIR had errors"); + assert!(tcx.dcx().has_errors().is_some(), "mir_const_qualif: MIR had errors"); return Default::default(); } diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 42edbeaa622..cd434fecce2 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -1,6 +1,7 @@ use std::cmp::Ordering; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_type_ir::visit::TypeVisitableExt; use rustc_type_ir::{ self as ty, Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, ConstTy, InferCtxtLike, Interner, IntoKind, PlaceholderLike, @@ -62,8 +63,8 @@ impl<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> Canonicalizer<'a, Infc let value = value.fold_with(&mut canonicalizer); // FIXME: Restore these assertions. Should we uplift type flags? - // assert!(!value.has_infer(), "unexpected infer in {value:?}"); - // assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}"); + assert!(!value.has_infer(), "unexpected infer in {value:?}"); + assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}"); let (max_universe, variables) = canonicalizer.finalize(); diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 7a24b819b5f..445d5b2ce79 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -900,7 +900,7 @@ impl<'a> Parser<'a> { // fn foo() -> Foo { // field: value, // } - info!(?maybe_struct_name, ?self.token); + debug!(?maybe_struct_name, ?self.token); let mut snapshot = self.create_snapshot_for_diagnostic(); let path = Path { segments: ThinVec::new(), diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index c1fe8c2133b..17ad08b0569 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -957,8 +957,9 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { // available as we'd like it to be. // FIXME: only remove `libc` when `stdbuild` is active. // FIXME: remove special casing for `test`. - remaining_lib_features.remove(&sym::libc); - remaining_lib_features.remove(&sym::test); + // FIXME(#120456) - is `swap_remove` correct? + remaining_lib_features.swap_remove(&sym::libc); + remaining_lib_features.swap_remove(&sym::test); /// For each feature in `defined_features`.. /// @@ -996,7 +997,8 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { unnecessary_stable_feature_lint(tcx, *span, feature, since); } } - remaining_lib_features.remove(&feature); + // FIXME(#120456) - is `swap_remove` correct? + remaining_lib_features.swap_remove(&feature); // `feature` is the feature doing the implying, but `implied_by` is the feature with // the attribute that establishes this relationship. `implied_by` is guaranteed to be a diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 3ef9de7da74..8d7c0ca0144 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -4,7 +4,7 @@ use crate::query::plumbing::CycleError; use crate::query::DepKind; use crate::query::{QueryContext, QueryStackFrame}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{DiagCtxt, Diagnostic, DiagnosticBuilder, Level}; +use rustc_errors::{DiagCtxt, DiagnosticBuilder}; use rustc_hir::def::DefKind; use rustc_session::Session; use rustc_span::Span; @@ -628,15 +628,15 @@ pub fn print_query_stack<Qcx: QueryContext>( }; if Some(count_printed) < num_frames || num_frames.is_none() { // Only print to stderr as many stack frames as `num_frames` when present. - let mut diag = Diagnostic::new( - Level::FailureNote, - format!( - "#{} [{:?}] {}", - count_printed, query_info.query.dep_kind, query_info.query.description - ), - ); - diag.span = query_info.job.span.into(); - dcx.force_print_diagnostic(diag); + // FIXME: needs translation + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] + dcx.struct_failure_note(format!( + "#{} [{:?}] {}", + count_printed, query_info.query.dep_kind, query_info.query.description + )) + .with_span(query_info.job.span) + .emit(); count_printed += 1; } diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 9158ba00901..754757b5de5 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -429,16 +429,16 @@ where let formatter = query.format_value(); if old_hash != new_hash { // We have an inconsistency. This can happen if one of the two - // results is tainted by errors. In this case, delay a bug to - // ensure compilation is doomed. - qcx.dep_context().sess().dcx().delayed_bug(format!( + // results is tainted by errors. + assert!( + qcx.dep_context().sess().dcx().has_errors().is_some(), "Computed query value for {:?}({:?}) is inconsistent with fed value,\n\ computed={:#?}\nfed={:#?}", query.dep_kind(), key, formatter(&result), formatter(&cached_result), - )); + ); } } } diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index fc72d76c3a7..c14788b841d 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -92,7 +92,8 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> { } else { // This trait import is definitely used, in a way other than // method resolution. - self.r.maybe_unused_trait_imports.remove(&def_id); + // FIXME(#120456) - is `swap_remove` correct? + self.r.maybe_unused_trait_imports.swap_remove(&def_id); if let Some(i) = self.unused_imports.get_mut(&self.base_id) { i.unused.remove(&id); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 5d712461993..cb3b95a4434 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -460,7 +460,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { return (err, Vec::new()); } - let (found, candidates) = self.try_lookup_name_relaxed( + let (found, mut candidates) = self.try_lookup_name_relaxed( &mut err, source, path, @@ -473,10 +473,12 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { return (err, candidates); } - let mut fallback = self.suggest_trait_and_bounds(&mut err, source, res, span, &base_error); + if self.suggest_shadowed(&mut err, source, path, following_seg, span) { + // if there is already a shadowed name, don'suggest candidates for importing + candidates.clear(); + } - // if we have suggested using pattern matching, then don't add needless suggestions - // for typos. + let mut fallback = self.suggest_trait_and_bounds(&mut err, source, res, span, &base_error); fallback |= self.suggest_typo(&mut err, source, path, following_seg, span, &base_error); if fallback { @@ -872,25 +874,6 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { let ident_span = path.last().map_or(span, |ident| ident.ident.span); let typo_sugg = self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected); - let is_in_same_file = &|sp1, sp2| { - let source_map = self.r.tcx.sess.source_map(); - let file1 = source_map.span_to_filename(sp1); - let file2 = source_map.span_to_filename(sp2); - file1 == file2 - }; - // print 'you might have meant' if the candidate is (1) is a shadowed name with - // accessible definition and (2) either defined in the same crate as the typo - // (could be in a different file) or introduced in the same file as the typo - // (could belong to a different crate) - if let TypoCandidate::Shadowed(res, Some(sugg_span)) = typo_sugg - && res.opt_def_id().is_some_and(|id| id.is_local() || is_in_same_file(span, sugg_span)) - { - err.span_label( - sugg_span, - format!("you might have meant to refer to this {}", res.descr()), - ); - return true; - } let mut fallback = false; let typo_sugg = typo_sugg.to_opt_suggestion(); if !self.r.add_typo_suggestion(err, typo_sugg, ident_span) { @@ -918,6 +901,39 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { fallback } + fn suggest_shadowed( + &mut self, + err: &mut Diagnostic, + source: PathSource<'_>, + path: &[Segment], + following_seg: Option<&Segment>, + span: Span, + ) -> bool { + let is_expected = &|res| source.is_expected(res); + let typo_sugg = + self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected); + let is_in_same_file = &|sp1, sp2| { + let source_map = self.r.tcx.sess.source_map(); + let file1 = source_map.span_to_filename(sp1); + let file2 = source_map.span_to_filename(sp2); + file1 == file2 + }; + // print 'you might have meant' if the candidate is (1) is a shadowed name with + // accessible definition and (2) either defined in the same crate as the typo + // (could be in a different file) or introduced in the same file as the typo + // (could belong to a different crate) + if let TypoCandidate::Shadowed(res, Some(sugg_span)) = typo_sugg + && res.opt_def_id().is_some_and(|id| id.is_local() || is_in_same_file(span, sugg_span)) + { + err.span_label( + sugg_span, + format!("you might have meant to refer to this {}", res.descr()), + ); + return true; + } + false + } + fn err_code_special_cases( &mut self, err: &mut Diagnostic, diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index d35f951e2ae..a8b9ff056c2 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1435,12 +1435,16 @@ impl CheckCfg { // // When adding a new config here you should also update // `tests/ui/check-cfg/well-known-values.rs`. + // + // Don't forget to update `src/doc/unstable-book/src/compiler-flags/check-cfg.md` + // in the unstable book as well! ins!(sym::debug_assertions, no_values); - // These three are never set by rustc, but we set them anyway: they - // should not trigger a lint because `cargo doc`, `cargo test`, and - // `cargo miri run` (respectively) can set them. + // These four are never set by rustc, but we set them anyway: they + // should not trigger a lint because `cargo clippy`, `cargo doc`, + // `cargo test` and `cargo miri run` (respectively) can set them. + ins!(sym::clippy, no_values); ins!(sym::doc, no_values); ins!(sym::doctest, no_values); ins!(sym::miri, no_values); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 0c084660761..25c20be8e62 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -322,10 +322,9 @@ impl Session { } } - /// Used for code paths of expensive computations that should only take place when - /// warnings or errors are emitted. If no messages are emitted ("good path"), then - /// it's likely a bug. - pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) { + /// Record the fact that we called `trimmed_def_paths`, and do some + /// checking about whether its cost was justified. + pub fn record_trimmed_def_paths(&self) { if self.opts.unstable_opts.print_type_sizes || self.opts.unstable_opts.query_dep_graph || self.opts.unstable_opts.dump_mir.is_some() @@ -336,7 +335,7 @@ impl Session { return; } - self.dcx().good_path_delayed_bug(msg) + self.dcx().set_must_produce_diag() } #[inline] @@ -546,8 +545,8 @@ impl Session { if fuel.remaining == 0 && !fuel.out_of_fuel { if self.dcx().can_emit_warnings() { // We only call `msg` in case we can actually emit warnings. - // Otherwise, this could cause a `good_path_delayed_bug` to - // trigger (issue #79546). + // Otherwise, this could cause a `must_produce_diag` ICE + // (issue #79546). self.dcx().emit_warn(errors::OptimisationFuelExhausted { msg: msg() }); } fuel.out_of_fuel = true; diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index c05c9961750..81be5c09164 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -3,33 +3,27 @@ //! of our more general approach to "lazy normalization". //! //! This is done by first normalizing both sides of the goal, ending up in -//! either a concrete type, rigid projection, opaque, or an infer variable. +//! either a concrete type, rigid alias, or an infer variable. //! These are related further according to the rules below: //! -//! (1.) If we end up with a rigid projection and a rigid projection, then we -//! relate those projections structurally. +//! (1.) If we end up with two rigid aliases, then we relate them structurally. //! -//! (2.) If we end up with a rigid projection and an alias, then the opaque will -//! have its hidden type defined to be that rigid projection. -//! -//! (3.) If we end up with an opaque and an opaque, then we assemble two -//! candidates, one defining the LHS to be the hidden type of the RHS, and vice -//! versa. -//! -//! (4.) If we end up with an infer var and an opaque or rigid projection, then +//! (2.) If we end up with an infer var and a rigid alias, then //! we assign the alias to the infer var. //! -//! (5.) If we end up with an opaque and a rigid (non-projection) type, then we -//! define the hidden type of the opaque to be the rigid type. -//! -//! (6.) Otherwise, if we end with two rigid (non-projection) or infer types, +//! (3.) Otherwise, if we end with two rigid (non-projection) or infer types, //! relate them structurally. +//! +//! Subtle: when relating an opaque to another type, we emit a +//! `NormalizesTo(opaque, ?fresh_var)` goal when trying to normalize the opaque. +//! This nested goal starts out as ambiguous and does not actually define the opaque. +//! However, if `?fresh_var` ends up geteting equated to another type, we retry the +//! `NormalizesTo` goal, at which point the opaque is actually defined. use super::{EvalCtxt, GoalSource}; -use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::traits::query::NoSolution; use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self), ret)] @@ -59,37 +53,32 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } - (Some(alias), None) => { + (Some(_), None) => { if rhs.is_infer() { self.relate(param_env, lhs, variance, rhs)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } else if alias.is_opaque(tcx) { - // FIXME: This doesn't account for variance. - self.define_opaque(param_env, alias, rhs) } else { Err(NoSolution) } } - (None, Some(alias)) => { + (None, Some(_)) => { if lhs.is_infer() { self.relate(param_env, lhs, variance, rhs)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } else if alias.is_opaque(tcx) { - // FIXME: This doesn't account for variance. - self.define_opaque(param_env, alias, lhs) } else { Err(NoSolution) } } (Some(alias_lhs), Some(alias_rhs)) => { - self.relate_rigid_alias_or_opaque(param_env, alias_lhs, variance, alias_rhs) + self.relate(param_env, alias_lhs, variance, alias_rhs)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } } // FIXME: This needs a name that reflects that it's okay to bottom-out with an inference var. - /// Normalize the `term` to equate it later. This does not define opaque types. + /// Normalize the `term` to equate it later. #[instrument(level = "debug", skip(self, param_env), ret)] fn try_normalize_term( &mut self, @@ -98,10 +87,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> Result<Option<ty::Term<'tcx>>, NoSolution> { match term.unpack() { ty::TermKind::Ty(ty) => { - // We do no define opaque types here but instead do so in `relate_rigid_alias_or_opaque`. - Ok(self - .try_normalize_ty_recur(param_env, DefineOpaqueTypes::No, 0, ty) - .map(Into::into)) + Ok(self.try_normalize_ty_recur(param_env, 0, ty).map(Into::into)) } ty::TermKind::Const(_) => { if let Some(alias) = term.to_alias_ty(self.tcx()) { @@ -119,51 +105,34 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } - fn define_opaque( + fn try_normalize_ty_recur( &mut self, param_env: ty::ParamEnv<'tcx>, - opaque: ty::AliasTy<'tcx>, - term: ty::Term<'tcx>, - ) -> QueryResult<'tcx> { - self.add_goal( - GoalSource::Misc, - Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias: opaque, term }), - ); - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - - fn relate_rigid_alias_or_opaque( - &mut self, - param_env: ty::ParamEnv<'tcx>, - lhs: ty::AliasTy<'tcx>, - variance: ty::Variance, - rhs: ty::AliasTy<'tcx>, - ) -> QueryResult<'tcx> { - let tcx = self.tcx(); - let mut candidates = vec![]; - if lhs.is_opaque(tcx) { - candidates.extend( - self.probe_misc_candidate("define-lhs-opaque") - .enter(|ecx| ecx.define_opaque(param_env, lhs, rhs.to_ty(tcx).into())), - ); + depth: usize, + ty: Ty<'tcx>, + ) -> Option<Ty<'tcx>> { + if !self.tcx().recursion_limit().value_within_limit(depth) { + return None; } - if rhs.is_opaque(tcx) { - candidates.extend( - self.probe_misc_candidate("define-rhs-opaque") - .enter(|ecx| ecx.define_opaque(param_env, rhs, lhs.to_ty(tcx).into())), - ); - } - - candidates.extend(self.probe_misc_candidate("args-relate").enter(|ecx| { - ecx.relate(param_env, lhs, variance, rhs)?; - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - })); + let ty::Alias(_, alias) = *ty.kind() else { + return Some(ty); + }; - if let Some(result) = self.try_merge_responses(&candidates) { - Ok(result) - } else { - self.flounder(&candidates) + match self.commit_if_ok(|this| { + let normalized_ty = this.next_ty_infer(); + let normalizes_to_goal = Goal::new( + this.tcx(), + param_env, + ty::NormalizesTo { alias, term: normalized_ty.into() }, + ); + this.add_goal(GoalSource::Misc, normalizes_to_goal); + this.try_evaluate_added_goals()?; + let ty = this.resolve_vars_if_possible(normalized_ty); + Ok(this.try_normalize_ty_recur(param_env, depth + 1, ty)) + }) { + Ok(ty) => ty, + Err(NoSolution) => Some(ty), } } } diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 6833d2ae330..00c22e9e2c3 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -276,11 +276,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &mut self, goal: Goal<'tcx, G>, ) -> Vec<Candidate<'tcx>> { - let Some(normalized_self_ty) = - self.try_normalize_ty(goal.param_env, goal.predicate.self_ty()) + let Ok(normalized_self_ty) = + self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty()) else { - debug!("overflow while evaluating self type"); - return self.forced_ambiguity(MaybeCause::Overflow); + return vec![]; }; if normalized_self_ty.is_ty_var() { @@ -338,6 +337,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let mut consider_impls_for_simplified_type = |simp| { if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) { for &impl_def_id in impls_for_type { + // For every `default impl`, there's always a non-default `impl` + // that will *also* apply. There's no reason to register a candidate + // for this impl, since it is *not* proof that the trait goal holds. + if tcx.defaultness(impl_def_id).is_default() { + return; + } + match G::consider_impl_candidate(self, goal, impl_def_id) { Ok(candidate) => candidates.push(candidate), Err(NoSolution) => (), @@ -441,6 +447,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let tcx = self.tcx(); let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx)); for &impl_def_id in trait_impls.blanket_impls() { + // For every `default impl`, there's always a non-default `impl` + // that will *also* apply. There's no reason to register a candidate + // for this impl, since it is *not* proof that the trait goal holds. + if tcx.defaultness(impl_def_id).is_default() { + return; + } + match G::consider_impl_candidate(self, goal, impl_def_id) { Ok(candidate) => candidates.push(candidate), Err(NoSolution) => (), @@ -635,19 +648,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return; } - match self.try_normalize_ty(goal.param_env, alias_ty.self_ty()) { - // Recurse on the self type of the projection. - Some(next_self_ty) => { - self.assemble_alias_bound_candidates_recur(next_self_ty, goal, candidates); - } - // Bail if we overflow when normalizing, adding an ambiguous candidate. - None => { - if let Ok(result) = - self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW) - { - candidates.push(Candidate { source: CandidateSource::AliasBound, result }); - } + // Recurse on the self type of the projection. + match self.structurally_normalize_ty(goal.param_env, alias_ty.self_ty()) { + Ok(next_self_ty) => { + self.assemble_alias_bound_candidates_recur(next_self_ty, goal, candidates) } + Err(NoSolution) => {} } } @@ -857,19 +863,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let tcx = self.tcx(); let result = self.probe_misc_candidate("coherence unknowable").enter(|ecx| { let trait_ref = goal.predicate.trait_ref(tcx); - #[derive(Debug)] - struct Overflow; - let lazily_normalize_ty = |ty| match ecx.try_normalize_ty(goal.param_env, ty) { - Some(ty) => Ok(ty), - None => Err(Overflow), - }; + let lazily_normalize_ty = |ty| ecx.structurally_normalize_ty(goal.param_env, ty); - match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty) { - Err(Overflow) => { - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW) - } - Ok(Ok(())) => Err(NoSolution), - Ok(Err(_)) => { + match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty)? { + Ok(()) => Err(NoSolution), + Err(_) => { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } } diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index a7330136fe7..94a3cef8ad1 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -15,15 +15,13 @@ //! about it on zulip. use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; -use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::traits::query::NoSolution; use rustc_middle::infer::canonical::CanonicalVarInfos; use rustc_middle::traits::solve::{ CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, IsNormalizesToHack, QueryResult, Response, }; -use rustc_middle::traits::Reveal; -use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, UniverseIndex}; +use rustc_middle::ty::{self, AliasRelationDirection, Ty, TyCtxt, UniverseIndex}; use rustc_middle::ty::{ CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate, }; @@ -267,71 +265,32 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Ok(self.make_ambiguous_response_no_constraints(maybe_cause)) } - /// Normalize a type when it is structually matched on. + /// Normalize a type for when it is structurally matched on. /// - /// In nearly all cases this function must be used before matching on a type. + /// This function is necessary in nearly all cases before matching on a type. /// Not doing so is likely to be incomplete and therefore unsound during /// coherence. - #[instrument(level = "debug", skip(self), ret)] - fn try_normalize_ty( - &mut self, - param_env: ty::ParamEnv<'tcx>, - ty: Ty<'tcx>, - ) -> Option<Ty<'tcx>> { - self.try_normalize_ty_recur(param_env, DefineOpaqueTypes::Yes, 0, ty) - } - - fn try_normalize_ty_recur( + fn structurally_normalize_ty( &mut self, param_env: ty::ParamEnv<'tcx>, - define_opaque_types: DefineOpaqueTypes, - depth: usize, ty: Ty<'tcx>, - ) -> Option<Ty<'tcx>> { - if !self.tcx().recursion_limit().value_within_limit(depth) { - return None; - } - - let ty::Alias(kind, alias) = *ty.kind() else { - return Some(ty); - }; - - // We do no always define opaque types eagerly to allow non-defining uses - // in the defining scope. However, if we can unify this opaque to an existing - // opaque, then we should attempt to eagerly reveal the opaque, and we fall - // through. - if let DefineOpaqueTypes::No = define_opaque_types - && let Reveal::UserFacing = param_env.reveal() - && let ty::Opaque = kind - && let Some(def_id) = alias.def_id.as_local() - && self.can_define_opaque_ty(def_id) - { - if self - .unify_existing_opaque_tys( - param_env, - OpaqueTypeKey { def_id, args: alias.args }, - self.next_ty_infer(), - ) - .is_empty() - { - return Some(ty); - } - } - - match self.commit_if_ok(|this| { - let normalized_ty = this.next_ty_infer(); - let normalizes_to_goal = Goal::new( - this.tcx(), + ) -> Result<Ty<'tcx>, NoSolution> { + if let ty::Alias(..) = ty.kind() { + let normalized_ty = self.next_ty_infer(); + let alias_relate_goal = Goal::new( + self.tcx(), param_env, - ty::NormalizesTo { alias, term: normalized_ty.into() }, + ty::PredicateKind::AliasRelate( + ty.into(), + normalized_ty.into(), + AliasRelationDirection::Equate, + ), ); - this.add_goal(GoalSource::Misc, normalizes_to_goal); - this.try_evaluate_added_goals()?; - let ty = this.resolve_vars_if_possible(normalized_ty); - Ok(this.try_normalize_ty_recur(param_env, define_opaque_types, depth + 1, ty)) - }) { - Ok(ty) => ty, - Err(NoSolution) => Some(ty), + self.add_goal(GoalSource::Misc, alias_relate_goal); + self.try_evaluate_added_goals()?; + Ok(self.resolve_vars_if_possible(normalized_ty)) + } else { + Ok(ty) } } } diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs index b5d1aa06e4e..356c3776c04 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs @@ -58,21 +58,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } - let expected = match self.try_normalize_ty(goal.param_env, expected) { - Some(ty) => { - if ty.is_ty_var() { - return self.evaluate_added_goals_and_make_canonical_response( - Certainty::AMBIGUOUS, - ); - } else { - ty - } - } - None => { - return self - .evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW); - } - }; + let expected = self.structurally_normalize_ty(goal.param_env, expected)?; + if expected.is_ty_var() { + return self + .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + } // Otherwise, define a new opaque type self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?; diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 7a466241bfa..eacdd9fde51 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -584,11 +584,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let a_ty = goal.predicate.self_ty(); // We need to normalize the b_ty since it's matched structurally // in the other functions below. - let b_ty = match ecx - .try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1)) - { - Some(b_ty) => b_ty, - None => return vec![misc_candidate(ecx, Certainty::OVERFLOW)], + let Ok(b_ty) = ecx.structurally_normalize_ty( + goal.param_env, + goal.predicate.trait_ref.args.type_at(1), + ) else { + return vec![]; }; let goal = goal.with(ecx.tcx(), (a_ty, b_ty)); diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 81c72fc4b7b..fbf96833187 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -524,13 +524,15 @@ impl<'tcx> AutoTraitFinder<'tcx> { if let Entry::Occupied(v) = vid_map.entry(*smaller) { let smaller_deps = v.into_mut(); smaller_deps.larger.insert(*larger); - smaller_deps.larger.remove(&target); + // FIXME(#120456) - is `swap_remove` correct? + smaller_deps.larger.swap_remove(&target); } if let Entry::Occupied(v) = vid_map.entry(*larger) { let larger_deps = v.into_mut(); larger_deps.smaller.insert(*smaller); - larger_deps.smaller.remove(&target); + // FIXME(#120456) - is `swap_remove` correct? + larger_deps.smaller.swap_remove(&target); } } (&RegionTarget::RegionVid(v1), &RegionTarget::Region(r1)) => { @@ -543,13 +545,15 @@ impl<'tcx> AutoTraitFinder<'tcx> { if let Entry::Occupied(v) = vid_map.entry(*smaller) { let smaller_deps = v.into_mut(); smaller_deps.larger.insert(*larger); - smaller_deps.larger.remove(&target); + // FIXME(#120456) - is `swap_remove` correct? + smaller_deps.larger.swap_remove(&target); } if let Entry::Occupied(v) = vid_map.entry(*larger) { let larger_deps = v.into_mut(); larger_deps.smaller.insert(*smaller); - larger_deps.smaller.remove(&target); + // FIXME(#120456) - is `swap_remove` correct? + larger_deps.smaller.swap_remove(&target); } } } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 522c645253a..1edf6b11fc3 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -62,9 +62,10 @@ pub fn is_const_evaluatable<'tcx>( match unexpanded_ct.kind() { ty::ConstKind::Expr(_) => { - // FIXME(generic_const_exprs): we have a `ConstKind::Expr` which is fully concrete, but - // currently it is not possible to evaluate `ConstKind::Expr` so we are unable to tell if it - // is evaluatable or not. For now we just ICE until this is implemented. + // FIXME(generic_const_exprs): we have a `ConstKind::Expr` which is fully concrete, + // but currently it is not possible to evaluate `ConstKind::Expr` so we are unable + // to tell if it is evaluatable or not. For now we just ICE until this is + // implemented. Err(NotConstEvaluatable::Error(tcx.dcx().span_delayed_bug( span, "evaluating `ConstKind::Expr` is not currently supported", diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index efaea4e54dc..73effb33560 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -236,9 +236,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } - // It could be that we don't report an error because we have seen an `ErrorReported` from another source. - // We should probably be able to fix most of these, but some are delayed bugs that get a proper error - // after this function. + // It could be that we don't report an error because we have seen an `ErrorReported` from + // another source. We should probably be able to fix most of these, but some are delayed + // bugs that get a proper error after this function. reported.unwrap_or_else(|| self.dcx().delayed_bug("failed to report fulfillment errors")) } @@ -519,7 +519,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { trait_ref, span, ) { - GetSafeTransmuteErrorAndReason::Silent => return self.dcx().span_delayed_bug(span, "silent safe transmute error"), + GetSafeTransmuteErrorAndReason::Silent => { + return self.dcx().span_delayed_bug( + span, "silent safe transmute error" + ); + } GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation, @@ -3112,10 +3116,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { obligation.param_env, trait_ref.args.const_at(3), ) else { - span_bug!( + self.dcx().span_delayed_bug( span, - "Unable to construct rustc_transmute::Assume where it was previously possible" + "Unable to construct rustc_transmute::Assume where it was previously possible", ); + return GetSafeTransmuteErrorAndReason::Silent; }; match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable( diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 29a4a078fe0..7b715984c2b 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -555,7 +555,7 @@ fn virtual_call_violations_for_method<'tcx>( // NOTE: This check happens last, because it results in a lint, and not a // hard error. - if tcx.predicates_of(method.def_id).predicates.iter().any(|&(pred, span)| { + if tcx.predicates_of(method.def_id).predicates.iter().any(|&(pred, _span)| { // dyn Trait is okay: // // trait Trait { @@ -594,7 +594,10 @@ fn virtual_call_violations_for_method<'tcx>( // would already have reported an error at the definition of the // auto trait. if pred_trait_ref.args.len() != 1 { - tcx.dcx().span_delayed_bug(span, "auto traits cannot have generic parameters"); + assert!( + tcx.dcx().has_errors().is_some(), + "auto traits cannot have generic parameters" + ); } return false; } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 31a0d6271fa..149dc4c75a7 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -566,6 +566,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { { return; } + + // For every `default impl`, there's always a non-default `impl` + // that will *also* apply. There's no reason to register a candidate + // for this impl, since it is *not* proof that the trait goal holds. + if self.tcx().defaultness(impl_def_id).is_default() { + return; + } + if self.reject_fn_ptr_impls( impl_def_id, obligation, diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 2ce33a4d122..e4a70c537d2 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -192,13 +192,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut obligations, ); - obligations.extend(self.infcx.commit_if_ok(|_| { + obligations.extend( self.infcx .at(&obligation.cause, obligation.param_env) .sup(DefineOpaqueTypes::No, placeholder_trait_predicate, candidate) .map(|InferOk { obligations, .. }| obligations) - .map_err(|_| Unimplemented) - })?); + .map_err(|_| Unimplemented)?, + ); // FIXME(compiler-errors): I don't think this is needed. if let ty::Alias(ty::Projection, alias_ty) = placeholder_self_ty.kind() { @@ -532,13 +532,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut nested, ); - nested.extend(self.infcx.commit_if_ok(|_| { + nested.extend( self.infcx .at(&obligation.cause, obligation.param_env) .sup(DefineOpaqueTypes::No, obligation_trait_ref, upcast_trait_ref) .map(|InferOk { obligations, .. }| obligations) - .map_err(|_| Unimplemented) - })?); + .map_err(|_| Unimplemented)?, + ); // Check supertraits hold. This is so that their associated type bounds // will be checked in the code below. diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 185b735594f..ab0c53e6a9b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1605,6 +1605,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { on_ambiguity: impl FnOnce(), ) -> ControlFlow<T, ()> { let mut idx = 0; + let mut in_parent_alias_type = false; + loop { let (kind, alias_ty) = match *self_ty.kind() { ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) => (kind, alias_ty), @@ -1618,6 +1620,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { for bound in self.tcx().item_bounds(alias_ty.def_id).instantiate(self.tcx(), alias_ty.args) { + // HACK: On subsequent recursions, we only care about bounds that don't + // share the same type as `self_ty`. This is because for truly rigid + // projections, we will never be able to equate, e.g. `<T as Tr>::A` + // with `<<T as Tr>::A as Tr>::A`. + if in_parent_alias_type { + match bound.kind().skip_binder() { + ty::ClauseKind::Trait(tr) if tr.self_ty() == self_ty => continue, + ty::ClauseKind::Projection(p) if p.self_ty() == self_ty => continue, + _ => {} + } + } + for_each(self, bound, idx)?; idx += 1; } @@ -1627,6 +1641,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } else { return ControlFlow::Continue(()); } + + in_parent_alias_type = true; } } diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 73df1be6b55..e1a49ecf1b6 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -522,7 +522,8 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti for (p, _) in predicates { if let Some(poly_trait_ref) = p.as_trait_clause() { if Some(poly_trait_ref.def_id()) == sized_trait { - types_without_default_bounds.remove(&poly_trait_ref.self_ty().skip_binder()); + // FIXME(#120456) - is `swap_remove` correct? + types_without_default_bounds.swap_remove(&poly_trait_ref.self_ty().skip_binder()); continue; } } diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index 89459f377dd..e6b42f15d51 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -1,7 +1,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; -use rustc_span::Span; use std::ops::ControlFlow; /// This method traverses the structure of `ty`, trying to find an @@ -30,19 +29,16 @@ use std::ops::ControlFlow; /// that arose when the requirement was not enforced completely, see /// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307. pub fn search_for_structural_match_violation<'tcx>( - span: Span, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, ) -> Option<Ty<'tcx>> { - ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default() }).break_value() + ty.visit_with(&mut Search { tcx, seen: FxHashSet::default() }).break_value() } /// This implements the traversal over the structure of a given type to try to /// find instances of ADTs (specifically structs or enums) that do not implement /// `StructuralPartialEq`. struct Search<'tcx> { - span: Span, - tcx: TyCtxt<'tcx>, /// Tracks ADTs previously encountered during search, so that @@ -138,7 +134,6 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Search<'tcx> { bug!("unexpected type during structural-match checking: {:?}", ty); } ty::Error(_) => { - self.tcx.dcx().span_delayed_bug(self.span, "ty::Error in structural-match check"); // We still want to check other types after encountering an error, // as this may still emit relevant errors. return ControlFlow::Continue(()); diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs new file mode 100644 index 00000000000..57f961ac97e --- /dev/null +++ b/compiler/rustc_type_ir/src/binder.rs @@ -0,0 +1,7 @@ +use crate::Interner; + +pub trait BoundVars<I: Interner> { + fn bound_vars(&self) -> I::BoundVars; + + fn has_no_bound_vars(&self) -> bool; +} diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 188910ecc52..7728ee0e842 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -2,9 +2,10 @@ use smallvec::SmallVec; use std::fmt::Debug; use std::hash::Hash; +use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; use crate::{ - BoundVar, CanonicalVarInfo, ConstKind, DebruijnIndex, DebugWithInfcx, RegionKind, TyKind, - UniverseIndex, + BoundVar, BoundVars, CanonicalVarInfo, ConstKind, DebruijnIndex, DebugWithInfcx, RegionKind, + TyKind, UniverseIndex, }; pub trait Interner: Sized { @@ -19,7 +20,10 @@ pub trait Interner: Sized { type GenericArg: Copy + DebugWithInfcx<Self> + Hash + Ord; type Term: Copy + Debug + Hash + Ord; - type Binder<T>; + type Binder<T: TypeVisitable<Self>>: BoundVars<Self> + TypeSuperVisitable<Self>; + type BoundVars: IntoIterator<Item = Self::BoundVar>; + type BoundVar; + type CanonicalVars: Copy + Debug + Hash + Eq + IntoIterator<Item = CanonicalVarInfo<Self>>; // Kinds of tys @@ -28,7 +32,9 @@ pub trait Interner: Sized { + Hash + Ord + Into<Self::GenericArg> - + IntoKind<Kind = TyKind<Self>>; + + IntoKind<Kind = TyKind<Self>> + + TypeSuperVisitable<Self> + + Flags; type Tys: Copy + Debug + Hash + Ord + IntoIterator<Item = Self::Ty>; type AliasTy: Copy + DebugWithInfcx<Self> + Hash + Ord; type ParamTy: Copy + Debug + Hash + Ord; @@ -48,7 +54,9 @@ pub trait Interner: Sized { + Ord + Into<Self::GenericArg> + IntoKind<Kind = ConstKind<Self>> - + ConstTy<Self>; + + ConstTy<Self> + + TypeSuperVisitable<Self> + + Flags; type AliasConst: Copy + DebugWithInfcx<Self> + Hash + Ord; type PlaceholderConst: Copy + Debug + Hash + Ord + PlaceholderLike; type ParamConst: Copy + Debug + Hash + Ord; @@ -62,7 +70,8 @@ pub trait Interner: Sized { + Hash + Ord + Into<Self::GenericArg> - + IntoKind<Kind = RegionKind<Self>>; + + IntoKind<Kind = RegionKind<Self>> + + Flags; type EarlyParamRegion: Copy + Debug + Hash + Ord; type LateParamRegion: Copy + Debug + Hash + Ord; type BoundRegion: Copy + Debug + Hash + Ord; @@ -70,7 +79,7 @@ pub trait Interner: Sized { type PlaceholderRegion: Copy + Debug + Hash + Ord + PlaceholderLike; // Predicates - type Predicate: Copy + Debug + Hash + Eq; + type Predicate: Copy + Debug + Hash + Eq + TypeSuperVisitable<Self> + Flags; type TraitPredicate: Copy + Debug + Hash + Eq; type RegionOutlivesPredicate: Copy + Debug + Hash + Eq; type TypeOutlivesPredicate: Copy + Debug + Hash + Eq; @@ -86,6 +95,9 @@ pub trait Interner: Sized { fn mk_bound_ty(self, debruijn: DebruijnIndex, var: BoundVar) -> Self::Ty; fn mk_bound_region(self, debruijn: DebruijnIndex, var: BoundVar) -> Self::Region; fn mk_bound_const(self, debruijn: DebruijnIndex, var: BoundVar, ty: Self::Ty) -> Self::Const; + + /// Assert that an error has been delayed or emitted. + fn expect_error_or_delayed_bug(); } /// Common capabilities of placeholder kinds diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index f498c5531fc..94ccbcbd8a5 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -30,6 +30,7 @@ pub mod visit; #[macro_use] mod macros; +mod binder; mod canonical; mod const_kind; mod debug; @@ -39,6 +40,7 @@ mod interner; mod predicate_kind; mod region_kind; +pub use binder::*; pub use canonical::*; #[cfg(feature = "nightly")] pub use codec::*; diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 7aa99004667..638fb9f7fa9 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -45,8 +45,7 @@ use rustc_index::{Idx, IndexVec}; use std::fmt; use std::ops::ControlFlow; -use crate::Interner; -use crate::Lrc; +use crate::{self as ty, BoundVars, Interner, IntoKind, Lrc, TypeFlags}; /// This trait is implemented for every type that can be visited, /// providing the skeleton of the traversal. @@ -88,38 +87,28 @@ pub trait TypeVisitor<I: Interner>: Sized { #[cfg(not(feature = "nightly"))] type BreakTy; - fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &I::Binder<T>) -> ControlFlow<Self::BreakTy> - where - I::Binder<T>: TypeSuperVisitable<I>, - { + fn visit_binder<T: TypeVisitable<I>>( + &mut self, + t: &I::Binder<T>, + ) -> ControlFlow<Self::BreakTy> { t.super_visit_with(self) } - fn visit_ty(&mut self, t: I::Ty) -> ControlFlow<Self::BreakTy> - where - I::Ty: TypeSuperVisitable<I>, - { + fn visit_ty(&mut self, t: I::Ty) -> ControlFlow<Self::BreakTy> { t.super_visit_with(self) } // The default region visitor is a no-op because `Region` is non-recursive - // and has no `super_visit_with` method to call. That also explains the - // lack of `I::Region: TypeSuperVisitable<I>` bound. + // and has no `super_visit_with` method to call. fn visit_region(&mut self, _r: I::Region) -> ControlFlow<Self::BreakTy> { ControlFlow::Continue(()) } - fn visit_const(&mut self, c: I::Const) -> ControlFlow<Self::BreakTy> - where - I::Const: TypeSuperVisitable<I>, - { + fn visit_const(&mut self, c: I::Const) -> ControlFlow<Self::BreakTy> { c.super_visit_with(self) } - fn visit_predicate(&mut self, p: I::Predicate) -> ControlFlow<Self::BreakTy> - where - I::Predicate: TypeSuperVisitable<I>, - { + fn visit_predicate(&mut self, p: I::Predicate) -> ControlFlow<Self::BreakTy> { p.super_visit_with(self) } } @@ -200,3 +189,363 @@ impl<I: Interner, T: TypeVisitable<I>, Ix: Idx> TypeVisitable<I> for IndexVec<Ix self.iter().try_for_each(|t| t.visit_with(visitor)) } } + +pub trait Flags { + fn flags(&self) -> TypeFlags; + fn outer_exclusive_binder(&self) -> ty::DebruijnIndex; +} + +pub trait TypeVisitableExt<I: Interner>: TypeVisitable<I> { + fn has_type_flags(&self, flags: TypeFlags) -> bool; + + /// Returns `true` if `self` has any late-bound regions that are either + /// bound by `binder` or bound by some binder outside of `binder`. + /// If `binder` is `ty::INNERMOST`, this indicates whether + /// there are any late-bound regions that appear free. + fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool; + + /// Returns `true` if this type has any regions that escape `binder` (and + /// hence are not bound by it). + fn has_vars_bound_above(&self, binder: ty::DebruijnIndex) -> bool { + self.has_vars_bound_at_or_above(binder.shifted_in(1)) + } + + /// Return `true` if this type has regions that are not a part of the type. + /// For example, `for<'a> fn(&'a i32)` return `false`, while `fn(&'a i32)` + /// would return `true`. The latter can occur when traversing through the + /// former. + /// + /// See [`HasEscapingVarsVisitor`] for more information. + fn has_escaping_bound_vars(&self) -> bool { + self.has_vars_bound_at_or_above(ty::INNERMOST) + } + + fn has_projections(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_PROJECTION) + } + + fn has_inherent_projections(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_INHERENT) + } + + fn has_opaque_types(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_OPAQUE) + } + + fn has_coroutines(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_COROUTINE) + } + + fn references_error(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_ERROR) + } + + fn error_reported(&self) -> Result<(), I::ErrorGuaranteed>; + + fn has_non_region_param(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_PARAM - TypeFlags::HAS_RE_PARAM) + } + + fn has_infer_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_RE_INFER) + } + + fn has_infer_types(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_INFER) + } + + fn has_non_region_infer(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_INFER - TypeFlags::HAS_RE_INFER) + } + + fn has_infer(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_INFER) + } + + fn has_placeholders(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_PLACEHOLDER) + } + + fn has_non_region_placeholders(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_PLACEHOLDER - TypeFlags::HAS_RE_PLACEHOLDER) + } + + fn has_param(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_PARAM) + } + + /// "Free" regions in this context means that it has any region + /// that is not (a) erased or (b) late-bound. + fn has_free_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) + } + + fn has_erased_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_RE_ERASED) + } + + /// True if there are any un-erased free regions. + fn has_erasable_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) + } + + /// Indicates whether this value references only 'global' + /// generic parameters that are the same regardless of what fn we are + /// in. This is used for caching. + fn is_global(&self) -> bool { + !self.has_type_flags(TypeFlags::HAS_FREE_LOCAL_NAMES) + } + + /// True if there are any late-bound regions + fn has_bound_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_RE_BOUND) + } + /// True if there are any late-bound non-region variables + fn has_non_region_bound_vars(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_BOUND_VARS - TypeFlags::HAS_RE_BOUND) + } + /// True if there are any bound variables + fn has_bound_vars(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_BOUND_VARS) + } + + /// Indicates whether this value still has parameters/placeholders/inference variables + /// which could be replaced later, in a way that would change the results of `impl` + /// specialization. + fn still_further_specializable(&self) -> bool { + self.has_type_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE) + } +} + +impl<I: Interner, T: TypeVisitable<I>> TypeVisitableExt<I> for T { + fn has_type_flags(&self, flags: TypeFlags) -> bool { + let res = + self.visit_with(&mut HasTypeFlagsVisitor { flags }) == ControlFlow::Break(FoundFlags); + res + } + + fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool { + self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }).is_break() + } + + fn error_reported(&self) -> Result<(), I::ErrorGuaranteed> { + if self.references_error() { + if let ControlFlow::Break(guar) = self.visit_with(&mut HasErrorVisitor) { + Err(guar) + } else { + panic!("type flags said there was an error, but now there is not") + } + } else { + Ok(()) + } + } +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +struct FoundFlags; + +// FIXME: Optimize for checking for infer flags +struct HasTypeFlagsVisitor { + flags: ty::TypeFlags, +} + +impl std::fmt::Debug for HasTypeFlagsVisitor { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.flags.fmt(fmt) + } +} + +// Note: this visitor traverses values down to the level of +// `Ty`/`Const`/`Predicate`, but not within those types. This is because the +// type flags at the outer layer are enough. So it's faster than it first +// looks, particular for `Ty`/`Predicate` where it's just a field access. +// +// N.B. The only case where this isn't totally true is binders, which also +// add `HAS_{RE,TY,CT}_LATE_BOUND` flag depending on the *bound variables* that +// are present, regardless of whether those bound variables are used. This +// is important for anonymization of binders in `TyCtxt::erase_regions`. We +// specifically detect this case in `visit_binder`. +impl<I: Interner> TypeVisitor<I> for HasTypeFlagsVisitor { + type BreakTy = FoundFlags; + + fn visit_binder<T: TypeVisitable<I>>( + &mut self, + t: &I::Binder<T>, + ) -> ControlFlow<Self::BreakTy> { + // If we're looking for the HAS_BINDER_VARS flag, check if the + // binder has vars. This won't be present in the binder's bound + // value, so we need to check here too. + if self.flags.intersects(TypeFlags::HAS_BINDER_VARS) && !t.has_no_bound_vars() { + return ControlFlow::Break(FoundFlags); + } + + t.super_visit_with(self) + } + + #[inline] + fn visit_ty(&mut self, t: I::Ty) -> ControlFlow<Self::BreakTy> { + // Note: no `super_visit_with` call. + let flags = t.flags(); + if flags.intersects(self.flags) { + ControlFlow::Break(FoundFlags) + } else { + ControlFlow::Continue(()) + } + } + + #[inline] + fn visit_region(&mut self, r: I::Region) -> ControlFlow<Self::BreakTy> { + // Note: no `super_visit_with` call, as usual for `Region`. + let flags = r.flags(); + if flags.intersects(self.flags) { + ControlFlow::Break(FoundFlags) + } else { + ControlFlow::Continue(()) + } + } + + #[inline] + fn visit_const(&mut self, c: I::Const) -> ControlFlow<Self::BreakTy> { + // Note: no `super_visit_with` call. + if c.flags().intersects(self.flags) { + ControlFlow::Break(FoundFlags) + } else { + ControlFlow::Continue(()) + } + } + + #[inline] + fn visit_predicate(&mut self, predicate: I::Predicate) -> ControlFlow<Self::BreakTy> { + // Note: no `super_visit_with` call. + if predicate.flags().intersects(self.flags) { + ControlFlow::Break(FoundFlags) + } else { + ControlFlow::Continue(()) + } + } +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +struct FoundEscapingVars; + +/// An "escaping var" is a bound var whose binder is not part of `t`. A bound var can be a +/// bound region or a bound type. +/// +/// So, for example, consider a type like the following, which has two binders: +/// +/// for<'a> fn(x: for<'b> fn(&'a isize, &'b isize)) +/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope +/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ inner scope +/// +/// This type has *bound regions* (`'a`, `'b`), but it does not have escaping regions, because the +/// binders of both `'a` and `'b` are part of the type itself. However, if we consider the *inner +/// fn type*, that type has an escaping region: `'a`. +/// +/// Note that what I'm calling an "escaping var" is often just called a "free var". However, +/// we already use the term "free var". It refers to the regions or types that we use to represent +/// bound regions or type params on a fn definition while we are type checking its body. +/// +/// To clarify, conceptually there is no particular difference between +/// an "escaping" var and a "free" var. However, there is a big +/// difference in practice. Basically, when "entering" a binding +/// level, one is generally required to do some sort of processing to +/// a bound var, such as replacing it with a fresh/placeholder +/// var, or making an entry in the environment to represent the +/// scope to which it is attached, etc. An escaping var represents +/// a bound var for which this processing has not yet been done. +struct HasEscapingVarsVisitor { + /// Anything bound by `outer_index` or "above" is escaping. + outer_index: ty::DebruijnIndex, +} + +impl<I: Interner> TypeVisitor<I> for HasEscapingVarsVisitor { + type BreakTy = FoundEscapingVars; + + fn visit_binder<T: TypeVisitable<I>>( + &mut self, + t: &I::Binder<T>, + ) -> ControlFlow<Self::BreakTy> { + self.outer_index.shift_in(1); + let result = t.super_visit_with(self); + self.outer_index.shift_out(1); + result + } + + #[inline] + fn visit_ty(&mut self, t: I::Ty) -> ControlFlow<Self::BreakTy> { + // If the outer-exclusive-binder is *strictly greater* than + // `outer_index`, that means that `t` contains some content + // bound at `outer_index` or above (because + // `outer_exclusive_binder` is always 1 higher than the + // content in `t`). Therefore, `t` has some escaping vars. + if t.outer_exclusive_binder() > self.outer_index { + ControlFlow::Break(FoundEscapingVars) + } else { + ControlFlow::Continue(()) + } + } + + #[inline] + fn visit_region(&mut self, r: I::Region) -> ControlFlow<Self::BreakTy> { + // If the region is bound by `outer_index` or anything outside + // of outer index, then it escapes the binders we have + // visited. + if r.outer_exclusive_binder() > self.outer_index { + ControlFlow::Break(FoundEscapingVars) + } else { + ControlFlow::Continue(()) + } + } + + fn visit_const(&mut self, ct: I::Const) -> ControlFlow<Self::BreakTy> { + // If the outer-exclusive-binder is *strictly greater* than + // `outer_index`, that means that `ct` contains some content + // bound at `outer_index` or above (because + // `outer_exclusive_binder` is always 1 higher than the + // content in `t`). Therefore, `t` has some escaping vars. + if ct.outer_exclusive_binder() > self.outer_index { + ControlFlow::Break(FoundEscapingVars) + } else { + ControlFlow::Continue(()) + } + } + + #[inline] + fn visit_predicate(&mut self, predicate: I::Predicate) -> ControlFlow<Self::BreakTy> { + if predicate.outer_exclusive_binder() > self.outer_index { + ControlFlow::Break(FoundEscapingVars) + } else { + ControlFlow::Continue(()) + } + } +} + +struct HasErrorVisitor; + +impl<I: Interner> TypeVisitor<I> for HasErrorVisitor { + type BreakTy = I::ErrorGuaranteed; + + fn visit_ty(&mut self, t: <I as Interner>::Ty) -> ControlFlow<Self::BreakTy> { + if let ty::Error(guar) = t.kind() { + ControlFlow::Break(guar) + } else { + t.super_visit_with(self) + } + } + + fn visit_const(&mut self, c: <I as Interner>::Const) -> ControlFlow<Self::BreakTy> { + if let ty::ConstKind::Error(guar) = c.kind() { + ControlFlow::Break(guar) + } else { + c.super_visit_with(self) + } + } + + fn visit_region(&mut self, r: <I as Interner>::Region) -> ControlFlow<Self::BreakTy> { + if let ty::ReError(guar) = r.kind() { + ControlFlow::Break(guar) + } else { + ControlFlow::Continue(()) + } + } +} |
