@@ -148,43 +148,90 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
148
148
ExprKind :: LogicalOp { op, lhs, rhs } => {
149
149
let condition_scope = this. local_scope ( ) ;
150
150
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
+ ) ;
167
163
} ;
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
+
185
232
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) ;
188
235
target. unit ( )
189
236
}
190
237
ExprKind :: Loop { body } => {
0 commit comments