Auto merge of #121036 - matthiaskrgr:rollup-ul05q8e, r=matthiaskrgr

Rollup of 8 pull requests

Successful merges:

 - #114877 (unstable-book: add quick-edit link)
 - #120548 (rustdoc: Fix handling of doc_auto_cfg feature for cfg attributes on glob reexport)
 - #120549 (modify alias-relate to also normalize ambiguous opaques)
 - #120959 (Remove good path delayed bugs)
 - #120978 (match lowering: simplify block creation)
 - #121019 (coverage: Simplify some parts of the coverage span refiner)
 - #121021 (Extend intra-doc link chapter in the rustdoc book)
 - #121031 (RustWrapper: adapt for coverage mapping API changes)

Failed merges:

 - #121014 (Remove `force_print_diagnostic`)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-02-13 17:27:25 +00:00
commit a84bb95a1f
107 changed files with 1278 additions and 1192 deletions

View File

@ -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 }

View File

@ -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);

View File

@ -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,

View File

@ -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

View File

@ -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),

View File

@ -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);

View File

@ -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);

View File

@ -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()

View File

@ -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()
}

View File

@ -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,

View File

@ -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.

View File

@ -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);
// 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);
// 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,
);
}
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

View File

@ -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>,
some_curr: Option<CurrCovspan>,
/// 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,
/// 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 {

View File

@ -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)
}
}

View File

@ -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;

View File

@ -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())),
let ty::Alias(_, alias) = *ty.kind() else {
return Some(ty);
};
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() },
);
}
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)
}));
if let Some(result) = self.try_merge_responses(&candidates) {
Ok(result)
} else {
self.flounder(&candidates)
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),
}
}
}

View File

@ -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)
}
}

View File

@ -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(
fn structurally_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(
&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)
}
}
}

View File

@ -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)?;

View File

@ -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));

View File

