diff options
Diffstat (limited to 'compiler/rustc_mir_transform/src')
7 files changed, 228 insertions, 274 deletions
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 241c3c79114..83e7ff99639 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -13,16 +13,15 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::mir::coverage::{ - CoverageKind, DecisionInfo, FunctionCoverageInfo, Mapping, MappingKind, SourceRegion, + CoverageKind, DecisionInfo, FunctionCoverageInfo, Mapping, MappingKind, }; use rustc_middle::mir::{ self, BasicBlock, BasicBlockData, SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, }; use rustc_middle::ty::TyCtxt; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; -use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, Pos, SourceFile, Span}; use tracing::{debug, debug_span, trace}; use crate::coverage::counters::{CoverageCounters, Site}; @@ -97,7 +96,7 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: let coverage_counters = CoverageCounters::make_bcb_counters(&basic_coverage_blocks, &bcbs_with_counter_mappings); - let mappings = create_mappings(tcx, &hir_info, &extracted_mappings, &coverage_counters); + let mappings = create_mappings(&extracted_mappings, &coverage_counters); if mappings.is_empty() { // No spans could be converted into valid mappings, so skip this function. debug!("no spans could be converted into valid mappings; skipping"); @@ -136,18 +135,12 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: /// /// Precondition: All BCBs corresponding to those spans have been given /// coverage counters. -fn create_mappings<'tcx>( - tcx: TyCtxt<'tcx>, - hir_info: &ExtractedHirInfo, +fn create_mappings( extracted_mappings: &ExtractedMappings, coverage_counters: &CoverageCounters, ) -> Vec<Mapping> { - let source_map = tcx.sess.source_map(); - let file = source_map.lookup_source_file(hir_info.body_span.lo()); - let term_for_bcb = |bcb| coverage_counters.term_for_bcb(bcb).expect("all BCBs with spans were given counters"); - let region_for_span = |span: Span| make_source_region(source_map, hir_info, &file, span); // Fully destructure the mappings struct to make sure we don't miss any kinds. let ExtractedMappings { @@ -160,22 +153,20 @@ fn create_mappings<'tcx>( } = extracted_mappings; let mut mappings = Vec::new(); - mappings.extend(code_mappings.iter().filter_map( + mappings.extend(code_mappings.iter().map( // Ordinary code mappings are the simplest kind. |&mappings::CodeMapping { span, bcb }| { - let source_region = region_for_span(span)?; let kind = MappingKind::Code(term_for_bcb(bcb)); - Some(Mapping { kind, source_region }) + Mapping { kind, span } }, )); - mappings.extend(branch_pairs.iter().filter_map( + mappings.extend(branch_pairs.iter().map( |&mappings::BranchPair { span, true_bcb, false_bcb }| { let true_term = term_for_bcb(true_bcb); let false_term = term_for_bcb(false_bcb); let kind = MappingKind::Branch { true_term, false_term }; - let source_region = region_for_span(span)?; - Some(Mapping { kind, source_region }) + Mapping { kind, span } }, )); @@ -183,7 +174,7 @@ fn create_mappings<'tcx>( |bcb| coverage_counters.term_for_bcb(bcb).expect("all BCBs with spans were given counters"); // MCDC branch mappings are appended with their decisions in case decisions were ignored. - mappings.extend(mcdc_degraded_branches.iter().filter_map( + mappings.extend(mcdc_degraded_branches.iter().map( |&mappings::MCDCBranch { span, true_bcb, @@ -192,10 +183,9 @@ fn create_mappings<'tcx>( true_index: _, false_index: _, }| { - let source_region = region_for_span(span)?; let true_term = term_for_bcb(true_bcb); let false_term = term_for_bcb(false_bcb); - Some(Mapping { kind: MappingKind::Branch { true_term, false_term }, source_region }) + Mapping { kind: MappingKind::Branch { true_term, false_term }, span } }, )); @@ -203,7 +193,7 @@ fn create_mappings<'tcx>( let num_conditions = branches.len() as u16; let conditions = branches .into_iter() - .filter_map( + .map( |&mappings::MCDCBranch { span, true_bcb, @@ -212,32 +202,28 @@ fn create_mappings<'tcx>( true_index: _, false_index: _, }| { - let source_region = region_for_span(span)?; let true_term = term_for_bcb(true_bcb); let false_term = term_for_bcb(false_bcb); - Some(Mapping { + Mapping { kind: MappingKind::MCDCBranch { true_term, false_term, mcdc_params: condition_info, }, - source_region, - }) + span, + } }, ) .collect::<Vec<_>>(); - if conditions.len() == num_conditions as usize - && let Some(source_region) = region_for_span(decision.span) - { + if conditions.len() == num_conditions as usize { // LLVM requires end index for counter mapping regions. let kind = MappingKind::MCDCDecision(DecisionInfo { bitmap_idx: (decision.bitmap_idx + decision.num_test_vectors) as u32, num_conditions, }); - mappings.extend( - std::iter::once(Mapping { kind, source_region }).chain(conditions.into_iter()), - ); + let span = decision.span; + mappings.extend(std::iter::once(Mapping { kind, span }).chain(conditions.into_iter())); } else { mappings.extend(conditions.into_iter().map(|mapping| { let MappingKind::MCDCBranch { true_term, false_term, mcdc_params: _ } = @@ -245,10 +231,7 @@ fn create_mappings<'tcx>( else { unreachable!("all mappings here are MCDCBranch as shown above"); }; - Mapping { - kind: MappingKind::Branch { true_term, false_term }, - source_region: mapping.source_region, - } + Mapping { kind: MappingKind::Branch { true_term, false_term }, span: mapping.span } })) } } @@ -391,121 +374,6 @@ fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb data.statements.insert(0, statement); } -fn ensure_non_empty_span( - source_map: &SourceMap, - hir_info: &ExtractedHirInfo, - span: Span, -) -> Option<Span> { - if !span.is_empty() { - return Some(span); - } - - let lo = span.lo(); - let hi = span.hi(); - - // The span is empty, so try to expand it to cover an adjacent '{' or '}', - // but only within the bounds of the body span. - let try_next = hi < hir_info.body_span.hi(); - let try_prev = hir_info.body_span.lo() < lo; - if !(try_next || try_prev) { - return None; - } - - source_map - .span_to_source(span, |src, start, end| try { - // We're only checking for specific ASCII characters, so we don't - // have to worry about multi-byte code points. - if try_next && src.as_bytes()[end] == b'{' { - Some(span.with_hi(hi + BytePos(1))) - } else if try_prev && src.as_bytes()[start - 1] == b'}' { - Some(span.with_lo(lo - BytePos(1))) - } else { - None - } - }) - .ok()? -} - -/// Converts the span into its start line and column, and end line and column. -/// -/// Line numbers and column numbers are 1-based. Unlike most column numbers emitted by -/// the compiler, these column numbers are denoted in **bytes**, because that's what -/// LLVM's `llvm-cov` tool expects to see in coverage maps. -/// -/// Returns `None` if the conversion failed for some reason. This shouldn't happen, -/// but it's hard to rule out entirely (especially in the presence of complex macros -/// or other expansions), and if it does happen then skipping a span or function is -/// better than an ICE or `llvm-cov` failure that the user might have no way to avoid. -fn make_source_region( - source_map: &SourceMap, - hir_info: &ExtractedHirInfo, - file: &SourceFile, - span: Span, -) -> Option<SourceRegion> { - let span = ensure_non_empty_span(source_map, hir_info, span)?; - - let lo = span.lo(); - let hi = span.hi(); - - // Column numbers need to be in bytes, so we can't use the more convenient - // `SourceMap` methods for looking up file coordinates. - let line_and_byte_column = |pos: BytePos| -> Option<(usize, usize)> { - let rpos = file.relative_position(pos); - let line_index = file.lookup_line(rpos)?; - let line_start = file.lines()[line_index]; - // Line numbers and column numbers are 1-based, so add 1 to each. - Some((line_index + 1, (rpos - line_start).to_usize() + 1)) - }; - - let (mut start_line, start_col) = line_and_byte_column(lo)?; - let (mut end_line, end_col) = line_and_byte_column(hi)?; - - // Apply an offset so that code in doctests has correct line numbers. - // FIXME(#79417): Currently we have no way to offset doctest _columns_. - start_line = source_map.doctest_offset_line(&file.name, start_line); - end_line = source_map.doctest_offset_line(&file.name, end_line); - - check_source_region(SourceRegion { - start_line: start_line as u32, - start_col: start_col as u32, - end_line: end_line as u32, - end_col: end_col as u32, - }) -} - -/// If `llvm-cov` sees a source region that is improperly ordered (end < start), -/// it will immediately exit with a fatal error. To prevent that from happening, -/// discard regions that are improperly ordered, or might be interpreted in a -/// way that makes them improperly ordered. -fn check_source_region(source_region: SourceRegion) -> Option<SourceRegion> { - let SourceRegion { start_line, start_col, end_line, end_col } = source_region; - - // Line/column coordinates are supposed to be 1-based. If we ever emit - // coordinates of 0, `llvm-cov` might misinterpret them. - let all_nonzero = [start_line, start_col, end_line, end_col].into_iter().all(|x| x != 0); - // Coverage mappings use the high bit of `end_col` to indicate that a - // region is actually a "gap" region, so make sure it's unset. - let end_col_has_high_bit_unset = (end_col & (1 << 31)) == 0; - // If a region is improperly ordered (end < start), `llvm-cov` will exit - // with a fatal error, which is inconvenient for users and hard to debug. - let is_ordered = (start_line, start_col) <= (end_line, end_col); - - if all_nonzero && end_col_has_high_bit_unset && is_ordered { - Some(source_region) - } else { - debug!( - ?source_region, - ?all_nonzero, - ?end_col_has_high_bit_unset, - ?is_ordered, - "Skipping source region that would be misinterpreted or rejected by LLVM" - ); - // If this happens in a debug build, ICE to make it easier to notice. - debug_assert!(false, "Improper source region: {source_region:?}"); - None - } -} - /// Function information extracted from HIR by the coverage instrumentor. #[derive(Debug)] struct ExtractedHirInfo { diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index 17c8348140a..91e1395e764 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -129,18 +129,29 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { let mut patch = MirPatch::new(body); - // create temp to store second discriminant in, `_s` in example above - let second_discriminant_temp = - patch.new_temp(opt_data.child_ty, opt_data.child_source.span); + let (second_discriminant_temp, second_operand) = if opt_data.need_hoist_discriminant { + // create temp to store second discriminant in, `_s` in example above + let second_discriminant_temp = + patch.new_temp(opt_data.child_ty, opt_data.child_source.span); - patch.add_statement(parent_end, StatementKind::StorageLive(second_discriminant_temp)); + patch.add_statement( + parent_end, + StatementKind::StorageLive(second_discriminant_temp), + ); - // create assignment of discriminant - patch.add_assign( - parent_end, - Place::from(second_discriminant_temp), - Rvalue::Discriminant(opt_data.child_place), - ); + // create assignment of discriminant + patch.add_assign( + parent_end, + Place::from(second_discriminant_temp), + Rvalue::Discriminant(opt_data.child_place), + ); + ( + Some(second_discriminant_temp), + Operand::Move(Place::from(second_discriminant_temp)), + ) + } else { + (None, Operand::Copy(opt_data.child_place)) + }; // create temp to store inequality comparison between the two discriminants, `_t` in // example above @@ -149,11 +160,9 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { let comp_temp = patch.new_temp(comp_res_type, opt_data.child_source.span); patch.add_statement(parent_end, StatementKind::StorageLive(comp_temp)); - // create inequality comparison between the two discriminants - let comp_rvalue = Rvalue::BinaryOp( - nequal, - Box::new((parent_op.clone(), Operand::Move(Place::from(second_discriminant_temp)))), - ); + // create inequality comparison + let comp_rvalue = + Rvalue::BinaryOp(nequal, Box::new((parent_op.clone(), second_operand))); patch.add_statement( parent_end, StatementKind::Assign(Box::new((Place::from(comp_temp), comp_rvalue))), @@ -170,14 +179,17 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { let eq_targets = SwitchTargets::new(eq_new_targets, parent_targets.otherwise()); // Create `bbEq` in example above - let eq_switch = BasicBlockData::new(Some(Terminator { - source_info: bbs[parent].terminator().source_info, - kind: TerminatorKind::SwitchInt { - // switch on the first discriminant, so we can mark the second one as dead - discr: parent_op, - targets: eq_targets, - }, - })); + let eq_switch = BasicBlockData::new( + Some(Terminator { + source_info: bbs[parent].terminator().source_info, + kind: TerminatorKind::SwitchInt { + // switch on the first discriminant, so we can mark the second one as dead + discr: parent_op, + targets: eq_targets, + }, + }), + bbs[parent].is_cleanup, + ); let eq_bb = patch.new_block(eq_switch); @@ -189,8 +201,13 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { TerminatorKind::if_(Operand::Move(Place::from(comp_temp)), true_case, false_case), ); - // generate StorageDead for the second_discriminant_temp not in use anymore - patch.add_statement(parent_end, StatementKind::StorageDead(second_discriminant_temp)); + if let Some(second_discriminant_temp) = second_discriminant_temp { + // generate StorageDead for the second_discriminant_temp not in use anymore + patch.add_statement( + parent_end, + StatementKind::StorageDead(second_discriminant_temp), + ); + } // Generate a StorageDead for comp_temp in each of the targets, since we moved it into // the switch @@ -218,6 +235,7 @@ struct OptimizationData<'tcx> { child_place: Place<'tcx>, child_ty: Ty<'tcx>, child_source: SourceInfo, + need_hoist_discriminant: bool, } fn evaluate_candidate<'tcx>( @@ -226,49 +244,21 @@ fn evaluate_candidate<'tcx>( parent: BasicBlock, ) -> Option<OptimizationData<'tcx>> { let bbs = &body.basic_blocks; + // NB: If this BB is a cleanup, we may need to figure out what else needs to be handled. + if bbs[parent].is_cleanup { + return None; + } let TerminatorKind::SwitchInt { targets, discr: parent_discr } = &bbs[parent].terminator().kind else { return None; }; let parent_ty = parent_discr.ty(body.local_decls(), tcx); - if !bbs[targets.otherwise()].is_empty_unreachable() { - // Someone could write code like this: - // ```rust - // let Q = val; - // if discriminant(P) == otherwise { - // let ptr = &mut Q as *mut _ as *mut u8; - // // It may be difficult for us to effectively determine whether values are valid. - // // Invalid values can come from all sorts of corners. - // unsafe { *ptr = 10; } - // } - // - // match P { - // A => match Q { - // A => { - // // code - // } - // _ => { - // // don't use Q - // } - // } - // _ => { - // // don't use Q - // } - // }; - // ``` - // - // Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant - // of an invalid value, which is UB. - // In order to fix this, **we would either need to show that the discriminant computation of - // `place` is computed in all branches**. - // FIXME(#95162) For the moment, we adopt a conservative approach and - // consider only the `otherwise` branch has no statements and an unreachable terminator. - return None; - } let (_, child) = targets.iter().next()?; - let child_terminator = &bbs[child].terminator(); - let TerminatorKind::SwitchInt { targets: child_targets, discr: child_discr } = - &child_terminator.kind + + let Terminator { + kind: TerminatorKind::SwitchInt { targets: child_targets, discr: child_discr }, + source_info, + } = bbs[child].terminator() else { return None; }; @@ -276,25 +266,115 @@ fn evaluate_candidate<'tcx>( if child_ty != parent_ty { return None; } - let Some(StatementKind::Assign(boxed)) = &bbs[child].statements.first().map(|x| &x.kind) else { + + // We only handle: + // ``` + // bb4: { + // _8 = discriminant((_3.1: Enum1)); + // switchInt(move _8) -> [2: bb7, otherwise: bb1]; + // } + // ``` + // and + // ``` + // bb2: { + // switchInt((_3.1: u64)) -> [1: bb5, otherwise: bb1]; + // } + // ``` + if bbs[child].statements.len() > 1 { return None; + } + + // When thie BB has exactly one statement, this statement should be discriminant. + let need_hoist_discriminant = bbs[child].statements.len() == 1; + let child_place = if need_hoist_discriminant { + if !bbs[targets.otherwise()].is_empty_unreachable() { + // Someone could write code like this: + // ```rust + // let Q = val; + // if discriminant(P) == otherwise { + // let ptr = &mut Q as *mut _ as *mut u8; + // // It may be difficult for us to effectively determine whether values are valid. + // // Invalid values can come from all sorts of corners. + // unsafe { *ptr = 10; } + // } + // + // match P { + // A => match Q { + // A => { + // // code + // } + // _ => { + // // don't use Q + // } + // } + // _ => { + // // don't use Q + // } + // }; + // ``` + // + // Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant of an + // invalid value, which is UB. + // In order to fix this, **we would either need to show that the discriminant computation of + // `place` is computed in all branches**. + // FIXME(#95162) For the moment, we adopt a conservative approach and + // consider only the `otherwise` branch has no statements and an unreachable terminator. + return None; + } + // Handle: + // ``` + // bb4: { + // _8 = discriminant((_3.1: Enum1)); + // switchInt(move _8) -> [2: bb7, otherwise: bb1]; + // } + // ``` + let [ + Statement { + kind: StatementKind::Assign(box (_, Rvalue::Discriminant(child_place))), + .. + }, + ] = bbs[child].statements.as_slice() + else { + return None; + }; + *child_place + } else { + // Handle: + // ``` + // bb2: { + // switchInt((_3.1: u64)) -> [1: bb5, otherwise: bb1]; + // } + // ``` + let Operand::Copy(child_place) = child_discr else { + return None; + }; + *child_place }; - let (_, Rvalue::Discriminant(child_place)) = &**boxed else { - return None; + let destination = if need_hoist_discriminant || bbs[targets.otherwise()].is_empty_unreachable() + { + child_targets.otherwise() + } else { + targets.otherwise() }; - let destination = child_targets.otherwise(); // Verify that the optimization is legal for each branch for (value, child) in targets.iter() { - if !verify_candidate_branch(&bbs[child], value, *child_place, destination) { + if !verify_candidate_branch( + &bbs[child], + value, + child_place, + destination, + need_hoist_discriminant, + ) { return None; } } Some(OptimizationData { destination, - child_place: *child_place, + child_place, child_ty, - child_source: child_terminator.source_info, + child_source: *source_info, + need_hoist_discriminant, }) } @@ -303,31 +383,48 @@ fn verify_candidate_branch<'tcx>( value: u128, place: Place<'tcx>, destination: BasicBlock, + need_hoist_discriminant: bool, ) -> bool { - // In order for the optimization to be correct, the branch must... - // ...have exactly one statement - if let [statement] = branch.statements.as_slice() - // ...assign the discriminant of `place` in that statement - && let StatementKind::Assign(boxed) = &statement.kind - && let (discr_place, Rvalue::Discriminant(from_place)) = &**boxed - && *from_place == place - // ...make that assignment to a local - && discr_place.projection.is_empty() - // ...terminate on a `SwitchInt` that invalidates that local - && let TerminatorKind::SwitchInt { discr: switch_op, targets, .. } = - &branch.terminator().kind - && *switch_op == Operand::Move(*discr_place) - // ...fall through to `destination` if the switch misses - && destination == targets.otherwise() - // ...have a branch for value `value` - && let mut iter = targets.iter() - && let Some((target_value, _)) = iter.next() - && target_value == value - // ...and have no more branches - && iter.next().is_none() - { - true + // In order for the optimization to be correct, the terminator must be a `SwitchInt`. + let TerminatorKind::SwitchInt { discr: switch_op, targets } = &branch.terminator().kind else { + return false; + }; + if need_hoist_discriminant { + // If we need hoist discriminant, the branch must have exactly one statement. + let [statement] = branch.statements.as_slice() else { + return false; + }; + // The statement must assign the discriminant of `place`. + let StatementKind::Assign(box (discr_place, Rvalue::Discriminant(from_place))) = + statement.kind + else { + return false; + }; + if from_place != place { + return false; + } + // The assignment must invalidate a local that terminate on a `SwitchInt`. + if !discr_place.projection.is_empty() || *switch_op != Operand::Move(discr_place) { + return false; + } } else { - false + // If we don't need hoist discriminant, the branch must not have any statements. + if !branch.statements.is_empty() { + return false; + } + // The place on `SwitchInt` must be the same. + if *switch_op != Operand::Copy(place) { + return false; + } } + // It must fall through to `destination` if the switch misses. + if destination != targets.otherwise() { + return false; + } + // It must have exactly one branch for value `value` and have no more branches. + let mut iter = targets.iter(); + let (Some((target_value, _)), None) = (iter.next(), iter.next()) else { + return false; + }; + target_value == value } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index f0acbaf56b6..35699acb318 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -572,11 +572,13 @@ impl<'tcx> Inliner<'tcx> { let return_block = if let Some(block) = target { // Prepare a new block for code that should execute when call returns. We don't use // target block directly since it might have other predecessors. - let mut data = BasicBlockData::new(Some(Terminator { - source_info: terminator.source_info, - kind: TerminatorKind::Goto { target: block }, - })); - data.is_cleanup = caller_body[block].is_cleanup; + let data = BasicBlockData::new( + Some(Terminator { + source_info: terminator.source_info, + kind: TerminatorKind::Goto { target: block }, + }), + caller_body[block].is_cleanup, + ); Some(caller_body.basic_blocks_mut().push(data)) } else { None diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index beed007589b..8feb90ff7a0 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -35,7 +35,6 @@ //! Likewise, applying the optimisation can create a lot of new MIR, so we bound the instruction //! cost by `MAX_COST`. -use rustc_abi::{TagEncoding, Variants}; use rustc_arena::DroplessArena; use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable}; @@ -565,31 +564,15 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { StatementKind::SetDiscriminant { box place, variant_index } => { let Some(discr_target) = self.map.find_discr(place.as_ref()) else { return }; let enum_ty = place.ty(self.body, self.tcx).ty; - // `SetDiscriminant` may be a no-op if the assigned variant is the untagged variant - // of a niche encoding. If we cannot ensure that we write to the discriminant, do - // nothing. - let Ok(enum_layout) = self.ecx.layout_of(enum_ty) else { + // `SetDiscriminant` guarantees that the discriminant is now `variant_index`. + // Even if the discriminant write does nothing due to niches, it is UB to set the + // discriminant when the data does not encode the desired discriminant. + let Some(discr) = + self.ecx.discriminant_for_variant(enum_ty, *variant_index).discard_err() + else { return; }; - let writes_discriminant = match enum_layout.variants { - Variants::Single { index } => { - assert_eq!(index, *variant_index); - true - } - Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => true, - Variants::Multiple { - tag_encoding: TagEncoding::Niche { untagged_variant, .. }, - .. - } => *variant_index != untagged_variant, - }; - if writes_discriminant { - let Some(discr) = - self.ecx.discriminant_for_variant(enum_ty, *variant_index).discard_err() - else { - return; - }; - self.process_immediate(bb, discr_target, discr, state); - } + self.process_immediate(bb, discr_target, discr, state); } // If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`. StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume( diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs index 8be5a63d008..490e7dd8f7e 100644 --- a/compiler/rustc_mir_transform/src/large_enums.rs +++ b/compiler/rustc_mir_transform/src/large_enums.rs @@ -216,7 +216,7 @@ impl EnumSizeOpt { }; let layout = tcx.layout_of(typing_env.as_query_input(ty)).ok()?; let variants = match &layout.variants { - Variants::Single { .. } => return None, + Variants::Single { .. } | Variants::Empty => return None, Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, .. } => return None, Variants::Multiple { variants, .. } if variants.len() <= 1 => return None, diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs index 139b25be0ab..f01bab75c4a 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -96,7 +96,7 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { typing_env, stack: Vec::with_capacity(Self::MAX_STACK_LEN), - last_bb: bbs.push(BasicBlockData::new(None)), + last_bb: bbs.push(BasicBlockData::new(None, false)), top_cleanup_bb: match tcx.sess.panic_strategy() { PanicStrategy::Unwind => { // Don't drop input arg because it's just a pointer diff --git a/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs b/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs index 57e255b7c32..55dcad0680a 100644 --- a/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs +++ b/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs @@ -54,6 +54,10 @@ fn variant_discriminants<'tcx>( tcx: TyCtxt<'tcx>, ) -> FxHashSet<u128> { match &layout.variants { + Variants::Empty => { + // Uninhabited, no valid discriminant. + FxHashSet::default() + } Variants::Single { index } => { let mut res = FxHashSet::default(); res.insert( |
