Is it good practice to exploit a compiler bug? - delphi

Recently I found some odd-looking (to me) Delphi code and I have isolated it to a separate small project. Here is what I discovered. Unit1 compiles with no errors. Unit2 (which I provide for comparison) does not. The difference is in the way that Classes is used.
unit Unit1;
interface
uses Classes; // difference here
type TThread = class(Classes.TThread)
public
property Terminated;
end;
implementation
end.
Unit2 does not compile. Various errors are produced.
unit Unit2;
interface
uses System.Classes; // difference here
type TThread = class(Classes.TThread)
public
property Terminated;
end;
implementation
end.
[dcc32 Error] Unit1.pas(7): E2003 Undeclared identifier: 'Classes'
[dcc32 Error] Unit1.pas(7): E2029 ',' or ':' expected but ')' found
[dcc32 Error] Unit1.pas(9): E2147 Property 'Terminated' does not exist in base class
So my concern is that this project is exploiting a compiler bug to achieve it's goals. The compiler bug might be fixed in a later release, and then the code won't work anymore.

There is no compiler bug that makes Unit1 compile. It compiles because in the project setting the entry for Unit Scope Names contains at least the item System, which is used to resolve the reference to Classes in the full name System.Classes. As the uses contains Classes, the reference to Classes.TThread also succeeds.
In Unit2 the uses contains System.Classes. Therefore the reference Classes.TThread cannot be resolved anymore. Change it to System.Classes.TThread and it works.

If you uses System.Classes you must also use System.Classes when referring to the classes unit in the code as shown below.
unit Unit2;
interface
uses System.Classes; // difference here
type TThread = class(System.Classes.TThread)
public
property Terminated;
end;
implementation
end.

Related

Delphi - How do you resolve a conflict when a unit name is the same as a property name?

The trivial example below is a condensation of a problem I had trying to resolve a conflict where I had an enumerated type member with the same name as a VCL member.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
type
TSomeType = (
alNone,
alSome,
alMany) ;
procedure TForm1.FormCreate(Sender: TObject);
begin
Self.Align := alNone ; // 1. type mismatch
Self.Align := Controls.alNone ; // 2. "Controls" is also a property of TForm
end ;
end.
The first assignment fails because the compiler thinks alNone is the one I declared and not the TAlign member defined in Controls.pas.
The second one fails because it took Controls to mean the TForm property of that name.
I realise there are ways around this (renaming the alNone member being the simplest), but I'm curious as to whether there is a way of qualifying a reference to a property in another unit where the unit name conflicts with an identifier in the current scope.
Qualify it with the type name:
TAlign.alNone
I had not realised when I wrote this that the compiler version was relevant. This syntax only became available in Delphi 2010 or XE. There the answer is not appropriate for the tagged version, Delphi 2007. Deltics answer covers much more detail.
As David's answer suggests, for an enum type or other situation where a type can be used to qualify the identifier involved then you can of course simply use the type name as required:
someAlign := TAlign.alNone;
someMyType := TMyType.alNone;
This use of enums is referred to as "scoped enums" and is not supported in older versions of the Delphi compiler. I believe XE2 may have been when it was introduced. Certainly this was the version that made scoping enums in this way mandatory by default.
Though it can be turned off via a compiler directive. When turned off, you can still use scoped enums but you are not required to.
In versions that support this, you must qualify any enums that are defined while this is turned on. You can choose whether to quality or not when using enums that are defined when this is turned off.
type
{$SCOPEDENUMS ON}
TFoo = (Black, White); // MUST qualify: eg. "TFoo.Black"
{$SCOPEDENUMS OFF}
TBar = (Black, White); // MAY qualify or not if/as needed
For older versions of Delphi without scoped enum support, or in situations where the identifier is not an enum member and cannot otherwise be qualified by type - for example if your identifiers conflict with some unit-level identifier (e.g. such as mrOk, in Controls) then you need to a little bit more work, but not much.
In these cases, simply define a new constant to create an unambiguous "local alias" for the constant in the other unit, and introduce this where the unit name is unambiguous. Similar to:
type
TMyResult = (
mrOk,
mrFailed) ;
const
Controls_mrOk = Controls.mrOk; // mrOk is a const, not an enum member

Delphi giving syntax errors: . expected but ; found error

