integrated-application-development / integrated-application-development/sonar-delphi
Implement support for constant expressions
- Dominant language
- Java
- Stars
- 159
- Forks
- 31
- Avg merge
- 5d 4h
- Merged PRs (30d)
- 4
Description
### 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
```pas
// 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
- [Constant folding](https://en.wikipedia.org/wiki/Constant_folding)
### 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
Contributor guide
Assessment
This issue has not been assessed yet.