Recent

Author Topic: [SOLVED] Issue with precedence of a duplicate identifier in 2 different Units  (Read 3527 times)

paweld

  • Hero Member
  • *****
  • Posts: 1519
Re: Issue with precedence of a duplicate identifier in 2 different Units
« Reply #15 on: September 26, 2025, 07:27:43 pm »
Quote from: 440bx
The last declaration should _always_ take precedence, no exceptions.
It makes no sense, in this case the structures defined in the interface section of the current unit are unusable after adding units with structures of the same names to uses section in the implementation section.
 You could precede them with the name of the current unit, but that would be the cause of unnecessary prefixes.
Best regards / Pozdrawiam
paweld

Hartmut

  • Hero Member
  • *****
  • Posts: 1028
Re: Issue with precedence of a duplicate identifier in 2 different Units
« Reply #16 on: September 26, 2025, 07:29:25 pm »
The last declaration should _always_ take precedence, no exceptions.
Only have precedence when scope same. When scope not same then scope have priority rule.
Please wherefrom do you "know" that?
Are you one of the FPC developers or is this only a guess from you?
Is there any documentation about this "same scope priority rule"?

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 11911
  • Debugger - SynEdit - and more
    • wiki
Re: Issue with precedence of a duplicate identifier in 2 different Units
« Reply #17 on: September 26, 2025, 07:41:08 pm »
The last declaration should _always_ take precedence, no exceptions.
Only have precedence when scope same. When scope not same then scope have priority rule.
Please wherefrom do you "know" that?
Are you one of the FPC developers or is this only a guess from you?
Is there any documentation about this "same scope priority rule"?

I am with Paweld on this. Though, I haven't bothered looking up the docs...

In any case, in the following code, the compiler clearly goes: "scope first". And not "last first".
It prints out: 3
Even if "BAR = 4" is the last declaration of BAR (before the writeln line). And even though, that declaration also is in scope.

Code: Pascal  [Select][+][-]
  1. program Project1;
  2. {$Mode objfpc}
  3. type
  4.   TFoo = class
  5.     const BAR = 3;
  6.     procedure PrintBar;
  7.   end;
  8.  
  9. const BAR = 4;
  10.  
  11. procedure TFoo.PrintBar;
  12. begin
  13.   writeln(BAR);
  14. end;
  15.  
  16. var a: TFoo;
  17. begin
  18.   a.PrintBar;
  19.   readln;
  20. end.
  21.  


If it wasn't "scope first" then a lot of code would fail, as somewhere in some unit appears a global var with the same name as a field of some class, and whenever that unit would be used after the class declaration, it would cause havoc.
« Last Edit: September 26, 2025, 07:46:17 pm by Martin_fr »

Thausand

  • Sr. Member
  • ****
  • Posts: 444
Re: Issue with precedence of a duplicate identifier in 2 different Units
« Reply #18 on: September 26, 2025, 07:48:42 pm »
Please wherefrom do you "know" that?
Experience many different (pascal) compiler / dialect.

Quote
Are you one of the FPC developers or is this only a guess from you?
I not official developer. Is no guess, can test.

Quote
Is there any documentation about this "same scope priority rule"?
It written in language definition (I not have direct link, must have look up first). I have remember Delphi have better explain for scope rule and how work.

I make discussion wonder, why is need think Pascal compiler is same working that have c-compiler ?


Thausand

  • Sr. Member
  • ****
  • Posts: 444
Re: Issue with precedence of a duplicate identifier in 2 different Units
« Reply #19 on: September 26, 2025, 08:17:30 pm »
I have remember Delphi have better explain for scope rule and how work.

https://docwiki.embarcadero.com/RADStudio/Sydney/en/Using_Namespaces_with_Delphi
Quote
Searching Namespaces

A unit must declare the other units on which it depends. The compiler must search these units for identifiers. For units in explicit namespaces the search scope is already known, but for generic units, the compiler must establish a namespace search scope.

Consider the following unit and uses declarations:

unit MyCompany.ProjectX.ProgramY.MyUnit1;
uses MyCompany.Libs.Unit2, Unit3, Unit4;

These declarations establish MyUnit1 as a member of the MyCompany.ProjectX.ProgramY namespace. MyUnit1 depends on three other units: MyCompany.Libs.Unit2, and the generic units, Unit3, and Unit4. The compiler can resolve identifier names in Unit2, since the uses clause specified the fully qualified unit name. To resolve identifier names in Unit3 and Unit4, the compiler must establish a namespace search order.
Namespace search order

Search locations can come from three possible sources: compiler options, the project default namespace, and the current unit's namespace.

The compiler resolves identifier names in the following order:

1  The current unit namespace (if any)
2  The project default namespace (if any)
3  Namespaces specified by compiler options.

440bx

  • Hero Member
  • *****
  • Posts: 5889