I am toying around in Delphi XE6, and add this procedure on a button:
procedure TTabbedForm.btnLoadingClick(Sender: TObject);
var
dlg: Unit2;
begin
dlg := Form2.Create(nil);
Form2.ShowModal();
end;
Delphi gives me the following error while compiling:
[DCC Error] TabbedTemplate.pas(53): E2029 '.' expected but ';' found
[DCC Error] TabbedTemplate.pas(55): E2029 ';' expected but 'BEGIN' found
[DCC Fatal Error] Speelpleintjes.dpr(7): F2063 Could not compile used unit 'TabbedTemplate.pas'
Line 53 being: dlg: Unit2;
Honestly i'm quite puzzled, the syntax seems correct, the procedure is auto generated from the events tab.
Any suggestions?
If Unit2 is another unit in your project (judging by it's name it probably is), it can't be used as a type directly. Units only declare things. That's why the compiler is expecting a . since you can prefix an identifier with the unit's name to direct to a declaration in that specific unit.
To correct this, write dlg: TForm2;
Also it's common practice to call constructors from the class declaration, not a variable so change it to:
dlg := TForm2.Create(nil);
Or use the Form2 variable, which typically will hold an instance of TForm2 already. By default extra forms added to a Delphi project are created hidden, ready for Show or ShowModal.

Why doesn't the Delphi compiler warn for a redefined constant?

A colleague of mine bumped into a constant that had suddenly 'changed value';
Turned out, it was redeclared:
unit Unit1;
interface
const
MyConstant = 1;
implementation
end.
--
unit Unit2;
interface
const
MyConstant = 2;
implementation
end.
--
Uses Unit1, Unit2;
// Uses Unit2, Unit1;
procedure TFrmRedefineConstant.FormShow(Sender: TObject);
begin
ShowMessage('MyConstant: ' + IntToStr(MyConstant));
end;
This shows 2. If you swap the unit order in the Uses statement, it shows 1.
Fine, but why does the Delphi compiler not warn about the duplicate constant name (That would be very helpful)?
Is there anything I can do to enable warnings (does not look that way).
Because of Delphi documented scoping rules. From the Language Guide:
The order in which units appear in the uses clause determines the
order of their initialization and affects the way identifiers are
located by the compiler. If two units declare a variable, constant,
type, procedure, or function with the same name, the compiler uses the
one from the unit listed last in the uses clause. (To access the
identifier from the other unit, you would have to add a qualifier:
UnitName.Identifier.)
This is the expected behaviour since Turbo Pascal 4.0, which introduced units.

delphi XE multi-unit namespace question

I am reading RAD Studio Documentaion in Delphi XE.
here a some texts.
[ Delphi Reference -> Delphi Language Guide -> Programs and Units -> Using Namespaces -> Searching Namespaces -> Multi-unit Namespaces ]
Multi-unit Namespaces
Multiple units can belong to the same namespace, if the unit declarations refer to the same namespace.
For example, one can create two files, unit1.pas and unit2.pas, with the following unit declarations:
// in file 'unit1.pas'
unit MyCompany.ProjectX.ProgramY.Unit1
// in file 'unit2.pas'
unit MyCompany.ProjectX.ProgramY.Unit2
In this example, the namespace MyCompany.ProjectX.ProgramY logically contains all of the interface symbols from unit1.pas and unit2.pas.
Symbol names in a namespace must be unique, across all units in the namespace.
In the example above, it is an error for Unit1 and Unit2 to both define a global interface symbol named mySymbol
I tested this.
code below .
-----------------------------------------------------------------
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils,
Lib.A in 'Lib.A.pas',
Lib.B in 'Lib.B.pas';
begin
WriteLn ( TestValue ) ;
ReadLn ;
end.
-----------------------------------------------------------------
unit Lib.A;
interface
const TestValue : Integer = 10 ;
implementation
end.
-----------------------------------------------------------------
unit Lib.B;
interface
const TestValue : Integer = 10 ;
implementation
end.
This is not error. Why? I don't understand.
Your code does not match the documentation. Documentation explicitly states that file name of 'unit MyCompany.ProjectX.ProgramY.Unit1' is unit1.pas, not MyCompany.ProjectX.ProgramY.Unit1.
I do not believe, however, that this feature is implemented at all. If I change your code to store first unit in file a.pas and second unit in file b.pas, units don't compile at all and the error is
[DCC Error] A.pas(1): E1038 Unit identifier 'Lib.A' does not match file name
(Which is exactly as I was expecting to see.)
In your case there's no conflict because you can always use a full name of 'conflicting' global - Lib.A.TestValue and Lib.B.TestValue.
In Delphi.NET (before Prism): unit name = namespace. That's the way they used it in that time - and in dotNET an unit was really an namespace (inclusive appear as such in generated IL).
In native Delphi, I don't see the difference (if that exists at all).

Delphi - Using interfaces from another unit

I am constantly getting the: Undeclared identifier for an interface type I have defined in another unit.
Here is what I have:
unit Drawers;
interface
implementation
type
IDrawer = interface
['{070B4742-89C6-4A69-80E2-9441F170F876}']
procedure Draw();
end;
end.
unit Field;
interface
uses
Graphics, Classes, Drawers;
TField = class(TInterfacedObject, IField)
private
FSymbolDrawer: IDrawer;
At FSymbolDrawer I get the complier error.
Of course I have the uses Drawers; in the unit where TField is defined.
What's this about?
Thank you
In the unit Drawers the type declaration of IDrawer has to be in the interface part of the unit. You have inserted it in the implementation part where it is only visible for in-unit declarations.
Here is the code:
unit Drawers;
interface
type
IDrawer = interface
['{070B4742-89C6-4A69-80E2-9441F170F876}']
procedure Draw();
end;
implementation
end.
Which uses clause do you add Drawers to? It has to be in the interface uses clause (above the definition of TField that uses it).

Resources