variant.mx raw

   1  package cm
   2  
   3  import "unsafe"
   4  
   5  // Discriminant is the set of types that can represent the tag or discriminator of a variant.
   6  // Use uint8 where there are 256 or fewer cases, uint16 for up to 65,536 cases, or uint32 for anything greater.
   7  type Discriminant interface {
   8  	uint8 | uint16 | uint32
   9  }
  10  
  11  // Variant represents a loosely-typed Component Model variant.
  12  // Shape and Align must be non-zero sized types. To create a variant with no associated
  13  // types, use an enum.
  14  type Variant[Tag Discriminant, Shape, Align any] struct {
  15  	_ HostLayout
  16  	variant[Tag, Shape, Align]
  17  }
  18  
  19  // AnyVariant is a type constraint for generic functions that accept any [Variant] type.
  20  type AnyVariant[Tag Discriminant, Shape, Align any] interface {
  21  	~struct {
  22  		_ HostLayout
  23  		variant[Tag, Shape, Align]
  24  	}
  25  }
  26  
  27  // NewVariant returns a [Variant] with tag of type Disc, storage and GC shape of type Shape,
  28  // aligned to type Align, with a value of type T.
  29  func NewVariant[Tag Discriminant, Shape, Align any, T any](tag Tag, data T) Variant[Tag, Shape, Align] {
  30  	validateVariant[Tag, Shape, Align, T]()
  31  	var v Variant[Tag, Shape, Align]
  32  	v.tag = tag
  33  	*(*T)(unsafe.Pointer(&v.data)) = data
  34  	return v
  35  }
  36  
  37  // New returns a [Variant] with tag of type Disc, storage and GC shape of type Shape,
  38  // aligned to type Align, with a value of type T.
  39  func New[V AnyVariant[Tag, Shape, Align], Tag Discriminant, Shape, Align any, T any](tag Tag, data T) V {
  40  	validateVariant[Tag, Shape, Align, T]()
  41  	var v variant[Tag, Shape, Align]
  42  	v.tag = tag
  43  	*(*T)(unsafe.Pointer(&v.data)) = data
  44  	return *(*V)(unsafe.Pointer(&v))
  45  }
  46  
  47  // Case returns a non-nil *T if the [Variant] case is equal to tag, otherwise it returns nil.
  48  func Case[T any, V AnyVariant[Tag, Shape, Align], Tag Discriminant, Shape, Align any](v *V, tag Tag) *T {
  49  	validateVariant[Tag, Shape, Align, T]()
  50  	v2 := (*variant[Tag, Shape, Align])(unsafe.Pointer(v))
  51  	if v2.tag == tag {
  52  		return (*T)(unsafe.Pointer(&v2.data))
  53  	}
  54  	return nil
  55  }
  56  
  57  // variant is the internal representation of a Component Model variant.
  58  // Shape and Align must be non-zero sized types.
  59  type variant[Tag Discriminant, Shape, Align any] struct {
  60  	_    HostLayout
  61  	tag  Tag
  62  	_    [0]Align
  63  	data Shape // [unsafe.Sizeof(*(*Shape)(unsafe.Pointer(nil)))]byte
  64  }
  65  
  66  // Tag returns the tag (discriminant) of variant v.
  67  func (v *variant[Tag, Shape, Align]) Tag() Tag {
  68  	return v.tag
  69  }
  70  
  71  // This function is sized so it can be inlined and optimized away.
  72  func validateVariant[Disc Discriminant, Shape, Align any, T any]() {
  73  	var v variant[Disc, Shape, Align]
  74  	var t T
  75  
  76  	// Check if size of T is greater than Shape
  77  	if unsafe.Sizeof(t) > unsafe.Sizeof(v.data) {
  78  		panic("variant: size of requested type > data type")
  79  	}
  80  
  81  	// Check if Shape is zero-sized, but size of result != 1
  82  	if unsafe.Sizeof(v.data) == 0 && unsafe.Sizeof(v) != 1 {
  83  		panic("variant: size of data type == 0, but variant size != 1")
  84  	}
  85  }
  86