What does delphi compiler error E2134 mean? - delphi

In some code I am fixing up, which makes heavy use of generics and interfaced types, I am getting error
E2134, Type '<void>' has no type info.
I believe it is because I am in the middle of a refactor where some deeply nested set of units that all use generics are out of sync, but the error is not happening in a place where I can make use of the error message to fix the code, because there is nothing wrong with the code, at the location where the error is appearing.
Here is the context, mocked up, because I can not post the code, there is too much:
unit GenericThing;
...
interface
...
type
...
IThingListOf<ThingT> = interface( IThingContainer )
function getEnumerator: TEnumerator<ThingT>;
function getCount: Integer;
function getThing( Index: integer ): ThingT;
function getFirst: ThingT;
function IndexOf( value: ThingT): integer;
function addItem( const Thing: ThingT ): ThingT;
function removeItem( const Thing: ThingT ): Integer;
procedure clear;
procedure Sort; overload;
procedure Sort(const AComparer: IComparer<ThingT>); overload;
property Count: integer read getCount;
property First: ThingT read getFirst;
property Items[Index: integer]: ThingT read getThing; default;
end;
// error appears on whatever line number comes after the declaration of IThingListOf<ThingT>...end;
function AnythingYouLikeHere:Integer; // there is nothign wrong with this line, but you get the E2134 here.
It appears that the problem is in IThingContainer itself:
IThingContainer = interface ...
...
procedure DoSomething(const Param);
end;
THe above "const Param" has no type information. This is a weird (armpit) of Pascal/Delphi in my opinion, where you completely violate Wirth's idea of strong typing. It is about as weakly typed as a "void *" pointer in C, or the "Pointer" type in Delphi, but it is rarely used, except for in places like the standard pre-object-pascal RTL functions like Move, and so on. In my opinion, untyped parameters in interfaces, used in generics, should either be allowed, or disallowed, but not allowed sometimes, and disallowed other times.
This is a case of a Pascal feature from 1978 mixing badly with an ObjectPascal feature from 2009.

The error message means there's no type info available for the given type.
Here's a minimal program which produces the message:
type
{$M+}
IThing = interface
procedure P(const X);
end;
{$M-}
begin
end.
The problem, it would appear, is that IThingListOf<>, or one of its ancestors, was compiled with {$M+} active. The compiler presumes from this that you really want full type info for the interface; originally it was used by SOAP etc. support to produce stubs etc. The interface RTTI doesn't support untyped parameters (logically enough, they can't be marshalled by SOAP etc.) - and they show up as being of void type, and you end up with this error message.
The solution is to either not use {$M+} - though presumably the RTTI is being used, otherwise it wouldn't be enabled - or use e.g. Pointer instead, and pass the address explicitly.

It's kinda hard to say from this, especially without the definition of IThingContainer available. If you comment the interface definition out, will it compile past that point? Obviously it will break when you try to create a class that implements the interface, but does commenting it out fix this problem?
If so, then the compiler's choking on something in the interface definition. Try commenting out parts of it to figure out where the problem is. If not, then you'll have to look somewhere else.

Related

Generic function with interface constraint [duplicate]

I just tried my first use of generics in Delphi 2009 and am perplexed on how to use a generic type as the input to the Supports function used to see if an object implements a given interface. I've created a small sample illustrating the problem.
Given the following types and utility function:
IMyInterface = interface
['{60F37191-5B95-45BC-8C14-76633826889E}']
end;
TMyObject = class(TInterfacedObject, IMyInterface)
end;
class function TFunctions.GetInterface<T>(myObject: TObject): T;
var
specificInterface: T;
begin
// This would compile, but looses the generic capability
//Supports(myObject, IMyInterface, specificInterface);
// This results in compile errors
Supports(myObject, T, specificInterface);
result := specificInterface;
end;
and the following code snippet:
class procedure TFunctions.Test;
var
myObject: TMyObject;
myInterface: IMyInterface;
begin
myObject := TMyObject.Create;
myInterface := GetInterface<IMyInterface>(myObject);
end;
I would expect no problems but I get the following compile time errors:
[DCC Error] GenericExample.pas(37): E2029 '(' expected but ',' found
[DCC Error] GenericExample.pas(37): E2014 Statement expected, but expression of type 'T' found
I'm not sure what the compiler is expecting me to do with the T when used as the actual argument to the function.
I've searched around quite a bit and haven't been able to crack this one. A part of me suspects that if I could understand how an interface name gets converted to the IID: TGUID type during compilation, when using a concrete interface name, I could make some headway, but that has evaded me also.
Any help is much appreciated.
There is no guarantee that T has a GUID associated with it, and there is no means in the language to write a constraint on the type parameter to make that guarantee.
The interface name is converted into a GUID by the compiler looking up the name in the symbol table, getting the compiler's data structure representing the interface, and checking the corresponding field for the GUID. But generics are not like C++ templates; they need to be compiled and type-checked and known to work for any valid type parameter, and that means constraining the type parameter in its declaration.
You can get the GUID using RTTI (first checking that T does indeed represent an interface) with something like GetTypeData(TypeInfo(T))^.Guid and pass the GUID to Supports that way.
Why are you even bothering?
To use this TFunctions.GetInterface you need:
an interface
an object reference
If you have those, then you can just call Supports() directly:
intf := TFunctions.GetInterface<IMyInterface>(myObject);
is exactly equivalent to:
Supports(IMyInterface, myObject, intf);
Using generics here is a waste of time and effort and really begs the question "Why Do It?".
It is just making things harder to read (as is so often the case with generics) and is more cumbersome to use.
Supports() returns a convenient boolean to indicate success/failure, which you have to test for separately using your wrapper:
intf := TFunctions.GetInterface<IMyInterface>(myObject);
if Assigned(intf) then
// ...
versus:
if Supports(IMyInterface, myObject, intf) then
// We can use intf
When creating wrappers around functionality it is generally the case that the result is an improvement in readabilty or usability.
imho this fails on both counts and you should just stick with the Supports() function itself.

