Size/Signedness issues with unordered enums
### Description

While #23951 fixes casting negative enum values for ordinal types, it is still broken for "holey" enums:

```nim
type Foo = enum A B = -1
proc foo =
  doAssert cast[Foo](-1) == A
  doAssert ord(A) == -1
static: foo()
foo()
```
In fact, both CT and RT fail the assertion.

This is because, in order to determine the storage type during codegen, we depend on the assumption that `firstOrd(Foo) < 0` implies `NI`.  Likewise, this example:

```nim
proc foo =
  type Foo = enum A B=8, C=1
  let s1 = {A}
  let s2 = {B}
  doAssert s1 != s2
static: foo()
foo()
```

fails the assertion at CT and fails to compile without `static`. In this case, we only look at `lastOrd(Foo)` to choose between `u8..u64`.

### Nim Version

Nim Compiler Version 2.1.9 [Linux: amd64]
Compiled at 2024-08-13
Copyright (c) 2006-2024 by Andreas Rumpf

git hash: 1de181604e9b893388177128c1d337c5cd8b1bb2
active boot switches: -d:release

### Current Output

_No response_

### Expected Output

_No response_

### Possible Solution

1. In `firstOrd/lastOrd` for `tyEnum` with holes, iterate through all fields to choose lowest/highest value. This should probably be cached somewhere (where?).
Pro: Less code to change
Con: This changes the semantics of `low/high` i.e in the first example, `low(Foo) != A`. In any case, since holey enums are not ordinal, they can't be iterated over or used in range types, so the utility of `low/high` is debatable.  
2. As above, but introduce `minOrd/maxOrd` and use those only for size/sign computation.
Pro: Lower possibility to introduce new bugs...

### Additional Information

_No response_