Code change:
For details see , opening an issue since PR was closed:
While this one particular test case was fixed, the transformation still suffers from issues hinted at earlier comment: assumes that terminator always switches on a local that was assigned last in the block. The assumption is false. moves a read of a discriminant from the second block into first without ensuring that it holds the same value still. some executions it introduces a read that would not have happen otherwise and could have an undefined behaviour. some executions it skips execution of statements from one the blocks without checking if they are side-effect free. cc
, EarlyOtherwiseBranch only applies to basic blocks of which the last statement is a discriminant (and the terminator is a switch): And, We could require the discriminant read in the second basic block to be the only statement within the block. Not sure by how much would the change affect optimization hit rate, but at least the example in would still be optimized. What makes me really concerned is this one: When would the first discriminant be safe but not the second? The example you constructed is a legit one: the second read assumes the value of the first read and does a downcast; that is the case my patch catches. The only other possible invalid case I could think of, is that when the second discriminant call's argument is an invalid pointer. Probably something like: But today the MIR that FE emits dodges the problem by accident: Because these two switch instructions have different blocks. So, my guess is, even though EarlyOtherwiseBranch is unsound, today the compiler FE is still unable to produce MIR that could trigger that.
If one considers shape of MIR immediately after it has been built, that is probably true. One thing to keep mind: this transformation runs on MIR that have been transformed already. In fact, the whole optimization pipeline might have run an arbitrary number of times as a result of inlining. Transformations shouldn't rely on any invariants that aren't preserved by all transformations. The might be unrelated to a local used in switch, since this is not checked anywhere. Last time out of curiosity I an assertion checking that, enabled the transform by default and bootstraped rustc, the assertion did fail.
Ah! I misunderstood your point , but now I get it. You are right. I think we could resolve your , , and by adding the following restrictions: discriminant read in the second block must be the only statement in there; switch must use the value of the discriminant read. But I found that the same effect of EarlyOtherwiseBranch could be achieved through a much easier change; we could provide a hint for LLVM optimizer to compare the discriminant values before entering the switch, e.g.: With this hint, (unfortunately, isn't enough) produces: IMO this is much more elegant than EarlyOtherwiseBranch; it should also be pretty easy to implement, so I will give it a try.

Related discussion:
text: return None; } <ins> // when the second place is a projection of the first one, it's not safe to calculate their discriminant values sequentially. // for example, this should not be optimized: // // ```rust // enum E<'a> { Empty, Some(&'a E<'a>), } // let Some(Some(_)) = e; // ``` // // ```mir // bb0: { //   _2 = discriminant(*_1) //   switchInt(_2) -> [...] // } // bb1: { //   _3 = discriminant(*(((*_1) as Some).0: &E)) //   switchInt(_3) -> [...] // } // ``` let discr_place = discr_info.place_of_adt_discr_read; let this_discr_place = this_bb_discr_info.place_of_adt_discr_read; if discr_place.local == this_discr_place.local && this_discr_place.projection.starts_with(discr_place.projection) { trace!("NO: one target is the projection of another"); return None; } </ins> // if we reach this point, the optimization applies, and we should be able to optimize this case // store the info that is needed to apply the optimization