sonar-delphi
sonar-delphi copied to clipboard
Implement support for constant expressions
Prerequisites
- [X] This improvement has not already been suggested.
- [X] 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
- Having access to evaluated constant expressions would be extremely useful for some rules.
- Really, any rule where we might want to raise an issue based on the contents of a string
- see: https://github.com/integrated-application-development/sonar-delphi/issues/94#issuecomment-1825031266