Generic methods type inference

Let's say I have a class with two generic methods:
TMyClass = class
procedure DoWith<T: class> (obj: T);
procedure DoFor<T: class> ( proc: TProc<T> );
end;
Now, when I want to call either of these two methods with a specific type parameter, Delphi can infer the type for the DoWith method, so I can call it with either
MyClass.DoWith <TButton> ( MyButton )
or
MyClass.DoWith ( MyButton )
The Delphi Compiler will happily compile both.
But if I omit the type parameter in the DoFor method, the Delphi compiler complains about the missing type parameter:
MyClass.DoFor<TButton>(procedure (Button: TButton) begin .... end); // compiles
MyClass.DoFor(procedure (Button: TButton) begin .... end); // doesn't compile
Now my question is: Is this just a shortcoming of the compiler, or is there any logical reason (that I haven't figured out yet) that prohibits the compiler from correctly inferring the type for the DoFor method?
The reason it cannot infer T from a TProc<T>argument is that at that time TProc<TButton> is a constructed type without any information that it originally was a TProc<T>.
To do that it would have to infer the type from the anonymous method signature which does not work (I guess Barry Kelly could explain that better and I think he once wrote about the difficulties of lambdas and type inference in Delphi).
The only type inference the Delphi compiler is capable of is an argument of type T. Even with multiple arguments that does not work often and even less if you have more than one generic type parameters.
Edit: I found a comment where Barry explained a bit about the difficulties of type inference and lambdas in the Delphi compiler: http://www.deltics.co.nz/blog/posts/244/comment-page-1#comment-107

Using SmartPointer as result data type in function requires invoke call explicitly

I am using the SmartPointer in http://members.adug.org.au/2011/12/05/smart-pointers/
I defined a IStringList:
type
IStringList = ISmartPtr<TStringList>;
I may then use as follow without memory leak prompt:
var S: IStringList;
begin
S := TSmartPtr<TStringList>.Create();
S.Add('abc');
end;
If I use the IStringList as result data type in a function:
function GetList: IStringList;
begin
Result := TSmartPtr<TStringList>.Create();
Result.Add('abc'); // E2010
end;
I get a compiler error:
[dcc32 Error] Unit2.pas(31): E2010 Incompatible types: 'SmartPointer.ISmartPtr<System.Classes.TStringList>' and 'Procedure of object'
A workaround solution would be:
Result.Invoke.Add('abc');
But that defeat the purpose of syntax cleanliness of using SmartPointer. Is there a solution?
It's quite an interesting one. For whatever reason, the compiler treats Result differently from other variables. I see no good reason for that, so this feels like a compiler bug.
I see a few workarounds:
Declare a local variable, use that, and finish the function by assigning that local variable to Result.
Using parens to call the function: Result().Add('abc').
Using a better smart pointer. For instance, a smart pointer that is not based on method invocation would be a good start. A generic record with an implicit conversion operator would presumably work.
Find a version of the compiler without the bug, if indeed it is a compiler bug.
Give up on smart pointers and write traditional style code. Every time I contemplate using smart pointers in Delphi I conclude that I'd rather see the explicit try/finally blocks and understand when my objects are created and destroyed.
FWIW, you can greatly simplify the issue by stripping out the smart pointer code, and removing the generic types. Here is the cleanest SSCCE that I can concoct:
{$APPTYPE CONSOLE}
type
TFunc = reference to function: TObject;
procedure Foo;
var
F: TFunc;
begin
F.ClassName; // compiles
end;
function Bar: TFunc;
begin
Result().ClassName; // compiles
Result.ClassName; // [dcc32 Error] E2003 Undeclared identifier: 'ClassName'
end;
begin
end.
I'm now convinced that this is a compiler bug.
It is presumably related to the rather unusual language feature of Delphi that means the function call parentheses can be omitted for a function that has no parameters. This convenience sometimes brings with it ambiguity. However, in this case there is no ambiguity. The . operator has no meaning when applied to a TFunc<TObject> and so the only way to interpret these statements is that the function is called, and the . operator applied to the returned value.
Bug report: QC123218.
{ I'm not allowed to comment... -.-' Feel free making it one. }
I'm afraid there is no way without defeating the purpose of smart pointers. I tried to solve your problem as part of trying to find a way to assign an anonymous method to an interface variable or parameter (SO question from July 2013). Coming back to it every now and then or asking around didn't help in finding a solution.

Why Delphi compiler can't see I'm trying to free an interface?

I've done a small mistake while coding this week-end.
In the following code, I'm creating an object and cast it to an interface. Later, I'm trying to free it with FreeAndNil();
type
IMyIntf = interface
[...]
end;
TMyClass = class(TInterfacedObject, IMyIntf)
[...]
end;
var
Myintf : IMyIntf;
begin
Myintf := TMyClass.Create;
[...] // Some process
FreeAndNil(Myintf); // CRASH !!!
end;
Of course, the program crash at this line.
I totally understand the issue, but what I don't understand is why the compiler doesn't warn me about it ? There is no dynamic things behind, it's just that I'm trying to free an interface !!! Why don't it write me an error / warning ?
Is there any real explanation behind or is it just a compiler limitation ?
As you know, the correct way to do this is to write Myintf := nil, or just to let it go out of scope. The question you ask is why the compiler accepts FreeAndNil(Myintf) and does not complain at compile time.
The declaration of FreeAndNil is
procedure FreeAndNil(var Obj);
This is an untyped parameter. Consequently it will accept anything. You passed an interface, but you could have passed an integer, a string and so on.
Why did the designers choose an untyped parameter? Well, they needed to use a var parameter since the whole purpose of FreeAndNil is to free the object and set the object reference to nil. That can't be done by a method of the target object and so a var parameter of a standalone function is needed.
You might imagine that you could write
procedure FreeAndNil(var Obj: TObject);
since all objects are descended from TObject. But this does not do the job. The reason being that the object you pass to a var parameters must be exactly the type of that parameter. If FreeAndNil was declared this way you would have to cast to TObject every time you called it.
So, the designers decided that the best solution to the design problem, the least bad choice, is to use the untyped var parameter.

Use of Supports() function with generic interface type

I just tried my first use of generics in Delphi 2009 and am perplexed on how to use a generic type as the input to the Supports function used to see if an object implements a given interface. I've created a small sample illustrating the problem.
Given the following types and utility function:
IMyInterface = interface
['{60F37191-5B95-45BC-8C14-76633826889E}']
end;
TMyObject = class(TInterfacedObject, IMyInterface)
end;
class function TFunctions.GetInterface<T>(myObject: TObject): T;
var
specificInterface: T;
begin
// This would compile, but looses the generic capability
//Supports(myObject, IMyInterface, specificInterface);
// This results in compile errors
Supports(myObject, T, specificInterface);
result := specificInterface;
end;
and the following code snippet:
class procedure TFunctions.Test;
var
myObject: TMyObject;
myInterface: IMyInterface;
begin
myObject := TMyObject.Create;
myInterface := GetInterface<IMyInterface>(myObject);
end;
I would expect no problems but I get the following compile time errors:
[DCC Error] GenericExample.pas(37): E2029 '(' expected but ',' found
[DCC Error] GenericExample.pas(37): E2014 Statement expected, but expression of type 'T' found
I'm not sure what the compiler is expecting me to do with the T when used as the actual argument to the function.
I've searched around quite a bit and haven't been able to crack this one. A part of me suspects that if I could understand how an interface name gets converted to the IID: TGUID type during compilation, when using a concrete interface name, I could make some headway, but that has evaded me also.
Any help is much appreciated.
There is no guarantee that T has a GUID associated with it, and there is no means in the language to write a constraint on the type parameter to make that guarantee.
The interface name is converted into a GUID by the compiler looking up the name in the symbol table, getting the compiler's data structure representing the interface, and checking the corresponding field for the GUID. But generics are not like C++ templates; they need to be compiled and type-checked and known to work for any valid type parameter, and that means constraining the type parameter in its declaration.
You can get the GUID using RTTI (first checking that T does indeed represent an interface) with something like GetTypeData(TypeInfo(T))^.Guid and pass the GUID to Supports that way.
Why are you even bothering?
To use this TFunctions.GetInterface you need:
an interface
an object reference
If you have those, then you can just call Supports() directly:
intf := TFunctions.GetInterface<IMyInterface>(myObject);
is exactly equivalent to:
Supports(IMyInterface, myObject, intf);
Using generics here is a waste of time and effort and really begs the question "Why Do It?".
It is just making things harder to read (as is so often the case with generics) and is more cumbersome to use.
Supports() returns a convenient boolean to indicate success/failure, which you have to test for separately using your wrapper:
intf := TFunctions.GetInterface<IMyInterface>(myObject);
if Assigned(intf) then
// ...
versus:
if Supports(IMyInterface, myObject, intf) then
// We can use intf
When creating wrappers around functionality it is generally the case that the result is an improvement in readabilty or usability.
imho this fails on both counts and you should just stick with the Supports() function itself.

Resources