-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
sem: decide storage type for enums on definition (#839)
## Summary Centralize the decision about the underlying storage types for enum values in semantic analysis. Since the decision now happens in a single place instead of being scattered across the compiler, this makes it significantly easier to change the enum storage behaviour later on. In addition, this fixes two bugs with 1- and 2-byte enum values (which use unsigned integer storage) where the most significant bit was set that affected code running in the VM: - loading the values from memory resulted in non-valid values - (only affected the VM and JS backend) storing them inside aggregate `const`s resulted in them having invalid values at run-time ## Details The key changes are: - `tyEnum` now stores the storage type in the second type slot, meaning that `skipTypes(tyEnum)` yields the storage type - size and alignment computation redirects to the storage type - `isUnsigned` considers a `tyEnum`'s storage type - the code generators use the `tyEnum`'s provided storage type and don't decide it themselves Using the second type slot of `tyEnum` is done for both forward- and backward-compatibility: it's possible that some of the current code still depends on an enum type's first slot always being `nil`, and in the future the slot could possibly be used to store the enum's base type. The storage type is directly set on enum definition (`semEnum`). Before analyzing the enum fields, a preliminary storage type (`tyInt`) is already added for the enum: this allows for the enum's values to be used *before* the enum type is fully produced. Once the full value range is known, the correct storage type is selected. Choosing the storage type happens in the same way as it previously did in the C code generator (`ccgtypes`). Apart from the information now being available during semantic analysis already, this only affects the VM target, where non-negative enum values requiring 32 or 64 bit of storage were previously stored as unsigned integer (they're now stored as *signed* integers, in line with the C target). For the VM code generator, `tyEnum` is added to the `IrrelevantTypes` set, meaning that the code generator now transparently treats all enum types as their storage type, with an exception being made for the to- string operation.
- Loading branch information
Showing
12 changed files
with
142 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
discard """ | ||
targets: "c js vm" | ||
description: " | ||
Ensure that enum values stored as usigned integers are properly read as | ||
such | ||
" | ||
""" | ||
|
||
type | ||
EnumA = enum | ||
enuA_a = 0 | ||
enuA_b = high(uint8) | ||
|
||
EnumB = enum | ||
enuB_a = 0 | ||
enuB_b = high(uint16) | ||
|
||
doAssert sizeof(EnumA) == sizeof(uint8) | ||
doAssert sizeof(EnumB) == sizeof(uint16) | ||
|
||
proc test() = | ||
# globals could interfere with the test, so use a procedure in order to | ||
# ensure that locals are used. In addition, assign the enum values to | ||
# variables so that the ``ord`` calls are not folded away | ||
var val1 = enuA_b | ||
doAssert ord(val1) == int high(uint8) | ||
|
||
var val2 = enuB_b | ||
doAssert ord(val2) == int high(uint16) | ||
|
||
# the VM has to narrow the value after loading it from a memory | ||
# location, so also test that case by accessing an array | ||
|
||
var arr1 = [enuA_b] | ||
doAssert ord(arr1[0]) == int high(uint8) | ||
|
||
var arr2 = [enuB_b] | ||
doAssert ord(arr2[0]) == int high(uint16) | ||
|
||
test() | ||
|
||
block compile_time_run_time_boundary: | ||
# make sure that enum values stored as unsigned integers properly cross the | ||
# compile-time/run-time boundary | ||
proc get[T](): T = high(T) | ||
|
||
block uint8_value: | ||
const | ||
folded = (enuA_b,) # the VM is not used | ||
computed = (get[EnumA](),) # ``vmcompilerserdes`` is used | ||
|
||
var val = folded | ||
doAssert val[0] == enuA_b | ||
val = computed | ||
doAssert val[0] == enuA_b | ||
|
||
block uint16_value: | ||
const | ||
folded = (enuB_b,) # the VM is not used | ||
computed = (get[EnumB](),) # ``vmcompilerserdes`` is used | ||
|
||
var val = folded | ||
doAssert val[0] == enuB_b | ||
val = computed | ||
doAssert val[0] == enuB_b |