Re: Issue with precedence of a duplicate identifier in 2 different Units
« Reply #20 on: September 26, 2025, 09:27:13 pm »
Actually, the compiler is right.  It is the value 5 that should be output.


Here is why:
Code: [Select]
uses U1;

begin
  test;

  readln;
end.
in that program, U1 is in scope first.  In that scope, Elements is declared to have the value 5. When "test" (immediately after the "begin") is executed, it is the value 5 that is in the nearest scope, not the value 6 (as it is in the scope of U1 U2 but, that is not the active scope when "test" is executed.)

ETA:

corrected the unit in scope.



« Last Edit: September 26, 2025, 09:40:29 pm by 440bx »
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

ASerge

  • Hero Member
  • *****
  • Posts: 2469
Re: Issue with precedence of a duplicate identifier in 2 different Units
« Reply #21 on: September 27, 2025, 06:11:19 am »
From Namespaces: Dotted Units: When resolving symbols, unit scopes always take precedence over symbols inside units.
That is, the constant Elements in this unit takes precedence over the constant Elements inside the U2 unit.

Hartmut

  • Hero Member
  • *****
  • Posts: 1028
Re: Issue with precedence of a duplicate identifier in 2 different Units
« Reply #22 on: September 27, 2025, 09:26:28 am »
Thanks a lot to all for your contribution.

In any case, in the following code, the compiler clearly goes: "scope first". And not "last first".
It prints out: 3
I think this is a different case. 'const BAR = 3' is inside a class, which could be seen similar to the mechanism of a local const inside a procedure, which has precedence over a global const with the same name.

This Topic is about precedence of duplicate identifiers caused by including Units. And the official documentation says only, that the last one wins and does not mention any exeptions about scopes.



Please wherefrom do you "know" that?
Experience many different (pascal) compiler / dialect.
Is this "sure enough" to change the documentation and the Code Tools?

I have remember Delphi have better explain for scope rule and how work.
https://docwiki.embarcadero.com/RADStudio/Sydney/en/Using_Namespaces_with_Delphi

Quote
"In Delphi, a namespace is a container of Delphi units."
From my understanding this is about, where/how the compiler searches to find the correct Unit, but not about the precedence of duplicate identifiers caused by including Units.



From Namespaces: Dotted Units: When resolving symbols, unit scopes always take precedence over symbols inside units.
That is, the constant Elements in this unit takes precedence over the constant Elements inside the U2 unit.
From my understanding this is only about, how the Compiler handles dotted unit names, if multiple Units with ambiguous names are included in the interface section of a program. But in "my" case the conflict is between a local identifier and a Unit, which is included in the implementation section.

Summary: please don't misunderstand me: I absolute do not want to insist that the Compiler is wrong!! I only hope to get either a clear, unambiguous and reliable statement from an experienced FPC developer or to find a clear, unambiguous and reliable documentation.

If we accept the Compiler to be correct, then we must change the documentation accordingly and then the Code Tools would be wrong and had to be also changed. Of course both should we do only, if we are 100% sure. But not from "guessing" or "concluding" or "the Compiler can't be wrong" ;-)

440bx

  • Hero Member
  • *****
  • Posts: 5889
Re: Issue with precedence of a duplicate identifier in 2 different Units
« Reply #23 on: September 27, 2025, 10:10:23 am »
@Hartmut,

I'm going to try to explain this in a way that is reasonably easy to understand and see why things happen the way they happen and, show why the compiler is correct.

First, I made some minor changes to the files you provided to make some things more related to each other.  The changes don't make any difference at all and, as Mathematicians say: without loss of generality.

Main program:
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2.  
  3. program _test;
  4.  
  5. uses U1, U2;  { note that both units are used }
  6.  
  7. begin
  8.   test;
  9.  
  10.   readln;
  11. end.  
  12.  

Unit U1:
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2.  
  3. unit U1;
  4.  
  5. interface
  6.  
  7. var v : DWORD = 1;   { U1 has the value 1 for v }
  8.  
  9. procedure test;
  10.  
  11. implementation
  12.  
  13. uses U2; { has v : DWORD = 2; }
  14.  
  15. procedure test;
  16. begin
  17.   writeln('v = ', v);   { outputs 1 as it should  }
  18. end;
  19.  
  20. begin
  21.  
  22. end.    
  23.  

Unit U2:
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2.  
  3. unit U2;
  4.  
  5. interface
  6.  
  7. var v : DWORD = 2;  { U2 has the value 2 for v }
  8.  
  9. implementation
  10.  
  11. begin
  12.  
  13. end.  
  14.  

At first sight, it is reasonable to think that the presence of U2 would cause the value to be output to be "2" but, that would be incorrect and here is why:

