4
if exp1 and exp2 and exp3 then
    //something

In Delphi is the order of evaluation exp1, exp2 and exp3 (of course they are all Boolean) is defined or random?

Bruice
  • 543
  • 3
  • 11
  • 9
    Every time you have a question about Delphi, your first stop should be the official documentation. In this case, the section about [operator precedence](http://docwiki.embarcadero.com/RADStudio/Sydney/en/Expressions_(Delphi)#Operator_Precedence) gives you all information you need. To find this page quickly, search for "delphi docwiki operator precedence" on Google. – Andreas Rejbrand Mar 19 '21 at 20:35
  • The question is about order of operand evaluation, not about operator order – Stefan Glienke Mar 19 '21 at 21:29
  • @StefanGlienke: True, but the same page answers that question too (but AFAICS only in the case of lazy evaluation). – Andreas Rejbrand Mar 19 '21 at 21:40
  • Which justifies this question imho – Stefan Glienke Mar 19 '21 at 21:44
  • @StefanGlienke: I agree and your valid answer really is helpful (although the Q would probably have been better received if this particular ambiguity in the docs had been pointed out). – Andreas Rejbrand Mar 19 '21 at 21:46

2 Answers2

17

The order of evaluation is only defined when Boolean Short-Circuit Evaluation is enabled. Then, as the documentation in Complete Versus Short-Circuit Boolean Evaluation explains, evaluation is left to right.

If Boolean Short-Circuit Evaluation is not enabled, the order is undefined. The following code demonstrates this:

{$APPTYPE CONSOLE}
    
function A: boolean;
begin
  Result := True;
  Write('A ');
end;
    
function B: string;
begin
  Result := '';
  Write('B ');
end;
    
function C: Integer;
begin
  Result := 0;
  Write('C ');
end;
    
begin
  {$O+}
  Writeln('short circuit on');
  {$B-}
  if A and (B = '') and (C = 0) then Writeln;
  Writeln('short circuit off');
  {$B+}
  if A and (B = '') and (C = 0) then Writeln;
end.

For the first one, you get printed A B C, and for the second one you get B A C.

And that was compiled for Win32 - to make this spicy, and bring home the point of this being Undefined, lets run it on Win64 where we get A B C in both cases.

You might say: "Ok, but maybe just B was called first but the evaluation of the boolean expression B = '' is evaluated in the correct order." Let's take a look at the assembler code that gets executed:

if A and (B = '') and (C = 0) then Writeln;
0040B17C 8D45E8           lea eax,[ebp-$18]
0040B17F E864EAFFFF       call B
0040B184 837DE800         cmp dword ptr [ebp-$18],$00
0040B188 0F94C3           setz bl
0040B18B E81CEAFFFF       call A
0040B190 22D8             and bl,al
0040B192 E891EAFFFF       call C
0040B197 85C0             test eax,eax
0040B199 0F94C0           setz al
0040B19C 22D8             and bl,al

Nope:

  • call B, compare to ''
  • call A, and it with the result of the string comparison
  • call C, compare with 0, and it with the result of the previous and

Which translates to (written in left to right order):

((B = '') and A) and (C = 0)

Addendum: Because I saw this mentioned in another answer and discussed in the comments. Parentheses are no solution to force order of evaluation of operands. You can only group operations but the compiler might still decide to evaluate the right operand first. In the code above there is no way to put any parentheses to get A B C simply because the compiler flipped the first two operands. It then however executed the operators from left to right and that is something to easily confuse - the question was not whether the first or the second and was executed first but the order of the boolean expressions - the operands.

Stefan Glienke
  • 20,860
  • 2
  • 48
  • 102
  • and what about "An operator with higher precedence is evaluated before an operator with lower precedence, while operators of equal precedence associate to the left. " from http://docwiki.embarcadero.com/RADStudio/Sydney/en/Expressions_(Delphi) ? – Bruice Mar 19 '21 at 20:59
  • And if to write if (A) and (B = '') and (C = 0) then Writeln; ?Can you please check on your compiler? – Bruice Mar 19 '21 at 22:07
  • Why would pointless parentheses make any difference - nothing different. But you gave me an idea, if you actually write `if (A = True) and (B = '') and (C = 0) then Writeln;` (don't do this! never check a boolean against true or false, thats just eww) then you get `A B C` - as I wrote: undefined behavior - only trust on order if you know that you have `$B-` or separate into multiple statements to force the order. – Stefan Glienke Mar 19 '21 at 22:12
  • The undefined order can be easily understood/accepted when having experienced SQL, where [the evaluation order of multiple WHERE filters/conditions is also unreliable](https://stackoverflow.com/a/909770/4299358). – AmigoJack Mar 20 '21 at 00:13
  • I see you forced the `{$O+}` optimization flag, so I guess the out-of-order evaluation is an effect of the optimizer. – Olivier Mar 20 '21 at 09:15
  • @Bruice The statement "operators of equal precedence associate to the left" doesn't apply to `and` and `or`, which are special operators The doc should be changed indeed to mention that. – Olivier Mar 20 '21 at 10:13
  • @Olivier No, it's not for the code shown - however in an earlier version I had no instruction after the `then` causing Delphi to flip orders depending on `$O` but I found that rather unrealistic code with no code after an if. – Stefan Glienke Mar 20 '21 at 13:12
-2

The documentation will guide you of course.

I have found that in more complex if statements the compiler does not do what even experienced programmers expected when they wrote the code, often because they are including logical nots and mixing boolean ands and ors. Remembering the precedence and association rules is not always easy.

My tip is to be explicit about what you want and put brackets around the terms. The compiler won't mind and it makes it clear what you intended when you or someone else reviews the code.

With compound if statements laying out the logical parts together can aso help humans to understand what was meant.

For example:

  if( ((pObjectRef<>nil) And (pObjecRef.Property=SomeValue)) Or
      ((pAlternateObject<>nil) And 
       ((pAlternatObject.Property=AnotherValue) Or 
        (pAlternatObject.Property=YetAnotherValue))) ) then
  begin
    ...
  end
Rob Lambden
  • 2,175
  • 6
  • 15
  • If I use only AND, if exp1 and exp2 and exp3 then, can I be sure that exp1 will be evaluated first and exp3 the last? – Bruice Mar 19 '21 at 21:01
  • @Bruice: YES, if you use the default compiler setting with lazy (short-circuit) boolean evaluation. There is almost never any need to change this, and many Delphi units in fact *depend on* short-circuit evaluation. I almost daily write code that would fail without it! But please note that `exp2` and `exp3` might not be evaluated at all. – Andreas Rejbrand Mar 19 '21 at 21:04
  • Your question 'can I be sure' is why I gave this tip... it's easy to put in brackets and be explicit about what you mean so you don't have to remember the rules, and worry about Stefan's warning about esoteric compiler switches. If you get in the habit of using brackets then not only can you be sure, it's easy to tell what you intended when the code is reviewed at a later date. – Rob Lambden Mar 19 '21 at 21:05
  • Personally, I think the compiler always does exactly what I expect. `not` > `and` > `or`. I almost get annoyed when I see stuff like `if ((not A) and (not B)) or C then` because to be the parentheses are very clearly redundant! – Andreas Rejbrand Mar 19 '21 at 21:05
  • So regarding "in more complex if statements the compiler does not do what even experienced programmers expected", I don't think these programmers really are that experienced in Delphi. – Andreas Rejbrand Mar 19 '21 at 21:07
  • 1
    @AndreasRejbrand As I pointed out in my answer there are always different opinions available, including your opinion on how experienced programmers need to be not to trip over complex code. If all programmers had limitless talent there would be no need for stack overflow, or for code reviews either. – Rob Lambden Mar 19 '21 at 21:09
  • (Hint: in boolean algebra, `and` is like multiplication and `or` like addition, so `and` has higher precedence.) – Andreas Rejbrand Mar 19 '21 at 21:09
  • 1
    @AndreasRejbrand I note your comment that parenthesis which you consider to be very clearly redundant annoys you. I did point out in my answer that the _compiler_ won't mind (inferring that there are some programmers who do, like there are some that don't like camelCase or hungarian notation or any other convention). In my experience (which I accept is not the same as yours) seeing explicitly what another programmer meant is usually very helpful when maintaining code. – Rob Lambden Mar 19 '21 at 21:15
  • 2
    I don't understand the downvote on this. ITSM that liberal use of brackets in this sort of situation is sound defensive programming. – MartynA Mar 20 '21 at 11:20
  • 2
    @MartynA: I didn't downvote (even though unnecessary brackets only annoy me), but one possible reason for the downvotes might be that this A doesn't strictly answer the Q. – Andreas Rejbrand Mar 20 '21 at 11:54