// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package eval import ( "big" "go/ast" "go/token" "log" "reflect" "sort" "unsafe" // For Sizeof ) // XXX(Spec) The type compatibility section is very confusing because // it makes it seem like there are three distinct types of // compatibility: plain compatibility, assignment compatibility, and // comparison compatibility. As I understand it, there's really only // assignment compatibility and comparison and conversion have some // restrictions and have special meaning in some cases where the types // are not otherwise assignment compatible. The comparison // compatibility section is almost all about the semantics of // comparison, not the type checking of it, so it would make much more // sense in the comparison operators section. The compatibility and // assignment compatibility sections should be rolled into one. type Type interface { // compat returns whether this type is compatible with another // type. If conv is false, this is normal compatibility, // where two named types are compatible only if they are the // same named type. If conv if true, this is conversion // compatibility, where two named types are conversion // compatible if their definitions are conversion compatible. // // TODO(austin) Deal with recursive types compat(o Type, conv bool) bool // lit returns this type's literal. If this is a named type, // this is the unnamed underlying type. Otherwise, this is an // identity operation. lit() Type // isBoolean returns true if this is a boolean type. isBoolean() bool // isInteger returns true if this is an integer type. isInteger() bool // isFloat returns true if this is a floating type. isFloat() bool // isIdeal returns true if this is an ideal int or float. isIdeal() bool // Zero returns a new zero value of this type. Zero() Value // String returns the string representation of this type. String() string // The position where this type was defined, if any. Pos() token.Pos } type BoundedType interface { Type // minVal returns the smallest value of this type. minVal() *big.Rat // maxVal returns the largest value of this type. maxVal() *big.Rat } var universePos = token.NoPos /* * Type array maps. These are used to memoize composite types. */ type typeArrayMapEntry struct { key []Type v interface{} next *typeArrayMapEntry } type typeArrayMap map[uintptr]*typeArrayMapEntry func hashTypeArray(key []Type) uintptr { hash := uintptr(0) for _, t := range key { hash = hash * 33 if t == nil { continue } addr := reflect.NewValue(t).(*reflect.PtrValue).Get() hash ^= addr } return hash } func newTypeArrayMap() typeArrayMap { return make(map[uintptr]*typeArrayMapEntry) } func (m typeArrayMap) Get(key []Type) interface{} { ent, ok := m[hashTypeArray(key)] if !ok { return nil } nextEnt: for ; ent != nil; ent = ent.next { if len(key) != len(ent.key) { continue } for i := 0; i < len(key); i++ { if key[i] != ent.key[i] { continue nextEnt } } // Found it return ent.v } return nil } func (m typeArrayMap) Put(key []Type, v interface{}) interface{} { hash := hashTypeArray(key) ent := m[hash] new := &typeArrayMapEntry{key, v, ent} m[hash] = new return v } /* * Common type */ type commonType struct{} func (commonType) isBoolean() bool { return false } func (commonType) isInteger() bool { return false } func (commonType) isFloat() bool { return false } func (commonType) isIdeal() bool { return false } func (commonType) Pos() token.Pos { return token.NoPos } /* * Bool */ type boolType struct { commonType } var BoolType = universe.DefineType("bool", universePos, &boolType{}) func (t *boolType) compat(o Type, conv bool) bool { _, ok := o.lit().(*boolType) return ok } func (t *boolType) lit() Type { return t } func (t *boolType) isBoolean() bool { return true } func (boolType) String() string { // Use angle brackets as a convention for printing the // underlying, unnamed type. This should only show up in // debug output. return "" } func (t *boolType) Zero() Value { res := boolV(false) return &res } /* * Uint */ type uintType struct { commonType // 0 for architecture-dependent types Bits uint // true for uintptr, false for all others Ptr bool name string } var ( Uint8Type = universe.DefineType("uint8", universePos, &uintType{commonType{}, 8, false, "uint8"}) Uint16Type = universe.DefineType("uint16", universePos, &uintType{commonType{}, 16, false, "uint16"}) Uint32Type = universe.DefineType("uint32", universePos, &uintType{commonType{}, 32, false, "uint32"}) Uint64Type = universe.DefineType("uint64", universePos, &uintType{commonType{}, 64, false, "uint64"}) UintType = universe.DefineType("uint", universePos, &uintType{commonType{}, 0, false, "uint"}) UintptrType = universe.DefineType("uintptr", universePos, &uintType{commonType{}, 0, true, "uintptr"}) ) func (t *uintType) compat(o Type, conv bool) bool { t2, ok := o.lit().(*uintType) return ok && t == t2 } func (t *uintType) lit() Type { return t } func (t *uintType) isInteger() bool { return true } func (t *uintType) String() string { return "<" + t.name + ">" } func (t *uintType) Zero() Value { switch t.Bits { case 0: if t.Ptr { res := uintptrV(0) return &res } else { res := uintV(0) return &res } case 8: res := uint8V(0) return &res case 16: res := uint16V(0) return &res case 32: res := uint32V(0) return &res case 64: res := uint64V(0) return &res } panic("unexpected uint bit count") } func (t *uintType) minVal() *big.Rat { return big.NewRat(0, 1) } func (t *uintType) maxVal() *big.Rat { bits := t.Bits if bits == 0 { if t.Ptr { bits = uint(8 * unsafe.Sizeof(uintptr(0))) } else { bits = uint(8 * unsafe.Sizeof(uint(0))) } } numer := big.NewInt(1) numer.Lsh(numer, bits) numer.Sub(numer, idealOne) return new(big.Rat).SetInt(numer) } /* * Int */ type intType struct { commonType // XXX(Spec) Numeric types: "There is also a set of // architecture-independent basic numeric types whose size // depends on the architecture." Should that be // architecture-dependent? // 0 for architecture-dependent types Bits uint name string } var ( Int8Type = universe.DefineType("int8", universePos, &intType{commonType{}, 8, "int8"}) Int16Type = universe.DefineType("int16", universePos, &intType{commonType{}, 16, "int16"}) Int32Type = universe.DefineType("int32", universePos, &intType{commonType{}, 32, "int32"}) Int64Type = universe.DefineType("int64", universePos, &intType{commonType{}, 64, "int64"}) IntType = universe.DefineType("int", universePos, &intType{commonType{}, 0, "int"}) ) func (t *intType) compat(o Type, conv bool) bool { t2, ok := o.lit().(*intType) return ok && t == t2 } func (t *intType) lit() Type { return t } func (t *intType) isInteger() bool { return true } func (t *intType) String() string { return "<" + t.name + ">" } func (t *intType) Zero() Value { switch t.Bits { case 8: res := int8V(0) return &res case 16: res := int16V(0) return &res case 32: res := int32V(0) return &res case 64: res := int64V(0) return &res case 0: res := intV(0) return &res } panic("unexpected int bit count") } func (t *intType) minVal() *big.Rat { bits := t.Bits if bits == 0 { bits = uint(8 * unsafe.Sizeof(int(0))) } numer := big.NewInt(-1) numer.Lsh(numer, bits-1) return new(big.Rat).SetInt(numer) } func (t *intType) maxVal() *big.Rat { bits := t.Bits if bits == 0 { bits = uint(8 * unsafe.Sizeof(int(0))) } numer := big.NewInt(1) numer.Lsh(numer, bits-1) numer.Sub(numer, idealOne) return new(big.Rat).SetInt(numer) } /* * Ideal int */ type idealIntType struct { commonType } var IdealIntType Type = &idealIntType{} func (t *idealIntType) compat(o Type, conv bool) bool { _, ok := o.lit().(*idealIntType) return ok } func (t *idealIntType) lit() Type { return t } func (t *idealIntType) isInteger() bool { return true } func (t *idealIntType) isIdeal() bool { return true } func (t *idealIntType) String() string { return "ideal integer" } func (t *idealIntType) Zero() Value { return &idealIntV{idealZero} } /* * Float */ type floatType struct { commonType // 0 for architecture-dependent type Bits uint name string } var ( Float32Type = universe.DefineType("float32", universePos, &floatType{commonType{}, 32, "float32"}) Float64Type = universe.DefineType("float64", universePos, &floatType{commonType{}, 64, "float64"}) ) func (t *floatType) compat(o Type, conv bool) bool { t2, ok := o.lit().(*floatType) return ok && t == t2 } func (t *floatType) lit() Type { return t } func (t *floatType) isFloat() bool { return true } func (t *floatType) String() string { return "<" + t.name + ">" } func (t *floatType) Zero() Value { switch t.Bits { case 32: res := float32V(0) return &res case 64: res := float64V(0) return &res } panic("unexpected float bit count") } var maxFloat32Val *big.Rat var maxFloat64Val *big.Rat var minFloat32Val *big.Rat var minFloat64Val *big.Rat func (t *floatType) minVal() *big.Rat { bits := t.Bits switch bits { case 32: return minFloat32Val case 64: return minFloat64Val } log.Panicf("unexpected floating point bit count: %d", bits) panic("unreachable") } func (t *floatType) maxVal() *big.Rat { bits := t.Bits switch bits { case 32: return maxFloat32Val case 64: return maxFloat64Val } log.Panicf("unexpected floating point bit count: %d", bits) panic("unreachable") } /* * Ideal float */ type idealFloatType struct { commonType } var IdealFloatType Type = &idealFloatType{} func (t *idealFloatType) compat(o Type, conv bool) bool { _, ok := o.lit().(*idealFloatType) return ok } func (t *idealFloatType) lit() Type { return t } func (t *idealFloatType) isFloat() bool { return true } func (t *idealFloatType) isIdeal() bool { return true } func (t *idealFloatType) String() string { return "ideal float" } func (t *idealFloatType) Zero() Value { return &idealFloatV{big.NewRat(0, 1)} } /* * String */ type stringType struct { commonType } var StringType = universe.DefineType("string", universePos, &stringType{}) func (t *stringType) compat(o Type, conv bool) bool { _, ok := o.lit().(*stringType) return ok } func (t *stringType) lit() Type { return t } func (t *stringType) String() string { return "" } func (t *stringType) Zero() Value { res := stringV("") return &res } /* * Array */ type ArrayType struct { commonType Len int64 Elem Type } var arrayTypes = make(map[int64]map[Type]*ArrayType) // Two array types are identical if they have identical element types // and the same array length. func NewArrayType(len int64, elem Type) *ArrayType { ts, ok := arrayTypes[len] if !ok { ts = make(map[Type]*ArrayType) arrayTypes[len] = ts } t, ok := ts[elem] if !ok { t = &ArrayType{commonType{}, len, elem} ts[elem] = t } return t } func (t *ArrayType) compat(o Type, conv bool) bool { t2, ok := o.lit().(*ArrayType) if !ok { return false } return t.Len == t2.Len && t.Elem.compat(t2.Elem, conv) } func (t *ArrayType) lit() Type { return t } func (t *ArrayType) String() string { return "[]" + t.Elem.String() } func (t *ArrayType) Zero() Value { res := arrayV(make([]Value, t.Len)) // TODO(austin) It's unfortunate that each element is // separately heap allocated. We could add ZeroArray to // everything, though that doesn't help with multidimensional // arrays. Or we could do something unsafe. We'll have this // same problem with structs. for i := int64(0); i < t.Len; i++ { res[i] = t.Elem.Zero() } return &res } /* * Struct */ type StructField struct { Name string Type Type Anonymous bool } type StructType struct { commonType Elems []StructField } var structTypes = newTypeArrayMap() // Two struct types are identical if they have the same sequence of // fields, and if corresponding fields have the same names and // identical types. Two anonymous fields are considered to have the // same name. func NewStructType(fields []StructField) *StructType { // Start by looking up just the types fts := make([]Type, len(fields)) for i, f := range fields { fts[i] = f.Type } tMapI := structTypes.Get(fts) if tMapI == nil { tMapI = structTypes.Put(fts, make(map[string]*StructType)) } tMap := tMapI.(map[string]*StructType) // Construct key for field names key := "" for _, f := range fields { // XXX(Spec) It's not clear if struct { T } and struct // { T T } are either identical or compatible. The // "Struct Types" section says that the name of that // field is "T", which suggests that they are // identical, but it really means that it's the name // for the purpose of selector expressions and nothing // else. We decided that they should be neither // identical or compatible. if f.Anonymous { key += "!" } key += f.Name + " " } // XXX(Spec) Do the tags also have to be identical for the // types to be identical? I certainly hope so, because // otherwise, this is the only case where two distinct type // objects can represent identical types. t, ok := tMap[key] if !ok { // Create new struct type t = &StructType{commonType{}, fields} tMap[key] = t } return t } func (t *StructType) compat(o Type, conv bool) bool { t2, ok := o.lit().(*StructType) if !ok { return false } if len(t.Elems) != len(t2.Elems) { return false } for i, e := range t.Elems { e2 := t2.Elems[i] // XXX(Spec) An anonymous and a non-anonymous field // are neither identical nor compatible. if e.Anonymous != e2.Anonymous || (!e.Anonymous && e.Name != e2.Name) || !e.Type.compat(e2.Type, conv) { return false } } return true } func (t *StructType) lit() Type { return t } func (t *StructType) String() string { s := "struct {" for i, f := range t.Elems { if i > 0 { s += "; " } if !f.Anonymous { s += f.Name + " " } s += f.Type.String() } return s + "}" } func (t *StructType) Zero() Value { res := structV(make([]Value, len(t.Elems))) for i, f := range t.Elems { res[i] = f.Type.Zero() } return &res } /* * Pointer */ type PtrType struct { commonType Elem Type } var ptrTypes = make(map[Type]*PtrType) // Two pointer types are identical if they have identical base types. func NewPtrType(elem Type) *PtrType { t, ok := ptrTypes[elem] if !ok { t = &PtrType{commonType{}, elem} ptrTypes[elem] = t } return t } func (t *PtrType) compat(o Type, conv bool) bool { t2, ok := o.lit().(*PtrType) if !ok { return false } return t.Elem.compat(t2.Elem, conv) } func (t *PtrType) lit() Type { return t } func (t *PtrType) String() string { return "*" + t.Elem.String() } func (t *PtrType) Zero() Value { return &ptrV{nil} } /* * Function */ type FuncType struct { commonType // TODO(austin) Separate receiver Type for methods? In []Type Variadic bool Out []Type builtin string } var funcTypes = newTypeArrayMap() var variadicFuncTypes = newTypeArrayMap() // Create singleton function types for magic built-in functions var ( capType = &FuncType{builtin: "cap"} closeType = &FuncType{builtin: "close"} closedType = &FuncType{builtin: "closed"} lenType = &FuncType{builtin: "len"} makeType = &FuncType{builtin: "make"} newType = &FuncType{builtin: "new"} panicType = &FuncType{builtin: "panic"} printType = &FuncType{builtin: "print"} printlnType = &FuncType{builtin: "println"} copyType = &FuncType{builtin: "copy"} ) // Two function types are identical if they have the same number of // parameters and result values and if corresponding parameter and // result types are identical. All "..." parameters have identical // type. Parameter and result names are not required to match. func NewFuncType(in []Type, variadic bool, out []Type) *FuncType { inMap := funcTypes if variadic { inMap = variadicFuncTypes } outMapI := inMap.Get(in) if outMapI == nil { outMapI = inMap.Put(in, newTypeArrayMap()) } outMap := outMapI.(typeArrayMap) tI := outMap.Get(out) if tI != nil { return tI.(*FuncType) } t := &FuncType{commonType{}, in, variadic, out, ""} outMap.Put(out, t) return t } func (t *FuncType) compat(o Type, conv bool) bool { t2, ok := o.lit().(*FuncType) if !ok { return false } if len(t.In) != len(t2.In) || t.Variadic != t2.Variadic || len(t.Out) != len(t2.Out) { return false } for i := range t.In { if !t.In[i].compat(t2.In[i], conv) { return false } } for i := range t.Out { if !t.Out[i].compat(t2.Out[i], conv) { return false } } return true } func (t *FuncType) lit() Type { return t } func typeListString(ts []Type, ns []*ast.Ident) string { s := "" for i, t := range ts { if i > 0 { s += ", " } if ns != nil && ns[i] != nil { s += ns[i].Name + " " } if t == nil { // Some places use nil types to represent errors s += "" } else { s += t.String() } } return s } func (t *FuncType) String() string { if t.builtin != "" { return "built-in function " + t.builtin } args := typeListString(t.In, nil) if t.Variadic { if len(args) > 0 { args += ", " } args += "..." } s := "func(" + args + ")" if len(t.Out) > 0 { s += " (" + typeListString(t.Out, nil) + ")" } return s } func (t *FuncType) Zero() Value { return &funcV{nil} } type FuncDecl struct { Type *FuncType Name *ast.Ident // nil for function literals // InNames will be one longer than Type.In if this function is // variadic. InNames []*ast.Ident OutNames []*ast.Ident } func (t *FuncDecl) String() string { s := "func" if t.Name != nil { s += " " + t.Name.Name } s += funcTypeString(t.Type, t.InNames, t.OutNames) return s } func funcTypeString(ft *FuncType, ins []*ast.Ident, outs []*ast.Ident) string { s := "(" s += typeListString(ft.In, ins) if ft.Variadic { if len(ft.In) > 0 { s += ", " } s += "..." } s += ")" if len(ft.Out) > 0 { s += " (" + typeListString(ft.Out, outs) + ")" } return s } /* * Interface */ // TODO(austin) Interface values, types, and type compilation are // implemented, but none of the type checking or semantics of // interfaces are. type InterfaceType struct { commonType // TODO(austin) This should be a map from names to // *FuncType's. We only need the sorted list for generating // the type map key. It's detrimental for everything else. methods []IMethod } type IMethod struct { Name string Type *FuncType } var interfaceTypes = newTypeArrayMap() func NewInterfaceType(methods []IMethod, embeds []*InterfaceType) *InterfaceType { // Count methods of embedded interfaces nMethods := len(methods) for _, e := range embeds { nMethods += len(e.methods) } // Combine methods allMethods := make([]IMethod, nMethods) copy(allMethods, methods) n := len(methods) for _, e := range embeds { for _, m := range e.methods { allMethods[n] = m n++ } } // Sort methods sort.Sort(iMethodSorter(allMethods)) mts := make([]Type, len(allMethods)) for i, m := range methods { mts[i] = m.Type } tMapI := interfaceTypes.Get(mts) if tMapI == nil { tMapI = interfaceTypes.Put(mts, make(map[string]*InterfaceType)) } tMap := tMapI.(map[string]*InterfaceType) key := "" for _, m := range allMethods { key += m.Name + " " } t, ok := tMap[key] if !ok { t = &InterfaceType{commonType{}, allMethods} tMap[key] = t } return t } type iMethodSorter []IMethod func (s iMethodSorter) Less(a, b int) bool { return s[a].Name < s[b].Name } func (s iMethodSorter) Swap(a, b int) { s[a], s[b] = s[b], s[a] } func (s iMethodSorter) Len() int { return len(s) } func (t *InterfaceType) compat(o Type, conv bool) bool { t2, ok := o.lit().(*InterfaceType) if !ok { return false } if len(t.methods) != len(t2.methods) { return false } for i, e := range t.methods { e2 := t2.methods[i] if e.Name != e2.Name || !e.Type.compat(e2.Type, conv) { return false } } return true } func (t *InterfaceType) lit() Type { return t } func (t *InterfaceType) String() string { // TODO(austin) Instead of showing embedded interfaces, this // shows their methods. s := "interface {" for i, m := range t.methods { if i > 0 { s += "; " } s += m.Name + funcTypeString(m.Type, nil, nil) } return s + "}" } // implementedBy tests if o implements t, returning nil, true if it does. // Otherwise, it returns a method of t that o is missing and false. func (t *InterfaceType) implementedBy(o Type) (*IMethod, bool) { if len(t.methods) == 0 { return nil, true } // The methods of a named interface types are those of the // underlying type. if it, ok := o.lit().(*InterfaceType); ok { o = it } // XXX(Spec) Interface types: "A type implements any interface // comprising any subset of its methods" It's unclear if // methods must have identical or compatible types. 6g // requires identical types. switch o := o.(type) { case *NamedType: for _, tm := range t.methods { sm, ok := o.methods[tm.Name] if !ok || sm.decl.Type != tm.Type { return &tm, false } } return nil, true case *InterfaceType: var ti, oi int for ti < len(t.methods) && oi < len(o.methods) { tm, om := &t.methods[ti], &o.methods[oi] switch { case tm.Name == om.Name: if tm.Type != om.Type { return tm, false } ti++ oi++ case tm.Name > om.Name: oi++ default: return tm, false } } if ti < len(t.methods) { return &t.methods[ti], false } return nil, true } return &t.methods[0], false } func (t *InterfaceType) Zero() Value { return &interfaceV{} } /* * Slice */ type SliceType struct { commonType Elem Type } var sliceTypes = make(map[Type]*SliceType) // Two slice types are identical if they have identical element types. func NewSliceType(elem Type) *SliceType { t, ok := sliceTypes[elem] if !ok { t = &SliceType{commonType{}, elem} sliceTypes[elem] = t } return t } func (t *SliceType) compat(o Type, conv bool) bool { t2, ok := o.lit().(*SliceType) if !ok { return false } return t.Elem.compat(t2.Elem, conv) } func (t *SliceType) lit() Type { return t } func (t *SliceType) String() string { return "[]" + t.Elem.String() } func (t *SliceType) Zero() Value { // The value of an uninitialized slice is nil. The length and // capacity of a nil slice are 0. return &sliceV{Slice{nil, 0, 0}} } /* * Map type */ type MapType struct { commonType Key Type Elem Type } var mapTypes = make(map[Type]map[Type]*MapType) func NewMapType(key Type, elem Type) *MapType { ts, ok := mapTypes[key] if !ok { ts = make(map[Type]*MapType) mapTypes[key] = ts } t, ok := ts[elem] if !ok { t = &MapType{commonType{}, key, elem} ts[elem] = t } return t } func (t *MapType) compat(o Type, conv bool) bool { t2, ok := o.lit().(*MapType) if !ok { return false } return t.Elem.compat(t2.Elem, conv) && t.Key.compat(t2.Key, conv) } func (t *MapType) lit() Type { return t } func (t *MapType) String() string { return "map[" + t.Key.String() + "] " + t.Elem.String() } func (t *MapType) Zero() Value { // The value of an uninitialized map is nil. return &mapV{nil} } /* type ChanType struct { // TODO(austin) } */ /* * Named types */ type Method struct { decl *FuncDecl fn Func } type NamedType struct { NamePos token.Pos Name string // Underlying type. If incomplete is true, this will be nil. // If incomplete is false and this is still nil, then this is // a placeholder type representing an error. Def Type // True while this type is being defined. incomplete bool methods map[string]Method } // TODO(austin) This is temporarily needed by the debugger's remote // type parser. This should only be possible with block.DefineType. func NewNamedType(name string) *NamedType { return &NamedType{token.NoPos, name, nil, true, make(map[string]Method)} } func (t *NamedType) Pos() token.Pos { return t.NamePos } func (t *NamedType) Complete(def Type) { if !t.incomplete { log.Panicf("cannot complete already completed NamedType %+v", *t) } // We strip the name from def because multiple levels of // naming are useless. if ndef, ok := def.(*NamedType); ok { def = ndef.Def } t.Def = def t.incomplete = false } func (t *NamedType) compat(o Type, conv bool) bool { t2, ok := o.(*NamedType) if ok { if conv { // Two named types are conversion compatible // if their literals are conversion // compatible. return t.Def.compat(t2.Def, conv) } else { // Two named types are compatible if their // type names originate in the same type // declaration. return t == t2 } } // A named and an unnamed type are compatible if the // respective type literals are compatible. return o.compat(t.Def, conv) } func (t *NamedType) lit() Type { return t.Def.lit() } func (t *NamedType) isBoolean() bool { return t.Def.isBoolean() } func (t *NamedType) isInteger() bool { return t.Def.isInteger() } func (t *NamedType) isFloat() bool { return t.Def.isFloat() } func (t *NamedType) isIdeal() bool { return false } func (t *NamedType) String() string { return t.Name } func (t *NamedType) Zero() Value { return t.Def.Zero() } /* * Multi-valued type */ // MultiType is a special type used for multi-valued expressions, akin // to a tuple type. It's not generally accessible within the // language. type MultiType struct { commonType Elems []Type } var multiTypes = newTypeArrayMap() func NewMultiType(elems []Type) *MultiType { if t := multiTypes.Get(elems); t != nil { return t.(*MultiType) } t := &MultiType{commonType{}, elems} multiTypes.Put(elems, t) return t } func (t *MultiType) compat(o Type, conv bool) bool { t2, ok := o.lit().(*MultiType) if !ok { return false } if len(t.Elems) != len(t2.Elems) { return false } for i := range t.Elems { if !t.Elems[i].compat(t2.Elems[i], conv) { return false } } return true } var EmptyType Type = NewMultiType([]Type{}) func (t *MultiType) lit() Type { return t } func (t *MultiType) String() string { if len(t.Elems) == 0 { return "" } return typeListString(t.Elems, nil) } func (t *MultiType) Zero() Value { res := make([]Value, len(t.Elems)) for i, t := range t.Elems { res[i] = t.Zero() } return multiV(res) } /* * Initialize the universe */ func init() { numer := big.NewInt(0xffffff) numer.Lsh(numer, 127-23) maxFloat32Val = new(big.Rat).SetInt(numer) numer.SetInt64(0x1fffffffffffff) numer.Lsh(numer, 1023-52) maxFloat64Val = new(big.Rat).SetInt(numer) minFloat32Val = new(big.Rat).Neg(maxFloat32Val) minFloat64Val = new(big.Rat).Neg(maxFloat64Val) // To avoid portability issues all numeric types are distinct // except byte, which is an alias for uint8. // Make byte an alias for the named type uint8. Type aliases // are otherwise impossible in Go, so just hack it here. universe.defs["byte"] = universe.defs["uint8"] // Built-in functions universe.DefineConst("cap", universePos, capType, nil) universe.DefineConst("close", universePos, closeType, nil) universe.DefineConst("closed", universePos, closedType, nil) universe.DefineConst("copy", universePos, copyType, nil) universe.DefineConst("len", universePos, lenType, nil) universe.DefineConst("make", universePos, makeType, nil) universe.DefineConst("new", universePos, newType, nil) universe.DefineConst("panic", universePos, panicType, nil) universe.DefineConst("print", universePos, printType, nil) universe.DefineConst("println", universePos, printlnType, nil) }