Friday, October 3, 2014

New language feature in XE7

Nope, not the dynamic array enhancement that everyone talked about already.

In fact I am going to show you something that I haven't seen anyone talking about yet.
It wasn't even mentioned on the official XE7 shows and the reason probably is because it is not been finished completely as in used in generics in the RTL that would benefit from that.

While it is not the desired compiler/linker support for reducing generics code bloat it certainly is something that can help reducing it when used properly.

There are new compiler intrinsic routines called

function IsManagedType(T: TypeIdentifier): Boolean;
function HasWeakRef(T: TypeIdentifier): Boolean;
function GetTypeKind(T: TypeIdentifier): TTypeKind;

These functions when used are evaluated at compiletime and can be used in if and case statements.
Since the expressions of these statements are constant the compiler can eliminate the unused code paths.

Oh, and by the way TTypeKind has been moved to System.pas so no need to include TypInfo for that anymore, yay!

Sounds complicated? Let's see some code:

type
  TFoo<T> = class
    procedure Bar;
  end;

procedure TFoo<T>.Bar;
begin
  case GetTypeKind(T) of
    tkInteger: Writeln('int');
  else
    Writeln('not int');
  end;
end;

begin
  TFoo<Integer>.Bar;
  Readln;
end.

Ok you say, before XE7 we had to write this:

class procedure TFoo<T>.Bar;
begin
  case PTypeInfo(TypeInfo(T)).Kind of
    tkInteger: Writeln('int');
  else
    Writeln('not int');
  end;
end;

So what's the difference? This basically works similar to a compiler directive that we don't have.

When you compile the code using GetTypeKind in XE7 you can see that there is only a blue dot in the line with the Writeln('int') but not in the other one. That indicates only this line can be executed. It would be different when using the other version because that expression is not evaluated to a constant expression at compile time. Hence the compiler cannot remove any part of the case statement.

What does that mean? Well you can write generic types as thin wrappers around code that is written explicitly for the different types without blowing up your binary in the end.

I guess in XE8 we will see that been used in Generics.Collections to reduce the size of the resulting binary. However it cannot remove all duplicated binary code since regardless how thin the generic class is it gets compiled for every instantiation (any different type put in for the T) even if the code is exactly the same. So we will see what other ideas they come up with to solve that problem.

I am also investigating this further to find out if that might be any useful in Spring4D.

Oh, btw compiler guys - while you are adding compiler magic, can you please add the NameOf function aswell? Especially when dealing with databinding and things that are currently referenced as magic strings in the source code this could prevent breaking code by typos or refactorings. Thanks!