integrated-application-development / integrated-application-development/sonar-delphi

Implement support for constant expressions

Open
#116 0 comments 1 reaction 1 assignee Claimed by @cirras View on GitHub
engine enhancement
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.