When the compiler is compiling U1, it is in the scope of U1 (that's what it's compiling) and in that scope the variable v with value 1 is defined.  The compiler uses that v because that variable with value 1 is in the scope of U1.  The important thing to realize is that the declaration in U2 does NOT mask the v in U1 because U2 is in a different scope and the compiler will always resolve references using the current scope and only inspect other available scopes if it cannot resolve a reference.

When the compiler compiles "writeln('v = ', v);" which is a statement in the scope of U1, it finds a declaration of v in the current scope (U1), therefore it uses that (as it should.)  Therefore, the address that writeln uses is the address of U1.v (as it should.)

Now, for fun and maybe some enlightenment comment out the declaration "var v : DWORD = 1;" in U1, recompile and re-run then the value 2 will be output.  The reaon is because when the compiler was compiling "writeln('v = ', v);" it did NOT find a variable v in the CURRENT scope, because of this it was forced to search other available scope which in this case is U2 and, it finds a variable v there and uses that one. 

The important thing to understand is that unlike what happens in nested functions/procedures, the re-declaration of an identifier in units does not mask declarations of the same identifier in inner scopes.  IOW, because U2 is a unit, the presence of a variable v there does NOT mask the presence of the variable v in U1.

Also, to show that units don't mask each other, in the main program change the sequence from "U1, U2;" to "U2, U1;" in the uses clause and you'll see it makes no difference.

HTH.




« Last Edit: September 27, 2025, 10:14:56 am by 440bx »
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 11911
  • Debugger - SynEdit - and more
    • wiki
Re: Issue with precedence of a duplicate identifier in 2 different Units
« Reply #24 on: September 27, 2025, 10:16:45 am »
In any case, in the following code, the compiler clearly goes: "scope first". And not "last first".
It prints out: 3
I think this is a different case. 'const BAR = 3' is inside a class, which could be seen similar to the mechanism of a local const inside a procedure, which has precedence over a global const with the same name.

A local var is always going to be the "last by order" too. Try to write some code, where there is a global declaration between the local declaration and the local code body.

Yet, yes, I agree, I think a local declaration takes precedence due to scoping, not due to ordering. => And yet, I have not searched if I can find that documented.




