Thursday, October 29, 2009

Progress: Unboxing and explicit specialization

I've made a bunch of progress this last week. Unboxing now works, and what's even better is that it is implemented in pure Tart - although I did need to fix several bugs in the compiler, I did not need to add any special support in the compiler for either boxing or unboxing.

Auto-boxing is done via a coercion function in the Object class:

/** Implicitly convert non-object values to Boxed types. */
static def coerce[%T] (value:T) -> Object { return ValueRef[T](value); }
static def coerce(value:Object) -> Object { return value; }

If the input argument derives from Object then it is returned unchanged, otherwise if it's a value type, such an int, it's wrapped in a ValueRef. This happens automatically whenever you attempt to pass a primitive type to a function that takes an Object as an argument.

The ValueRef class itself is fairly simple:

/** A reference type used to contain a value type. */
class ValueRef[%T] : Ref {
  let value:T;
  
  def construct(value:T) {
    self.value = value;
  }

  /** Return a string representation of the contained value. */
  def toString() -> String {
    return self.value.toString();
  }
}

To unbox a boxed value, use the static "valueOf" method in class "Ref". It requires an explicit type parameter:

var v = Ref.valueOf[int](obj);

The 'valueOf' method is defined for each different primitive type and uses the 'classify' statement to downcast to the correct wrapper type. Here's the entire 'Ref' class:

/** Abstract class used to represent a reference to some value. */
abstract class Ref {
  private static {
    /// Convert signed integers to their unsigned equivalents. Supresses conversion warnings.

    def asUnsigned(v:int8) -> uint8 { return uint8(v); }
    def asUnsigned(v:int16) -> uint16 { return uint16(v); }
    def asUnsigned(v:int32) -> uint32 { return uint32(v); }
    def asUnsigned(v:int64) -> uint64 { return uint64(v); }

    /// Convenience functions to check whether the given input is within the numeric range
    /// of the type specified by the type parameter.

    def rangeCheck[%T](value:int32) {
      if value < T.minVal or value > T.maxVal {
        throw TypecastException();
      }
    }

    def rangeCheck[%T](value:int64) {
      if value < T.minVal or value > T.maxVal {
        throw TypecastException();
      }
    }

    def rangeCheck[%T](value:uint32) {
      if value > asUnsigned(T.maxVal) {
        throw TypecastException();
      }
    }

    def rangeCheck[%T](value:uint64) {
      if value > asUnsigned(T.maxVal) {
        throw TypecastException();
      }
    }

    def rangeCheck[%T](value:char) {
      if uint32(value) > asUnsigned(T.maxVal) {
        throw TypecastException();
      }
    }
  
    def check(value:bool) {
      if not value {
        throw TypecastException();
      }
    }

    /// Check to insure that the input value is not negative.

    def signCheck(value:int32) {
      if value < 0 {
        throw TypecastException();
      }
    }

    def signCheck(value:int64) {
      if value < 0 {
        throw TypecastException();
      }
    }
  }

  /// Convert an object containing a number to the requested number type.

  static def valueOf[bool](ref:Object) -> bool {
    classify ref {
      as v:ValueRef[bool]   { return v.value; }
      else {
        throw TypecastException();
      }
    }
  }

  static def valueOf[char](ref:Object) -> char {
    classify ref {
      as v:ValueRef[int8]   { signCheck(v.value); return char(v.value); }
      as v:ValueRef[int16]  { signCheck(v.value); return char(v.value); }
      as v:ValueRef[int32]  { 
        check(v.value >= 0 and uint32(v.value) <= uint32(char.maxVal));
        return char(v.value);
      }
      as v:ValueRef[int64]  {
        check(v.value >= 0 and uint64(v.value) <= uint64(char.maxVal));
        return char(v.value);
      }
      as v:ValueRef[uint8]  { return char(v.value); }
      as v:ValueRef[uint16] { return char(v.value); }
      as v:ValueRef[uint32] { check(v.value <= uint32(char.maxVal)); return char(v.value); }
      as v:ValueRef[uint64] { check(v.value <= uint64(char.maxVal)); return char(v.value); }
      as v:ValueRef[char]   { return v.value; }
      else {
        throw TypecastException();
      }
    }
  }

