Unexpected PCHAR behavior (bug ?) lazarus / 67-Windows ---------------------------------------------------------------------- 440bx · 2020-01-15 03:17 Hello, It seems that FPC does automatic dereferencing even when it has been explicitly told not to.  Consider the following code:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$MODESWITCH     ANSISTRINGS-      }   { note the use of NO AUTOMATIC DEREFERENCING } {$MODESWITCH     AUTODEREF-        }   {$MODESWITCH   UNICODESTRINGS-   }{$MODESWITCH     POINTERTOPROCVAR- } {$LONGSTRINGS    OFF               }{$WRITEABLECONST OFF               }{$TYPEDADDRESS   ON                } program _AtSignInCase; var  a : packed array[0..15] of char = '1234567890/abcd';   p : pchar;   i : integer; begin  // this works as it should   p := @a[low(a)];  while true do  begin     if p^ = '/' then break; // dereference p and compare to char, all good.    inc(p);                 // look at next character  end;    i := low(a);  p := @a[low(a)];  while true do  begin     // this doesn't work and no error is emitted    // for the statement:    //    // if p = '/' then break; <-- no type error !?    //    // and no break on the '/' either!    //     if p = '/' then break;  // no type error !?    inc(p);     // the following is just to avoid    // an infinite loop     inc(i);    if i > high(a) then    begin      writeln('broke loop on value of i instead of p');      writeln('value of (p - 2)^ is : ', (p - 2)^);      break;    end;  end;   writeln;  writeln('press enter/return to end this program');  readln;end.On line 52 the pointer to char "p" is being compared against a character.  It seems that should cause the compiler to state that a pointer cannot be compared to a character but, it accepts it in spite of the compiler directive AUTODEREF being set to off. The result is, where it not for the additional variable i (added specifically for the sake of this example), the while loop would be an infinite loop. Shouldn't the compiler have emitted a type error message for line 52 ? Thanks. ---------------------------------------------------------------------- ASerge · 2020-01-15 18:38 Quote from: 440bx on January 15, 2020, 04:17:52 amShouldn't the compiler have emitted a type error message for line 52 ? Most likely, there are hardly any options that allow you to generate an error message in this case. As far as I know, string constants are compatible with any string type. Based on the directives specified in the example, the compiler converts the character pointer to a short string and then compares it to a constant. ---------------------------------------------------------------------- 440bx · 2020-01-15 20:42 Quote from: ASerge on January 15, 2020, 07:38:03 pm the compiler converts the character pointer to a short string and then compares it to a constant. You're right, that's what it does but, it doesn't seem right for the compiler to be doing that, particularly when it's been told not to do automatic dereferencing. ---------------------------------------------------------------------- Remy Lebeau · 2020-01-16 02:20 Quote from: 440bx on January 15, 2020, 09:42:58 pmYou're right, that's what it does but, it doesn't seem right for the compiler to be doing that, particularly when it's been told not to do automatic dereferencing. There is no dereferencing happening in the second case. ---------------------------------------------------------------------- 440bx · 2020-01-16 04:08 Quote from: Remy Lebeau on January 16, 2020, 03:20:50 amThere is no dereferencing happening in the second case. Not in code but, logically it is dereferencing, otherwise there would be a type error, i.e, a pointer cannot be compared to a character (type mismatch) but, a dereferenced pointer to char can be compared to a char. ---------------------------------------------------------------------- Martin_fr · 2020-01-16 11:25 My understanding: It is not dereferencing, but converting. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure Foo(s: String);...var p: PChar;Foo(p); "p" will not be dereferenced. If it was, it would pass a single char too Foo. But it passes an entire (null terminated) string. Whenever a pchar is used, where a string would be required, it is converted. So in your comparison, it is converted too. Probably lucky that it does not cause a crash, as your array of char has no null termination. This also explains why it is never equal to '/'. Because it is a string starting with '/abcd', continuing to a null byte. ---------------------------------------------------------------------- 440bx · 2020-01-16 12:56 Quote from: Martin_fr on January 16, 2020, 12:25:44 pmMy understanding: It is not dereferencing, but converting. Yes, that's correct.  The code is converting the "pointer to array of characters" into a shortstring with the same characters. Quote from: Martin_fr on January 16, 2020, 12:25:44 pm"p" will not be dereferenced. If it was, it would pass a single char too Foo. But it passes an entire (null terminated) string. Yes, that's correct too. Quote from: Martin_fr on January 16, 2020, 12:25:44 pmWhenever a pchar is used, where a string would be required, it is converted. That is what leads to "strange things".  In a strongly typed language a "pointer to char" cannot be compared to a "char". The types don't match (C++ won't accept it and quite likely C won't either.)  The fact that such a comparison works gives the impression that either the "pointer to char" is being automatically dereferenced (which would make the types match) or, what C/C++ do, compare the value of the pointer to the address of the character constant (saved in a read-only section by the compiler) or, lastly what FPC is doing, converting a "pointer to char" into a shortstring and comparing the shortstrings - which is a strange thing to do since the compiler can't always know what p points to looks like (I guess it will just accumulate characters into a shortstring until it finds a null.) Quote from: Martin_fr on January 16, 2020, 12:25:44 pmProbably lucky that it does not cause a crash, as your array of char has no null termination. In the example, I chose to add a counter to prevent going out of bounds but, your point is valid. I definitely wouldn't have the array nor the code that way in a real program. Quote from: Martin_fr on January 16, 2020, 12:25:44 pmThis also explains why it is never equal to '/'. Because it is a string starting with '/abcd', continuing to a null byte. Actually, it's comparing '/' to '1234567890/abcd', as you stated, that will definitely never be equal. I guess, that's just the way FPC does it.  It opens the door to unexpected problems when the programmer missed typing the "^", which is how I "bumped" into this situation.  That reminds me of C, one typo and good luck... you'll need it. ---------------------------------------------------------------------- Remy Lebeau · 2020-01-17 18:11 Quote from: 440bx on January 16, 2020, 05:08:25 ama pointer cannot be compared to a character (type mismatch) True, but it can be compared to another pointer.  I was actually expecting the compiler to treat the `p = '/'` statement as if it were `p = PChar('/')` since it should be less work for the compiler to treat the string literal as a PChar constant at compile-time than to convert `p` to a string at runtime. ---------------------------------------------------------------------- 440bx · 2020-01-18 00:59 Quote from: Remy Lebeau on January 17, 2020, 07:11:28 pmI was actually expecting the compiler to treat the `p = '/'` statement as if it were `p = PChar('/')` since it should be less work for the compiler to treat the string literal as a PChar constant at compile-time than to convert `p` to a string at runtime. That would be reasonable and is "type legal".  In FPC, that behavior can be obtained if the constant is explicitly typecast to pchar just as you suggested. Just for curiosity I tried it in MSVC and that's what it does, it compares p to the address of the constant "/" which it saved in a read-only section of the PE file. Interestingly, as a result of doing conversion,  FPC will compile something like this:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  c : char;  p : pchar;begin  if p = c then c := 'a';end; while C/C++ won't accept such a comparison. In the meantime, omitting the "^" due to a typo yields results that are not particularly desirable. ---------------------------------------------------------------------- PascalDragon · 2020-01-18 10:57 The PChar type (or more general ^Char) is an exception to the rule due to its close relationship with strings. There are some internal conversions between strings, PChars and array of Char that aren't done for other types (mainly because Delphi does it this way as well). If you look at the assembly code of the following example: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program tpchar; var  p: PChar;  c: Char;begin  if p = 'a' then ;  if p = c then ;end. Code: ASM  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i # [7] if p = 'a' then ;        movq    U_$P$TPCHAR_$$_P(%rip),%rax        leaq  -256(%rbp),%rcx        movq    $255,%rdx        movq    %rax,%r8      call    fpc_pchar_to_shortstr        leaq    -256(%rbp),%rcx        leaq    _$TPCHAR$_Ld1(%rip),%rdx        call    fpc_shortstr_compare_equal        testl   %eax,%eax        je      .Lj3        jmp     .Lj4.Lj4:.Lj3:# [8] if p = c then ;        movq  U_$P$TPCHAR_$$_P(%rip),%r8        leaq    -256(%rbp),%rcx        movq    $255,%rdx        call    fpc_pchar_to_shortstr        leaq    -256(%rbp),%rcx        movzbl  U_$P$TPCHAR_$$_C(%rip),%eax        shll    $8,%eax        orl     $1,%eax        movw    %ax,-512(%rbp)      leaq    -512(%rbp),%rdx        call    fpc_shortstr_compare_equal        testl   %eax,%eax        je      .Lj15        jmp     .Lj16.Lj16:.Lj15: You'll notice that FPC inserts explicit conversions of the PChar to a (in this case) ShortString. This is simply how things are and won't be changed. ---------------------------------------------------------------------- 440bx · 2020-01-18 12:48 Quote from: PascalDragon on January 18, 2020, 11:57:14 am(mainly because Delphi does it this way as well). I figured that was the culprit. Quote from: PascalDragon on January 18, 2020, 11:57:14 amYou'll notice that FPC inserts explicit conversions of the PChar to a (in this case) ShortString. This is simply how things are and won't be changed. I understand.  The problem has its origins in making pchars and array of chars, as well as other string types, "compatible".  At this point, it's quite likely that the only consistent way of "compatibilizing" pchars and the various string forms is to convert them as necessary to "force" compatibility (even when it isn't always desirable.) Thank you for the additional clarification.