From d1acb76df8f93ce3f9a1de483ea096180367852c Mon Sep 17 00:00:00 2001
From: Jonas Maebe <jonas@freepascal.org>
Date: Fri, 9 Mar 2012 20:26:32 +0000
Subject: [PATCH]   * don't replace "expr1 or expr1" or "expr1 and expr1" with
 just "expr1"     if expr1 has sideeffects. This can't be done safely even in
 case of     short boolean evaluation, because expr1 may return the inverse
 the     second time its called (and "0 or 1" is not the same as "0", and    
 neither is "1 and 0"), based on comment by Michael Karcher   * perform a full
 string compare when comparing stringconstnodes     before the string constant
 labels have been generated (patch by     Michael Karcher, mantis #21255)

git-svn-id: trunk@20485 -
---
 .gitattributes          |   1 +
 compiler/nadd.pas       |   9 ++--
 compiler/ncon.pas       |  11 ++--
 tests/webtbs/tw21255.pp | 112 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 126 insertions(+), 7 deletions(-)
 create mode 100644 tests/webtbs/tw21255.pp

diff --git a/.gitattributes b/.gitattributes
index a3b98e9593..a7d574e239 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -12233,6 +12233,7 @@ tests/webtbs/tw21146.pp svneol=native#text/pascal
 tests/webtbs/tw21151.pp svneol=native#text/plain
 tests/webtbs/tw21177.pp svneol=native#text/plain
 tests/webtbs/tw21179.pp svneol=native#text/pascal
+tests/webtbs/tw21255.pp svneol=native#text/plain
 tests/webtbs/tw2128.pp svneol=native#text/plain
 tests/webtbs/tw2129.pp svneol=native#text/plain
 tests/webtbs/tw2129b.pp svneol=native#text/plain
diff --git a/compiler/nadd.pas b/compiler/nadd.pas
index 328f3512c7..33f8478158 100644
--- a/compiler/nadd.pas
+++ b/compiler/nadd.pas
@@ -833,9 +833,12 @@ implementation
               this simplification always
             }
             if is_boolean(left.resultdef) and is_boolean(right.resultdef) and
-            { since the expressions might have sideeffects, we may only remove them
-              if short boolean evaluation is turned on }
-            (nf_short_bool in flags) then
+               { even when short circuit boolean evaluation is active, this
+                 optimization cannot be performed in case the node has
+                 side effects, because this can change the result (e.g., in an
+                 or-node that calls the same function twice and first returns
+                 false and then true because of a global state change }
+               not might_have_sideeffects(left) then
               begin
                 if left.isequal(right) then
                   begin
diff --git a/compiler/ncon.pas b/compiler/ncon.pas
index ee8d1723b2..e25b9ff445 100644
--- a/compiler/ncon.pas
+++ b/compiler/ncon.pas
@@ -968,10 +968,13 @@ implementation
         docompare :=
           inherited docompare(p) and
           (len = tstringconstnode(p).len) and
-          { Don't compare the pchars, since they may contain null chars }
-          { Since all equal constant strings are replaced by the same   }
-          { label, the following compare should be enough (JM)          }
-          (lab_str = tstringconstnode(p).lab_str);
+          (lab_str = tstringconstnode(p).lab_str) and
+          { This is enough as soon as labels are allocated, otherwise }
+          { fall back to content compare.                             }
+          (assigned(lab_str) or
+            (cst_type = tstringconstnode(p).cst_type) and
+            (fullcompare(tstringconstnode(p)) = 0))
+          ;
       end;
 
 
diff --git a/tests/webtbs/tw21255.pp b/tests/webtbs/tw21255.pp
new file mode 100644
index 0000000000..9c0cb56f91
--- /dev/null
+++ b/tests/webtbs/tw21255.pp
@@ -0,0 +1,112 @@
+Program fpcbugcheck;
+
+{$mode macpas} { for | }
+
+Var
+  FrobCounter : Integer;
+
+const
+  ok: boolean = true;
+
+Function Frob : Boolean;
+begin
+  Inc(FrobCounter);
+  Frob := False;
+end;
+
+{
+  This program tests whether "Frob or Frob" is contracted to a single
+  "Frob" call, in different contexts:
+    - without shortcut evaluation (no contraction allowed)
+    - with shortcut evaluation enabled
+    - using the MacPas "|" instead of "or"
+  It prints the number of Frob calls for each variant.
+}
+
+{
+  The second test this program does is related to the first one,
+  it exercises a bug in fpc deeming all compares with string constants
+  to be equal at the stage of compilation the elision of duplicate expression
+  in bools is performed.
+
+  This bug has been active since the introduction of the boolean shortcut
+  optimization (r14714), but as that optimization only applies to the
+  explicit shortcut form (in form of the MacPas operators), that bug
+  didn't show.
+}
+
+{$B+}
+Procedure CountFrobsBoolFull;
+begin
+  FrobCounter := 0;
+  if Frob or Frob then
+    begin
+      WriteLn('Complete: Huh');
+      ok:=false;
+    end
+  else
+    begin
+      WriteLn('Complete: Frobbed ', FrobCounter, ' times');
+      if FrobCounter<>2 then
+         ok:=false;
+    end;
+end;
+
+{$B-}
+Procedure CountFrobsBoolShort;
+begin
+  FrobCounter := 0;
+  if Frob or Frob then
+    begin
+      WriteLn('Short: Huh');
+      ok:=false;
+    end
+  else
+    begin
+      WriteLn('Short: Frobbed ', FrobCounter, ' times');
+      if FrobCounter<>2 then
+        ok:=false;
+    end;
+end;
+
+Procedure CountFrobsMac;
+begin
+  FrobCounter := 0;
+  if Frob | Frob then
+    begin
+      WriteLn('Mac: Huh');
+      ok:=false;
+    end
+  else
+    begin
+      WriteLn('Mac: Frobbed ', FrobCounter, ' times');
+      if FrobCounter<>2 then
+        ok:=false;
+    end;
+end;
+
+var
+  test: String;
+{$B-}
+begin
+  ok:=true;
+  { test conditions for application of boolean contraction }
+  CountFrobsBoolFull;
+  CountFrobsBoolShort;
+  CountFrobsMac;
+  { test for faulty boolean contraction }
+  test:='b';
+  if (test='a') | (test='b') then
+    test := 'OK'
+  else
+    ok:=false;
+  if test <> 'OK' then WriteLn('Mac: fpc failed!');
+  test:='b';
+  if (test='a') or (test='b') then
+    test := 'OK'
+  else
+    ok:=false;
+  if test <> 'OK' then WriteLn('Short: fpc failed!');
+  if not ok then
+    halt(1);
+end.