@ -151,3 +151,21 @@ will be given, even if the link fails to resolve. For example, any link containi
characters will be ignored.
[#72243]: https://github.com/rust-lang/rust/issues/72243
## What happens in case an intra-doc link cannot be generated
In some cases (items behind a `cfg` for example), an intra-doc link cannot be generated to item.
There are different ways to create a link in markdown, and depending on the one you use, it will
render differently in this case:
```md
1. [a]
2. [b][c]
3. [d](e)
4. [f]
[f]: g
```
`1.` and `2.` will will be displayed as is in the rendered documentation (ie, `[a]` and `[b][c]`)
whereas `3.` and `4.` will be replaced by a link targetting `e` for `[d](e)` and `g` for `[f]`.

View File

@ -170,3 +170,32 @@ There are a few attributes which are not inlined though:
All other attributes are inherited when inlined, so that the documentation matches the behavior if
the inlined item was directly defined at the spot where it's shown.
These rules also apply if the item is inlined with a glob re-export:
```rust,ignore (inline)
mod private_mod {
/// First
#[cfg(a)]
pub struct InPrivate;
}
#[cfg(c)]
pub use self::private_mod::*;
```
Otherwise, the attributes displayed will be from the re-exported item and the attributes on the
re-export itself will be ignored:
```rust,ignore (inline)
mod private_mod {
/// First
#[cfg(a)]
pub struct InPrivate;
}
#[cfg(c)]
pub use self::private_mod::InPrivate;
```
In the above case, `cfg(c)` will not be displayed in the docs.

View File

@ -4,3 +4,4 @@ author = "The Rust Community"
[output.html]
git-repository-url = "https://github.com/rust-lang/rust/tree/master/src/doc/unstable-book"
edit-url-template = "https://github.com/rust-lang/rust/edit/master/src/doc/unstable-book/{path}"

View File

@ -2736,7 +2736,7 @@ fn add_without_unwanted_attributes<'hir>(
if ident == sym::doc {
filter_doc_attr(normal, is_inline);
attrs.push((Cow::Owned(attr), import_parent));
} else if ident != sym::cfg {
} else if is_inline || ident != sym::cfg {
// If it's not a `cfg()` attribute, we keep it.
attrs.push((Cow::Owned(attr), import_parent));
}

View File

@ -110,7 +110,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body@$DIR/async_await.rs:15:18: 18:2}>,
bb0: {
_39 = discriminant((*(_1.0: &mut {async fn body@$DIR/async_await.rs:15:18: 18:2})));
switchInt(move _39) -> [0: bb1, 1: bb29, 3: bb27, 4: bb28, otherwise: bb9];
switchInt(move _39) -> [0: bb1, 1: bb29, 3: bb27, 4: bb28, otherwise: bb8];
}
bb1: {
@ -165,10 +165,14 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body@$DIR/async_await.rs:15:18: 18:2}>,
StorageDead(_10);
PlaceMention(_9);
_16 = discriminant(_9);
switchInt(move _16) -> [0: bb10, 1: bb8, otherwise: bb9];
switchInt(move _16) -> [0: bb10, 1: bb9, otherwise: bb8];
}
bb8: {
unreachable;
}
bb9: {
_8 = const ();
StorageDead(_14);
StorageDead(_12);
@ -186,10 +190,6 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body@$DIR/async_await.rs:15:18: 18:2}>,
return;
}
bb9: {
unreachable;
}
bb10: {
StorageLive(_17);
_17 = ((_9 as Ready).0: ());
@ -267,7 +267,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body@$DIR/async_await.rs:15:18: 18:2}>,
StorageDead(_26);
PlaceMention(_25);
_32 = discriminant(_25);
switchInt(move _32) -> [0: bb21, 1: bb20, otherwise: bb9];
switchInt(move _32) -> [0: bb21, 1: bb20, otherwise: bb8];
}
bb20: {

View File

@ -27,13 +27,13 @@ fn main() -> () {
StorageLive(_5);
PlaceMention(_1);
_6 = discriminant(_1);
switchInt(move _6) -> [1: bb4, otherwise: bb3];
switchInt(move _6) -> [1: bb5, otherwise: bb4];
}
bb1: {
StorageLive(_3);
StorageLive(_4);
_4 = begin_panic::<&str>(const "explicit panic") -> bb7;
_4 = begin_panic::<&str>(const "explicit panic") -> bb8;
}
bb2: {
@ -43,14 +43,19 @@ fn main() -> () {
}
bb3: {
goto -> bb6;
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}
bb4: {
falseEdge -> [real: bb5, imaginary: bb3];
goto -> bb7;
}
bb5: {
falseEdge -> [real: bb6, imaginary: bb4];
}
bb6: {
_5 = ((_1 as Some).0: u8);
_0 = const ();
StorageDead(_5);
@ -58,12 +63,12 @@ fn main() -> () {
return;
}
bb6: {
bb7: {
StorageDead(_5);
goto -> bb1;
}
bb7 (cleanup): {
bb8 (cleanup): {
resume;
}
}

View File

@ -17,7 +17,7 @@ fn main() -> () {
}
bb1: {
falseUnwind -> [real: bb2, unwind: bb11];
falseUnwind -> [real: bb2, unwind: bb12];
}
bb2: {
@ -25,41 +25,46 @@ fn main() -> () {
StorageLive(_3);
_3 = const true;
PlaceMention(_3);
switchInt(_3) -> [0: bb3, otherwise: bb4];
switchInt(_3) -> [0: bb4, otherwise: bb5];
}
bb3: {
falseEdge -> [real: bb5, imaginary: bb4];
}
bb4: {
_0 = const ();
goto -> bb10;
}
bb5: {
_2 = const 4_i32;
goto -> bb8;
}
bb6: {
FakeRead(ForMatchedPlace(None), _3);
unreachable;
}
bb4: {
falseEdge -> [real: bb6, imaginary: bb5];
}
bb5: {
_0 = const ();
goto -> bb11;
}
bb6: {
_2 = const 4_i32;
goto -> bb9;
}
bb7: {
goto -> bb8;
unreachable;
}
bb8: {
goto -> bb9;
}
bb9: {
FakeRead(ForLet(None), _2);
StorageDead(_3);
StorageLive(_5);
StorageLive(_6);
_6 = &_2;
_5 = std::mem::drop::<&i32>(move _6) -> [return: bb9, unwind: bb11];
_5 = std::mem::drop::<&i32>(move _6) -> [return: bb10, unwind: bb12];
}
bb9: {
bb10: {
StorageDead(_6);
StorageDead(_5);
_1 = const ();
@ -67,13 +72,13 @@ fn main() -> () {
goto -> bb1;
}
bb10: {
bb11: {
StorageDead(_3);
StorageDead(_2);
return;
}
bb11 (cleanup): {
bb12 (cleanup): {
resume;
}
}

View File

@ -19,168 +19,178 @@ fn test_complex() -> () {
bb0: {
StorageLive(_1);
StorageLive(_2);
_2 = E::f() -> [return: bb1, unwind: bb31];
_2 = E::f() -> [return: bb1, unwind: bb33];
}
bb1: {
PlaceMention(_2);
_3 = discriminant(_2);
switchInt(move _3) -> [0: bb2, otherwise: bb3];
switchInt(move _3) -> [0: bb4, otherwise: bb3];
}
bb2: {
falseEdge -> [real: bb4, imaginary: bb3];
FakeRead(ForMatchedPlace(None), _2);
unreachable;
}
bb3: {
goto -> bb19;
goto -> bb20;
}
bb4: {
StorageLive(_4);
_4 = always_true() -> [return: bb5, unwind: bb31];
falseEdge -> [real: bb5, imaginary: bb3];
}
bb5: {
switchInt(move _4) -> [0: bb7, otherwise: bb6];
StorageLive(_4);
_4 = always_true() -> [return: bb6, unwind: bb33];
}
bb6: {
switchInt(move _4) -> [0: bb8, otherwise: bb7];
}
bb7: {
StorageLive(_5);
StorageLive(_6);
StorageLive(_7);
_7 = Droppy(const 0_u8);
_6 = (_7.0: u8);
_5 = Gt(move _6, const 0_u8);
switchInt(move _5) -> [0: bb9, otherwise: bb8];
}
bb7: {
goto -> bb13;
switchInt(move _5) -> [0: bb10, otherwise: bb9];
}
bb8: {
drop(_7) -> [return: bb10, unwind: bb31];
goto -> bb14;
}
bb9: {
goto -> bb11;
drop(_7) -> [return: bb11, unwind: bb33];
}
bb10: {
StorageDead(_7);
StorageDead(_6);
goto -> bb16;
goto -> bb12;
}
bb11: {
drop(_7) -> [return: bb12, unwind: bb31];
StorageDead(_7);
StorageDead(_6);
goto -> bb17;
}
bb12: {
StorageDead(_7);
StorageDead(_6);
goto -> bb13;
drop(_7) -> [return: bb13, unwind: bb33];
}
bb13: {
StorageDead(_7);
StorageDead(_6);
goto -> bb14;
}
bb14: {
StorageLive(_8);
StorageLive(_9);
StorageLive(_10);
_10 = Droppy(const 1_u8);
_9 = (_10.0: u8);
_8 = Gt(move _9, const 1_u8);
switchInt(move _8) -> [0: bb15, otherwise: bb14];
}
bb14: {
drop(_10) -> [return: bb16, unwind: bb31];
switchInt(move _8) -> [0: bb16, otherwise: bb15];
}
bb15: {
goto -> bb17;
drop(_10) -> [return: bb17, unwind: bb33];
}
bb16: {
StorageDead(_10);
StorageDead(_9);
_1 = const ();
goto -> bb20;
goto -> bb18;
}
bb17: {
drop(_10) -> [return: bb18, unwind: bb31];
StorageDead(_10);
StorageDead(_9);
_1 = const ();
goto -> bb21;
}
bb18: {
StorageDead(_10);
StorageDead(_9);
goto -> bb19;
drop(_10) -> [return: bb19, unwind: bb33];
}
bb19: {
_1 = const ();
StorageDead(_10);
StorageDead(_9);
goto -> bb20;
}
bb20: {
_1 = const ();
goto -> bb21;
}
bb21: {
StorageDead(_8);
StorageDead(_5);
StorageDead(_4);
StorageDead(_2);
StorageDead(_1);
StorageLive(_11);
_11 = always_true() -> [return: bb21, unwind: bb31];
}
bb21: {
switchInt(move _11) -> [0: bb23, otherwise: bb22];
_11 = always_true() -> [return: bb22, unwind: bb33];
}
bb22: {
goto -> bb29;
switchInt(move _11) -> [0: bb24, otherwise: bb23];
}
bb23: {
goto -> bb24;
goto -> bb31;
}
bb24: {
StorageLive(_12);
_12 = E::f() -> [return: bb25, unwind: bb31];
goto -> bb25;
}
bb25: {
PlaceMention(_12);
_13 = discriminant(_12);
switchInt(move _13) -> [1: bb27, otherwise: bb26];
StorageLive(_12);
_12 = E::f() -> [return: bb26, unwind: bb33];
}
bb26: {
goto -> bb29;
PlaceMention(_12);
_13 = discriminant(_12);
switchInt(move _13) -> [1: bb29, otherwise: bb28];
}
bb27: {
falseEdge -> [real: bb28, imaginary: bb26];
FakeRead(ForMatchedPlace(None), _12);
unreachable;
}
bb28: {
_0 = const ();
goto -> bb30;
goto -> bb31;
}
bb29: {
_0 = const ();
goto -> bb30;
falseEdge -> [real: bb30, imaginary: bb28];
}
bb30: {
_0 = const ();
goto -> bb32;
}
bb31: {
_0 = const ();
goto -> bb32;
}
bb32: {
StorageDead(_11);
StorageDead(_12);
return;
}
bb31 (cleanup): {
bb33 (cleanup): {
resume;
}
}

View File

@ -28,25 +28,25 @@ fn full_tested_match() -> () {
_2 = Option::<i32>::Some(const 42_i32);
PlaceMention(_2);
_3 = discriminant(_2);
switchInt(move _3) -> [0: bb1, 1: bb2, otherwise: bb4];
switchInt(move _3) -> [0: bb2, 1: bb3, otherwise: bb1];
}
bb1: {
FakeRead(ForMatchedPlace(None), _2);
unreachable;
}
bb2: {
_1 = (const 3_i32, const 3_i32);
goto -> bb11;
}
bb2: {
falseEdge -> [real: bb5, imaginary: bb3];
}
bb3: {
falseEdge -> [real: bb10, imaginary: bb1];
falseEdge -> [real: bb5, imaginary: bb4];
}
bb4: {
FakeRead(ForMatchedPlace(None), _2);
unreachable;
falseEdge -> [real: bb10, imaginary: bb2];
}
bb5: {
@ -54,7 +54,7 @@ fn full_tested_match() -> () {
_6 = &((_2 as Some).0: i32);
_4 = &fake _2;
StorageLive(_7);
_7 = guard() -> [return: bb6, unwind: bb12];
_7 = guard() -> [return: bb6, unwind: bb13];
}
bb6: {
@ -83,7 +83,7 @@ fn full_tested_match() -> () {
bb9: {
StorageDead(_7);
StorageDead(_6);
goto -> bb3;
goto -> bb4;
}
bb10: {
@ -105,7 +105,12 @@ fn full_tested_match() -> () {
return;
}
bb12 (cleanup): {
bb12: {
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}
bb13 (cleanup): {
resume;
}
}

View File

@ -28,18 +28,23 @@ fn full_tested_match2() -> () {
_2 = Option::<i32>::Some(const 42_i32);
PlaceMention(_2);
_3 = discriminant(_2);
switchInt(move _3) -> [0: bb1, 1: bb2, otherwise: bb4];
switchInt(move _3) -> [0: bb2, 1: bb3, otherwise: bb1];
}
bb1: {
falseEdge -> [real: bb10, imaginary: bb3];
FakeRead(ForMatchedPlace(None), _2);
unreachable;
}
bb2: {
falseEdge -> [real: bb5, imaginary: bb1];
falseEdge -> [real: bb10, imaginary: bb4];
}
bb3: {
falseEdge -> [real: bb5, imaginary: bb2];
}
bb4: {
StorageLive(_9);
_9 = ((_2 as Some).0: i32);
StorageLive(_10);
@ -50,17 +55,12 @@ fn full_tested_match2() -> () {
goto -> bb11;
}
bb4: {
FakeRead(ForMatchedPlace(None), _2);
unreachable;
}
bb5: {
StorageLive(_6);
_6 = &((_2 as Some).0: i32);
_4 = &fake _2;
StorageLive(_7);
_7 = guard() -> [return: bb6, unwind: bb12];
_7 = guard() -> [return: bb6, unwind: bb13];
}
bb6: {
@ -89,7 +89,7 @@ fn full_tested_match2() -> () {
bb9: {
StorageDead(_7);
StorageDead(_6);
falseEdge -> [real: bb3, imaginary: bb1];
falseEdge -> [real: bb4, imaginary: bb2];
}
bb10: {
@ -105,7 +105,12 @@ fn full_tested_match2() -> () {
return;
}
bb12 (cleanup): {
bb12: {
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}
bb13 (cleanup): {
resume;
}
}

View File

@ -39,55 +39,60 @@ fn main() -> () {
_2 = Option::<i32>::Some(const 1_i32);
PlaceMention(_2);
_4 = discriminant(_2);
switchInt(move _4) -> [1: bb2, otherwise: bb1];
switchInt(move _4) -> [1: bb7, otherwise: bb2];
}
bb1: {
falseEdge -> [real: bb13, imaginary: bb6];
FakeRead(ForMatchedPlace(None), _2);
unreachable;
}
bb2: {
falseEdge -> [real: bb8, imaginary: bb1];
falseEdge -> [real: bb14, imaginary: bb5];
}
bb3: {
goto -> bb1;
_3 = discriminant(_2);
switchInt(move _3) -> [1: bb5, otherwise: bb4];
}
bb4: {
_3 = discriminant(_2);
switchInt(move _3) -> [1: bb6, otherwise: bb5];
}
bb5: {
StorageLive(_14);
_14 = _2;
_1 = const 4_i32;
StorageDead(_14);
goto -> bb19;
goto -> bb20;
}
bb5: {
falseEdge -> [real: bb15, imaginary: bb4];
}
bb6: {
falseEdge -> [real: bb14, imaginary: bb5];
goto -> bb4;
}
bb7: {
goto -> bb5;
falseEdge -> [real: bb9, imaginary: bb2];
}
bb8: {
goto -> bb2;
}
bb9: {
StorageLive(_7);
_7 = &((_2 as Some).0: i32);
_5 = &fake _2;
StorageLive(_8);
_8 = guard() -> [return: bb9, unwind: bb20];
}
bb9: {
switchInt(move _8) -> [0: bb11, otherwise: bb10];
_8 = guard() -> [return: bb10, unwind: bb22];
}
bb10: {
switchInt(move _8) -> [0: bb12, otherwise: bb11];
}
bb11: {
StorageDead(_8);
FakeRead(ForMatchGuard, _5);
FakeRead(ForGuardBinding, _7);
@ -96,42 +101,42 @@ fn main() -> () {
_1 = const 1_i32;
StorageDead(_6);
StorageDead(_7);
goto -> bb19;
}
bb11: {
goto -> bb12;
goto -> bb20;
}
bb12: {
StorageDead(_8);
StorageDead(_7);
falseEdge -> [real: bb3, imaginary: bb1];
goto -> bb13;
}
bb13: {
StorageDead(_8);
StorageDead(_7);
falseEdge -> [real: bb8, imaginary: bb2];
}
bb14: {
StorageLive(_9);
_9 = _2;
_1 = const 2_i32;
StorageDead(_9);
goto -> bb19;
goto -> bb20;
}
bb14: {
bb15: {
StorageLive(_11);
_11 = &((_2 as Some).0: i32);
_5 = &fake _2;
StorageLive(_12);
StorageLive(_13);
_13 = (*_11);
_12 = guard2(move _13) -> [return: bb15, unwind: bb20];
}
bb15: {
switchInt(move _12) -> [0: bb17, otherwise: bb16];
_12 = guard2(move _13) -> [return: bb16, unwind: bb22];
}
bb16: {
switchInt(move _12) -> [0: bb18, otherwise: bb17];
}
bb17: {
StorageDead(_13);
StorageDead(_12);
FakeRead(ForMatchGuard, _5);
@ -141,21 +146,21 @@ fn main() -> () {
_1 = const 3_i32;
StorageDead(_10);
StorageDead(_11);
goto -> bb19;
}
bb17: {
goto -> bb18;
goto -> bb20;
}
bb18: {
StorageDead(_13);
StorageDead(_12);
StorageDead(_11);
falseEdge -> [real: bb7, imaginary: bb5];
goto -> bb19;
}
bb19: {
StorageDead(_13);
StorageDead(_12);
StorageDead(_11);
falseEdge -> [real: bb6, imaginary: bb4];
}
bb20: {
PlaceMention(_1);
StorageDead(_2);
StorageDead(_1);
@ -163,7 +168,12 @@ fn main() -> () {
return;
}
bb20 (cleanup): {
bb21: {
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}
bb22 (cleanup): {
resume;
}
}

View File

@ -6,24 +6,29 @@ fn match_bool(_1: bool) -> usize {
bb0: {
PlaceMention(_1);
switchInt(_1) -> [0: bb2, otherwise: bb1];
switchInt(_1) -> [0: bb2, otherwise: bb3];
}
bb1: {
falseEdge -> [real: bb3, imaginary: bb2];
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}
bb2: {
_0 = const 20_usize;
goto -> bb4;
goto -> bb5;
}
bb3: {
_0 = const 10_usize;
goto -> bb4;
falseEdge -> [real: bb4, imaginary: bb2];
}
bb4: {
_0 = const 10_usize;
goto -> bb5;
}
bb5: {
return;
}
}

View File

@ -30,7 +30,7 @@ fn move_out_by_subslice() -> () {
StorageLive(_2);
_3 = SizeOf(i32);
_4 = AlignOf(i32);
_5 = alloc::alloc::exchange_malloc(move _3, move _4) -> [return: bb1, unwind: bb12];
_5 = alloc::alloc::exchange_malloc(move _3, move _4) -> [return: bb1, unwind: bb13];
}
bb1: {
@ -38,7 +38,7 @@ fn move_out_by_subslice() -> () {
_6 = ShallowInitBox(move _5, i32);
(*_6) = const 1_i32;
_2 = move _6;
drop(_6) -> [return: bb2, unwind: bb11];
drop(_6) -> [return: bb2, unwind: bb12];
}
bb2: {
@ -46,7 +46,7 @@ fn move_out_by_subslice() -> () {
StorageLive(_7);
_8 = SizeOf(i32);
_9 = AlignOf(i32);
_10 = alloc::alloc::exchange_malloc(move _8, move _9) -> [return: bb3, unwind: bb11];
_10 = alloc::alloc::exchange_malloc(move _8, move _9) -> [return: bb3, unwind: bb12];
}
bb3: {
@ -54,18 +54,18 @@ fn move_out_by_subslice() -> () {
_11 = ShallowInitBox(move _10, i32);
(*_11) = const 2_i32;
_7 = move _11;
drop(_11) -> [return: bb4, unwind: bb10];
drop(_11) -> [return: bb4, unwind: bb11];
}
bb4: {
StorageDead(_11);
_1 = [move _2, move _7];
drop(_7) -> [return: bb5, unwind: bb11];
drop(_7) -> [return: bb5, unwind: bb12];
}
bb5: {
StorageDead(_7);
drop(_2) -> [return: bb6, unwind: bb12];
drop(_2) -> [return: bb6, unwind: bb13];
}
bb6: {
@ -75,32 +75,37 @@ fn move_out_by_subslice() -> () {
StorageLive(_12);
_12 = move _1[0..2];
_0 = const ();
drop(_12) -> [return: bb7, unwind: bb9];
drop(_12) -> [return: bb8, unwind: bb10];
}
bb7: {
StorageDead(_12);
drop(_1) -> [return: bb8, unwind: bb12];
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}
bb8: {
StorageDead(_12);
drop(_1) -> [return: bb9, unwind: bb13];
}
bb9: {
StorageDead(_1);
return;
}
bb9 (cleanup): {
drop(_1) -> [return: bb12, unwind terminate(cleanup)];
}
bb10 (cleanup): {
drop(_7) -> [return: bb11, unwind terminate(cleanup)];
drop(_1) -> [return: bb13, unwind terminate(cleanup)];
}
bb11 (cleanup): {
drop(_2) -> [return: bb12, unwind terminate(cleanup)];
drop(_7) -> [return: bb12, unwind terminate(cleanup)];
}
bb12 (cleanup): {
drop(_2) -> [return: bb13, unwind terminate(cleanup)];
}
bb13 (cleanup): {
resume;
}
}

View File

@ -30,7 +30,7 @@ fn move_out_from_end() -> () {
StorageLive(_2);
_3 = SizeOf(i32);
_4 = AlignOf(i32);
_5 = alloc::alloc::exchange_malloc(move _3, move _4) -> [return: bb1, unwind: bb12];
_5 = alloc::alloc::exchange_malloc(move _3, move _4) -> [return: bb1, unwind: bb13];
}
bb1: {
@ -38,7 +38,7 @@ fn move_out_from_end() -> () {
_6 = ShallowInitBox(move _5, i32);
(*_6) = const 1_i32;
_2 = move _6;
drop(_6) -> [return: bb2, unwind: bb11];
drop(_6) -> [return: bb2, unwind: bb12];
}
bb2: {
@ -46,7 +46,7 @@ fn move_out_from_end() -> () {
StorageLive(_7);
_8 = SizeOf(i32);
_9 = AlignOf(i32);
_10 = alloc::alloc::exchange_malloc(move _8, move _9) -> [return: bb3, unwind: bb11];
_10 = alloc::alloc::exchange_malloc(move _8, move _9) -> [return: bb3, unwind: bb12];
}
bb3: {
@ -54,18 +54,18 @@ fn move_out_from_end() -> () {
_11 = ShallowInitBox(move _10, i32);
(*_11) = const 2_i32;
_7 = move _11;
drop(_11) -> [return: bb4, unwind: bb10];
drop(_11) -> [return: bb4, unwind: bb11];
}
bb4: {
StorageDead(_11);
_1 = [move _2, move _7];
drop(_7) -> [return: bb5, unwind: bb11];
drop(_7) -> [return: bb5, unwind: bb12];
}
bb5: {
StorageDead(_7);
drop(_2) -> [return: bb6, unwind: bb12];
drop(_2) -> [return: bb6, unwind: bb13];
}
bb6: {
@ -75,32 +75,37 @@ fn move_out_from_end() -> () {
StorageLive(_12);
_12 = move _1[1 of 2];
_0 = const ();
drop(_12) -> [return: bb7, unwind: bb9];
drop(_12) -> [return: bb8, unwind: bb10];
}
bb7: {
StorageDead(_12);
drop(_1) -> [return: bb8, unwind: bb12];
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}
bb8: {
StorageDead(_12);
drop(_1) -> [return: bb9, unwind: bb13];
}
bb9: {
StorageDead(_1);
return;
}
bb9 (cleanup): {
drop(_1) -> [return: bb12, unwind terminate(cleanup)];
}
bb10 (cleanup): {
drop(_7) -> [return: bb11, unwind terminate(cleanup)];
drop(_1) -> [return: bb13, unwind terminate(cleanup)];
}
bb11 (cleanup): {
drop(_2) -> [return: bb12, unwind terminate(cleanup)];
drop(_7) -> [return: bb12, unwind terminate(cleanup)];
}
bb12 (cleanup): {
drop(_2) -> [return: bb13, unwind terminate(cleanup)];
}
bb13 (cleanup): {
resume;
}
}

View File

@ -79,10 +79,14 @@
bb4: {
StorageDead(_12);
_14 = discriminant(_11);
switchInt(move _14) -> [0: bb7, 1: bb5, otherwise: bb6];
switchInt(move _14) -> [0: bb7, 1: bb6, otherwise: bb5];
}
bb5: {
unreachable;
}
bb6: {
- StorageLive(_16);
_16 = ((_11 as Some).0: usize);
StorageLive(_17);
@ -95,10 +99,6 @@
+ assert(move _20, "index out of bounds: the length is {} but the index is {}", move _19, _16) -> [success: bb8, unwind unreachable];
}
bb6: {
unreachable;
}
bb7: {
_0 = const ();
StorageDead(_13);

View File

@ -79,10 +79,14 @@
bb4: {
StorageDead(_12);
_14 = discriminant(_11);
switchInt(move _14) -> [0: bb7, 1: bb5, otherwise: bb6];
switchInt(move _14) -> [0: bb7, 1: bb6, otherwise: bb5];
}
bb5: {
unreachable;
}
bb6: {
- StorageLive(_16);
_16 = ((_11 as Some).0: usize);
StorageLive(_17);
@ -95,10 +99,6 @@
+ assert(move _20, "index out of bounds: the length is {} but the index is {}", move _19, _16) -> [success: bb8, unwind continue];
}
bb6: {
unreachable;
}
bb7: {
_0 = const ();
StorageDead(_13);

View File

@ -26,12 +26,16 @@
_1 = const _;
StorageLive(_2);
- _3 = discriminant(_1);
- switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2];
- switchInt(move _3) -> [0: bb3, 1: bb2, otherwise: bb1];
+ _3 = const 0_isize;
+ switchInt(const 0_isize) -> [0: bb3, 1: bb1, otherwise: bb2];
+ switchInt(const 0_isize) -> [0: bb3, 1: bb2, otherwise: bb1];
}
bb1: {
unreachable;
}
bb2: {
StorageLive(_5);
_5 = ((_1 as V2).0: i32);
_2 = _5;
@ -39,10 +43,6 @@
goto -> bb4;
}
bb2: {
unreachable;
}
bb3: {
StorageLive(_4);
- _4 = ((_1 as V1).0: i32);

View File

@ -26,12 +26,16 @@
_1 = const _;
StorageLive(_2);
- _3 = discriminant(_1);
- switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2];
- switchInt(move _3) -> [0: bb3, 1: bb2, otherwise: bb1];
+ _3 = const 0_isize;
+ switchInt(const 0_isize) -> [0: bb3, 1: bb1, otherwise: bb2];
+ switchInt(const 0_isize) -> [0: bb3, 1: bb2, otherwise: bb1];
}
bb1: {
unreachable;
}
bb2: {
StorageLive(_5);
_5 = ((_1 as V2).0: i32);
_2 = _5;
@ -39,10 +43,6 @@
goto -> bb4;
}
bb2: {
unreachable;
}
bb3: {
StorageLive(_4);
- _4 = ((_1 as V1).0: i32);

View File

@ -49,16 +49,16 @@
StorageDead(_4);
StorageLive(_6);
_7 = discriminant(_3);
switchInt(move _7) -> [0: bb4, 1: bb6, otherwise: bb5];
switchInt(move _7) -> [0: bb5, 1: bb6, otherwise: bb4];
}
bb4: {
_6 = const 0_u8;
goto -> bb7;
unreachable;
}
bb5: {
unreachable;
_6 = const 0_u8;
goto -> bb7;
}
bb6: {

View File

@ -49,16 +49,16 @@
StorageDead(_4);
StorageLive(_6);
_7 = discriminant(_3);
switchInt(move _7) -> [0: bb4, 1: bb6, otherwise: bb5];
switchInt(move _7) -> [0: bb5, 1: bb6, otherwise: bb4];
}
bb4: {
_6 = const 0_u8;
goto -> bb7;
unreachable;
}
bb5: {
unreachable;
_6 = const 0_u8;
goto -> bb7;
}
bb6: {

View File

@ -20,7 +20,7 @@ fn simple() {
// CHECK: [[e]] = const E::V1(0_i32);
let e = E::V1(0);
// CHECK: switchInt(const 0_isize) -> [0: [[target_bb:bb.*]], 1: bb1, otherwise: bb2];
// CHECK: switchInt(const 0_isize) -> [0: [[target_bb:bb.*]], 1: bb2, otherwise: bb1];
// CHECK: [[target_bb]]: {
// CHECK: [[x]] = const 0_i32;
let x = match e { E::V1(x1) => x1, E::V2(x2) => x2 };
@ -36,7 +36,7 @@ fn constant() {
// CHECK: [[e]] = const _;
let e = C;
// CHECK: switchInt(const 0_isize) -> [0: [[target_bb:bb.*]], 1: bb1, otherwise: bb2];
// CHECK: switchInt(const 0_isize) -> [0: [[target_bb:bb.*]], 1: bb2, otherwise: bb1];
// CHECK: [[target_bb]]: {
// CHECK: [[x]] = const 0_i32;
let x = match e { E::V1(x1) => x1, E::V2(x2) => x2 };
@ -55,7 +55,7 @@ fn statics() {
// CHECK: [[e1]] = const E::V1(0_i32);
let e1 = C;
// CHECK: switchInt(const 0_isize) -> [0: [[target_bb:bb.*]], 1: bb1, otherwise: bb2];
// CHECK: switchInt(const 0_isize) -> [0: [[target_bb:bb.*]], 1: bb2, otherwise: bb1];
// CHECK: [[target_bb]]: {
// CHECK: [[x1]] = const 0_i32;
let x1 = match e1 { E::V1(x11) => x11, E::V2(x12) => x12 };

View File

@ -27,12 +27,16 @@
+ _1 = const E::V1(0_i32);
StorageLive(_2);
- _3 = discriminant(_1);
- switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2];
- switchInt(move _3) -> [0: bb3, 1: bb2, otherwise: bb1];
+ _3 = const 0_isize;
+ switchInt(const 0_isize) -> [0: bb3, 1: bb1, otherwise: bb2];
+ switchInt(const 0_isize) -> [0: bb3, 1: bb2, otherwise: bb1];
}
bb1: {
unreachable;
}
bb2: {
StorageLive(_5);
_5 = ((_1 as V2).0: i32);
_2 = _5;
@ -40,10 +44,6 @@
goto -> bb4;
}
bb2: {
unreachable;
}
bb3: {
StorageLive(_4);
- _4 = ((_1 as V1).0: i32);

View File

@ -27,12 +27,16 @@
+ _1 = const E::V1(0_i32);
StorageLive(_2);
- _3 = discriminant(_1);
- switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2];
- switchInt(move _3) -> [0: bb3, 1: bb2, otherwise: bb1];
+ _3 = const 0_isize;
+ switchInt(const 0_isize) -> [0: bb3, 1: bb1, otherwise: bb2];
+ switchInt(const 0_isize) -> [0: bb3, 1: bb2, otherwise: bb1];
}
bb1: {
unreachable;
}
bb2: {
StorageLive(_5);
_5 = ((_1 as V2).0: i32);
_2 = _5;
@ -40,10 +44,6 @@
goto -> bb4;
}
bb2: {
unreachable;
}
bb3: {
StorageLive(_4);
- _4 = ((_1 as V1).0: i32);

View File

@ -49,12 +49,16 @@
StorageDead(_2);
StorageLive(_3);
- _4 = discriminant(_1);
- switchInt(move _4) -> [0: bb3, 1: bb1, otherwise: bb2];
- switchInt(move _4) -> [0: bb3, 1: bb2, otherwise: bb1];
+ _4 = const 0_isize;
+ switchInt(const 0_isize) -> [0: bb3, 1: bb1, otherwise: bb2];
+ switchInt(const 0_isize) -> [0: bb3, 1: bb2, otherwise: bb1];
}
bb1: {
unreachable;
}
bb2: {
StorageLive(_6);
_6 = ((_1 as V2).0: i32);
_3 = _6;
@ -62,10 +66,6 @@
goto -> bb4;
}
bb2: {
unreachable;
}
bb3: {
StorageLive(_5);
- _5 = ((_1 as V1).0: i32);
@ -84,7 +84,7 @@
StorageDead(_8);
StorageLive(_9);
_10 = discriminant((*_7));
switchInt(move _10) -> [0: bb6, 1: bb5, otherwise: bb2];
switchInt(move _10) -> [0: bb6, 1: bb5, otherwise: bb1];
}
bb5: {

View File

@ -49,12 +49,16 @@
StorageDead(_2);
StorageLive(_3);
- _4 = discriminant(_1);
- switchInt(move _4) -> [0: bb3, 1: bb1, otherwise: bb2];
- switchInt(move _4) -> [0: bb3, 1: bb2, otherwise: bb1];
+ _4 = const 0_isize;
+ switchInt(const 0_isize) -> [0: bb3, 1: bb1, otherwise: bb2];
+ switchInt(const 0_isize) -> [0: bb3, 1: bb2, otherwise: bb1];
}
bb1: {
unreachable;
}
bb2: {
StorageLive(_6);
_6 = ((_1 as V2).0: i32);
_3 = _6;
@ -62,10 +66,6 @@
goto -> bb4;
}
bb2: {
unreachable;
}
bb3: {
StorageLive(_5);
- _5 = ((_1 as V1).0: i32);
@ -84,7 +84,7 @@
StorageDead(_8);
StorageLive(_9);
_10 = discriminant((*_7));
switchInt(move _10) -> [0: bb6, 1: bb5, otherwise: bb2];
switchInt(move _10) -> [0: bb6, 1: bb5, otherwise: bb1];
}
bb5: {

View File

@ -25,58 +25,66 @@
_7 = Len((*_2));
_8 = const 4_usize;
_9 = Ge(move _7, move _8);
switchInt(move _9) -> [0: bb6, otherwise: bb2];
- switchInt(move _9) -> [0: bb2, otherwise: bb7];
+ switchInt(move _9) -> [0: bb2, otherwise: bb6];
}
bb2: {
switchInt((*_2)[0 of 4]) -> [47: bb3, otherwise: bb6];
}
bb3: {
switchInt((*_2)[1 of 4]) -> [47: bb4, otherwise: bb6];
}
bb4: {
switchInt((*_2)[2 of 4]) -> [47: bb5, otherwise: bb6];
}
bb5: {
- switchInt((*_2)[3 of 4]) -> [47: bb11, otherwise: bb6];
+ switchInt((*_2)[3 of 4]) -> [47: bb10, otherwise: bb6];
}
bb6: {
_4 = Len((*_2));
_5 = const 3_usize;
_6 = Ge(move _4, move _5);
switchInt(move _6) -> [0: bb10, otherwise: bb7];
- switchInt(move _6) -> [0: bb3, otherwise: bb4];
+ switchInt(move _6) -> [0: bb10, otherwise: bb3];
}
bb7: {
switchInt((*_2)[0 of 3]) -> [47: bb8, otherwise: bb10];
bb3: {
- _0 = const false;
- goto -> bb14;
+ switchInt((*_2)[0 of 3]) -> [47: bb4, otherwise: bb10];
}
bb8: {
switchInt((*_2)[1 of 3]) -> [47: bb9, otherwise: bb10];
bb4: {
- switchInt((*_2)[0 of 3]) -> [47: bb5, otherwise: bb3];
+ switchInt((*_2)[1 of 3]) -> [47: bb5, otherwise: bb10];
}
bb9: {
- switchInt((*_2)[2 of 3]) -> [47: bb12, 33: bb13, otherwise: bb10];
bb5: {
- switchInt((*_2)[1 of 3]) -> [47: bb6, otherwise: bb3];
+ switchInt((*_2)[2 of 3]) -> [47: bb11, 33: bb11, otherwise: bb10];
}
bb6: {
- switchInt((*_2)[2 of 3]) -> [47: bb12, 33: bb13, otherwise: bb3];
+ switchInt((*_2)[0 of 4]) -> [47: bb7, otherwise: bb2];
}
bb7: {
- switchInt((*_2)[0 of 4]) -> [47: bb8, otherwise: bb2];
+ switchInt((*_2)[1 of 4]) -> [47: bb8, otherwise: bb2];
}
bb8: {
- switchInt((*_2)[1 of 4]) -> [47: bb9, otherwise: bb2];
+ switchInt((*_2)[2 of 4]) -> [47: bb9, otherwise: bb2];
}
bb9: {
- switchInt((*_2)[2 of 4]) -> [47: bb10, otherwise: bb2];
+ switchInt((*_2)[3 of 4]) -> [47: bb10, otherwise: bb2];
}
bb10: {
- switchInt((*_2)[3 of 4]) -> [47: bb11, otherwise: bb2];
- }
-
- bb11: {
_0 = const false;
- goto -> bb14;
+ goto -> bb12;
}
bb11: {
- _0 = const false;
- goto -> bb14;
- }
-
- bb12: {
+ bb11: {
_0 = const true;
- goto -> bb14;
+ goto -> bb12;

View File

@ -25,58 +25,66 @@
_7 = Len((*_2));
_8 = const 4_usize;
_9 = Ge(move _7, move _8);
switchInt(move _9) -> [0: bb6, otherwise: bb2];
- switchInt(move _9) -> [0: bb2, otherwise: bb7];
+ switchInt(move _9) -> [0: bb2, otherwise: bb6];
}
bb2: {
switchInt((*_2)[0 of 4]) -> [47: bb3, otherwise: bb6];
}
bb3: {
switchInt((*_2)[1 of 4]) -> [47: bb4, otherwise: bb6];
}
bb4: {
switchInt((*_2)[2 of 4]) -> [47: bb5, otherwise: bb6];
}
bb5: {
- switchInt((*_2)[3 of 4]) -> [47: bb11, otherwise: bb6];
+ switchInt((*_2)[3 of 4]) -> [47: bb10, otherwise: bb6];
}
bb6: {
_4 = Len((*_2));
_5 = const 3_usize;
_6 = Ge(move _4, move _5);
switchInt(move _6) -> [0: bb10, otherwise: bb7];
- switchInt(move _6) -> [0: bb3, otherwise: bb4];
+ switchInt(move _6) -> [0: bb10, otherwise: bb3];
}
bb7: {
switchInt((*_2)[0 of 3]) -> [47: bb8, otherwise: bb10];
bb3: {
- _0 = const false;
- goto -> bb14;
+ switchInt((*_2)[0 of 3]) -> [47: bb4, otherwise: bb10];
}
bb8: {
switchInt((*_2)[1 of 3]) -> [47: bb9, otherwise: bb10];
bb4: {
- switchInt((*_2)[0 of 3]) -> [47: bb5, otherwise: bb3];
+ switchInt((*_2)[1 of 3]) -> [47: bb5, otherwise: bb10];
}
bb9: {
- switchInt((*_2)[2 of 3]) -> [47: bb12, 33: bb13, otherwise: bb10];
bb5: {
- switchInt((*_2)[1 of 3]) -> [47: bb6, otherwise: bb3];
+ switchInt((*_2)[2 of 3]) -> [47: bb11, 33: bb11, otherwise: bb10];
}
bb6: {
- switchInt((*_2)[2 of 3]) -> [47: bb12, 33: bb13, otherwise: bb3];
+ switchInt((*_2)[0 of 4]) -> [47: bb7, otherwise: bb2];
}
bb7: {
- switchInt((*_2)[0 of 4]) -> [47: bb8, otherwise: bb2];
+ switchInt((*_2)[1 of 4]) -> [47: bb8, otherwise: bb2];
}
bb8: {
- switchInt((*_2)[1 of 4]) -> [47: bb9, otherwise: bb2];
+ switchInt((*_2)[2 of 4]) -> [47: bb9, otherwise: bb2];
}
bb9: {
- switchInt((*_2)[2 of 4]) -> [47: bb10, otherwise: bb2];
+ switchInt((*_2)[3 of 4]) -> [47: bb10, otherwise: bb2];
}
bb10: {
- switchInt((*_2)[3 of 4]) -> [47: bb11, otherwise: bb2];
- }
-
- bb11: {
_0 = const false;
- goto -> bb14;
+ goto -> bb12;
}
bb11: {
- _0 = const false;
- goto -> bb14;
- }
-
- bb12: {
+ bb11: {
_0 = const true;
- goto -> bb14;
+ goto -> bb12;

View File

@ -55,10 +55,14 @@
StorageDead(_8);
PlaceMention(_7);
_10 = discriminant(_7);
switchInt(move _10) -> [0: bb6, 1: bb4, otherwise: bb5];
switchInt(move _10) -> [0: bb6, 1: bb5, otherwise: bb4];
}
bb4: {
unreachable;
}
bb5: {
StorageLive(_12);
- _12 = (*((_7 as Some).0: &i32));
+ _15 = deref_copy ((_7 as Some).0: &i32);
@ -68,10 +72,6 @@
_6 = std::mem::drop::<i32>(move _13) -> [return: bb7, unwind: bb8];
}
bb5: {
unreachable;
}
bb6: {
_0 = const ();
StorageDead(_9);

View File

@ -55,10 +55,14 @@
StorageDead(_8);
PlaceMention(_7);
_10 = discriminant(_7);
switchInt(move _10) -> [0: bb6, 1: bb4, otherwise: bb5];
switchInt(move _10) -> [0: bb6, 1: bb5, otherwise: bb4];
}
bb4: {
unreachable;
}
bb5: {
StorageLive(_12);
- _12 = (*((_7 as Some).0: &i32));
+ _15 = deref_copy ((_7 as Some).0: &i32);
@ -68,10 +72,6 @@
_6 = std::mem::drop::<i32>(move _13) -> [return: bb7, unwind continue];
}
bb5: {
unreachable;
}
bb6: {
_0 = const ();
StorageDead(_9);

View File

@ -30,7 +30,7 @@
StorageDead(_5);
StorageDead(_4);
_8 = discriminant((_3.0: std::option::Option<u32>));
- switchInt(move _8) -> [0: bb1, 1: bb3, otherwise: bb2];
- switchInt(move _8) -> [0: bb2, 1: bb3, otherwise: bb1];
+ StorageLive(_11);
+ _11 = discriminant((_3.1: std::option::Option<u32>));
+ StorageLive(_12);
@ -40,24 +40,23 @@
}
bb1: {
- _6 = discriminant((_3.1: std::option::Option<u32>));
- switchInt(move _6) -> [0: bb5, otherwise: bb2];
- }
-
- bb2: {
+ StorageDead(_12);
_0 = const 1_u32;
- goto -> bb6;
+ goto -> bb4;
}
bb2: {
- _6 = discriminant((_3.1: std::option::Option<u32>));
- switchInt(move _6) -> [0: bb5, otherwise: bb1];
- }
-
- bb3: {
- _7 = discriminant((_3.1: std::option::Option<u32>));
- switchInt(move _7) -> [1: bb4, otherwise: bb2];
- switchInt(move _7) -> [1: bb4, otherwise: bb1];
- }
-
- bb4: {
+ bb2: {
StorageLive(_10);
_10 = (((_3.1: std::option::Option<u32>) as Some).0: u32);
StorageLive(_9);

View File

@ -78,16 +78,10 @@
StorageDead(_5);
_34 = deref_copy (_4.0: &ViewportPercentageLength);
_11 = discriminant((*_34));
switchInt(move _11) -> [0: bb1, 1: bb3, 2: bb4, 3: bb5, otherwise: bb2];
switchInt(move _11) -> [0: bb2, 1: bb3, 2: bb4, 3: bb5, otherwise: bb1];
}
bb1: {
_35 = deref_copy (_4.1: &ViewportPercentageLength);
_7 = discriminant((*_35));
switchInt(move _7) -> [0: bb6, otherwise: bb2];
}
bb2: {
StorageLive(_33);
_33 = ();
_0 = Result::<ViewportPercentageLength, ()>::Err(move _33);
@ -97,22 +91,28 @@
goto -> bb11;
}
bb2: {
_35 = deref_copy (_4.1: &ViewportPercentageLength);
_7 = discriminant((*_35));
switchInt(move _7) -> [0: bb6, otherwise: bb1];
}
bb3: {
_36 = deref_copy (_4.1: &ViewportPercentageLength);
_8 = discriminant((*_36));
switchInt(move _8) -> [1: bb7, otherwise: bb2];
switchInt(move _8) -> [1: bb7, otherwise: bb1];
}
bb4: {
_37 = deref_copy (_4.1: &ViewportPercentageLength);
_9 = discriminant((*_37));
switchInt(move _9) -> [2: bb8, otherwise: bb2];
switchInt(move _9) -> [2: bb8, otherwise: bb1];
}
bb5: {
_38 = deref_copy (_4.1: &ViewportPercentageLength);
_10 = discriminant((*_38));
switchInt(move _10) -> [3: bb9, otherwise: bb2];
switchInt(move _10) -> [3: bb9, otherwise: bb1];
}
bb6: {

View File

@ -36,26 +36,26 @@
StorageDead(_5);
StorageDead(_4);
_8 = discriminant((_3.0: std::option::Option<u32>));
switchInt(move _8) -> [0: bb1, 1: bb4, otherwise: bb3];
switchInt(move _8) -> [0: bb2, 1: bb4, otherwise: bb1];
}
bb1: {
_6 = discriminant((_3.1: std::option::Option<u32>));
switchInt(move _6) -> [0: bb2, 1: bb7, otherwise: bb3];
unreachable;
}
bb2: {
_6 = discriminant((_3.1: std::option::Option<u32>));
switchInt(move _6) -> [0: bb3, 1: bb7, otherwise: bb1];
}
bb3: {
_0 = const 3_u32;
goto -> bb8;
}
bb3: {
unreachable;
}
bb4: {
_7 = discriminant((_3.1: std::option::Option<u32>));
switchInt(move _7) -> [0: bb6, 1: bb5, otherwise: bb3];
switchInt(move _7) -> [0: bb6, 1: bb5, otherwise: bb1];
}
bb5: {

View File

@ -21,18 +21,18 @@
+ _2 = Option::<T>::Some(_1);
StorageDead(_3);
- _4 = discriminant(_2);
- switchInt(move _4) -> [0: bb1, 1: bb3, otherwise: bb2];
- switchInt(move _4) -> [0: bb2, 1: bb3, otherwise: bb1];
+ _4 = const 1_isize;
+ switchInt(const 1_isize) -> [0: bb1, 1: bb3, otherwise: bb2];
+ switchInt(const 1_isize) -> [0: bb2, 1: bb3, otherwise: bb1];
}
bb1: {
StorageLive(_6);
_6 = begin_panic::<&str>(const "explicit panic") -> unwind unreachable;
unreachable;
}
bb2: {
unreachable;
StorageLive(_6);
_6 = begin_panic::<&str>(const "explicit panic") -> unwind unreachable;
}
bb3: {

View File

@ -21,18 +21,18 @@
+ _2 = Option::<T>::Some(_1);
StorageDead(_3);
- _4 = discriminant(_2);
- switchInt(move _4) -> [0: bb1, 1: bb3, otherwise: bb2];
- switchInt(move _4) -> [0: bb2, 1: bb3, otherwise: bb1];
+ _4 = const 1_isize;
+ switchInt(const 1_isize) -> [0: bb1, 1: bb3, otherwise: bb2];
+ switchInt(const 1_isize) -> [0: bb2, 1: bb3, otherwise: bb1];
}
bb1: {
StorageLive(_6);
_6 = begin_panic::<&str>(const "explicit panic") -> unwind continue;
unreachable;
}
bb2: {
unreachable;
StorageLive(_6);
_6 = begin_panic::<&str>(const "explicit panic") -> unwind continue;
}
bb3: {

View File

@ -28,18 +28,18 @@
+ StorageLive(_3);
+ StorageLive(_5);
+ _3 = discriminant(_2);
+ switchInt(move _3) -> [0: bb1, 1: bb3, otherwise: bb2];
+ switchInt(move _3) -> [0: bb2, 1: bb3, otherwise: bb1];
}
bb1: {
+ StorageLive(_4);
+ _4 = cfg!(debug_assertions);
+ assume(_4);
+ _5 = unreachable_unchecked::precondition_check() -> [return: bb2, unwind unreachable];
+ unreachable;
+ }
+
+ bb2: {
+ unreachable;
+ StorageLive(_4);
+ _4 = cfg!(debug_assertions);
+ assume(_4);
+ _5 = unreachable_unchecked::precondition_check() -> [return: bb1, unwind unreachable];
+ }
+
+ bb3: {

View File

@ -28,22 +28,22 @@
+ StorageLive(_3);
+ StorageLive(_5);
+ _3 = discriminant(_2);
+ switchInt(move _3) -> [0: bb1, 1: bb3, otherwise: bb2];
+ switchInt(move _3) -> [0: bb2, 1: bb3, otherwise: bb1];
}
bb1: {
- StorageDead(_2);
- return;
+ StorageLive(_4);
+ _4 = cfg!(debug_assertions);
+ assume(_4);
+ _5 = unreachable_unchecked::precondition_check() -> [return: bb2, unwind unreachable];
+ unreachable;
}
- bb2 (cleanup): {
- resume;
+ bb2: {
+ unreachable;
+ StorageLive(_4);
+ _4 = cfg!(debug_assertions);
+ assume(_4);
+ _5 = unreachable_unchecked::precondition_check() -> [return: bb1, unwind unreachable];
+ }
+
+ bb3: {

View File

@ -47,10 +47,14 @@ fn test() -> Option<Box<u32>> {
StorageDead(_7);
PlaceMention(_6);
_8 = discriminant(_6);
switchInt(move _8) -> [0: bb3, 1: bb5, otherwise: bb4];
switchInt(move _8) -> [0: bb4, 1: bb5, otherwise: bb3];
}
bb3: {
unreachable;
}
bb4: {
StorageLive(_12);
_12 = ((_6 as Continue).0: u32);
(*_5) = _12;
@ -59,10 +63,6 @@ fn test() -> Option<Box<u32>> {
drop(_5) -> [return: bb7, unwind: bb11];
}
bb4: {
unreachable;
}
bb5: {
StorageLive(_9);
_9 = ((_6 as Break).0: std::option::Option<std::convert::Infallible>);

View File

@ -47,10 +47,14 @@ fn test() -> Option<Box<u32>> {
StorageDead(_7);
PlaceMention(_6);
_8 = discriminant(_6);
switchInt(move _8) -> [0: bb3, 1: bb5, otherwise: bb4];
switchInt(move _8) -> [0: bb4, 1: bb5, otherwise: bb3];
}
bb3: {
unreachable;
}
bb4: {
StorageLive(_12);
_12 = ((_6 as Continue).0: u32);
(*_5) = _12;
@ -59,10 +63,6 @@ fn test() -> Option<Box<u32>> {
drop(_5) -> [return: bb7, unwind: bb11];
}
bb4: {
unreachable;
}
bb5: {
StorageLive(_9);
_9 = ((_6 as Break).0: std::option::Option<std::convert::Infallible>);

View File

@ -14,4 +14,9 @@ fn bar(_1: [(Never, u32); 1]) -> u32 {
StorageDead(_2);
return;
}
bb1: {
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}
}

View File

@ -22,7 +22,7 @@ fn main() -> () {
bb0: {
StorageLive(_1);
_1 = std::mem::size_of::<Foo>() -> [return: bb1, unwind: bb3];
_1 = std::mem::size_of::<Foo>() -> [return: bb1, unwind: bb5];
}
bb1: {
@ -42,10 +42,15 @@ fn main() -> () {
_6 = const 0_usize;
_7 = Len(_2);
_8 = Lt(_6, _7);
assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb2, unwind: bb3];
assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb3, unwind: bb5];
}
bb2: {
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}
bb3: {
_5 = (_2[_6].0: u64);
PlaceMention(_5);
StorageDead(_6);
@ -55,7 +60,12 @@ fn main() -> () {
return;
}
bb3 (cleanup): {
bb4: {
FakeRead(ForMatchedPlace(None), _5);
unreachable;
}
bb5 (cleanup): {
resume;
}
}

View File

@ -6,11 +6,15 @@ fn f(_1: Void) -> ! {
bb0: {
PlaceMention(_1);
goto -> bb1;
}
bb1: {
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}
bb1: {
bb2: {
return;
}
}

View File

@ -12,7 +12,7 @@ fn bar(_1: Box<[T]>) -> () {
StorageLive(_2);
StorageLive(_3);
_3 = &(*_1);
_2 = <[T] as Index<usize>>::index(move _3, const 0_usize) -> [return: bb1, unwind: bb3];
_2 = <[T] as Index<usize>>::index(move _3, const 0_usize) -> [return: bb1, unwind: bb4];
}
bb1: {
@ -20,18 +20,23 @@ fn bar(_1: Box<[T]>) -> () {
PlaceMention((*_2));
StorageDead(_2);
_0 = const ();
drop(_1) -> [return: bb2, unwind: bb4];
drop(_1) -> [return: bb3, unwind: bb5];
}
bb2: {
FakeRead(ForMatchedPlace(None), (*_2));
unreachable;
}
bb3: {
return;
}
bb3 (cleanup): {
drop(_1) -> [return: bb4, unwind terminate(cleanup)];
bb4 (cleanup): {
drop(_1) -> [return: bb5, unwind terminate(cleanup)];
}
bb4 (cleanup): {
bb5 (cleanup): {
resume;
}
}

View File

@ -14,7 +14,7 @@ fn hey(_1: &[T]) -> () {
StorageLive(_3);
StorageLive(_4);
_4 = &(*_1);
_3 = <[T] as Index<usize>>::index(move _4, const 0_usize) -> [return: bb1, unwind: bb2];
_3 = <[T] as Index<usize>>::index(move _4, const 0_usize) -> [return: bb1, unwind: bb3];
}
bb1: {
@ -27,7 +27,12 @@ fn hey(_1: &[T]) -> () {
return;
}
bb2 (cleanup): {
bb2: {
FakeRead(ForMatchedPlace(None), _2);
unreachable;
}
bb3 (cleanup): {
resume;
}
}

View File

@ -67,7 +67,7 @@ fn main() -> () {
StorageLive(_2);
StorageLive(_3);
StorageLive(_4);
_4 = function_with_bytes::<&*b"AAAA">() -> [return: bb1, unwind: bb21];
_4 = function_with_bytes::<&*b"AAAA">() -> [return: bb1, unwind: bb23];
}
bb1: {
@ -91,24 +91,29 @@ fn main() -> () {
_11 = &(*_8);
StorageLive(_12);
_12 = &(*_9);
_10 = <&[u8] as PartialEq<&[u8; 4]>>::eq(move _11, move _12) -> [return: bb2, unwind: bb21];
_10 = <&[u8] as PartialEq<&[u8; 4]>>::eq(move _11, move _12) -> [return: bb3, unwind: bb23];
}
bb2: {
switchInt(move _10) -> [0: bb4, otherwise: bb3];
FakeRead(ForMatchedPlace(None), _2);
unreachable;
}
bb3: {
StorageDead(_12);
StorageDead(_11);
goto -> bb8;
switchInt(move _10) -> [0: bb5, otherwise: bb4];
}
bb4: {
goto -> bb5;
StorageDead(_12);
StorageDead(_11);
goto -> bb9;
}
bb5: {
goto -> bb6;
}
bb6: {
StorageDead(_12);
StorageDead(_11);
StorageLive(_14);
@ -127,10 +132,10 @@ fn main() -> () {
_19 = &(*_20);
StorageLive(_21);
_21 = Option::<Arguments<'_>>::None;
_15 = core::panicking::assert_failed::<&[u8], &[u8; 4]>(move _16, move _17, move _19, move _21) -> bb21;
_15 = core::panicking::assert_failed::<&[u8], &[u8; 4]>(move _16, move _17, move _19, move _21) -> bb23;
}
bb6: {
bb7: {
StorageDead(_21);
StorageDead(_19);
StorageDead(_17);
@ -142,23 +147,23 @@ fn main() -> () {
unreachable;
}
bb7: {
goto -> bb9;
}
bb8: {
_1 = const ();
goto -> bb9;
goto -> bb10;
}
bb9: {
StorageDead(_10);
StorageDead(_9);
StorageDead(_8);
_1 = const ();
goto -> bb10;
}
bb10: {
StorageDead(_10);
StorageDead(_9);
StorageDead(_8);
goto -> bb11;
}
bb11: {
StorageDead(_7);
StorageDead(_6);
StorageDead(_4);
@ -168,10 +173,10 @@ fn main() -> () {
StorageLive(_23);
StorageLive(_24);
StorageLive(_25);
_25 = function_with_bytes::<&*b"AAAA">() -> [return: bb11, unwind: bb21];
_25 = function_with_bytes::<&*b"AAAA">() -> [return: bb12, unwind: bb23];
}
bb11: {
bb12: {
_24 = &_25;
StorageLive(_26);
StorageLive(_27);
@ -190,24 +195,29 @@ fn main() -> () {
_31 = &(*_28);
StorageLive(_32);
_32 = &(*_29);
_30 = <&[u8] as PartialEq<&[u8; 4]>>::eq(move _31, move _32) -> [return: bb12, unwind: bb21];
}
bb12: {
switchInt(move _30) -> [0: bb14, otherwise: bb13];
_30 = <&[u8] as PartialEq<&[u8; 4]>>::eq(move _31, move _32) -> [return: bb14, unwind: bb23];
}
bb13: {
StorageDead(_32);
StorageDead(_31);
goto -> bb18;
FakeRead(ForMatchedPlace(None), _23);
unreachable;
}
bb14: {
goto -> bb15;
switchInt(move _30) -> [0: bb16, otherwise: bb15];
}
bb15: {
StorageDead(_32);
StorageDead(_31);
goto -> bb20;
}
bb16: {
goto -> bb17;
}
bb17: {
StorageDead(_32);
StorageDead(_31);
StorageLive(_34);
@ -226,10 +236,10 @@ fn main() -> () {
_39 = &(*_40);
StorageLive(_41);
_41 = Option::<Arguments<'_>>::None;
_35 = core::panicking::assert_failed::<&[u8], &[u8; 4]>(move _36, move _37, move _39, move _41) -> bb21;
_35 = core::panicking::assert_failed::<&[u8], &[u8; 4]>(move _36, move _37, move _39, move _41) -> bb23;
}
bb16: {
bb18: {
StorageDead(_41);
StorageDead(_39);
StorageDead(_37);
@ -241,23 +251,23 @@ fn main() -> () {
unreachable;
}
bb17: {
goto -> bb19;
}
bb18: {
_22 = const ();
goto -> bb19;
}
bb19: {
StorageDead(_30);
StorageDead(_29);
StorageDead(_28);
goto -> bb20;
goto -> bb21;
}
bb20: {
_22 = const ();
goto -> bb21;
}
bb21: {
StorageDead(_30);
StorageDead(_29);
StorageDead(_28);
goto -> bb22;
}
bb22: {
StorageDead(_27);
StorageDead(_25);
StorageDead(_23);
@ -266,7 +276,7 @@ fn main() -> () {
return;
}
bb21 (cleanup): {
bb23 (cleanup): {
resume;
}
}

View File

@ -67,7 +67,7 @@ fn main() -> () {
StorageLive(_2);
StorageLive(_3);
StorageLive(_4);
_4 = function_with_bytes::<&*b"AAAA">() -> [return: bb1, unwind: bb21];
_4 = function_with_bytes::<&*b"AAAA">() -> [return: bb1, unwind: bb23];
}
bb1: {
@ -91,24 +91,29 @@ fn main() -> () {
_11 = &(*_8);
StorageLive(_12);
_12 = &(*_9);
_10 = <&[u8] as PartialEq<&[u8; 4]>>::eq(move _11, move _12) -> [return: bb2, unwind: bb21];
_10 = <&[u8] as PartialEq<&[u8; 4]>>::eq(move _11, move _12) -> [return: bb3, unwind: bb23];
}
bb2: {
switchInt(move _10) -> [0: bb4, otherwise: bb3];
FakeRead(ForMatchedPlace(None), _2);
unreachable;
}
bb3: {
StorageDead(_12);
StorageDead(_11);
goto -> bb8;
switchInt(move _10) -> [0: bb5, otherwise: bb4];
}
bb4: {
goto -> bb5;
StorageDead(_12);
StorageDead(_11);
goto -> bb9;
}
bb5: {
goto -> bb6;
}
bb6: {
StorageDead(_12);
StorageDead(_11);
StorageLive(_14);
@ -127,10 +132,10 @@ fn main() -> () {
_19 = &(*_20);
StorageLive(_21);
_21 = Option::<Arguments<'_>>::None;
_15 = core::panicking::assert_failed::<&[u8], &[u8; 4]>(move _16, move _17, move _19, move _21) -> bb21;
_15 = core::panicking::assert_failed::<&[u8], &[u8; 4]>(move _16, move _17, move _19, move _21) -> bb23;
}
bb6: {
bb7: {
StorageDead(_21);
StorageDead(_19);
StorageDead(_17);
@ -142,23 +147,23 @@ fn main() -> () {
unreachable;
}
bb7: {
goto -> bb9;
}
bb8: {
_1 = const ();
goto -> bb9;
goto -> bb10;
}
bb9: {
StorageDead(_10);
StorageDead(_9);
StorageDead(_8);
_1 = const ();
goto -> bb10;
}
bb10: {
StorageDead(_10);
StorageDead(_9);
StorageDead(_8);
goto -> bb11;
}
bb11: {
StorageDead(_7);
StorageDead(_6);
StorageDead(_4);
@ -168,10 +173,10 @@ fn main() -> () {
StorageLive(_23);
StorageLive(_24);
StorageLive(_25);
_25 = function_with_bytes::<&*b"AAAA">() -> [return: bb11, unwind: bb21];
_25 = function_with_bytes::<&*b"AAAA">() -> [return: bb12, unwind: bb23];
}
bb11: {
bb12: {
_24 = &_25;
StorageLive(_26);
StorageLive(_27);
@ -190,24 +195,29 @@ fn main() -> () {
_31 = &(*_28);
StorageLive(_32);
_32 = &(*_29);
_30 = <&[u8] as PartialEq<&[u8; 4]>>::eq(move _31, move _32) -> [return: bb12, unwind: bb21];
}
bb12: {
switchInt(move _30) -> [0: bb14, otherwise: bb13];
_30 = <&[u8] as PartialEq<&[u8; 4]>>::eq(move _31, move _32) -> [return: bb14, unwind: bb23];
}
bb13: {
StorageDead(_32);
StorageDead(_31);
goto -> bb18;
FakeRead(ForMatchedPlace(None), _23);
unreachable;
}
bb14: {
goto -> bb15;
switchInt(move _30) -> [0: bb16, otherwise: bb15];
}
bb15: {
StorageDead(_32);
StorageDead(_31);
goto -> bb20;
}
bb16: {
goto -> bb17;
}
bb17: {
StorageDead(_32);
StorageDead(_31);
StorageLive(_34);
@ -226,10 +236,10 @@ fn main() -> () {
_39 = &(*_40);
StorageLive(_41);
_41 = Option::<Arguments<'_>>::None;
_35 = core::panicking::assert_failed::<&[u8], &[u8; 4]>(move _36, move _37, move _39, move _41) -> bb21;
_35 = core::panicking::assert_failed::<&[u8], &[u8; 4]>(move _36, move _37, move _39, move _41) -> bb23;
}
bb16: {
bb18: {
StorageDead(_41);
StorageDead(_39);
StorageDead(_37);
@ -241,23 +251,23 @@ fn main() -> () {
unreachable;
}
bb17: {
goto -> bb19;
}
bb18: {
_22 = const ();
goto -> bb19;
}
bb19: {
StorageDead(_30);
StorageDead(_29);
StorageDead(_28);
goto -> bb20;
goto -> bb21;
}
bb20: {
_22 = const ();
goto -> bb21;
}
bb21: {
StorageDead(_30);
StorageDead(_29);
StorageDead(_28);
goto -> bb22;
}
bb22: {
StorageDead(_27);
StorageDead(_25);
StorageDead(_23);
@ -266,7 +276,7 @@ fn main() -> () {
return;
}
bb21 (cleanup): {
bb23 (cleanup): {
resume;
}
}

View File

@ -24,20 +24,20 @@
bb1: {
_4 = discriminant(_1);
switchInt(move _4) -> [0: bb4, 1: bb5, 2: bb6, 3: bb2, otherwise: bb3];
switchInt(move _4) -> [0: bb4, 1: bb5, 2: bb6, 3: bb3, otherwise: bb2];
}
bb2: {
unreachable;
}
bb3: {
_0 = const ();
StorageDead(_2);
StorageDead(_1);
return;
}
bb3: {
unreachable;
}
bb4: {
StorageLive(_5);
_5 = DFA::B;

View File

@ -24,20 +24,20 @@
bb1: {
_4 = discriminant(_1);
switchInt(move _4) -> [0: bb4, 1: bb5, 2: bb6, 3: bb2, otherwise: bb3];
switchInt(move _4) -> [0: bb4, 1: bb5, 2: bb6, 3: bb3, otherwise: bb2];
}
bb2: {
unreachable;
}
bb3: {
_0 = const ();
StorageDead(_2);
StorageDead(_1);
return;
}
bb3: {
unreachable;
}
bb4: {
StorageLive(_5);
_5 = DFA::B;

View File

@ -56,10 +56,14 @@
StorageLive(_11);
StorageLive(_12);
_10 = discriminant(_4);
switchInt(move _10) -> [0: bb7, 1: bb6, otherwise: bb2];
switchInt(move _10) -> [0: bb7, 1: bb6, otherwise: bb1];
}
bb1: {
unreachable;
}
bb2: {
StorageLive(_9);
_9 = ((_3 as Continue).0: i32);
_2 = _9;
@ -70,10 +74,6 @@
goto -> bb4;
}
bb2: {
unreachable;
}
bb3: {
StorageLive(_6);
_6 = ((_3 as Break).0: std::result::Result<std::convert::Infallible, i32>);
@ -103,8 +103,8 @@
StorageDead(_10);
StorageDead(_4);
_5 = discriminant(_3);
- switchInt(move _5) -> [0: bb1, 1: bb3, otherwise: bb2];
+ goto -> bb1;
- switchInt(move _5) -> [0: bb2, 1: bb3, otherwise: bb1];
+ goto -> bb2;
}
bb6: {

View File

@ -56,10 +56,14 @@
StorageLive(_11);
StorageLive(_12);
_10 = discriminant(_4);
switchInt(move _10) -> [0: bb7, 1: bb6, otherwise: bb2];
switchInt(move _10) -> [0: bb7, 1: bb6, otherwise: bb1];
}
bb1: {
unreachable;
}
bb2: {
StorageLive(_9);
_9 = ((_3 as Continue).0: i32);
_2 = _9;
@ -70,10 +74,6 @@
goto -> bb4;
}
bb2: {
unreachable;
}
bb3: {
StorageLive(_6);
_6 = ((_3 as Break).0: std::result::Result<std::convert::Infallible, i32>);
@ -103,8 +103,8 @@
StorageDead(_10);
StorageDead(_4);
_5 = discriminant(_3);
- switchInt(move _5) -> [0: bb1, 1: bb3, otherwise: bb2];
+ goto -> bb1;
- switchInt(move _5) -> [0: bb2, 1: bb3, otherwise: bb1];
+ goto -> bb2;
}
bb6: {

View File

@ -12,12 +12,12 @@ use std::ops::ControlFlow;
fn too_complex(x: Result<i32, usize>) -> Option<i32> {
// CHECK-LABEL: fn too_complex(
// CHECK: bb0: {
// CHECK: switchInt(move {{_.*}}) -> [0: bb3, 1: bb1, otherwise: bb2];
// CHECK: switchInt(move {{_.*}}) -> [0: bb3, 1: bb2, otherwise: bb1];
// CHECK: bb1: {
// CHECK: unreachable;
// CHECK: bb2: {
// CHECK: [[controlflow:_.*]] = ControlFlow::<usize, i32>::Break(
// CHECK: goto -> bb8;
// CHECK: bb2: {
// CHECK: unreachable;
// CHECK: bb3: {
// CHECK: [[controlflow]] = ControlFlow::<usize, i32>::Continue(
// CHECK: goto -> bb4;
@ -50,13 +50,13 @@ fn identity(x: Result<i32, i32>) -> Result<i32, i32> {
// CHECK-LABEL: fn identity(
// CHECK: bb0: {
// CHECK: [[x:_.*]] = _1;
// CHECK: switchInt(move {{_.*}}) -> [0: bb7, 1: bb6, otherwise: bb2];
// CHECK: switchInt(move {{_.*}}) -> [0: bb7, 1: bb6, otherwise: bb1];
// CHECK: bb1: {
// CHECK: unreachable;
// CHECK: bb2: {
// CHECK: {{_.*}} = (([[controlflow:_.*]] as Continue).0: i32);
// CHECK: _0 = Result::<i32, i32>::Ok(
// CHECK: goto -> bb4;
// CHECK: bb2: {
// CHECK: unreachable;
// CHECK: bb3: {
// CHECK: {{_.*}} = (([[controlflow]] as Break).0: std::result::Result<std::convert::Infallible, i32>);
// CHECK: _0 = Result::<i32, i32>::Err(
@ -64,7 +64,7 @@ fn identity(x: Result<i32, i32>) -> Result<i32, i32> {
// CHECK: bb4: {
// CHECK: return;
// CHECK: bb5: {
// CHECK: goto -> bb1;
// CHECK: goto -> bb2;
// CHECK: bb6: {
// CHECK: {{_.*}} = move (([[x]] as Err).0: i32);
// CHECK: [[controlflow]] = ControlFlow::<Result<Infallible, i32>, i32>::Break(
@ -93,11 +93,11 @@ fn dfa() {
// CHECK: {{_.*}} = DFA::A;
// CHECK: goto -> bb1;
// CHECK: bb1: {
// CHECK: switchInt({{.*}}) -> [0: bb4, 1: bb5, 2: bb6, 3: bb2, otherwise: bb3];
// CHECK: switchInt({{.*}}) -> [0: bb4, 1: bb5, 2: bb6, 3: bb3, otherwise: bb2];
// CHECK: bb2: {
// CHECK: return;
// CHECK: bb3: {
// CHECK: unreachable;
// CHECK: bb3: {
// CHECK: return;
// CHECK: bb4: {
// CHECK: {{_.*}} = DFA::B;
// CHECK: goto -> bb1;

View File

@ -30,10 +30,14 @@
bb0: {
StorageLive(_2);
_3 = discriminant(_1);
switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2];
switchInt(move _3) -> [0: bb3, 1: bb2, otherwise: bb1];
}
bb1: {
unreachable;
}
bb2: {
StorageLive(_6);
_6 = ((_1 as Err).0: usize);
StorageLive(_7);
@ -45,10 +49,6 @@
+ goto -> bb8;
}
bb2: {
unreachable;
}
bb3: {
StorageLive(_4);
_4 = ((_1 as Ok).0: i32);
@ -62,7 +62,7 @@
bb4: {
_8 = discriminant(_2);
- switchInt(move _8) -> [0: bb6, 1: bb5, otherwise: bb2];
- switchInt(move _8) -> [0: bb6, 1: bb5, otherwise: bb1];
+ goto -> bb6;
}

View File

@ -30,10 +30,14 @@
bb0: {
StorageLive(_2);
_3 = discriminant(_1);
switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2];
switchInt(move _3) -> [0: bb3, 1: bb2, otherwise: bb1];
}
bb1: {
unreachable;
}
bb2: {
StorageLive(_6);
_6 = ((_1 as Err).0: usize);
StorageLive(_7);
@ -45,10 +49,6 @@
+ goto -> bb8;
}
bb2: {
unreachable;
}
bb3: {
StorageLive(_4);
_4 = ((_1 as Ok).0: i32);
@ -62,7 +62,7 @@
bb4: {
_8 = discriminant(_2);
- switchInt(move _8) -> [0: bb6, 1: bb5, otherwise: bb2];
- switchInt(move _8) -> [0: bb6, 1: bb5, otherwise: bb1];
+ goto -> bb6;
}

View File

@ -32,33 +32,25 @@
bb0: {
PlaceMention(_2);
- switchInt((_2.0: bool)) -> [0: bb1, otherwise: bb2];
- switchInt((_2.0: bool)) -> [0: bb6, otherwise: bb1];
+ switchInt((_2.0: bool)) -> [0: bb5, otherwise: bb1];
}
bb1: {
- falseEdge -> [real: bb8, imaginary: bb3];
- switchInt((_2.1: bool)) -> [0: bb5, otherwise: bb2];
+ switchInt((_2.1: bool)) -> [0: bb10, otherwise: bb2];
}
bb2: {
- switchInt((_2.1: bool)) -> [0: bb3, otherwise: bb4];
- switchInt((_2.0: bool)) -> [0: bb4, otherwise: bb3];
+ switchInt((_2.0: bool)) -> [0: bb3, otherwise: bb17];
}
bb3: {
- falseEdge -> [real: bb13, imaginary: bb5];
- falseEdge -> [real: bb20, imaginary: bb4];
- }
-
- bb4: {
- switchInt((_2.0: bool)) -> [0: bb6, otherwise: bb5];
- }
-
- bb5: {
- falseEdge -> [real: bb20, imaginary: bb6];
- }
-
- bb6: {
StorageLive(_15);
_15 = (_2.1: bool);
StorageLive(_16);
@ -67,6 +59,14 @@
+ goto -> bb16;
}
- bb5: {
- falseEdge -> [real: bb13, imaginary: bb3];
- }
-
- bb6: {
- falseEdge -> [real: bb8, imaginary: bb5];
- }
-
- bb7: {
+ bb4: {
_0 = const 1_i32;
@ -127,7 +127,7 @@
StorageDead(_9);
StorageDead(_8);
StorageDead(_6);
- falseEdge -> [real: bb2, imaginary: bb3];
- falseEdge -> [real: bb1, imaginary: bb5];
+ goto -> bb1;
}
@ -184,7 +184,7 @@
StorageDead(_12);
StorageDead(_8);
StorageDead(_6);
- falseEdge -> [real: bb4, imaginary: bb5];
- falseEdge -> [real: bb2, imaginary: bb3];
+ goto -> bb2;
}

View File

@ -32,33 +32,25 @@
bb0: {
PlaceMention(_2);
- switchInt((_2.0: bool)) -> [0: bb1, otherwise: bb2];
- switchInt((_2.0: bool)) -> [0: bb6, otherwise: bb1];
+ switchInt((_2.0: bool)) -> [0: bb5, otherwise: bb1];
}
bb1: {
- falseEdge -> [real: bb8, imaginary: bb3];
- switchInt((_2.1: bool)) -> [0: bb5, otherwise: bb2];
+ switchInt((_2.1: bool)) -> [0: bb10, otherwise: bb2];
}
bb2: {
- switchInt((_2.1: bool)) -> [0: bb3, otherwise: bb4];
- switchInt((_2.0: bool)) -> [0: bb4, otherwise: bb3];
+ switchInt((_2.0: bool)) -> [0: bb3, otherwise: bb17];
}
bb3: {
- falseEdge -> [real: bb13, imaginary: bb5];
- falseEdge -> [real: bb20, imaginary: bb4];
- }
-
- bb4: {
- switchInt((_2.0: bool)) -> [0: bb6, otherwise: bb5];
- }
-
- bb5: {
- falseEdge -> [real: bb20, imaginary: bb6];
- }
-
- bb6: {
StorageLive(_15);
_15 = (_2.1: bool);
StorageLive(_16);
@ -67,6 +59,14 @@
+ goto -> bb16;
}
- bb5: {
- falseEdge -> [real: bb13, imaginary: bb3];
- }
-
- bb6: {
- falseEdge -> [real: bb8, imaginary: bb5];
- }
-
- bb7: {
+ bb4: {
_0 = const 1_i32;
@ -127,7 +127,7 @@
StorageDead(_9);
StorageDead(_8);
StorageDead(_6);
- falseEdge -> [real: bb2, imaginary: bb3];
- falseEdge -> [real: bb1, imaginary: bb5];
+ goto -> bb1;
}
@ -184,7 +184,7 @@
StorageDead(_12);
StorageDead(_8);
StorageDead(_6);
- falseEdge -> [real: bb4, imaginary: bb5];
- falseEdge -> [real: bb2, imaginary: bb3];
+ goto -> bb2;
}

View File

@ -32,12 +32,12 @@ fn main() -> () {
}
bb1: {
falseEdge -> [real: bb9, imaginary: bb4];
_3 = const 3_i32;
goto -> bb14;
}
bb2: {
_3 = const 3_i32;
goto -> bb14;
falseEdge -> [real: bb9, imaginary: bb4];
}
bb3: {
@ -50,11 +50,11 @@ fn main() -> () {
}
bb5: {
switchInt(_1) -> [4294967295: bb6, otherwise: bb2];
switchInt(_1) -> [4294967295: bb6, otherwise: bb1];
}
bb6: {
falseEdge -> [real: bb13, imaginary: bb2];
falseEdge -> [real: bb13, imaginary: bb1];
}
bb7: {
@ -64,7 +64,7 @@ fn main() -> () {
bb8: {
_7 = Lt(_1, const 10_i32);
switchInt(move _7) -> [0: bb3, otherwise: bb1];
switchInt(move _7) -> [0: bb3, otherwise: bb2];
}
bb9: {
@ -83,7 +83,7 @@ fn main() -> () {
bb11: {
StorageDead(_9);
falseEdge -> [real: bb2, imaginary: bb4];
falseEdge -> [real: bb1, imaginary: bb4];
}
bb12: {

View File

@ -8,16 +8,16 @@
bb0: {
_2 = discriminant(_1);
switchInt(move _2) -> [0: bb3, 1: bb1, otherwise: bb2];
switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1];
}
bb1: {
_0 = const 1_u8;
goto -> bb4;
unreachable;
}
bb2: {
unreachable;
_0 = const 1_u8;
goto -> bb4;
}
bb3: {

View File

@ -8,16 +8,16 @@
bb0: {
_2 = discriminant(_1);
switchInt(move _2) -> [0: bb3, 1: bb1, otherwise: bb2];
switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1];
}
bb1: {
_0 = const 1_i8;
goto -> bb4;
unreachable;
}
bb2: {
unreachable;
_0 = const 1_i8;
goto -> bb4;
}
bb3: {

View File

@ -15,16 +15,16 @@ fn unwrap(_1: Option<T>) -> T {
bb0: {
_2 = discriminant(_1);
switchInt(move _2) -> [0: bb1, 1: bb3, otherwise: bb2];
switchInt(move _2) -> [0: bb2, 1: bb3, otherwise: bb1];
}
bb1: {
StorageLive(_4);
_4 = begin_panic::<&str>(const "explicit panic") -> unwind unreachable;
unreachable;
}
bb2: {
unreachable;
StorageLive(_4);
_4 = begin_panic::<&str>(const "explicit panic") -> unwind unreachable;
}
bb3: {

View File

@ -15,16 +15,16 @@ fn unwrap(_1: Option<T>) -> T {
bb0: {
_2 = discriminant(_1);
switchInt(move _2) -> [0: bb1, 1: bb3, otherwise: bb2];
switchInt(move _2) -> [0: bb2, 1: bb3, otherwise: bb1];
}
bb1: {
StorageLive(_4);
_4 = begin_panic::<&str>(const "explicit panic") -> bb4;
unreachable;
}
bb2: {
unreachable;
StorageLive(_4);
_4 = begin_panic::<&str>(const "explicit panic") -> bb4;
}
bb3: {

View File

@ -67,10 +67,14 @@
StorageLive(_7);
_7 = Option::<i32>::Some(const 0_i32);
_8 = discriminant(_7);
switchInt(move _8) -> [0: bb3, 1: bb1, otherwise: bb2];
switchInt(move _8) -> [0: bb3, 1: bb2, otherwise: bb1];
}
bb1: {
unreachable;
}
bb2: {
StorageLive(_9);
_27 = const _;
_9 = &(((*_27) as Some).0: i32);
@ -79,10 +83,6 @@
goto -> bb4;
}
bb2: {
unreachable;
}
bb3: {
- _6 = const ();
goto -> bb4;

View File

@ -55,10 +55,14 @@
bb3: {
- StorageDead(_8);
_10 = discriminant(_7);
switchInt(move _10) -> [0: bb6, 1: bb4, otherwise: bb5];
switchInt(move _10) -> [0: bb6, 1: bb5, otherwise: bb4];
}
bb4: {
unreachable;
}
bb5: {
- StorageLive(_12);
_12 = ((_7 as Some).0: i32);
- StorageLive(_13);
@ -74,10 +78,6 @@
goto -> bb2;
}
bb5: {
unreachable;
}
bb6: {
_0 = const ();
- StorageDead(_9);

View File

@ -55,10 +55,14 @@
bb3: {
- StorageDead(_8);
_10 = discriminant(_7);
switchInt(move _10) -> [0: bb6, 1: bb4, otherwise: bb5];
switchInt(move _10) -> [0: bb6, 1: bb5, otherwise: bb4];
}
bb4: {
unreachable;
}
bb5: {
- StorageLive(_12);
_12 = ((_7 as Some).0: i32);
- StorageLive(_13);
@ -74,10 +78,6 @@
goto -> bb2;
}
bb5: {
unreachable;
}
bb6: {
_0 = const ();
- StorageDead(_9);

View File

@ -18,10 +18,14 @@
- _5 = const false;
- _5 = const true;
_2 = discriminant(_1);
switchInt(move _2) -> [0: bb3, 1: bb1, otherwise: bb2];
switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1];
}
bb1: {
unreachable;
}
bb2: {
StorageLive(_3);
_3 = move ((_1 as Some).0: std::boxed::Box<()>);
StorageLive(_4);
@ -32,10 +36,6 @@
goto -> bb4;
}
bb2: {
unreachable;
}
bb3: {
_0 = Option::<Box<()>>::None;
goto -> bb4;

View File

@ -30,11 +30,15 @@
StorageLive(_4);
_4 = &(_1.1: Test3);
_5 = discriminant((*_4));
- switchInt(move _5) -> [0: bb3, 1: bb4, 2: bb5, 3: bb1, otherwise: bb2];
+ switchInt(move _5) -> [0: bb12, 1: bb12, 2: bb5, 3: bb1, otherwise: bb12];
- switchInt(move _5) -> [0: bb3, 1: bb4, 2: bb5, 3: bb2, otherwise: bb1];
+ switchInt(move _5) -> [0: bb12, 1: bb12, 2: bb5, 3: bb2, otherwise: bb12];
}
bb1: {
unreachable;
}
bb2: {
StorageLive(_8);
_8 = const "D";
_3 = &(*_8);
@ -42,10 +46,6 @@
goto -> bb6;
}
bb2: {
unreachable;
}
bb3: {
_3 = const "A(Empty)";
goto -> bb6;
@ -72,7 +72,7 @@
StorageDead(_3);
StorageLive(_9);
_10 = discriminant((_1.1: Test3));
- switchInt(move _10) -> [0: bb8, 1: bb9, 2: bb10, 3: bb7, otherwise: bb2];
- switchInt(move _10) -> [0: bb8, 1: bb9, 2: bb10, 3: bb7, otherwise: bb1];
+ switchInt(move _10) -> [0: bb12, 1: bb12, 2: bb10, 3: bb7, otherwise: bb12];
}

View File

@ -13,11 +13,15 @@
StorageLive(_2);
_2 = Test2::D;
_3 = discriminant(_2);
- switchInt(move _3) -> [4: bb3, 5: bb1, otherwise: bb2];
+ switchInt(move _3) -> [4: bb3, 5: bb1, otherwise: bb5];
- switchInt(move _3) -> [4: bb3, 5: bb2, otherwise: bb1];
+ switchInt(move _3) -> [4: bb3, 5: bb2, otherwise: bb5];
}
bb1: {
unreachable;
}
bb2: {
StorageLive(_4);
_4 = const "E";
_1 = &(*_4);
@ -25,10 +29,6 @@
goto -> bb4;
}
bb2: {
unreachable;
}
bb3: {
_1 = const "D";
goto -> bb4;

View File

@ -32,7 +32,7 @@ struct Plop {
fn simple() {
// CHECK-LABEL: fn simple(
// CHECK: [[discr:_.*]] = discriminant(
// CHECK: switchInt(move [[discr]]) -> [0: [[unreachable:bb.*]], 1: [[unreachable]], 2: bb1, otherwise: [[unreachable]]];
// CHECK: switchInt(move [[discr]]) -> [0: [[unreachable:bb.*]], 1: [[unreachable]], 2: bb2, otherwise: [[unreachable]]];
// CHECK: [[unreachable]]: {
// CHECK-NEXT: unreachable;
match Test1::C {
@ -46,7 +46,7 @@ fn simple() {
fn custom_discriminant() {
// CHECK-LABEL: fn custom_discriminant(
// CHECK: [[discr:_.*]] = discriminant(
// CHECK: switchInt(move [[discr]]) -> [4: bb3, 5: bb1, otherwise: bb5];
// CHECK: switchInt(move [[discr]]) -> [4: bb3, 5: bb2, otherwise: bb5];
// CHECK: bb5: {
// CHECK-NEXT: unreachable;
match Test2::D {
@ -61,7 +61,7 @@ fn byref() {
let plop = Plop { xx: 51, test3: Test3::C };
// CHECK: [[ref_discr:_.*]] = discriminant((*
// CHECK: switchInt(move [[ref_discr]]) -> [0: [[unreachable:bb.*]], 1: [[unreachable]], 2: bb5, 3: bb1, otherwise: [[unreachable]]];
// CHECK: switchInt(move [[ref_discr]]) -> [0: [[unreachable:bb.*]], 1: [[unreachable]], 2: bb5, 3: bb2, otherwise: [[unreachable]]];
match &plop.test3 {
Test3::A(_) => "A(Empty)",
Test3::B(_) => "B(Empty)",

View File

@ -14,11 +14,15 @@
StorageLive(_2);
_2 = Test1::C;
_3 = discriminant(_2);
- switchInt(move _3) -> [0: bb3, 1: bb4, 2: bb1, otherwise: bb2];
+ switchInt(move _3) -> [0: bb6, 1: bb6, 2: bb1, otherwise: bb6];
- switchInt(move _3) -> [0: bb3, 1: bb4, 2: bb2, otherwise: bb1];
+ switchInt(move _3) -> [0: bb6, 1: bb6, 2: bb2, otherwise: bb6];
}
bb1: {
unreachable;
}
bb2: {
StorageLive(_5);
_5 = const "C";
_1 = &(*_5);
@ -26,10 +30,6 @@
goto -> bb5;
}
bb2: {
unreachable;
}
bb3: {
_1 = const "A(Empty)";
goto -> bb5;

View File

@ -19,20 +19,20 @@
bb1: {
_2 = discriminant(_1);
- switchInt(move _2) -> [0: bb4, 1: bb2, otherwise: bb3];
- switchInt(move _2) -> [0: bb4, 1: bb3, otherwise: bb2];
+ _5 = Eq(_2, const 0_isize);
+ assume(move _5);
+ goto -> bb4;
}
bb2: {
- StorageLive(_3);
- _3 = move ((_1 as Some).0: Empty);
- StorageLive(_4);
unreachable;
}
bb3: {
- StorageLive(_3);
- _3 = move ((_1 as Some).0: Empty);
- StorageLive(_4);
unreachable;
}

View File

@ -19,20 +19,20 @@
bb1: {
_2 = discriminant(_1);
- switchInt(move _2) -> [0: bb4, 1: bb2, otherwise: bb3];
- switchInt(move _2) -> [0: bb4, 1: bb3, otherwise: bb2];
+ _5 = Eq(_2, const 0_isize);
+ assume(move _5);
+ goto -> bb4;
}
bb2: {
- StorageLive(_3);
- _3 = move ((_1 as Some).0: Empty);
- StorageLive(_4);
unreachable;
}
bb3: {
- StorageLive(_3);
- _3 = move ((_1 as Some).0: Empty);
- StorageLive(_4);
unreachable;
}

View File

@ -0,0 +1,29 @@
// This test ensures that non-glob reexports don't get their attributes merge with
// the reexported item whereas glob reexports do with the `doc_auto_cfg` feature.
#![crate_name = "foo"]
#![feature(doc_auto_cfg)]
// @has 'foo/index.html'
// There are two items.
// @count - '//*[@class="item-table"]//div[@class="item-name"]' 2
// Only one of them should have an attribute.
// @count - '//*[@class="item-table"]//div[@class="item-name"]/*[@class="stab portability"]' 1
mod a {
#[cfg(not(feature = "a"))]
pub struct Test1;
}
mod b {
#[cfg(not(feature = "a"))]
pub struct Test2;
}
// @has 'foo/struct.Test1.html'
// @count - '//*[@id="main-content"]/*[@class="item-info"]' 1
// @has - '//*[@id="main-content"]/*[@class="item-info"]' 'Available on non-crate feature a only.'
pub use a::*;
// @has 'foo/struct.Test2.html'
// @count - '//*[@id="main-content"]/*[@class="item-info"]' 0
pub use b::Test2;

View File

@ -0,0 +1,23 @@
error[E0282]: type annotations needed
--> $DIR/recursive-coroutine-boxed.rs:10:23
|
LL | let mut gen = Box::pin(foo());
| ^^^^^^^^ cannot infer type of the type parameter `T` declared on the struct `Box`
...
LL | let mut r = gen.as_mut().resume(());
| ------ type must be known at this point
|
help: consider specifying the generic argument
|
LL | let mut gen = Box::<T>::pin(foo());
| +++++
error[E0282]: type annotations needed
--> $DIR/recursive-coroutine-boxed.rs:10:32
|
LL | let mut gen = Box::pin(foo());
| ^^^^^ cannot infer type for opaque type `impl Coroutine<Yield = (), Return = ()>`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0282`.

View File

@ -1,5 +1,5 @@
// check-pass
// revisions: current next
//[current] check-pass
//[next] compile-flags: -Znext-solver
#![feature(coroutines, coroutine_trait)]
@ -8,6 +8,8 @@ use std::ops::{Coroutine, CoroutineState};
fn foo() -> impl Coroutine<Yield = (), Return = ()> {
|| {
let mut gen = Box::pin(foo());
//[next]~^ ERROR type annotations needed
//[next]~| ERROR type annotations needed
let mut r = gen.as_mut().resume(());
while let CoroutineState::Yielded(v) = r {
yield v;

View File

@ -1,8 +1,8 @@
error[E0284]: type annotations needed: cannot satisfy `A <: B`
error[E0284]: type annotations needed: cannot satisfy `A == B`
--> $DIR/two_tait_defining_each_other2.rs:11:5
|
LL | x // B's hidden type is A (opaquely)
| ^ cannot satisfy `A <: B`
| ^ cannot satisfy `A == B`
error: aborting due to 1 previous error

View File

@ -10,7 +10,7 @@ trait Foo {}
fn muh(x: A) -> B {
x // B's hidden type is A (opaquely)
//[current]~^ ERROR opaque type's hidden type cannot be another opaque type
//[next]~^^ ERROR type annotations needed: cannot satisfy `A <: B`
//[next]~^^ ERROR type annotations needed: cannot satisfy `A == B`
}
struct Bar;

View File

@ -1,9 +1,10 @@
// compile-flags: -Znext-solver
// check-pass
// FIXME(-Znext-solver): This test is currently broken because the `deduce_closure_signature`
// is unable to look at nested obligations.
trait Foo {
fn test() -> impl Fn(u32) -> u32 {
|x| x.count_ones()
//~^ ERROR type annotations needed
}
}

View File

@ -0,0 +1,14 @@
error[E0282]: type annotations needed
--> $DIR/deduce-closure-signature-after-normalization.rs:6:10
|
LL | |x| x.count_ones()
| ^ - type must be known at this point
|
help: consider giving this closure parameter an explicit type
|
LL | |x: /* Type */| x.count_ones()
| ++++++++++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0282`.

View File

@ -1,16 +1,9 @@
error[E0283]: type annotations needed: cannot satisfy `Foo: Send`
error[E0284]: type annotations needed: cannot satisfy `Foo == _`
--> $DIR/dont-type_of-tait-in-defining-scope.rs:15:18
|
LL | needs_send::<Foo>();
| ^^^
|
= note: cannot satisfy `Foo: Send`
note: required by a bound in `needs_send`
--> $DIR/dont-type_of-tait-in-defining-scope.rs:12:18
|
LL | fn needs_send<T: Send>() {}
| ^^^^ required by this bound in `needs_send`
| ^^^ cannot satisfy `Foo == _`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0283`.
For more information about this error, try `rustc --explain E0284`.

View File

@ -1,16 +1,9 @@
error[E0283]: type annotations needed: cannot satisfy `Foo: Send`
error[E0284]: type annotations needed: cannot satisfy `Foo == _`
--> $DIR/dont-type_of-tait-in-defining-scope.rs:15:18
|
LL | needs_send::<Foo>();
| ^^^
|
= note: cannot satisfy `Foo: Send`
note: required by a bound in `needs_send`
--> $DIR/dont-type_of-tait-in-defining-scope.rs:12:18
|
LL | fn needs_send<T: Send>() {}
| ^^^^ required by this bound in `needs_send`
| ^^^ cannot satisfy `Foo == _`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0283`.
For more information about this error, try `rustc --explain E0284`.

View File

@ -13,7 +13,7 @@ fn needs_send<T: Send>() {}
fn test(_: Foo) {
needs_send::<Foo>();
//~^ ERROR type annotations needed: cannot satisfy `Foo: Send`
//~^ ERROR type annotations needed: cannot satisfy `Foo == _`
}
fn defines(_: Foo) {

View File

@ -1,10 +0,0 @@
// compile-flags: -Zeagerly-emit-delayed-bugs
trait Foo {}
fn main() {}
fn f() -> impl Foo {
//~^ ERROR the trait bound `i32: Foo` is not satisfied
1i32
}

Some files were not shown because too many files have changed in this diff Show More