  static def valueOf[int8](ref:Object) -> int8 {
    classify ref {
      as v:ValueRef[int8]   { return v.value; }
      as v:ValueRef[int16]  { rangeCheck[int8](v.value); return int8(v.value); }
      as v:ValueRef[int32]  { rangeCheck[int8](v.value); return int8(v.value); }
      as v:ValueRef[int64]  { rangeCheck[int8](v.value); return int8(v.value); }
      as v:ValueRef[uint8]  { rangeCheck[int8](v.value); return int8(v.value); }
      as v:ValueRef[uint16] { rangeCheck[int8](v.value); return int8(v.value); }
      as v:ValueRef[uint32] { rangeCheck[int8](v.value); return int8(v.value); }
      as v:ValueRef[uint64] { rangeCheck[int8](v.value); return int8(v.value); }
      as v:ValueRef[char]   { rangeCheck[int8](v.value); return int8(v.value); }
      else {
        throw TypecastException();
      }
    }
  }

  static def valueOf[int16](ref:Object) -> int16 {
    classify ref {
      as v:ValueRef[int8]   { return int16(v.value); }
      as v:ValueRef[int16]  { return v.value; }
      as v:ValueRef[int32]  { rangeCheck[int16](v.value); return int16(v.value); }
      as v:ValueRef[int64]  { rangeCheck[int16](v.value); return int16(v.value); }
      as v:ValueRef[uint8]  { return int16(v.value); }
      as v:ValueRef[uint16] { rangeCheck[int16](v.value); return int16(v.value); }
      as v:ValueRef[uint32] { rangeCheck[int16](v.value); return int16(v.value); }
      as v:ValueRef[uint64] { rangeCheck[int16](v.value); return int16(v.value); }
      as v:ValueRef[char]   { rangeCheck[int16](v.value); return int16(v.value); }
      else {
        throw TypecastException();
      }
    }
  }

  static def valueOf[int32](ref:Object) -> int32 {
    classify ref {
      as v:ValueRef[int8]   { return v.value; }
      as v:ValueRef[int16]  { return int32(v.value); }
      as v:ValueRef[int32]  { return v.value; }
      as v:ValueRef[int64]  { rangeCheck[int32](v.value); return int32(v.value); }
      as v:ValueRef[uint8]  { return int32(v.value); }
      as v:ValueRef[uint16] { return int32(v.value); }
      as v:ValueRef[uint32] { rangeCheck[int32](v.value); return int32(v.value); }
      as v:ValueRef[uint64] { rangeCheck[int32](v.value); return int32(v.value); }
      as v:ValueRef[char]   { rangeCheck[int32](v.value); return int32(v.value); }
      else {
        throw TypecastException();
      }
    }
  }

  static def valueOf[int64](ref:Object) -> int64 {
    classify ref {
      as v:ValueRef[int8]   { return int64(v.value); }
      as v:ValueRef[int16]  { return int64(v.value); }
      as v:ValueRef[int32]  { return int64(v.value); }
      as v:ValueRef[int64]  { return v.value; }
      as v:ValueRef[uint8]  { return int64(v.value); }
      as v:ValueRef[uint16] { return int64(v.value); }
      as v:ValueRef[uint32] { return int64(v.value); }
      as v:ValueRef[uint64] { rangeCheck[int64](v.value); return int64(v.value); }
      as v:ValueRef[char]   { rangeCheck[int64](v.value); return int64(v.value); }
      else {
        throw TypecastException();
      }
    }
  }

  static def valueOf[uint8](ref:Object) -> uint8 {
    classify ref {
      as v:ValueRef[int8]   { signCheck(v.value); return uint8(v.value); }
      as v:ValueRef[int16]  {
        check(v.value >= 0 and v.value <= int16(uint8.maxVal));
        return uint8(v.value);
      }
      as v:ValueRef[int32]  {
        check(v.value >= 0 and v.value <= int32(uint8.maxVal));
        return uint8(v.value);
      }
      as v:ValueRef[int64]  {
        check(v.value >= 0 and v.value <= int64(uint8.maxVal));
        return uint8(v.value);
      }
      as v:ValueRef[uint8]  { return v.value; }
      as v:ValueRef[uint16] { rangeCheck[uint8](v.value); return uint8(v.value); }
      as v:ValueRef[uint32] { rangeCheck[uint8](v.value); return uint8(v.value); }
      as v:ValueRef[uint64] { rangeCheck[uint8](v.value); return uint8(v.value); }
      as v:ValueRef[char]   { rangeCheck[uint8](v.value); return uint8(v.value); }
      else {
        throw TypecastException();
      }
    }
  }

