From c61ee66f4c40f8a12e9934b3f05848f43bdc207a Mon Sep 17 00:00:00 2001 From: Judah Caruso Date: Sat, 31 Jan 2026 00:56:05 -0700 Subject: [PATCH] remove union --- union/union.go | 150 ---------------------------------- union/union_test.go | 192 -------------------------------------------- 2 files changed, 342 deletions(-) delete mode 100644 union/union.go delete mode 100644 union/union_test.go diff --git a/union/union.go b/union/union.go deleted file mode 100644 index 5379ba8..0000000 --- a/union/union.go +++ /dev/null @@ -1,150 +0,0 @@ -package union - -import ( - "errors" - "fmt" - "reflect" - "strings" - "unsafe" - - "git.brut.systems/judah/xx/mem" -) - -var ( - ErrUninitializedAccess = errors.New("access of uninitialized union") - ErrInvalidType = errors.New("type does not exist within union") -) - -// anystruct represents a struct type with any members. -// -// Note: because Go's type constraint system can't enforce -// this, anystruct is here for documentation purposes. -type anystruct any - -// @note(judah): is there a way to declare the type parameters -// to allow 'type Value union.Of[...]' so users can define their -// own methods? - -// Of represents a union of different types. -// -// Since members are accessed by type instead of name, -// T is expected to be a struct of types like so: -// -// type Value = union.Of[struct { -// int32 -// uint32 -// float32 -// }) -type Of[T anystruct] struct { - typ reflect.Type - mem []byte -} - -func (u Of[T]) Size() uintptr { - return mem.Sizeof[T]() -} - -// String returns the string representation of a union. -func (u Of[T]) String() string { - var b strings.Builder - - fmt.Fprintf(&b, "union[%s] = ", reflect.TypeFor[T]().String()) - if u.typ == nil { - b.WriteString("none") - } else { - b.WriteString(u.typ.String()) - } - - return b.String() -} - -// Is returns true if the given type is currently stored in the union. -func Is[E any, T anystruct](u Of[T]) bool { - // Explicit invalid check to make sure invalid types don't result in false-positives. - if u.typ == nil { - return false - } - - return u.typ == reflect.TypeFor[E]() -} - -// Set overwrites the backing memory of a union with the given value; initializing the union if uninitialized. -// -// Set is unsafe and will not verify if the backing memory has enough capacity to store the value. -// Use [SetSafe] for more safety checks. -func Set[V any, T anystruct](u *Of[T], value V) { - if u.mem == nil { - u.mem = make([]byte, mem.Sizeof[T]()) - } - - unsafe.Slice((*V)(unsafe.Pointer(&u.mem[0])), 1)[0] = value - u.typ = reflect.TypeFor[V]() -} - -// SetSafe overwrites the backing memory of a union with the given value, -// returning an error if the value cannot be stored in the union. -// -// Use [Set] for fewer safety checks. -func SetSafe[V any, T anystruct](u *Of[T], value V) error { - if u.mem == nil { - u.mem = make([]byte, mem.Sizeof[T]()) - } - - vt := reflect.TypeFor[V]() - for _, field := range getInternalFields(*u) { - if field.Type == vt { - unsafe.Slice((*V)(unsafe.Pointer(&u.mem[0])), 1)[0] = value - u.typ = reflect.TypeFor[V]() - return nil - } - } - - return fmt.Errorf("%s - %w", vt, ErrInvalidType) -} - -// Get returns the union's backing memory interpreted as a value of type V, panicking if the union is uninitialized. -// -// Get is unsafe and will not verify if the type exists within the union. -// Use [GetSafe] for more safety checks. -func Get[V any, T anystruct](u Of[T]) V { - if u.mem == nil { - panic(ErrUninitializedAccess) - } - - return unsafe.Slice((*V)(unsafe.Pointer(&u.mem[0])), 1)[0] -} - -// GetSafe returns the union's backing memory interpreted as a value of type V, returning an error if the type -// does not exist within the union or the union is uninitialized. -// -// Use [Get] for fewer safety checks. -func GetSafe[V any, T anystruct](u Of[T]) (V, error) { - if u.mem == nil { - return mem.ZeroValue[V](), ErrUninitializedAccess - } - - vt := reflect.TypeFor[V]() - for _, field := range getInternalFields(u) { - if field.Type == vt { - return unsafe.Slice((*V)(unsafe.Pointer(&u.mem[0])), 1)[0], nil - } - } - - return mem.ZeroValue[V](), ErrInvalidType -} - -// getInternalFields returns an array of reflect.StructField belonging -// to the internal type of a union. -func getInternalFields[U Of[T], T anystruct](_ U) []reflect.StructField { - backing := reflect.TypeFor[T]() - if backing.Kind() != reflect.Struct { - return nil - } - - var fields []reflect.StructField - for i := range backing.NumField() { - fields = append(fields, backing.Field(i)) - } - - return fields -} diff --git a/union/union_test.go b/union/union_test.go deleted file mode 100644 index 11b93a5..0000000 --- a/union/union_test.go +++ /dev/null @@ -1,192 +0,0 @@ -package union_test - -import ( - "testing" - - "git.brut.systems/judah/xx/union" -) - -func TestUnion_BasicGetSet(t *testing.T) { - type Numbers = union.Of[struct { - uint8 - bool - }] - - var num Numbers - union.Set[uint8](&num, 1) - - b := union.Get[bool](num) - if !b { - t.Errorf("expected bool value to be true, was %v", b) - } - - union.Set(&num, false) - - i := union.Get[uint8](num) - if i != 0 { - t.Errorf("expected uint8 value to be 0, was %v", i) - } -} - -type ( - expr = union.Of[struct { - binaryExpr - intExpr - floatExpr - }] - binaryExpr struct { - Op string - Lhs expr - Rhs expr - } - intExpr int64 - floatExpr float64 -) - -func TestUnion_OfStructs(t *testing.T) { - makeInt := func(value int64) (e expr) { - union.Set(&e, intExpr(value)) - return - } - makeFloat := func(value float64) (e expr) { - union.Set(&e, floatExpr(value)) - return - } - makeBinop := func(op string, lhs, rhs expr) (e expr) { - union.Set(&e, binaryExpr{ - Op: op, - Lhs: lhs, - Rhs: rhs, - }) - return - } - - expr1 := makeBinop("+", makeInt(10), makeInt(20)) - bin1 := union.Get[binaryExpr](expr1) - if bin1.Op != "+" { - t.Errorf("incorrect op returned from union: %s", bin1.Op) - } - if lhs := union.Get[intExpr](bin1.Lhs); lhs != 10 { - t.Errorf("incorrect lhs returned from union: %v", lhs) - } - if rhs := union.Get[intExpr](bin1.Rhs); rhs != 20 { - t.Errorf("incorrect rhs returned from union: %v", rhs) - } - - expr2 := makeBinop("-", expr1, makeFloat(3.14)) - bin2 := union.Get[binaryExpr](expr2) - if bin2.Op != "-" { - t.Errorf("incorrect op returned from union of union: %s", bin2.Op) - } - if lhs := union.Get[binaryExpr](bin2.Lhs); lhs.Op != "+" { - t.Errorf("incorrect lhs returned from union of union: %v", lhs) - } - if rhs := union.Get[floatExpr](bin2.Rhs); rhs != 3.14 { - t.Errorf("incorrect rhs returned from union of union: %v", rhs) - } -} - -func TestUnion_OfPointers(t *testing.T) { - type Value = union.Of[struct { - *float64 - *uint64 - }] - - var ( - original uint64 = 100 - value Value - ) - - if union.Is[*uint64](value) || union.Is[*float64](value) { - t.Error("union internal type was incorrect before usage") - } - - union.Set(&value, &original) - - if !union.Is[*uint64](value) { - t.Error("union internal type was incorrect after Set") - } - - fptr := union.Get[*float64](value) - *fptr = 3.14 - - if original == 100 { - t.Error("original value did not change") - } - - uptr := union.Get[*uint64](value) - *uptr = 200 - - if *fptr == 3.14 { - t.Error("float pointer value did not change after modification") - } - - if original != 200 { - t.Errorf("original value was incorrect: %v", original) - } -} - -func TestUnion_ToString(t *testing.T) { - type ( - Struct = union.Of[struct { - int32 - uint32 - }] - Interface = union.Of[interface { - Int() - Bool() - }] - Bool = union.Of[bool] - ) - - var ( - s Struct - i Interface - b Bool - ) - - if s.String() != "union[none] { int32; uint32 }" { - t.Errorf("valid union had invalid stringification: %s", s.String()) - } - - if i.String() != b.String() { - t.Errorf("invalid union had invalid stringification: %s, %s", i.String(), b.String()) - } - - union.Set[int32](&s, 10) - - if s.String() != "union[int32] { int32; uint32 }" { - t.Errorf("valid union had invalid stringification after Set: %s", s.String()) - } -} - -func TestUnion_SafeUsage(t *testing.T) { - type Value = union.Of[struct { - int32 - uint32 - float32 - }] - - var v Value - if _, err := union.GetSafe[int32](v); err == nil { - t.Errorf("GetSafe did not error for an uninitialized union") - } - - if err := union.SetSafe(&v, false); err == nil { - t.Error("SetSafe allowed invalid type") - } - - if err := union.SetSafe[int32](&v, 10); err != nil { - t.Errorf("SetSafe failed with valid type: %s", err) - } - - if _, err := union.GetSafe[bool](v); err == nil { - t.Errorf("GetSafe allowed invalid type") - } - - if v, err := union.GetSafe[int32](v); err != nil { - t.Errorf("GetSafe failed with valid type: %s", err) - } else if v != 10 { - t.Errorf("GetSafe returned invalid value: %v", v) - } -}