Unexpected FPC v3.0.4 Behavior lazarus / 62-General ---------------------------------------------------------------------- 440bx · 2020-06-27 01:08 Hello, First, I don't know if the new version of FPC behaves the same way, I am still using v3.0.4. Consider the sample program below: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 { without any of the defines below, FPC v3.0.4 compiles this sample program   }{ Delphi v2.0 does NOT.                                                     } { with any of the defines active, FPC complains about one thing or another    }{ and the compile fails.                                                    } //{$DEFINE CALL_A}//{$DEFINE CALL_B}//{$DEFINE CALL_C} program TestProcedureName;   type    ProcedureName = array[1..2] of integer;   procedure ProcedureA;     procedure ProcedureName(ParameterA : ProcedureName);    begin      { if no attempt is made to use ParameterA then the program compiles     }     { neither of the statements below are accepted    }        { illegal expression                                                    }       {$IFDEF CALL_A} ParameterA;                {$ENDIF}        { illegal expression;   Syntax error, ";" expected but "(" found        }       {$IFDEF CALL_B} ParameterA(ParameterA);    {$ENDIF}        { illegal expression;   Syntax error, ";" expected but "(" found        }       {$IFDEF CALL_C} ParameterA(ProcedureName); {$ENDIF}    end;   begin  end;  beginend. When _none_ of the defines are active the program compiles.  Once any of the defines are active the compiler will complain about one thing or another. With none of the defines active, unlike FPC, Delphi v2.0 does _not_ compile the program. Aside from the difference in behavior between Delphi and FPC, I do not see how FPC expects ParameterA to be used.  I suppose that, if it considers the sample program correct, there must be a way to use ParameterA but, it doesn't seem to like any of the expected constructions (and a few possibly unexpected ones too (unexpected constructions not included in the sample.)) ---------------------------------------------------------------------- Kays · 2020-06-27 04:20 What are you trying to achieve? I can’t figure out what your program is supposed to do. ParameterA is a (is of type) array[1..2] of integer. You can’t just put ParameterA in a routine’s definition. You have to insert statements between begin and end. Writing ParameterA is a factor of an expression.ParameterA is neither a function, nor a type. You can’t put parentheses after it.Same as B.Maybe, did you want to use procedural variables such as TProcedure? ---------------------------------------------------------------------- 440bx · 2020-06-27 04:32 Quote from: Kays on June 27, 2020, 06:20:58 amWhat are you trying to achieve? I can’t figure out what your program is supposed to do. It's just a program to test FPC's scope resolution algorithms and the timing of the addition of identifiers in its parse tables. As far as the rest of your post, your questions are completely valid because, I don't see how it makes any sense for the compiler to accept and, successfully compile that code.  IOW, that code should _not_ compile (even when none of the defines are enabled).  That's part of what I am testing. ---------------------------------------------------------------------- lucamar · 2020-06-27 05:25 Just shooting in the dark here, but maybe it's finding an empty procedure and optimizing it all away? Though how it can even arrive at that without getting lost among all those ProcedureName baffles me no end ... ---------------------------------------------------------------------- 440bx · 2020-06-27 06:26 Quote from: lucamar on June 27, 2020, 07:25:10 amThough how it can even arrive at that without getting lost among all those ProcedureName baffles me no end ... All the ProcedureName (s) are ok up until the one used as the type of ParameterA, that one is _not_ ok.  The compiler should _not_ have accepted that instance of ProcedureName because in that location, it identifies a procedure not a type. As far as the usage of "ProcedureName" it could be worse!... For instance, it could be 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 program TestProcedureNameB;   type    ProcedureName = array[1..2] of integer;   procedure ProcedureA;     procedure ProcedureName(ProcedureName : ProcedureName);    begin    end;   begin  end;  beginend. That's not valid Pascal but, a very simple change makes it valid Pascal, as shown below: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 TestProcedureNameB;   type    ProcedureName = array[1..2] of integer;   procedure ProcedureA;     procedure ProcedureName(ProcedureName : integer);  { valid Pascal in spite of all the "ProcedureName" (s) }    begin    end;   begin  end;  beginend.  ETA: I forgot to mention, the "worse" (top in this post) example of reusing "ParameterName" also compiles with FPC. ---------------------------------------------------------------------- Kays · 2020-06-27 06:56 Quote from: 440bx on June 27, 2020, 08:26:40 am[…] All the ProcedureName (s) are ok up until the one used as the type of ParameterA, that one is _not_ ok.  The compiler should _not_ have accepted that instance of ProcedureName because in that location, it identifies a procedure not a type.[…]OK, now I see the problem. There’s one table with all identifiers identifying types. And there’s one table with all identifiers identifying routines. While parsing the formal parameters of the procedure ProcedureName after the colon the compiler expects a type identifier, thus (only) looks up the type identifier table. At that point, however, the routine ProcedureName is neither declared (= forward declaration) nor defined (= parsing of the begin…end frame). Are you saying, all identifiers have to be unique and can’t be both a label and a type identifier at the same time? ---------------------------------------------------------------------- MarkMLl · 2020-06-27 07:00 I know that this is a very minor niggle, but on the principle that when a programmer has a hard time finding a bug it's usually because he's looking in the wrong place I'd suggest replacing your  //{$DEFINE CALL_A}  and similar with  { DEFINE CALL_A}  etc., just in case different compilers or different FPC modes are getting confused by the continued presence of  MarkMLl ---------------------------------------------------------------------- 440bx · 2020-06-27 07:25 Quote from: Kays on June 27, 2020, 08:56:48 amThere’s one table with all identifiers identifying types. And there’s one table with all identifiers identifying routines. As far as that goes, the compiler writer could choose to do it that way but, the most common way (because it's the most convenient) is to have only _one_ table which, among other things has the identifier name (usually an id) and it type (also an id) then, there is the scope table, which is a list of references into that previous table which tracks which identifiers (and their types) are valid/known in that scope. When the compiler encounters "procedure ProcedureName", the "ProcedureName" is added to the scope that was started by "ProcedureA" and creates a _new_ scope for "ProcedureName" because procedure parameters are one scope below the procedure name, that's what allows a parameter name to have the same name as the procedure yet, be considered a different identifier and also makes the procedure's local variables be in the same scope as the procedure parameters (as they should be.) In the test program I posted, when the compiler looks for what "ProcedureName" means, it should be finding the definition from "Procedure ProcedureName" which identifies "ProcedureName" as a procedure, not a data type.  For this reason it should not accept "ProcedureName" as a data type identifier since it isn't one. Quote from: Kays on June 27, 2020, 08:56:48 amWhile parsing the formal parameters of the procedure ProcedureName after the colon the compiler expects a type identifier, thus (only) looks up the type identifier table. At that point, however, the routine ProcedureName is neither declared (= forward declaration) nor defined (= parsing of the begin…end frame).It is declared and, it finds it.  Proof of that is the message from the compiler stating that it expected a semicolon ";" when an attempt is made to use ParameterA in a statement, it knows ParameterA is a procedure, specifically a "ProcedureName". Quote from: Kays on June 27, 2020, 08:56:48 amAre you saying, all identifiers have to be unique and can’t be both a label and a type identifier at the same time? No.  In this particular case, I'm saying that "ParameterName" does not identify a data type therefore it is invalid to use it as if it were one. Quote from: MarkMLl on June 27, 2020, 09:00:50 amI know that this is a very minor niggle, but on the principle that when a programmer has a hard time finding a bug it's usually because he's looking in the wrong place I'd suggest replacing your  //{$DEFINE CALL_A}  and similar with  { DEFINE CALL_A}  etc., just in case different compilers or different FPC modes are getting confused by the continued presence of  Your suggestion is welcome.  I just want to mention that this is just a simple little test.  If the compiler gets confused with those defines, that would be another bug.  ---------------------------------------------------------------------- TRon · 2020-06-27 08:36 Quote from: 440bx on June 27, 2020, 03:08:14 amAside from the difference in behavior between Delphi and FPC, I do not see how FPC expects ParameterA to be used. That's an easy one  just as you would expect it to. 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       {$IFDEF CALL_A} ParameterA[1] := 12;                {$ENDIF}  Not that the code actually makes any sense but i believe that was besides the point ? ---------------------------------------------------------------------- ASerge · 2020-06-27 08:42 Quote from: MarkMLl on June 27, 2020, 09:00:50 amI'd suggest replacing your  //{$DEFINE CALL_A}  and similar with  { DEFINE CALL_A}  etc., just in case different compilers or different FPC modes are getting confused by the continued presence of  In this case, after a double slash, the string is treated as a comment only. For 440bx: it was easier to provide the mentioned small example, so as not to confuse the people. This code is compiled in FPC, but not in Delphi: 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 type  ProcedureName = array[1..2] of Integer; procedure ProcedureA;  procedure ProcedureName(ProcedureName: ProcedureName);  begin  end;beginend; beginend. ---------------------------------------------------------------------- MarkMLl · 2020-06-27 08:49 Quote from: ASerge on June 27, 2020, 10:42:08 amQuote from: MarkMLl on June 27, 2020, 09:00:50 amI'd suggest replacing your  //{$DEFINE CALL_A}  and similar with  { DEFINE CALL_A}  etc., just in case different compilers or different FPC modes are getting confused by the continued presence of  In this case, after a double slash, the string is treated as a comment only. I know that's what /should/ happen... :-) MarkMLl ---------------------------------------------------------------------- TRon · 2020-06-27 08:57 Quote from: ASerge on June 27, 2020, 10:42:08 amCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i ... Now, that snippet seems to be more problematic. Can't seem to differentiate whatsoever inside the nested procedure itself by what you want to use, the procedure or the parameter. The type can still be addressed by using the unit scope. Would be nice to be able to use self in such cases ? ---------------------------------------------------------------------- 440bx · 2020-06-27 09:05 Quote from: TRon on June 27, 2020, 10:36:42 amThat's an easy one  just as you would expect it to. 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       {$IFDEF CALL_A} ParameterA[1] := 12;                {$ENDIF} I could have sworn I had tried that but, obviously I didn't since, you're right, FPC compiles that and, it most definitely should not since it should not see ProcedureName as an array type. Quote from: TRon on June 27, 2020, 10:36:42 amNot that the code actually makes any sense but i believe that was besides the point ? You're right.  The code doesn't make any sense which is why it shouldn't compile. Quote from: ASerge on June 27, 2020, 10:42:08 amFor 440bx: it was easier to provide the mentioned small example, so as not to confuse the people. This code is compiled in FPC, but not in Delphi: Point taken but, it wasn't my intention to confuse anyone, just to show what I had tried and provide an easy way for others to try those things and see the results for themselves. ---------------------------------------------------------------------- TRon · 2020-06-27 09:10 Quote from: 440bx on June 27, 2020, 11:05:30 am... it most definitely should not since it should not see ProcedureName as an array type. Isn't that a matter of interpretation ? I looked at it from the point: last defined, first served (or overruled if you so like since i can't seem to address the procedure itself whatsoever). Then it seems pretty logical ? edit: e.g. the use of the parenthesis should have helped the compiler but it didn't. ---------------------------------------------------------------------- 440bx · 2020-06-27 09:28 Quote from: TRon on June 27, 2020, 11:10:15 amQuote from: 440bx on June 27, 2020, 11:05:30 am... it most definitely should not since it should not see ProcedureName as an array type. Isn't that a matter of interpretation ? I looked at it from the point: last defined, first served (or overruled if you so like since i can't seem to address the procedure itself whatsoever). Then it seems pretty logical ? No, because when the compiler searches for the definition of ParameterName in the scopes stack, it's supposed to find the definition of ParameterName as a procedure first since the definition as an array is further above it and the scope for ProcedureName _must_ already be defined when it starts parsing the procedure parameters because they must belong to the ProcedureName procedure scope. Quote from: TRon on June 27, 2020, 11:10:15 amedit: e.g. the use of the parenthesis should have helped the compiler but it didn't. The compiler will not use syntactic elements to determine the scope of an identifier (though it can use them to eliminate ambiguities).  [addition] For scope resolution, [/addition] it will only use scope identifiers such as the program name, unit name, etc. ---------------------------------------------------------------------- TRon · 2020-06-27 09:37 Quote from: 440bx on June 27, 2020, 11:28:38 amQuote from: TRon on June 27, 2020, 11:10:15 amQuote from: 440bx on June 27, 2020, 11:05:30 am... it most definitely should not since it should not see ProcedureName as an array type. Isn't that a matter of interpretation ? I looked at it from the point: last defined, first served (or overruled if you so like since i can't seem to address the procedure itself whatsoever). Then it seems pretty logical ? No, because when the compiler searches for the definition of ParameterName in the scopes stack, it's supposed to find the definition of ParameterName as a procedure first since the definition as an array is further above it and the scope for ProcedureName _must_ already be defined when it starts parsing the procedure parameters because they must belong to the ProcedureName procedure scope. I am not sure i understood correctly what you wrote there, but i believe you are forgetting there that the parameter of that procedure is defined last in that scope. Thereby, It seems the compiler has a mind of its own sometimes  I've experimented a little, because i refuse to believe that i can't be able to 'reach' the procedure. In doing so there where some nice pointers from the compiler that seem to suggest different as what you wrote. I finally was able to come up with this declaration inside the nested proc. 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     AnotherName : procedure(P: ProcedureName) absolute ProcedureName; As such you seem then able to differentiate between all three. ---------------------------------------------------------------------- 440bx · 2020-06-27 10:04 Quote from: TRon on June 27, 2020, 11:37:05 amI am not sure i understood correctly what you wrote there, but i believe you are forgetting there that the parameter of that procedure is defined last in that scope. I'll try to explain in a hopefully easier to see way.  The scope stack when "ParameterName" is used as ParameterA's data type looks like this:Code: Text  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Scope stack   Standard block    TestProcedureName                        { new scope }      ProcedureName         { is array     }      ProcedureA                             { new scope }        ProcedureName       { is procedure } { new scope }          ParameterA Given that stack (which is searched bottom up): in (ParameterA : ProcedureName), ProcedureName should be seen as a procedure not as an array because the array definition is masked by the later definition of a procedure with the same name as the array type (ProcedureName) Quote from: TRon on June 27, 2020, 11:37:05 amThereby, It seems the compiler has a mind of its own sometimes  That, it does. QuoteI've experimented a little, because i refuse to believe that i can't be able to 'reach' the procedure. In doing so there where some nice pointers from the compiler that seem to suggest different as what you wrote. Even if there is a way to "reach" the procedure, I wouldn't trust it because that code shouldn't compile. ---------------------------------------------------------------------- TRon · 2020-06-27 10:08 Let me address the (for me) simplest first, i need some time to comprehend your elaborations (for which i thank you). Quote from: 440bx on June 27, 2020, 12:04:53 pmEven if there is a way to "reach" the procedure, I wouldn't trust it because that code shouldn't compile. i don't quite see what can be wrong with: 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 { This compiles with fpc 3.2.0  } {$MODE OBJFPC} {$DEFINE CALL_A}{$DEFINE CALL_B}{$DEFINE CALL_C} program try2confusefpc4;   type    ProcedureName = array[1..2] of integer;   procedure ProcedureA;     procedure ProcedureName(ProcedureName : ProcedureName);    var      AnotherName : procedure(P: ProcedureName) absolute ProcedureName;    SomeData    : ProcedureName = (1,2);    begin      { use local proc parameter ProcedureName }       {$IFDEF CALL_A} ProcedureName[1] := 12;                {$ENDIF}      {$IFDEF CALL_A} SomeData := ProcedureName;             {$ENDIF}      {$IFDEF CALL_A} SomeData[1] := ProcedureName[1];       {$ENDIF}      {$IFDEF CALL_A} SomeData[2] := ProcedureName[2];       {$ENDIF}       { use the nested proc ProcedureName, and the local proc parameter ProcedureName }       {$IFDEF CALL_B} AnotherName(ProcedureName);     {$ENDIF}      {$IFDEF CALL_B} AnotherName(SomeData);          {$ENDIF}    end;   begin  end; beginend. ---------------------------------------------------------------------- TRon · 2020-06-27 10:41 Quote from: 440bx on June 27, 2020, 12:04:53 pmI'll try to explain in a hopefully easier to see way.  The scope stack when "ParameterName" is used as ParameterA's data type looks like this: Code: Text  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Scope stack   Standard block    TestProcedureName                        { new scope }      ProcedureName         { is array     }      ProcedureA                             { new scope }        ProcedureName       { is procedure } { new scope }          ParameterA Given that stack (which is searched bottom up): Ok, that is what i was doing as well. For me meaning that parameterA is last in scope (or as i wrote, overrules the name of the procedure, by lack of better/precise/academic naming). Quotein (ParameterA : ProcedureName), ProcedureName should be seen as a procedure not as an array because the array definition is masked by the later definition of a procedure with the same name as the array type (ProcedureName) That is where we seem to have a difference in interpretation of what the compiler actually does. Tbh, I have no idea what the compiler actually should do in this kind of situation. It is not to mock your example(s) or question(s) but I try to bring things back to basic and as the compiler tells me to (eg workaround the issue, in case there is any). For me that also means: garbage in, garbage out. The reuse (and imho 'misuse') of the same names meaning different things is an accident waiting to happen. That is for example why we all agreed to use t-prefix for type declarations, and for sure you are allowed to ignore that agreement. But isn't that what leads up to these kind of confusions in the first place ? Quote from: TRon on June 27, 2020, 11:37:05 amThereby, It seems the compiler has a mind of its own sometimes  That, it does. I agree that it sometimes is confusing. At least it sometimes confuses the hell out of me. But ... I usually do not try to come up with such kind of examples as you just did  QuoteEven if there is a way to "reach" the procedure, I wouldn't trust it because that code shouldn't compile. I can agree to that to a certain point. If i go with the "garbage in, garbage out" then I wouldn't expect/rely on anything that comes out of the snippet i posted. If you bring things back to basics then the code usually becomes more clear and as such easier to be interpreted. If you truly have the opinion this code should not lead to anything useful that can be relied on, then it would perhaps be better to take it up with one of the developers ? The snippet seems logical enough to me (even though the actual code doesn't make any sense) but that doesn't have to mean that it actually is  edit ps: I am more confused about the fact that the compiler seems to use another approach for the variable declaration block then it does for the actual code. ---------------------------------------------------------------------- TRon · 2020-06-27 11:09 Quote from: 440bx on June 27, 2020, 12:04:53 pmEven if there is a way to "reach" the procedure, I wouldn't trust it because that code shouldn't compile. Ok, i see what you mean now  Sorry for not catching that sooner. Compiler tells something different at compile-time then the code actually seems to do at runtime. Code: [Select]Runtime error 214 at $00000022] ---------------------------------------------------------------------- PascalDragon · 2020-06-27 14:00 Quote from: 440bx on June 27, 2020, 03:08:14 amWith none of the defines active, unlike FPC, Delphi v2.0 does _not_ compile the program. Aside from the difference in behavior between Delphi and FPC, I do not see how FPC expects ParameterA to be used.  I suppose that, if it considers the sample program correct, there must be a way to use ParameterA but, it doesn't seem to like any of the expected constructions (and a few possibly unexpected ones too (unexpected constructions not included in the sample.)) The behavior of FPC is rather logical once you know what it does (btw.: both TP 6 and Delphi 10.2 fail with "Type Identifier Expected"): the procedure ProcedureName is not a type. Thus it's not eligible to be used as a type for a parameter (or variable). Thus the compiler continues to search for ProcedureName and finds the type declaration for the array which is an eligible type. You can confirm this by doing the following (of course you'll need to call both ProcedureName and ProcedureA): 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 ProcedureName(ParameterA: ProcedureName);begin{$if FPC_FULLVERSION >= 30200}  Writeln(GetTypeKind(ParameterA));{$else}  Writeln(PTypeInfo(TypeInfo(ParameterA))^.Kind); // requires use of unit TypInfo{$endif}end; This will print tkArray. If I remember correctly this specific behavior is not documented for Delphi (or TP) and thus FPC is free to follow its own rules here. ---------------------------------------------------------------------- TRon · 2020-06-27 15:32 Quote from: PascalDragon on June 27, 2020, 04:00:40 pmThe behavior of FPC is rather logical once you know what it does (btw.: both TP 6 and Delphi 10.2 fail with "Type Identifier Expected"): the procedure ProcedureName is not a type. Thus it's not eligible to be used as a type for a parameter (or variable). Thus the compiler continues to search for ProcedureName and finds the type declaration for the array which is an eligible type. ... If I remember correctly this specific behavior is not documented for Delphi (or TP) and thus FPC is free to follow its own rules here. A precise and clear explanation  However, it seems to be a bit more sophisticated than that. If the compiler "selects" on type compatibility, taking the first eligible into account then why does the following fail ? 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 ProcedureA;  procedure ProcedureName(ProcedureName : ProcedureName);  var    AnotherName : procedure(P: ProcedureName) is nested;  begin    AnotherName := @ProcedureName;  end;beginend; Error: Incompatible types: got "Pointer" expected ""  And since we're here, and on a more general note, should it (not) be possible to be able to reach the nested procedure using unitname.procedureA.ProcedureName ? (*) edit: (*) By that i meant from the local point of view, not globally. ---------------------------------------------------------------------- PascalDragon · 2020-06-27 15:57 Quote from: TRon on June 27, 2020, 05:32:35 pmQuote from: PascalDragon on June 27, 2020, 04:00:40 pmThe behavior of FPC is rather logical once you know what it does (btw.: both TP 6 and Delphi 10.2 fail with "Type Identifier Expected"): the procedure ProcedureName is not a type. Thus it's not eligible to be used as a type for a parameter (or variable). Thus the compiler continues to search for ProcedureName and finds the type declaration for the array which is an eligible type. ... If I remember correctly this specific behavior is not documented for Delphi (or TP) and thus FPC is free to follow its own rules here. A precise and clear explanation  However, it seems to be a bit more sophisticated than that. If the compiler "selects" on type compatibility, taking the first eligible into account then why does the following fail ? 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 ProcedureA;  procedure ProcedureName(ProcedureName : ProcedureName);  var    AnotherName : procedure(P: ProcedureName) is nested;  begin    AnotherName := @ProcedureName;  end;beginend; Error: Incompatible types: got "Pointer" expected ""  The ProcedureName inside the declaration of AnotherName is still a type and thus only the array type declared in the unit/program scope is eligible. The pointer on the other hand is a pointer to the parameter ProcedureName, because that is closer in the symtable than the procedure ProcedureName. You can also see that if you enabled typed pointers using $T+. The error will then read got "^ProcedureName" expected .... If the right side would be a routine then you would have a routine header as error (e.g.
), not merely a type name. Please note that if you have a function and the modeswitch Result is not enabled, but DuplicateLocals is (e.g. mode TP, but not mode FPC), then you won't be able to access the result variable, because the parameter hides the result variable. Something inconsistent however happens if you declare the procedure ProcedureName as a function and have a local variable of type ProcedureName, then the compiler will complain about an error in the type definition. That should definitely be straightened out... Quote from: TRon on June 27, 2020, 05:32:35 pmAnd since we're here, and on a more general note, should it (not) be possible to be able to reach the nested procedure using unitname.procedureA.ProcedureName ? No, as this would potentially conflict with functions that return records or classes. ---------------------------------------------------------------------- 440bx · 2020-06-27 18:05 Quote from: PascalDragon on June 27, 2020, 04:00:40 pmThe behavior of FPC is rather logical once you know what it does (btw.: both TP 6 and Delphi 10.2 fail with "Type Identifier Expected"): the procedure ProcedureName is not a type. Thus it's not eligible to be used as a type for a parameter (or variable). Thus the compiler continues to search for ProcedureName and finds the type declaration for the array which is an eligible type. That certainly raises a brow.  That's quite a departure from how scope resolution is supposed to work and opens the door to very subtle, very confusing and very bug prone constructs. Quote from: PascalDragon on June 27, 2020, 04:00:40 pmIf I remember correctly this specific behavior is not documented for Delphi (or TP) and thus FPC is free to follow its own rules here. I'd have to review the standards but, using type information to determine when the scope search should be ended isn't something I've ever seen mentioned in any Pascal standard.  Just wondering and curious now... is that how FPC implements function/procedure overloading ?... would that be the reason for that departure in the scope resolution method ? Quote from: PascalDragon on June 27, 2020, 05:57:57 pmSomething inconsistent however happens if you declare the procedure ProcedureName as a function and have a local variable of type ProcedureName, then the compiler will complain about an error in the type definition. That should definitely be straightened out... Using type information for scope resolution opens the door to many other inconsistencies, among them: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 B;var  B : integer;begin  if B = 1 then  // can access the variable but...  begin    dec(B);    B;           // using type to resolve scope should make this work.  end;end; beginend.    According to the scope resolution rule you just mentioned, the compiler should be able to resolve "B;" as a procedure call, yet it does _not_ and putting "();" after it, thereby indicating it is a procedure call, makes no difference. ---------------------------------------------------------------------- PascalDragon · 2020-06-27 19:18 Quote from: 440bx on June 27, 2020, 08:05:18 pmQuote from: PascalDragon on June 27, 2020, 04:00:40 pmThe behavior of FPC is rather logical once you know what it does (btw.: both TP 6 and Delphi 10.2 fail with "Type Identifier Expected"): the procedure ProcedureName is not a type. Thus it's not eligible to be used as a type for a parameter (or variable). Thus the compiler continues to search for ProcedureName and finds the type declaration for the array which is an eligible type. That certainly raises a brow.  That's quite a departure from how scope resolution is supposed to work and opens the door to very subtle, very confusing and very bug prone constructs. Please note that I'm not saying that the behavior is good, I'm just saying how it is (and it would probably not be easy to solve either). Quote from: 440bx on June 27, 2020, 08:05:18 pmQuote from: PascalDragon on June 27, 2020, 04:00:40 pmIf I remember correctly this specific behavior is not documented for Delphi (or TP) and thus FPC is free to follow its own rules here. I'd have to review the standards but, using type information to determine when the scope search should be ended isn't something I've ever seen mentioned in any Pascal standard.  Just wondering and curious now... is that how FPC implements function/procedure overloading ?... would that be the reason for that departure in the scope resolution method ? FPC collects all routine bodies (in other units until one without overload is encountered) and then matches the provided parameters to the headers of that collection. If only one is left, perfect, otherwise it's an error. This has nothing to do with type resolution. Quote from: 440bx on June 27, 2020, 08:05:18 pmQuote from: PascalDragon on June 27, 2020, 05:57:57 pmSomething inconsistent however happens if you declare the procedure ProcedureName as a function and have a local variable of type ProcedureName, then the compiler will complain about an error in the type definition. That should definitely be straightened out... Using type information for scope resolution opens the door to many other inconsistencies, among them: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 B;var  B : integer;begin  if B = 1 then  // can access the variable but...  begin    dec(B);    B;           // using type to resolve scope should make this work.  end;end; beginend.    According to the scope resolution rule you just mentioned, the compiler should be able to resolve "B;" as a procedure call, yet it does _not_ and putting "();" after it, thereby indicating it is a procedure call, makes no difference. The compiler differentiates between the body of a function and e.g. declarations. Slightly different rules can apply in these situations. For example (different use case, but slightly related): 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 type  TRecord = record    hwnd: HWND;    hwnd2: HWND;  end; This record declaration compiles in both FPC and Delphi. If the compiler would not differentiate between types and other symbols then the second field declaration would not be possible. ---------------------------------------------------------------------- 440bx · 2020-06-27 20:07 Quote from: PascalDragon on June 27, 2020, 09:18:39 pmPlease note that I'm not saying that the behavior is good, I'm just saying how it is (and it would probably not be easy to solve either). I'm relieved you're not saying it's good.  Solving the problem isn't hard, it simply shouldn't accept "ProcedureName" as a type name.  Delphi doesn't accept it, therefore, if FPC didn't accept it either, it would not affect Delphi compatibility. Quote from: PascalDragon on June 27, 2020, 04:00:40 pmFPC collects all routine bodies (in other units until one without overload is encountered) and then matches the provided parameters to the headers of that collection. If only one is left, perfect, otherwise it's an error. This has nothing to do with type resolution. That's good.  Quote from: PascalDragon on June 27, 2020, 04:00:40 pmThe compiler differentiates between the body of a function and e.g. declarations. Slightly different rules can apply in these situations. For example (different use case, but slightly related): 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 type  TRecord = record    hwnd: HWND;    hwnd2: HWND;  end; This record declaration compiles in both FPC and Delphi. If the compiler would not differentiate between types and other symbols then the second field declaration would not be possible. That's the kind of stuff that causes all kinds of ambiguous and inconsistent constructions.  In the example you showed above, FPC and Delphi, go looking for HWND as a type which is correct because in that definition TRecord isn't defined yet, therefore there is no conflict with the field named "hwnd" but, keeping the case exactly the same (scope-wise) but changing the types, leads to different results.  Just for fun, here is an 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 //{$DEFINE TEST_A}//{$DEFINE TEST_B} { with both defines commented out, Delphi compiles this test but, FPC doesn't } { with TEST_A enabled, neither FPC nor Delphi compile the test and the error  }{ message emitted isn't true.                                               } { with TEST_B enabled, neither compiler accepts the test because integer is a }{ boolean (thi8s test included just for fun)                                }  program TestFieldTypes; type  TRECORD_TYPE_A = record    FieldA           : integer;    Integer        : boolean;  end;   TRECORD_TYPE_B = record    FieldA           : integer;    Integer          : integer;  end;   TRECORD_TYPE_C = record                { TRECORD_TYPE_C fully defined here  }    FieldA           : integer;    TRECORD_TYPE_C   : boolean;  end;   integer      = boolean;    function FunWithTypes : boolean;  type    { complains that TRECORD_TYPE_C isn't fully defined but that's not true!  }    { both, Delphi and FPC fail this scope test                             }     TRECORD_TYPE_C = record      FieldA            : integer;       {$IFDEF TEST_A}         TRECORD_TYPE_C : TRECORD_TYPE_C  { Fully defined up there ^^^^^^^^^^^}      {$ENDIF}    end;   begin  result := false;     { for some reason FPC doesn't like "result" here     }  end;  var  AVariable : integer;   { it may look like an integer but, it isn't one      } begin  {$IFDEF TEST_B}    AVariable := AVariable + 1;  // incompatible types, cannot add 1 to integers                               // anymore!  {$ENDIF}end. Change the type from HWND to TRECORD_TYPE_C and things don't work the same way anymore. Not really directly related to the topic but, combine the above with the ability of redefining standard types (such as integer) and some rather "interesting" results can be had.  Same applies to the presence of TRECORD_TYPE_A and TRECORD_TYPE_B (I simply couldn't resist.) Talking about "result"(s),  why does FPC complain about "result" in the function ?... Delphi has no problem with it and, I don't see why FPC would. ---------------------------------------------------------------------- Bart · 2020-06-27 20:17 Quote from: 440bx on June 27, 2020, 10:07:43 pmTalking about "result"(s),  why does FPC complain about "result" in the function ?... Delphi has no problem with it and, I don't see why FPC would. Needs {$mode objfpc  or delphi} or {$modeswitch result} Bart ---------------------------------------------------------------------- 440bx · 2020-06-27 20:27 Quote from: Bart on June 27, 2020, 10:17:04 pmNeeds {$mode objfpc  or delphi} or {$modeswitch result} Thank you Bart.  I didn't realize that "result" needed to be "enabled" with a switch. ---------------------------------------------------------------------- PascalDragon · 2020-06-28 14:18 Quote from: 440bx on June 27, 2020, 10:07:43 pmQuote from: PascalDragon on June 27, 2020, 09:18:39 pmPlease note that I'm not saying that the behavior is good, I'm just saying how it is (and it would probably not be easy to solve either). I'm relieved you're not saying it's good.  Solving the problem isn't hard, it simply shouldn't accept "ProcedureName" as a type name.  Delphi doesn't accept it, therefore, if FPC didn't accept it either, it would not affect Delphi compatibility. Solving the problem is not as easy as you think. Quote from: 440bx on June 27, 2020, 10:07:43 pmChange the type from HWND to TRECORD_TYPE_C and things don't work the same way anymore. The compiler complains about the record not being fully defined, because the record type that is currently declared must be in scope to allow constructs 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 type  TMyRecord = record    f: ^TMyRecord;  end; A pointer to the record can be used, but the non-pointer type is not allowed (except for method declarations when modeswitch AdvancedRecords is enabled, because they don't need the size of the record, yet). Quote from: 440bx on June 27, 2020, 10:07:43 pmNot really directly related to the topic but, combine the above with the ability of redefining standard types (such as integer) and some rather "interesting" results can be had.  Same applies to the presence of TRECORD_TYPE_A and TRECORD_TYPE_B (I simply couldn't resist.) First of Integer is not a built in type, it's just an alias for SmallInt in unit System or LongInt in unit ObjPas (if modes Delphi, ObjFPC or MacPas are used). Second you can use builtin types without any problems, because they are in fact namespaced to the System unit. Thus you can declare a type LongInt = Boolean, but continue to use the builtin type using System.LongInt. The compiler even allows you to redefine some apparent keywords like Break or Continue (and yes, that was a fun debug session when I discovered that back when I still used Delphi 6 ). Quote from: 440bx on June 27, 2020, 10:07:43 pmTalking about "result"(s),  why does FPC complain about "result" in the function ?... Delphi has no problem with it and, I don't see why FPC would. The default mode of FPC is mode FPC which is essentially a mode TP with some extensions (like overloading). Use of the Result variable is only enabled in the "modern" modes like Delphi or ObjFPC or if modeswitch Result is used.