  static def valueOf[uint16](ref:Object) -> uint16 {
    classify ref {
      as v:ValueRef[int8]   { signCheck(v.value); return uint16(v.value); }
      as v:ValueRef[int16]  { signCheck(v.value); return uint16(v.value); }
      as v:ValueRef[int32]  {
        check(v.value >= 0 and v.value <= int32(uint16.maxVal));
        return uint16(v.value);
      }
      as v:ValueRef[int64]  {
        check(v.value >= 0 and v.value <= int64(uint16.maxVal));
        return uint16(v.value);
      }
      as v:ValueRef[uint8]  { return v.value; }
      as v:ValueRef[uint16] { return v.value; }
      as v:ValueRef[uint32] { rangeCheck[uint16](v.value); return uint16(v.value); }
      as v:ValueRef[uint64] { rangeCheck[uint16](v.value); return uint16(v.value); }
      as v:ValueRef[char]   { rangeCheck[uint16](v.value); return uint16(v.value); }
      else {
        throw TypecastException();
      }
    }
  }

  static def valueOf[uint32](ref:Object) -> uint32 {
    classify ref {
      as v:ValueRef[int8]   { signCheck(v.value); return uint32(v.value); }
      as v:ValueRef[int16]  { signCheck(v.value); return uint32(v.value); }
      as v:ValueRef[int32]  { signCheck(v.value); return uint32(v.value); }
      as v:ValueRef[int64]  {
        check(v.value >= 0 and v.value <= int64(uint32.maxVal));
        return uint32(v.value);
      }
      as v:ValueRef[uint8]  { return uint32(v.value); }
      as v:ValueRef[uint16] { return uint32(v.value); }
      as v:ValueRef[uint32] { return v.value; }
      as v:ValueRef[uint64] { rangeCheck[uint32](v.value); return uint32(v.value); }
      as v:ValueRef[char]   { return uint32(v.value); }
      else {
        throw TypecastException();
      }
    }
  }

  static def valueOf[uint64](ref:Object) -> uint64 {
    classify ref {
      as v:ValueRef[int8]   { signCheck(v.value); return uint64(v.value); }
      as v:ValueRef[int16]  { signCheck(v.value); return uint64(v.value); }
      as v:ValueRef[int32]  { signCheck(v.value); return uint64(v.value); }
      as v:ValueRef[int64]  { signCheck(v.value); return uint64(v.value); }
      as v:ValueRef[uint8]  { return uint64(v.value); }
      as v:ValueRef[uint16] { return uint64(v.value); }
      as v:ValueRef[uint32] { return uint64(v.value); }
      as v:ValueRef[uint64] { return v.value; }
      as v:ValueRef[char]   { return uint64(v.value); }
      else {
        throw TypecastException();
      }
    }
  }

  static def valueOf[float](ref:Object) -> float {
    classify ref {
      as v:ValueRef[int8]   { return float(v.value); }
      as v:ValueRef[int16]  { return float(v.value); }
      as v:ValueRef[int32]  { return float(v.value); }
      as v:ValueRef[int64]  { return float(v.value); }
      as v:ValueRef[uint8]  { return float(v.value); }
      as v:ValueRef[uint16] { return float(v.value); }
      as v:ValueRef[uint32] { return float(v.value); }
      as v:ValueRef[uint64] { return float(v.value); }
      as v:ValueRef[float]  { return v.value; }
      as v:ValueRef[double] { return float(v.value); }
      else {
        throw TypecastException();
      }
    }
  }

  static def valueOf[double](ref:Object) -> double {
    classify ref {
      as v:ValueRef[int8]   { return double(v.value); }
      as v:ValueRef[int16]  { return double(v.value); }
      as v:ValueRef[int32]  { return double(v.value); }
      as v:ValueRef[int64]  { return double(v.value); }
      as v:ValueRef[uint8]  { return double(v.value); }
      as v:ValueRef[uint16] { return double(v.value); }
      as v:ValueRef[uint32] { return double(v.value); }
      as v:ValueRef[uint64] { return double(v.value); }
      as v:ValueRef[float]  { return double(v.value); }
      as v:ValueRef[double] { return v.value; }
      else {
        throw TypecastException();
      }
    }
  }
}

Although this class looks complex, that's mainly because there are a lot of possible combinations of conversions. In terms of usage, it's actually quite simple. It also demonstrates a number of features of Tart, including partial specialization, conversion constructors, type objects (T.minVal) and of course 'classify'.

BTW, I went ahead with Guido's suggestions about the integer type names as you can see.


No comments:

Post a Comment