Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions compiler/semantic/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/brimdata/super"
"github.com/brimdata/super/compiler/ast"
"github.com/brimdata/super/compiler/semantic/sem"
"github.com/brimdata/super/runtime/sam/expr/agg"
"github.com/brimdata/super/sup"
)

Expand Down Expand Up @@ -1155,11 +1154,11 @@ func (c *checker) error(loc ast.Node, err error) {
func (c *checker) newFuser() *fuser {
//XXX "complete" option will be true when we have dual support for fusion
// and static types
return &fuser{agg.NewFuser(c.t.sctx, false), c.unknown}
return &fuser{super.NewFuser(c.t.sctx, false), c.unknown}
}

type fuser struct {
fuser *agg.Fuser
fuser *super.Fuser
unknown super.Type
}

Expand Down
3 changes: 1 addition & 2 deletions csup/fusedtype.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"math"

"github.com/brimdata/super"
"github.com/brimdata/super/runtime/sam/expr/agg"
)

func FusedType(sctx *super.Context, r io.ReaderAt) (super.Type, error) {
Expand All @@ -21,7 +20,7 @@ func FusedType(sctx *super.Context, r io.ReaderAt) (super.Type, error) {
if _, err := s.Seek(0, io.SeekStart); err != nil {
return nil, err
}
fuser := agg.NewFuser(super.NewContext(), false)
fuser := super.NewFuser(super.NewContext(), false)
for size > 0 {
typ, n, err := readPart(sctx, r, size)
if err != nil {
Expand Down
5 changes: 2 additions & 3 deletions csup/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"io"

"github.com/brimdata/super"
"github.com/brimdata/super/runtime/sam/expr/agg"
"github.com/brimdata/super/sio"
"github.com/brimdata/super/sio/bsupio"
"github.com/brimdata/super/sup"
Expand All @@ -22,7 +21,7 @@ var maxObjectSize uint32 = 120_000
type Serializer struct {
writer io.WriteCloser
dynamic *vbuild.DynamicBuilder
fuser *agg.Fuser
fuser *super.Fuser
fuserSctx *super.Context
size uint64
}
Expand Down Expand Up @@ -118,7 +117,7 @@ func (w *Serializer) finalizeObject() error {
func (w *Serializer) fuse(dynamic *vector.Dynamic) {
if w.fuser == nil {
w.fuserSctx = super.NewContext()
w.fuser = agg.NewFuser(w.fuserSctx, false)
w.fuser = super.NewFuser(w.fuserSctx, false)
}
for _, vec := range dynamic.Values {
typ, err := w.fuserSctx.TranslateType(vec.Type())
Expand Down
129 changes: 63 additions & 66 deletions runtime/sam/expr/agg/fuser.go → fuser.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,25 @@
package agg
package super

import (
"fmt"
"slices"

"github.com/brimdata/super"
"github.com/brimdata/super/sup"
)

// Fuser constructs a fused supertype for all the types passed to Fuse.
type Fuser struct {
sctx *super.Context
sctx *Context
complete bool

typ super.Type
types map[super.Type]struct{}
typ Type
types map[Type]struct{}
}

// XXX this is used by type checker but I think we can use the other one
func NewFuser(sctx *super.Context, complete bool) *Fuser {
return &Fuser{sctx: sctx, complete: complete, types: make(map[super.Type]struct{})}
func NewFuser(sctx *Context, complete bool) *Fuser {
return &Fuser{sctx: sctx, complete: complete, types: make(map[Type]struct{})}
}

func (f *Fuser) Fuse(t super.Type) {
func (f *Fuser) Fuse(t Type) {
if _, ok := f.types[t]; ok {
return
}
Expand All @@ -36,26 +33,26 @@ func (f *Fuser) Fuse(t super.Type) {
}

// Type returns the computed supertype.
func (f *Fuser) Type() super.Type {
func (f *Fuser) Type() Type {
return f.typ
}

func (f *Fuser) fuse(a, b super.Type) super.Type {
func (f *Fuser) fuse(a, b Type) Type {
if a == b {
return a
}
if typ, ok := a.(*super.TypeFusion); ok {
if typ, ok := a.(*TypeFusion); ok {
return f.fusion(f.fuse(typ.Type, b))
}
if typ, ok := b.(*super.TypeFusion); ok {
if typ, ok := b.(*TypeFusion); ok {
return f.fusion(f.fuse(a, typ.Type))
}
if isAll(a) || isAll(b) {
return super.TypeAll
return TypeAll
}
switch a := a.(type) {
case *super.TypeRecord:
if b, ok := b.(*super.TypeRecord); ok {
case *TypeRecord:
if b, ok := b.(*TypeRecord); ok {
fields := slices.Clone(a.Fields)
// First change all fields to optional that are in "a" but not in "b".
for k, field := range fields {
Expand All @@ -71,7 +68,7 @@ func (f *Fuser) fuse(a, b super.Type) super.Type {
fields[i].Type = f.fuse(fields[i].Type, field.Type)
} else {
typ := f.makeOption(field.Type)
fields = append(fields, super.NewField(field.Name, typ))
fields = append(fields, NewField(field.Name, typ))
}
}
fusedRec := f.sctx.MustLookupTypeRecord(fields)
Expand All @@ -80,30 +77,30 @@ func (f *Fuser) fuse(a, b super.Type) super.Type {
}
return fusedRec
}
case *super.TypeArray:
if b, ok := b.(*super.TypeArray); ok {
case *TypeArray:
if b, ok := b.(*TypeArray); ok {
return f.fusion(f.sctx.LookupTypeArray(f.fuse(a.Type, b.Type)))
}
case *super.TypeSet:
if b, ok := b.(*super.TypeSet); ok {
case *TypeSet:
if b, ok := b.(*TypeSet); ok {
return f.fusion(f.sctx.LookupTypeSet(f.fuse(a.Type, b.Type)))
}
case *super.TypeMap:
if b, ok := b.(*super.TypeMap); ok {
case *TypeMap:
if b, ok := b.(*TypeMap); ok {
keyType := f.fuse(a.KeyType, b.KeyType)
valType := f.fuse(a.ValType, b.ValType)
return f.fusion(f.sctx.LookupTypeMap(keyType, valType))
}
case *super.TypeUnion:
case *TypeUnion:
types := f.fuseIntoUnionTypes(nil, a)
types = f.fuseIntoUnionTypes(types, b)
if len(types) == 1 {
return types[0]
}
union := f.sctx.MustLookupTypeUnion(super.Flatten(types))
union := f.sctx.MustLookupTypeUnion(Flatten(types))
return f.fusion(union)
case *super.TypeEnum:
if b, ok := b.(*super.TypeEnum); ok {
case *TypeEnum:
if b, ok := b.(*TypeEnum); ok {
var newSymbols []string
for _, s := range b.Symbols {
if !slices.Contains(a.Symbols, s) {
Expand All @@ -116,12 +113,12 @@ func (f *Fuser) fuse(a, b super.Type) super.Type {
symbols := append(slices.Clone(a.Symbols), newSymbols...)
return f.fusion(f.sctx.LookupTypeEnum(symbols))
}
case *super.TypeError:
if b, ok := b.(*super.TypeError); ok {
case *TypeError:
if b, ok := b.(*TypeError); ok {
return f.fusion(f.sctx.LookupTypeError(f.fuse(a.Type, b.Type)))
}
case *super.TypeNamed:
if b, ok := b.(*super.TypeNamed); ok && a.Name == b.Name {
case *TypeNamed:
if b, ok := b.(*TypeNamed); ok && a.Name == b.Name {
// if we got here without match a=b above, then there are
// two different types with the same name, which the type
// context shouldn't allow.
Expand All @@ -131,61 +128,61 @@ func (f *Fuser) fuse(a, b super.Type) super.Type {
// a barrier to type fusion. Instead we fall through here and ,
// fuse the named type with the other type.
}
if _, ok := b.(*super.TypeUnion); ok {
if _, ok := b.(*TypeUnion); ok {
return f.fuse(b, a)
}
// Neither a nor b can be an anonymous union at this point.
return f.fusion(f.sctx.MustLookupTypeUnion([]super.Type{a, b}))
return f.fusion(f.sctx.MustLookupTypeUnion([]Type{a, b}))
}

func (f *Fuser) makeOption(t super.Type) super.Type {
if fusion, ok := t.(*super.TypeFusion); ok {
func (f *Fuser) makeOption(t Type) Type {
if fusion, ok := t.(*TypeFusion); ok {
return f.sctx.LookupTypeFusion(f.makeOption(fusion.Type))
}
return f.sctx.Option(t)
}

func isAll(t super.Type) bool {
_, ok := t.(*super.TypeOfAll)
func isAll(t Type) bool {
_, ok := t.(*TypeOfAll)
return ok
}

func (f *Fuser) redefPanic(named *super.TypeNamed) {
func (f *Fuser) redefPanic(named *TypeNamed) {
previous := f.sctx.LookupByName(named.Name)
panic(fmt.Sprintf("type %s redefined: %s to %s", named.Name, sup.String(previous), sup.String(named.Type)))
panic(fmt.Sprintf("type %s redefined: %#v to %#v", named.Name, previous, named.Type))
}

func (f *Fuser) fuseInternal(typ super.Type) super.Type {
if typ, ok := typ.(*super.TypeFusion); ok {
func (f *Fuser) fuseInternal(typ Type) Type {
if typ, ok := typ.(*TypeFusion); ok {
return f.fusion(f.fuseInternal(typ.Type))
}
var out super.Type
var out Type
switch typ := typ.(type) {
case *super.TypeRecord:
case *TypeRecord:
fields := slices.Clone(typ.Fields)
for i, field := range fields {
fields[i].Type = f.fuseInternal(field.Type)
}
out = f.sctx.MustLookupTypeRecord(fields)
case *super.TypeArray:
case *TypeArray:
out = f.sctx.LookupTypeArray(f.fuseInternal(typ.Type))
case *super.TypeSet:
case *TypeSet:
out = f.sctx.LookupTypeSet(f.fuseInternal(typ.Type))
case *super.TypeMap:
case *TypeMap:
out = f.sctx.LookupTypeMap(f.fuseInternal(typ.KeyType), f.fuseInternal(typ.ValType))
case *super.TypeUnion:
var types []super.Type
case *TypeUnion:
var types []Type
for _, t := range typ.Types {
types = f.fuseIntoUnionTypes(types, f.fuseInternal(t))
}
if len(types) == 1 {
out = types[0]
} else {
out = f.sctx.MustLookupTypeUnion(super.Flatten(types))
out = f.sctx.MustLookupTypeUnion(Flatten(types))
}
case *super.TypeEnum:
case *TypeEnum:
return typ
case *super.TypeError:
case *TypeError:
out = f.sctx.LookupTypeError(f.fuseInternal(typ.Type))
default:
out = typ
Expand All @@ -198,16 +195,16 @@ func (f *Fuser) fuseInternal(typ super.Type) super.Type {

// fuseIntoUnionTypes fuses typ into types while maintaining the invariant that
// types contains at most one type of each complex kind but no unions.
func (f *Fuser) fuseIntoUnionTypes(types []super.Type, typ super.Type) []super.Type {
func (f *Fuser) fuseIntoUnionTypes(types []Type, typ Type) []Type {
switch typ := typ.(type) {
case *super.TypeNamed:
case *TypeNamed:
return f.addNamed(types, typ)
case *super.TypeUnion:
case *TypeUnion:
for _, t := range typ.Types {
types = f.fuseIntoUnionTypes(types, t)
}
return types
case *super.TypeFusion:
case *TypeFusion:
return f.fuseIntoUnionTypes(types, typ.Type)
}
typKind := typ.Kind()
Expand All @@ -216,17 +213,17 @@ func (f *Fuser) fuseIntoUnionTypes(types []super.Type, typ super.Type) []super.T
case t == typ:
// This is already in the union.
return types
case typKind != super.PrimitiveKind && typKind == t.Kind() && !super.IsTypeNamed(t):
case typKind != PrimitiveKind && typKind == t.Kind() && !IsTypeNamed(t):
types[i] = noFusion(f.fuse(t, typ))
return types
}
}
return append(types, noFusion(typ))
}

func (f *Fuser) addNamed(types []super.Type, named *super.TypeNamed) []super.Type {
func (f *Fuser) addNamed(types []Type, named *TypeNamed) []Type {
for _, t := range types {
if existingNamed, ok := t.(*super.TypeNamed); ok && existingNamed.Name == named.Name {
if existingNamed, ok := t.(*TypeNamed); ok && existingNamed.Name == named.Name {
if existingNamed.Type != named.Type {
f.redefPanic(named)
}
Expand All @@ -236,24 +233,24 @@ func (f *Fuser) addNamed(types []super.Type, named *super.TypeNamed) []super.Typ
return append(types, named)
}

func noFusion(typ super.Type) super.Type {
if s, ok := typ.(*super.TypeFusion); ok {
func noFusion(typ Type) Type {
if s, ok := typ.(*TypeFusion); ok {
return s.Type
}
return typ
}

func (f *Fuser) fusion(typ super.Type) super.Type {
func (f *Fuser) fusion(typ Type) Type {
if !f.complete {
return typ
}
if typ, ok := typ.(*super.TypeFusion); ok {
if typ, ok := typ.(*TypeFusion); ok {
return typ
}
return f.sctx.LookupTypeFusion(typ)
}

func indexOfField(fields []super.Field, name string) (int, bool) {
func indexOfField(fields []Field, name string) (int, bool) {
for i, f := range fields {
if f.Name == name {
return i, true
Expand All @@ -267,13 +264,13 @@ func indexOfField(fields []super.Field, name string) (int, bool) {
// As long as all the fields names and optionality are the same, then
// any type differences in the fused type of the child fields will be
// captured by a fusion wrapper somewhere in the descendent type.
func recChanged(a, b *super.TypeRecord) bool {
func recChanged(a, b *TypeRecord) bool {
if len(a.Fields) != len(b.Fields) {
return true
}
for k, af := range a.Fields {
bf := b.Fields[k]
if af.Name != bf.Name || super.IsOptionType(af.Type) != super.IsOptionType(bf.Type) {
if af.Name != bf.Name || IsOptionType(af.Type) != IsOptionType(bf.Type) {
return true
}
}
Expand Down
4 changes: 2 additions & 2 deletions runtime/sam/expr/agg/fuser_test.go → fuser_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package agg
package super_test

import (
"testing"
Expand All @@ -8,7 +8,7 @@ import (
)

func TestFuserSamePrimitiveTypeTwice(t *testing.T) {
s := NewFuser(super.NewContext(), false)
s := super.NewFuser(super.NewContext(), false)
typ := super.TypeInt64
s.Fuse(typ)
s.Fuse(typ)
Expand Down
2 changes: 1 addition & 1 deletion runtime/sam/expr/agg/fuse.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func (f *fuse) Result(sctx *super.Context) super.Value {
if len(f.shapes)+len(f.partials) == 0 {
return super.Null
}
fuser := NewFuser(sctx, f.complete)
fuser := super.NewFuser(sctx, f.complete)
for _, p := range f.partials {
typ, err := sctx.LookupByValue(p.Bytes())
if err != nil {
Expand Down
Loading
Loading