Support integral cast for ranges of symbolic integers. Previously we only support integral cast for concrete integers.

Reason about the ranges of SymbolCast expressions. Apply truncations, promotions and conversions to get a correct range set using nested types of a SymbolCast.

Fixes: https://bugs.llvm.org/show_bug.cgi?id=51036

The approach:

`long long`is of 8-byte size,

`int`- 4-byte,

`short`- 2-byte,

`char`- 1-byte.

From this point we consider every integral symbol may have 0-3 auxiliary symbols. Auxiliary symbols are based on the same symbol. For instance `(short) (reg_$0<long x>)`, `(int) (reg_$0<long x>)`, `(char) (reg_$0<long x>)` are based on `reg_$0<long x>`. Let's call the symbol `reg_$0<long x>` as **main** and cast symbols as **aux** for convinience.

We introduce a list of 4 types so called *nominal* types (**NTL** - nominal type list.). The types are of different sizes from 1 to 4 bytes and ordered in a list. We choose these types(`Char8Ty`, `Char16Ty`, `Char32Ty` and `LongLongTy`) to be sure about their sizes. Aux symbol is a `SymbolCast`. We create aux symbol artificially using a main symbol and one of NTL types. Every aux symbol is used specificaly as a key-value for the constraint map. Aux symbol represents a truncated(trimmed) version of the main symbol type and is associated to specific bytes of the main symbol. For example, with (Char8Ty) (reg_$0<int x>) we can set or get the range of the lowest byte of the main symbol `reg_$0<int x>`. That means that for the main symbol of type `char` it can't be any aux symbol. And for the main symbol of type `int` it can be no more than two aux symbols: `Char16Ty` and `Char8Ty`.

Let's show graphically. Consider integral symbols as plain bytes:

`[][][][][][][][]` - main symbol(x) of `long long`

` [][][][]` - aux symbol of `Char32Ty`

` [][]` - aux symbol of `Char16Ty`

` []` - aux symbol of `Char8Ty`

After `(short)(x) == 0xABCD` we have

`[][][][][][][][]` - main symbol of `long long`

` [][][][]` - aux symbol of `Char32Ty`

` ABCD` - aux symbol of `Char16Ty`

` []` - aux symbol of `Char8Ty`

We can not reason about "higher" ranges of `Char32Ty` and the main one. But we can reason about `Char8Ty`:

`[][][][][][][][]` - main symbol of `long long`

` [][][][]` - aux symbol of `Char32Ty`

` ABCD` - aux symbol of `Char16Ty`

` CD` - aux symbol of `Char8Ty`

Then, if `x == 0x12345678ABCDEF12`. Then update all existing constraints:

`12345678ABCDEF12` - main symbol of `long long`

` ABCDEF12` - aux symbol of `Char32Ty`

` XXXX` - aux symbol of `Char16Ty` - result range is null while intersecting,

` XX` - aux symbol of `Char8Ty` - result range is null while intersecting.

The branch, where `Char16Ty == ABCD`, is infeasible. The branch, where `Char8Ty == CD`, is infeasible.

We implement `VisitSymbolCast ` to get a valid range from constraint map using main and aux symbols. We get all related symbols from the map and intersect them. Note, that before intersection we shall cast them to needed type (see D103094 for details).

We implement `modifySymbolAndConstraints` as a wrapper for `getProperSymbolAndConstraint` and `updateExistingConstraints`. It invokes from `RangeConstraintManager::assume...` methods to do additional handling of integral symbols (see below).

We implement `getProperSymbolAndConstraint` to find(or create) a suitable symbol and adjust a given range to symbol's type. For instance, if we can reason about range of all the bytes of the symbol, then we just return the main symbol. And if we can reason only partially about range of a few bytes, then we produce an aux symbol. Then cast a given range to the return type.

We implement `updateExistingConstraints` to update constraints in the map for existing aux symbols. Every time we get a new range for some bytes of the main symbol, we shall to update all existing ranges for the symbols by *cast'n'intersect*, which types are smaller than the new range type. It may happen that the result range is *null*, then we can conclude that the considered branch is infeasible.

Let's consider an example:

int x; if ((char)x == 2) { // 1. `VisitSymbolCast`. // Get a range for main `reg_$0<int x>` - [-2147483648, 2147483647]. // Cast main range to `char` - [-2147483648, 2147483647] -> [-128, 127]. // Now we get a valid range for further bifurcation - [-128, 127]. // 2. `assumeSymEQ`. // Intersect retieved range according to the if-condition [-128, 127] x [2, 2]. // Now we get a range to store in the map - [2, 2]. // 3. `getProperSymbolAndConstraint` // Replace `((char) (reg_$0<int x>))` with `((Char8Ty) (reg_$0<int x>))`. // Store the pair in the constraint map. if ((short)x == 259) { // 1. `VisitSymbolCast`. // Get a range for main `reg_$0<int x>` - [-2147483648, 2147483647] // Cast main range to `short` - [-2147483648, 2147483647] -> [-32768, 32767]. // Now we get a valid range for further bifurcation - [-32768, 32767]. // 2. `assumeSymEQ`. // Intersect retieved range according to the if-condition [-32768, 32767] x [259, 259]. // Now we get a range to store in the map - [259, 259]. // 3. `getProperSymbolAndConstraint` // Replace `((short) (reg_$0<int x>))` with `((Char16Ty) (reg_$0<int x>))`. // 4. `updateExistingConstraints` // Update `((Char8Ty) (reg_$0<int x>))`. // Cast the range [259, 259] to `Char8Ty` - [3, 3]. // Intersect result range [3, 3] x [2, 2] = null. // Now we get a null range which says that the branch is impossible(infeasible). // It's important to update smaller existing constraints. // Without attempting of intersecting with known ranges of smaller types // we'll never know about the branch infeasibility. // That may cause unnecessary bifurcation. } }

See tests in *symbol-integral-cast.cpp* for more examples.