@@ -148,43 +148,90 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
148148 ExprKind :: LogicalOp { op, lhs, rhs } => {
149149 let condition_scope = this. local_scope ( ) ;
150150 let source_info = this. source_info ( expr. span ) ;
151- // We first evaluate the left-hand side of the predicate ...
152- let ( then_block, else_block) =
153- this. in_if_then_scope ( condition_scope, expr. span , |this| {
154- this. then_else_break (
155- block,
156- lhs,
157- Some ( condition_scope) , // Temp scope
158- source_info,
159- // This flag controls how inner `let` expressions are lowered,
160- // but either way there shouldn't be any of those in here.
161- true ,
162- )
163- } ) ;
164- let ( short_circuit, continuation, constant) = match op {
165- LogicalOp :: And => ( else_block, then_block, false ) ,
166- LogicalOp :: Or => ( then_block, else_block, true ) ,
151+
152+ let push_constant_bool = |this : & mut Builder < ' a , ' tcx > , bb, dest, value| {
153+ this. cfg . push_assign_constant (
154+ bb,
155+ source_info,
156+ dest,
157+ ConstOperand {
158+ span : expr. span ,
159+ user_ty : None ,
160+ const_ : Const :: from_bool ( this. tcx , value) ,
161+ } ,
162+ ) ;
167163 } ;
168- // At this point, the control flow splits into a short-circuiting path
169- // and a continuation path.
170- // - If the operator is `&&`, passing `lhs` leads to continuation of evaluation on `rhs`;
171- // failing it leads to the short-circuting path which assigns `false` to the place.
172- // - If the operator is `||`, failing `lhs` leads to continuation of evaluation on `rhs`;
173- // passing it leads to the short-circuting path which assigns `true` to the place.
174- this. cfg . push_assign_constant (
175- short_circuit,
176- source_info,
177- destination,
178- ConstOperand {
179- span : expr. span ,
180- user_ty : None ,
181- const_ : Const :: from_bool ( this. tcx , constant) ,
182- } ,
183- ) ;
184- let rhs = unpack ! ( this. expr_into_dest( destination, continuation, rhs) ) ;
164+ // A simple optimization on boolean expression with short-circuit
165+ // operators is to not create a branch for the last operand.
166+ // Example:
167+ // let x: bool = a && b;
168+ // would be compiled into something semantically closer to
169+ // let x = if a { b } else { false };
170+ // rather than
171+ // let x = if a && b { true } else { false };
172+ //
173+ // In case `a` is true, evaluate `b` and assign it to `x`,
174+ // thus there is no need to create an actual branch for `b`.
175+ // Otherwise, assign false to `x`.
176+ //
177+ // The exception is when we instrument the code for condition coverage,
178+ // which tracks the outcome of all operands of boolean expressions.
179+
180+ let ( outcome1, outcome2) = if this. tcx . sess . instrument_coverage_condition ( ) {
181+ // We first evaluate the left-hand side of the predicate ...
182+ let ( then_block, else_block) =
183+ this. in_if_then_scope ( condition_scope, expr. span , |this| {
184+ this. then_else_break (
185+ block,
186+ expr_id,
187+ Some ( condition_scope) , // Temp scope
188+ source_info,
189+ // This flag controls how inner `let` expressions are lowered,
190+ // but either way there shouldn't be any of those in here.
191+ true ,
192+ )
193+ } ) ;
194+
195+ // Write true on expression success...
196+ push_constant_bool ( this, then_block, destination, true ) ;
197+ // ...and false on failure.
198+ push_constant_bool ( this, else_block, destination, false ) ;
199+
200+ ( then_block, else_block)
201+ } else {
202+ // We first evaluate the left-hand side of the predicate ...
203+ let ( then_block, else_block) =
204+ this. in_if_then_scope ( condition_scope, expr. span , |this| {
205+ this. then_else_break (
206+ block,
207+ lhs,
208+ Some ( condition_scope) , // Temp scope
209+ source_info,
210+ // This flag controls how inner `let` expressions are lowered,
211+ // but either way there shouldn't be any of those in here.
212+ true ,
213+ )
214+ } ) ;
215+ let ( short_circuit, continuation, constant) = match op {
216+ LogicalOp :: And => ( else_block, then_block, false ) ,
217+ LogicalOp :: Or => ( then_block, else_block, true ) ,
218+ } ;
219+ // At this point, the control flow splits into a short-circuiting path
220+ // and a continuation path.
221+ // - If the operator is `&&`, passing `lhs` leads to continuation of evaluation on `rhs`;
222+ // failing it leads to the short-circuting path which assigns `false` to the place.
223+ // - If the operator is `||`, failing `lhs` leads to continuation of evaluation on `rhs`;
224+ // passing it leads to the short-circuting path which assigns `true` to the place.
225+ push_constant_bool ( this, short_circuit, destination, constant) ;
226+
227+ let rhs = unpack ! ( this. expr_into_dest( destination, continuation, rhs) ) ;
228+
229+ ( short_circuit, rhs)
230+ } ;
231+
185232 let target = this. cfg . start_new_block ( ) ;
186- this. cfg . goto ( rhs , source_info, target) ;
187- this. cfg . goto ( short_circuit , source_info, target) ;
233+ this. cfg . goto ( outcome1 , source_info, target) ;
234+ this. cfg . goto ( outcome2 , source_info, target) ;
188235 target. unit ( )
189236 }
190237 ExprKind :: Loop { body } => {
0 commit comments