diff options
Diffstat (limited to 'compiler')
17 files changed, 217 insertions, 354 deletions
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_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/lib.rs b/compiler/rustc_errors/src/lib.rs index da9ef6627be..e033d66fccf 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -435,7 +435,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 +445,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 +537,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 +562,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 +613,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(), @@ -666,13 +670,14 @@ impl DiagCtxt { inner.stashed_err_count = 0; inner.deduplicated_err_count = 0; inner.deduplicated_warn_count = 0; + inner.must_produce_diag = false; inner.has_printed = false; + inner.suppressed_expected_diag = 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(); @@ -934,7 +939,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 +1119,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<'_, ()> { @@ -1266,19 +1270,17 @@ 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 - { + 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; @@ -1297,12 +1299,6 @@ impl DiagCtxtInner { .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; - } Warning if !self.flags.can_emit_warnings => { if diagnostic.has_future_breakage() { (*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {}); @@ -1414,23 +1410,14 @@ impl DiagCtxtInner { 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 +1441,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 +1451,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 +1523,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 +1555,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 +1599,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 +1619,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 +1644,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_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/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 5ce80ef5c10..d84bce09ecb 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -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/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/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_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_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_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..733c415ead5 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() { @@ -635,19 +634,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 +849,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)); |
