16

Today I discovered a compiler bug (QC#108577).

The following program fails to compile:

program Project1;
{$APPTYPE CONSOLE}

procedure P(M: TArray<TArray<Integer>>);
begin
  SetLength(M, 1, 2);
end;

begin
end.

The compiler gags on the SetLength line and says:

[dcc32 Error] E2029 ')' expected but ',' found

I know I could fix it like this:

procedure P(M: TArray<TArray<Integer>>);
var
  i: Integer;
begin
  SetLength(M, 1);
  for i := low(M) to high(M) do
    SetLength(M[i], 2);
end;

but naturally I'm keen to avoid having to resort to this.

The following variant compiles and seems to work:

procedure P(M: TArray<TArray<Integer>>);
type
  TArrayOfArrayOfInteger = array of array of Integer;
begin
  SetLength(TArrayOfArrayOfInteger(M), 1, 2);
end;

I don't know enough about the implementation details of dynamic arrays, TArray<T> casting, reference counting etc. to be confident that this is safe.

Is there anybody out there who does know enough to say one way or another whether or not this will produce the correct code at runtime?

25
  • 1
    System.pas defines TArray<T> = array of T; so I'd expect that a hard cast should work. Commented Sep 7, 2012 at 16:12
  • 1
    Either one ends up in 'DynArraySetLength' (at least one with a 1 dimensional array anyway), so I'd agree the above.. Commented Sep 7, 2012 at 16:32
  • @afrazier System.pas is special though and there could be some special compiler magic intrinsic treatment for TArray<T>. That's what I'm concerned about. Naturally it's pretty unlikely that they would implement a new incompatible array code rather than use the tried and tested one that they already have. Commented Sep 7, 2012 at 16:46
  • @SertacAkyuz That's very strong evidence. Could you expand on it as an answer please? Commented Sep 7, 2012 at 16:49
  • 1
    The bug was fixed in XE4. Commented Feb 19, 2014 at 23:07

3 Answers 3

19

The compiler intrinsic procedure SetLength constructs an array of dimensions on the fly on the stack and calls DynArraySetLength for any dynamic array, be it generic or not. If a generic array wouldn't be structurally compatible with a regular dynamic array, the same implementation for setting the length possibly wouldn't be called.

In fact documentation of DynArraySetLength offers SetLength as an alternative for multi-dimensional arrays. DynArraySetLength could also be used instead of a typecast, but I don't see any reason to prefer one or the other.

Sign up to request clarification or add additional context in comments.

Comments

3

By design of the generics implementation, using a manual map to array of array of Integer will work.

But there is no benefit of using generics here!

Just code:

type
  TArrayOfArrayOfInteger = array of array of Integer;

procedure P(M: TArrayOfArrayOfInteger);
begin
  SetLength(TArrayOfArrayOfInteger, 1, 2);
end;

Note also that such TArray<> or array of .. are passed by value, and copied on the stack, unless you specify const or var:

procedure P(var M: TArrayOfArrayOfInteger);
begin
  SetLength(TArrayOfArrayOfInteger, 1, 2);
end; // now caller instance of the parameter will be resized

var A: TArrayOfArrayOfInteger;
...
A := nil;
P(A);
assert(length(A)=1);
assert(length(A[0])=2);

8 Comments

Do you have any documents to back up the "by design" assertion? There is a benefit in using generics here. In a generic container class I can write a function like this: function ToArray: TArray<T>. Clearly I can't do that without using the generic TArray<T>. And I know all about var and const. My example is clearly useless and pointless. It was designed to be the smallest possible example that illustrated the bug. Given that the code cannot run, worrying about runtime performance seems pointless.
@David using generics you can declare a function like function ToArray: array of T and using nested types would even allow you to name it.
@Arioch No, that code doesn't compile. There's no need to name TArray<T>. It already has a name. One name is enough.
@David, Yes, it already has a name and name is "array of T" ;-) I fail to see why you protect segregation of two ways to declare an array. Especially for that segregation is only backed up by 70-s "Pascal Report" that was already subverted for most other types like sets and ranges.
@Arioch That's your name for it. Using that name means your code won't compile. Pretty useless idea. The change you propose is sensible but I think hell will freeze over before Emba implement it. I may be wrong but I judge that a small number of RTL changes are more likely.
|
2

I was recently bitten by the fact that DynamicArray<T> and TArray<T> in C++ are actually implemented differently (DynamicArray is a standalone class, whereas TArray is a TObject descendant), which implies that array of T and TArray<T> do have some implementation differences in Delphi as well. They certainly produce different types of RTTI, at least. Which was the root cause of a problem in some of my C++ code that started failing when the Delphi compiler started outputting TArray typedefs in HPP files for Delphi array of ... types instead of DynamicArray typedefs.

4 Comments

I don't know Emba's C++ at all. What are DynamicArray<T> and TArray<T> in Emba C++ and how do they relate to Delphi?
They are templated classes that directly correspond to Delphi's array of ... and TArray types, much like the AnsiString, UnicodeString and Variant classes directly correspond to their Delphi counterparts. They are all C++ classes that allow C++ and Delphi to exchange compatible data with each other.
Then it yet another case at EMB when "left hand is oblivious to right hand's doings". In delphi type TArray<T> = array of T, by definition. It is not class like TList<T>. C++ is just different from Delphi once again.
But TArray<T> is a Generic, so I'm sure that also plays into the issues as well. I'm guessing that TArray<Byte> and array of Byte are actually different because of that. They does explain why they have different RTTI.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.