What’s New in the Delphi Compiler?

Here’s Danny‘s session, “What’s New in the Delphi Compiler”. At this point, I am actually caught up on my conference blogging. (Caught up on the first day. Will wonders never cease?)

What’s New in the Delphi Compiler

  • Major Win32 compiler work
    • Records with Methods
    • Operator Overloading
    • Class vars (per-type global storage)
    • Unit initialization consolidation
      • Large app, lots of units, lots of initialization -> loaded lots of pages from disk to run all those initialization sections.
      • When the linker does smart linking and assigning addresses, now it puts all those initializations together in one clump of code pages. Result: faster startup (on the order of 2-5%), and significant reduction in working set.
      • Next step is to put them in a separate segment, so they can be purged from the working set after startup. Not done yet.
      • Finalizations are also clumped together.
    • Inline-able magic functions (string Length(), etc.)
  • Minor .NET compiler work
    • Minor bug fixes, metadata improvements
  • .NET Compact Frameworks Enablement
    • Soften error checking for missing CLR stuff
    • Beef up class helpers

Records with Methods

type
  TBlock = record
    procedure Foo(I: Integer);
    function Bar: TBlock;
  private
    Data: Integer;
  public
    property Color: Integer …
  end;
  • In old-style objects, virtual methods were optional, so the VMT could be at a different offset in different types (if it was there at all). Difficult to work with.
  • In the Delphi language, “class” is always a reference type, for simplicity.
  • Value types: If they’re small, allocating them is fast.
  • Yes, you can reference the type within itself (see the declaration of Bar, above).
  • Private, protected, public. Probably has strict private and strict protected as well.
  • No inheritance. (So protected really doesn’t mean anything. Maybe it isn’t there after all.)
  • Default scope is public.
  • Can implement interfaces in .NET, but not in Win32
  • No published. Records have no VMT so they can’t have RTTI.
  • Can still have variant sections in the record, but they have to be at the end. May not be able to put visibility specifiers after you’ve started a variant section. Cannot put methods inside a variant section.
  • Assigning the record will copy everything regardless of visibility.
  • Note: if you put compiler-managed types in a record (string, interface, dynamic array, Variant), the compiler will generate try..finally code all over the place for you. (This is the same rules as before.)

Operator Overloading (Win32)

  • Requires value semantics. “A + B” must return a new instance. (Otherwise, who’s going to clean them up?)
  • Supported on record types only (see previous point)
  • Same syntax & rules as Delphi .NET
    • class operator Add(A, B: TQuark): TQuark;
    • class operator Add(A: TQuark; B: Byte): TQuark;
    • class operator Implicit(A: Integer): TQuark;
    • class operator Explicit(A: TQuark): Integer;
    • class operator Explicit(A: TQuark): string;
  • Implicitly overloaded, so you don’t have to use the “overload;” directive.
  • Can’t define new operators, but you can override all of the standard ones (except assignment and pointer dereferencing).
  • If you want to support all the different integer types, you have to do it yourself.
  • Commutativity is not assumed. So if you want both TQuark + Byte and Byte + TQuark, you have to define it. (Not a big deal – have one call the other, and get it inlined.)
  • Operator Explicit is the only place you’re allowed to overload on result type.
  • If TFoo defines Add(TFoo, TBar), and TBar defines Add(TFoo, TBar), the compiler will pick one. Don’t do this.
  • Implicit conversion happens when:
    • A := B;
    • Passing parameters
    • Don’t define an implicit conversion if you will lose information.
    • Will not do long-trip conversions. If A can be implicitly converted to B, and B to C, and you try to go straight from A to C, Delphi will say “Bad programmer!”
  • Explicit is for explicit casts, like TFoo(5), or Integer(MyFoo).
    • These are allowed to lose data, e.g., going from a LongInt to a Byte.
  • If two types both define the same implicit or explicit conversion (like TFoo to TBar), you will get a compiler error (ambiguous overload, or some such) at the point where you try to do the cast.
  • You cannot call TQuark.Add directly. It doesn’t exist.
  • Operators are implicitly static.
  • WriteLn(Q) uses Explicit(Quark): string
  • If you do a hard typecast on something that supports that as an implicit typecast, it’ll work. You don’t have to declare the same thing as both implicit and explicit.
  • Cast operators: Implicit, Explicit
  • Unary: Positive, Negative, Inc, Dec, LogicalNot, BitwiseNot, Trunc, Round
  • Comparison: Equal, NotEqual, GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual
  • Binary: Add, Subtract, Multiply, Divide, IntDivide, Modulus, ShiftLeft, ShiftRight, LogicalAnd, LogicalOr, LogicalXor, BitwiseAnd, BitwiseOr, BitwiseXor
  • If you define both LogicalOr and BinaryOr, the compiler will pick one. Don’t do that.
  • You can have an operator called Divide and a method called Divide. They won’t collide.
  • Operators should always create and return a new value, not modify the inputs. (Yes, this applies to operator Inc and Dec as well.)

Operator Selection

  • Expression: A + B
  • Types: TA, TB
  • Add operator is selected from the Add operators on TA and TB only, not from other classes. (Again, it’s not automatically transitive.)
  • If TA is convertible from TB, but not the other way around, TA is considered narrower, or more specific. This affects overloaded method calls.

Class vars

  • Global storage for a field within your type
  • Descendants share ancestor’s class vars
    • Not separate storage for each derived type
TMyClass = class
private
  class var SharedStuff: Integer;
public
  ...
end;
  • Cannot initialize these in Win32, yet. (No class constructors in Win32.) But you could initialize it from a unit initialization section.
  • Accessible from class functions
  • Same default initialization rules as globals: initialized to zero.
  • “class var” starts a new section. So do “var” and “type”. (“Var” and “class var” are there because of nested types in Delphi 2005 – “type” introduces a section, so you need “var” to get back out of that mode.)
  • Can have class constants as well (but not typed class constants).

Roadmap

  • These are objectives, not guaranteed feature lists
  • “Highlander” (calendar 2006)
    • Full .NET 2.0 support & integration
    • Delphi generics, class fragments, and more
    • Full .NET Compact Framework support, including VCL.NET for CF
    • Support .NET 2.0 64-bit app development
  • “Delphi Longhorn” (not the actual codename) (sometime after Highlander)
    • Win32 & .NET development for Windows Vista OS
    • Avalon (WPF)
  • Delphi Win64
    • Closest logical evolution of Win32
    • Same shortcomings as Win32 (DLLs, APIs, security)
    • Unicode VCL
    • Company green light, just needs time & resources

Leave a Reply

Your email address will not be published. Required fields are marked *