Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/RTL/Delphi    [ Add a report in this area ]  
Report #:  106246   Status: Open
array of string fails to be recognised
Project:  Delphi Build #:  any
Version:    16.4 Submitted By:   Dmitry Burov
Report Type:  Suggestion / Enhancement Request Date Reported:  6/7/2012 7:13:08 AM
Severity:    Extreme corner case Last Updated: 8/6/2012 1:00:20 PM
Platform:    All platforms Internal Tracking #:  
Resolution: None (Resolution Comments) Resolved in Build: : None
Duplicate of:  None
Voting and Rating
Overall Rating: No Ratings Yet
0.00 out of 5
Total Votes: 10
Description
This is a issue of RTL design.
'System.TArray<System.string>' and 'TStringDynArray' is not compatible.

For example,

uses Classes, IOUtils;

This works:
TStringList.Create.AddStrings(
     TArray<string>(
     TDirectory.GetFiles('c:\', '*.dll') ) );

This gives an error
[DCC Error] Unit1.pas(37): E2250 There is no overloaded version of 'AddStrings' that can be called with these arguments

TStringList.Create.AddStrings(
     TDirectory.GetFiles('c:\', '*.dll') ) ;

GetFiles return type is
  System.Types.TStringDynArray       = array of string;

AddStrings expected param type is  const Strings: TArray<string> and that is System.TArray<T> = array of T;



So both types ARE array of string.
None of them is alienated with typecast forbidding construction "type x = TYPE y;"
They ARE the same,

Yet compiler cannot find a proper function...

Either compiler is very wrong here, or RTL is very inconsistent and should be brought to common types set.
Steps to Reproduce:
This is a issue of RTL design.
'System.TArray<System.string>' and 'TStringDynArray' is not compatible.

For example,

Add this unit to any project and try to compile.
---------------------------
unit dummy.

interface

implementation

uses Classes, IOUtils;

procedure CompileMe;
begin
TStringList.Create.AddStrings(
     TDirectory.GetFiles('c:\', '*.dll') ) ;
(* yes, memory leak. Don't bother, the question here is
  ability to compile it without explicit typecasting
  to yet another alias of the same array-of-string type
  nor additional temporary var created to pollute namespace *)
end;

end.
---------------------------

This gives an error
[DCC Error] Unit1.pas(37): E2250 There is no overloaded version of 'AddStrings' that can be called with these arguments

[System.IOUtils.pas]
---------------------------
TDirectory = record
    ....
    class function GetFiles(const Path: string): TStringDynArray;
        overload; inline; static;
    ....
---------------------------

[System.Classes.pas]
---------------------------
....
TStrings = class(TPersistent)
    ....
public
    ....
    procedure AddStrings(Strings: TStrings); overload; virtual;
    procedure AddStrings(const Strings: TArray<string>); overload;
    procedure AddStrings(const Strings: TArray<string>; const Objects: TArray<TObject>); overload;
    ....
---------------------------
Workarounds
None
Attachment
None
Comments

Tomohiro Takahashi at 6/7/2012 6:30:11 PM -
Could you please attach sample project to reproduce/verify your issue?

Dmitry Burov at 6/7/2012 10:52:14 PM -
steps to reproduction is all u need.
It just would not compile.

okay, let's add some boilerplate, if u insist.

===
unit dummy.
interface
implementation
  uses Classes, IOUtils;

procedure CompileMe;
begin
TStringList.Create.AddStrings(
     TDirectory.GetFiles('c:\', '*.dll') ) ;
(* yes, memory leak. Don't bother, the question here is
  ability to compile it without explicit typecasting
  to yet another alias of the same array-of-string type
  nor additional temporary var created to pollute namespace *)
end;
====

Add this unit to any project and try to compile.

Tomohiro Takahashi at 6/8/2012 8:56:27 PM -
How about using class helper as below?
------------------------------------
type
  TStringListHelper = class helper for TStringList
    procedure AddStrings(const Strings: TStringDynArray);
  end;

{ TStringListHelper }
procedure TStringListHelper.AddStrings(const Strings: TStringDynArray);
var
  I: Integer;
begin
  BeginUpdate;
  try
    for I := Low(Strings) to High(Strings) do
      Add(Strings[I]);
  finally
    EndUpdate;
  end;
end;

procedure CompileMe;
begin
  TStringList.Create.AddStrings(TDirectory.GetFiles('c:\', '*.dll'));
end;
------------------------------------

Dmitry Burov at 6/10/2012 5:44:18 AM -
BTW, that is not elegant helper.
Elegant would enforce that type is the same no matter if used name aliases are different

{ TStringListHelper }
procedure TStringListHelper.AddStrings(const Strings: TStringDynArray);
begin
   AddStrings( TArrray<string>( Strings ) );
end;

procedure CompileMe;
begin
  TStringList.Create.AddStrings(TDirectory.GetFiles('c:\', '*.dll'));
end;

----

which again prooves that those are different names to the same type, and the compiler DOESKNOW it, but just fails to use it somehow, until hinted by programmer

Dmitry Burov at 6/10/2012 5:37:34 AM -
yeah, that might do.

But why should it be required at all ?

The question is not hwow to work around this, but why it happens in the first place

Tomohiro Takahashi at 6/8/2012 8:39:05 PM -
Thanks.
I updated [Steps] field.

> Either compiler is very wrong here, or RTL is very inconsistent ...
This is a issue of RTL design.

Dmitry Burov at 6/10/2012 5:41:14 AM -
why so?

why shouldn't those types be compatible ?

No, wrong queston.
Why those aliases to the same type are not recognised as the same type they are ?

again, they are not "type x = type y" that would break the equivalence.
They are "type x = y" which means "that very type by new name"

To me making such commmon type as dynamic array incompatible with TArray<X> not only having no sence, but frankly making TArray<x> practically useless.

If compiler cannot be fixed, i believe implicit typecasts should be made part of TArray<x> definition if possible.

Dmitry Burov at 6/15/2012 1:40:03 AM -
I ask the compiler architects to rethink this.

It is done with lines of Pascal Report of 1949.
But back then you even can not have non-named arrays as procedure parameters, only typedef'ed ones!

After Turbo Pascal introduced "Open Arrays" and Delphi introduced "Dynamic Arrays" - there is no reason to keep this limitation.

Look,

tpy T1 = set of byte; T2 = set of byte;
Those types are different too "to the book". But they can be assigned/passed/all that. Becausekeeping them incompatible as Wirth did is impractical outside school labs.

Open documentation about types, and you will read "types that and that are different. But that is so impractical, that we made name them 'different yet compatible' and effectively remove that isolation barrier'

If some political question insists calling all array types 'different' - be it. But call them compatible, like u did with open arrays and dynamic arrays - which "by the letter" should not work in Pascal for the same very reasons my code does not compile.

Cause today, not back in 1949, it is so out of all trends!
* Against the trend of all programming languages.
* Against the trend of TurboPascal/Delphi evolution.
* Against the composability pinciples lying behind all RADs and all generics.

The very fact that RTL architects made this mistake shows how ultimately ignorant and reasonlessly restrictive that incompatibility it. Most experienced and bright programmers of yours just could no think that arrays does not allow that every other type easily and naturally uses.

http://stackoverflow.com/questions/11029353

This restriction just dows not have any practical sense half-decade after 1949.
It has a number of bad consequences and no single good effect today.

Time to lift it.
And it would not be even breaking Pascal codebook, for it only describes fixed arrays.
Open arrays and dynamic arrays are not pascal types, just like generics.
Open arrays and dynamic arrays have no reason to be limited by 1949 restrictions, just like generics array is free of them!

Dmitry Burov at 9/25/2012 11:20:08 PM -
One more reason why they SHOULD BE compatible. From StackOverflow 12578185

---
procedure Some(Buf :TByteDynArray); overload;
  begin
    Writeln('Byte dynamic array');
  end;

procedure Some(Buf :TArray<Byte>); overload;
  begin
    Writeln('TBytes AKA byte generic array');
  end;
---

This happily compiled but can leads to ambiguos error when calling Some(nil);
In other words, compiler really treats them the same type and cannot distinguish which to call. Because they ARE the same.

Server Response from: ETNACODE01