Duplicate identifiers in the *exact* same scope are usually not allowed (I can't think of a case where they would be / apart from modeswitches for fields vs local - but those are diff scopes anyway).

Then (and again, not checked for docs) all resolution should be by scope, and by scope only, and never ever by "order of appearance". (Only: certain scopes can depend on some "order"...)

Units are scopes too.
Used units are a different scope than the current unit. And each used unit has a scope of its own (and those scopes are ordered by the reverse order in which those units are listed in uses).


The entire issue arises because https://www.freepascal.org/docs-html/ref/refsu102.html says
Quote from: https://www.freepascal.org/docs-html/ref/refsu102.html
that an identifier of a unit can be redefined
(Leaving aside that the entire help pages uses "unit" without much clarification for the "used unit" and the "current unit"....)

The problem is that it says "redefined". => IMHO that is plain wrong.

Quote from: https://www.freepascal.org/docs-html/ref/refsu102.html
Code: Pascal  [Select][+][-]
  1. Uses UnitA;
  2.  
  3. { Redeclaration of MyType}  
  4. Type MyType = Integer;  

- The Identifier "UnitA.MyType" is never "redeclared". It stays as it is.
- A new identifier "{prog.}MyType" is declared. And it hides the existing identifier.

But anyway, even then it should be pointed out that the scoping rules apply independent of the order of the "uses" vs the current-unit "type MyType =".
Well, scopes do/should not depend on their content. The scope of a "unit" or "program" is always the same, and affects anything that is inside it.

Of course embedded/nested scopes (e.g. class) may take precedence. And a "class" adds its parent as a scope, that takes precedence over the "unit", even if it comes from another unit.

But a "uses" clause, does not give precedence to the scope of the used units. If it did, then that precedence would be "global", i.e. visible in the entire "unit" in which the uses occurs, even before the uses.

So "uses" clauses have a curiosity regarding scoping.
We can not simply say, each used unit is added as a new scope (with the last having the highest prior). At least not to the entire using unit.

"uses" in interface are adding scopes visible to the entire using unit.

But "uses" in implementation add scopes only visible to the implementation section. So, does that mean the implementation section is treated as a separate scope? (however sharing uniqueness for identifier names with the interface section / like locals vs fields?)

Or is that just an effect that within a scope, there is no (or limited) forward visibility? But then "uses" would add scopes limited by forward visibility (that differs from order of declaration, because once visible scoping follows scoping rules, not ordering / see class inheritance / also direct interface content still comes before implementation uses and that would be scoping rule over ordering)



Anyway, all that said, it does not replace the missing (or missing clarity?) documentation.
« Last Edit: September 27, 2025, 10:28:52 am by Martin_fr »

creaothceann

  • Full Member
  • ***
  • Posts: 220
Re: Issue with precedence of a duplicate identifier in 2 different Units
« Reply #25 on: September 27, 2025, 11:26:32 am »
Duplicate identifiers in the *exact* same scope are usually not allowed (I can't think of a case where they would be / apart from modeswitches for fields vs local - but those are diff scopes anyway).

It's allowed for function overloading.
Quote from: Thaddy
And don't start an argument, I am right.
Quote from: Thaddy
You have a thorough misunderstanding of what I wrote. Can you provide an example this time? I doubt it. (because you never do out of incompentence)

Hartmut

  • Hero Member
  • *****
  • Posts: 1028
Re: Issue with precedence of a duplicate identifier in 2 different Units
« Reply #26 on: September 27, 2025, 01:41:42 pm »
Thanks a lot to 440bx and Martin_fr for your detailed posts and your effort.

You describe, how the Compiler currently works - which I never contradicted - and you explain and apply a rule about scopes, which could very well be the reason for this behavior, although this rule is definitely not mentioned in the "official documentation" (as far we have found until now) about the precedence of duplicate identifiers caused by including Units - although this scope rule would be a very important exception of the everybody known rule "the last one wins".

Meanwhile I "assume" that the Compiler probably works correct. But before we change the documentation accordingly and also change the Code Tools accordingly of course we should be 100% sure, that the current behavior is definitely intended. Therefore I still hope to get an unambiguous and reliable statement from an experienced FPC developer.

440bx

  • Hero Member
  • *****
  • Posts: 5889
Re: Issue with precedence of a duplicate identifier in 2 different Units
« Reply #27 on: September 27, 2025, 11:10:23 pm »
although this scope rule would be a very important exception of the everybody known rule "the last one wins".
That behavior is not an exception to the rule "the last one wins".

Consider this little program:
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2.  
  3. program _MostRecent;
  4.  
  5.  
  6. { think of the next few lines as being unit U1, therefore in the scope of U1 }
  7.  
  8. var v : DWORD = 1;   { U1 has the value 1 for v }
  9.  
  10.  
  11.  
  12. { now think of the next few lines as being unit U2 }
  13.  
  14. function U2() : DWORD;
  15.   { what follows is in the scope of U2 }
  16.  
  17. var v : DWORD = 2;
  18. begin
  19.   result := v;
  20. end;
  21.  
  22. { end of U2 }
  23.  
  24. { NOTE that the most recent declaration of v gives v the value 2              }
  25.  
  26.  
  27. { back in the scope of U1 }
  28.  
  29. procedure test;         { define test _after_ U2 just to make it clearer }
  30. begin
  31.   writeln('v = ', v);   { outputs 1 as it should  }
  32. end;
  33.  
  34.  
  35. begin
  36.   test();
  37.  
  38.   readln;
  39. end.
  40.  
The last declared value of v gives v the value 2 (not 1 but 2) yet the program outputs 1 (as it should.)

The reason is because the last declaration of v that is in the current scope is "v : DWORD = 1".  Note that that declaration is not the last one in the text.  The last one in the text is "v : DWORD = 2" but that one is in a different scope which is why it is not used.

In this example, if you comment out "var v : DWORD = 1;"  and try to compile, you'll get a compiler error because there will no longer be a variable v accessible in the "scope of U1" (in this case the main program's scope), because the compiler isn't going to use the declaration in "function U2" because what is in there is NOT shared with the main program.  That's the difference when using units.  if instead of having function U2, you had a unit and a "uses U2" (as the original code did) then the compiler would be free to rummage in U2 to find a declaration for variable v.

The crucial fact is, in the current scope, the last declaration of variable v, gives v the value 1.  Textually, the last declaration gives the variable v the value 2 BUT, in both cases (this example and the units example), that value is in a different scope and the compiler will only search a different scope if there isn't a declaration in the current scope because the current scope is _always_, logically, the most recent/last one to have been declared.

HTH.


FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Hartmut

  • Hero Member
  • *****
  • Posts: 1028
Re: Issue with precedence of a duplicate identifier in 2 different Units
« Reply #28 on: September 28, 2025, 02:10:32 pm »
although this scope rule would be a very important exception of the everybody known rule "the last one wins".
That behavior is not an exception to the rule "the last one wins".
This "everybody known rule 'the last one wins'" was only related to the "precedence by including units" and of course not for other things like local or class identifiers etc.

PascalDragon

  • Hero Member
  • *****
  • Posts: 6230
  • Compiler Developer
Re: Issue with precedence of a duplicate identifier in 2 different Units
« Reply #29 on: September 29, 2025, 09:35:18 pm »
I'll make it quick as most has been said already: the compiler's behavior is the correct one here.

 

TinyPortal © 2005-2018