Search code examples
compiler-constructioncompilationada

Explicit conversion of out-of-bounds value in Ada


Suppose I have declared some types

type ABC_TYPE is range 0 .. 7; subtype AB_SUBTYPE is ABC_TYPE range 0 .. 3; type DE_TYPE is range 1 .. 4;

what is the expected behavior of:

abc : ABC_TYPE := 7; ab : AB_SUBTYPE := AB_SUBTYPE(abc); de : DE_TYPE := DE_TYPE(abc);

when compiling without range checks?


Solution

  • As Jacob said, the result is not defined by the language. The most likely behavior is that the compiler will generate code that treats the values as integers with a certain "natural" range, and will simply copy the integers around as long as they fit into the range.

    In your example:

    type ABC_TYPE is range 0 .. 7;
    subtype AB_SUBTYPE is ABC_TYPE range 0 .. 3;
    type DE_TYPE is range 1 .. 4;
    
    abc : ABC_TYPE := 7;
    ab : AB_SUBTYPE := AB_SUBTYPE(abc);
    de : DE_TYPE := DE_TYPE(abc);
    

    On the most commonly used processors, the compiler will store the variables as a 1-, 2-, or 4-byte integer. The range of a 1-byte integer will be either 0..255 or -128..127--for this case, it doesn't really matter. So what will happen is that 7 will be stored in the 1-, 2-, or 4-byte integer allocated for de. Then, when the program accesses de, it most likely will just read that integer and work with it. I'd expect DE_TYPE'Image(de) to work fine, because there is likely to be some low-level function that converts an "integer" to an image, that accepts any 32-bit (or 64-bit) integer as a parameter. Please note that I am not recommending you rely on this behavior. This is just what I think is most likely to happen given what I know about how compilers generate code. Note also that although ab could be stored as a 2-bit integer, the compiler is unlikely to do so (except in a packed record or packed array) because it's not a natural integer size that processors can deal with.

    In this case, though:

    type ABC_TYPE is range 0 .. 10000;
    subtype ABC_SUBTYPE is ABC_TYPE range 0 .. 100;
    
    abc : ABC_TYPE := 1234;
    ab : AB_SUBTYPE := AB_SUBTYPE(abc);
    

    Now, it's possible that if range checking is suppressed, a compiler could truncate abc to a byte before assigning it to ab. This would happen by chopping off the upper byte, leaving 210, which means abc would have the value 210 or -46 depending on whether the byte is treated as signed. (It's still out of range for an AB_SUBTYPE, but since range checking is suppressed, that won't cause an exception.) Different compilers may have different behavior in this case.