Skip to content

Implement support for constant expressions #116

Open
@cirras

Description

@cirras

Prerequisites

  • This improvement has not already been suggested.
  • This improvement would be generally useful, not specific to my code or setup.

Engine area

Delphi language support

Improvement description

Background

The Delphi compiler implements the following optimizations around constant expressions:

  • constant folding
  • constant propagation

This has significant implications for type resolution - see example.

Example

// overload for each integer type:
procedure X(B: Byte); overload;     begin WriteLn('Byte');     end;
procedure X(S: ShortInt); overload; begin WriteLn('ShortInt'); end;
procedure X(S: SmallInt); overload; begin WriteLn('SmallInt'); end;
procedure X(W: Word); overload;     begin WriteLn('Word');     end;
procedure X(I: Integer); overload;  begin WriteLn('Integer');  end;
procedure X(C: Cardinal); overload; begin WriteLn('Cardinal'); end;
procedure X(I: Int64); overload;    begin WriteLn('Int64');    end;
procedure X(U: UInt64); overload;   begin WriteLn('UInt64');   end;

const
  CFive = 5;         // technically ShortInt, but the value "5" is propagated to constant expressions
  CHundred = 25 * 4; // technically ShortInt, but the value "100" is propagated to constant expressions

procedure Test;
var
  I8: ShortInt;
  I16: SmallInt;
  I32: Integer;
  I64: Int64;
  U8: Byte;
  U16: Word;
  U32: Cardinal;
  U64: UInt64;
begin
  // For variables, all we need to do to lose the original type is tack on a unary `+`.
  // This is because the expression is evaluated at runtime, and only the following options are available at runtime:
  //
  // Positive(Integer): Integer
  // Positive(Cardinal): Cardinal
  // Positive(Int64): Int64
  // Positive(UInt64): UInt64

  X(+I8);  // Integer
  X(+I16); // Integer
  X(+I32); // Integer
  X(+I64); // Int64

  X(+U8);  // Integer
  X(+U16); // Integer
  X(+U32); // Cardinal
  X(+U64); // UInt64

  // But for constant expressions, that unary `+` (and indeed, all other arithmetic operators) are done at compile-time.
  // The type is resolved *after* the expression is evaluated, based on the value.

  X(+(0));                   // ShortInt
  X(+(128));                 // Byte
  X(+(256));                 // SmallInt
  X(+(32768));               // Word
  X(+(65536));               // Integer
  X(+(2147483648));          // Cardinal
  X(+(4294967296));          // Int64
  X(+(9223372036854775808)); // UInt64

  // The following constant expressions feature constant propagation.

  X(+CFive);                 // ShortInt
  X(+CHundred);              // ShortInt
  X(+(CHundred + CHundred)); // Byte
  X(+(256 - CFive));         // Byte
end;

See Also

Rationale

This is required for fully accurate type resolution.

Known affected areas

  • Type conversions from array constructor -> set
  • Expression type resolution (especially unary -)
  • Subrange type modeling (low and high are constant expressions)

Other benefits

Metadata

Metadata

Assignees

Labels

engineImprovements to the core engineenhancementImprovements to an existing feature

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions