Tart: Integer constants and unification

Tuesday, June 9, 2009
Lots of work last night on integer constants. In Tart, an integer constant is "unsized" until you actually attempt to assign it to a variable or pass it to a function. Thus, a statement like the following produces no warning:
var x:byte = 10000001 - 10000000;
In the above example, the subtraction operation occurs before the type case, so what you are really doing is assigning the value '1' to the variable. If you really wanted to produce a warning, you could have said:
var x:byte = int(10000001) - 10000000;
Now, where this gets interesting is when we attempt to pass an integer constant to a generic function, such as max, which has the type signature max<[%T]>(a:T, b:T) -> T.
var x = max(1, 100); // Both arguments are unsized
var x = max(byte(1), 100); // Only one argument is unsized
var x = max(ubyte(1), 100); // Only one argument is unsized - and it is unsigned.
So what does type 'T' get bound to? In the first case, we try to find the smallest integer type that will hold both values, which in this case is a byte. The second case works the same way, except that one of the types is explicit - again, we combine the two types to find which one is inclusive of the other. If there's no specificity order between the two, then a warning is emitted.

For the third case, one of the types is unsigned - so when we calculate the smallest integer type that can contain the second argument, we also pass in a flag telling it to use unsigned integer types. If the integer constant is negative, then a warning will result.

From a compilation perspective, this is all a bit tricky because of the fact that the type of an integer constant is dependent on the value, which means that you can't evaluate a type expression purely on the basis of type. Fortunately, the damage is contained by the fact that most operations on unsized ints cause them to become sized, so the majority of type calculations are not affected by this.

One area that bears closer scrutiny is the assumption that the combination of two types yields the type that is more general. This is in fact not the case when combining a parameter type with a return type. Consider the following example:
var x:uint = max(1, 2); // OK
var x:uint = max(1, 200000000); // Truncation warning
While the parameter types try to widen the definition of T, the return type tries to narrows it. (This is because return types are covariant, and parameters are contravariant.) In the first example, it knows that the expected return type is unsigned, so both of the parameters must also be converted to unsigned types. In the second example, the widening and narrowing results in T being overconstrained, leading to a truncation warning.

Reblog this post [with Zemanta]

0 comments:

Post a Comment