package types import "git.smesh.lol/moxie/pkg/mxutil" // UniverseState holds all predeclared type system state. // Owned by the caller. No globals - all access through this struct. type UniverseState struct { Scope *Scope Typ [UntypedNil + 1]*Basic Error *TypeName Registry map[string]*TCPackage DirectImports map[string][]string ByName map[string]*TCPackage // name -> package cache byNameSeq int32 } // LookupByName returns the package with the given short name, or nil. func (u *UniverseState) LookupByName(name string) (pkg *TCPackage) { if u.ByName == nil || u.byNameSeq != int32(len(u.Registry)) { u.ByName = map[string]*TCPackage{} for _, p := range u.Registry { if p != nil { u.ByName[p.Name] = p } } u.byNameSeq = int32(len(u.Registry)) } return u.ByName[name] } // Global bridge - DEPRECATED. Will be removed once all call sites // thread UniverseState as a parameter. Do not add new reads. var Universe *Scope var Typ [UntypedNil + 1]*Basic var universeError *TypeName // InitUniverse creates the universe scope with predeclared types. // Returns all products. Caller owns the returned state. // Also sets globals for backward compat during migration. func InitUniverse() (u *UniverseState) { if Universe != nil { return &UniverseState{ Scope: Universe, Typ: Typ, Error: universeError, Registry: ImportRegistry, DirectImports: PkgDirectImports, } } return buildUniverse() } // SetUniverseGlobals sets the package-level globals from a UniverseState. // Called by the program's main after receiving the return value from // InitUniverse. The caller's arena owns the data. func SetUniverseGlobals(u *UniverseState) { Universe = u.Scope Typ = u.Typ universeError = u.Error ImportRegistry = u.Registry PkgDirectImports = u.DirectImports } // buildUniverse constructs the universe scope. No globals touched. // All products returned by value. func buildUniverse() (u *UniverseState) { u = &UniverseState{} u.Registry = map[string]*TCPackage{} u.DirectImports = map[string][]string{} u.Scope = NewScope(nil) // Initialize Basic types in Typ table. infos := [...]struct { kind BasicKind info BasicInfo name string }{ {Invalid, 0, "invalid type"}, {Bool, IsBoolean, "bool"}, {Int8, IsInteger | IsOrdered | IsNumeric, "int8"}, {Int16, IsInteger | IsOrdered | IsNumeric, "int16"}, {Int32, IsInteger | IsOrdered | IsNumeric, "int32"}, {Int64, IsInteger | IsOrdered | IsNumeric, "int64"}, {Uint8, IsInteger | IsUnsigned | IsOrdered | IsNumeric, "uint8"}, {Uint16, IsInteger | IsUnsigned | IsOrdered | IsNumeric, "uint16"}, {Uint32, IsInteger | IsUnsigned | IsOrdered | IsNumeric, "uint32"}, {Uint64, IsInteger | IsUnsigned | IsOrdered | IsNumeric, "uint64"}, {Float32, IsFloat | IsOrdered | IsNumeric, "float32"}, {Float64, IsFloat | IsOrdered | IsNumeric, "float64"}, // string and []byte share kind TCString in Moxie. {TCString, IsString | IsOrdered, "string"}, {UnsafePointer, 0, "Pointer"}, // lives in unsafe package {UntypedBool, IsBoolean | IsUntyped, "untyped bool"}, {UntypedInt, IsInteger | IsNumeric | IsUntyped, "untyped int32"}, {UntypedRune, IsInteger | IsNumeric | IsUntyped, "untyped rune"}, {UntypedFloat, IsFloat | IsNumeric | IsUntyped, "untyped float"}, {UntypedString, IsString | IsUntyped, "untyped string"}, {UntypedNil, IsUntyped, "untyped nil"}, } for i, info := range infos { u.Typ[i] = &Basic{Kind: info.kind, Info: info.info, Name: info.name} } s := u.Scope s.Insert(NewTypeName(nil, "bool", u.Typ[Bool])) s.Insert(NewTypeName(nil, "int8", u.Typ[Int8])) s.Insert(NewTypeName(nil, "int16", u.Typ[Int16])) s.Insert(NewTypeName(nil, "int32", u.Typ[Int32])) s.Insert(NewTypeName(nil, "int64", u.Typ[Int64])) s.Insert(NewTypeName(nil, "uint8", u.Typ[Uint8])) s.Insert(NewTypeName(nil, "uint16", u.Typ[Uint16])) s.Insert(NewTypeName(nil, "uint32", u.Typ[Uint32])) s.Insert(NewTypeName(nil, "uint64", u.Typ[Uint64])) s.Insert(NewTypeName(nil, "float32", u.Typ[Float32])) s.Insert(NewTypeName(nil, "float64", u.Typ[Float64])) s.Insert(NewTypeName(nil, "string", u.Typ[TCString])) for _, b := range []*Basic{ u.Typ[Bool], u.Typ[Int8], u.Typ[Int16], u.Typ[Int32], u.Typ[Int64], u.Typ[Uint8], u.Typ[Uint16], u.Typ[Uint32], u.Typ[Uint64], u.Typ[Float32], u.Typ[Float64], u.Typ[TCString], } { addBasicStringMethod(b, u.Typ[TCString]) } byteType := NewNamed(NewTypeName(nil, "byte", nil), u.Typ[Uint8]) s.Insert(byteType.Obj) byteType.Obj.Typ = byteType addStringMethod(byteType, u.Typ[TCString]) runeType := NewNamed(NewTypeName(nil, "rune", nil), u.Typ[Int32]) s.Insert(runeType.Obj) runeType.Obj.Typ = runeType addStringMethod(runeType, u.Typ[TCString]) uintptrUnderlying := u.Typ[Uint64] if mxutil.TargetArch == "wasm" { uintptrUnderlying = u.Typ[Uint32] } uintptrType := NewNamed(NewTypeName(nil, "uintptr", nil), uintptrUnderlying) s.Insert(uintptrType.Obj) uintptrType.Obj.Typ = uintptrType addStringMethod(uintptrType, u.Typ[TCString]) errSig := NewSignature(nil, nil, NewTuple(NewTCVar(nil, "", u.Typ[TCString])), false) strMeth := &IfaceMethod{Name: "String", Sig: errSig} errMeth := &IfaceMethod{Name: "Error", Sig: errSig} errIface := NewTCInterface([]*IfaceMethod{errMeth, strMeth}, nil) errIface.Complete() u.Error = NewTypeName(nil, "error", errIface) s.Insert(u.Error) complex64Type := NewNamed(NewTypeName(nil, "complex64", nil), u.Typ[Invalid]) s.Insert(complex64Type.Obj) complex128Type := NewNamed(NewTypeName(nil, "complex128", nil), u.Typ[Invalid]) s.Insert(complex128Type.Obj) anyIface := NewTCInterface(nil, nil) anyIface.Complete() s.Insert(NewTypeName(nil, "any", anyIface)) comparableIface := NewTCInterface(nil, nil) comparableIface.Complete() s.Insert(NewTypeName(nil, "comparable", comparableIface)) s.Insert(NewTCConst(nil, "true", u.Typ[UntypedBool], untypedBool(true))) s.Insert(NewTCConst(nil, "false", u.Typ[UntypedBool], untypedBool(false))) s.Insert(NewTCConst(nil, "iota", u.Typ[UntypedInt], untypedInt(0))) s.Insert(NewTCConst(nil, "nil", u.Typ[UntypedNil], untypedNil())) builtinNames := [18]string{ BuiltinAppend: "append", BuiltinCap: "cap", BuiltinClear: "clear", BuiltinClose: "close", BuiltinComplex: "complex", BuiltinCopy: "copy", BuiltinDelete: "delete", BuiltinImag: "imag", BuiltinLen: "len", BuiltinMake: "__slicealloc", BuiltinNew: "__ptralloc", BuiltinPanic: "panic", BuiltinPrint: "print", BuiltinPrintln: "println", BuiltinReal: "real", BuiltinRecover: "recover", BuiltinSpawn: "spawn", } for id, name := range builtinNames { s.Insert(newBuiltin(name, BuiltinID(id))) } s.Insert(newBuiltin("min", BuiltinMin)) s.Insert(newBuiltin("max", BuiltinMax)) return u } func defTypeName(name string, typ Type) { tn := NewTypeName(nil, name, typ) Universe.Insert(tn) } func addStringMethod(named *Named, strType *Basic) { stringSig := NewSignature( NewTCVar(nil, "", named), nil, NewTuple(NewTCVar(nil, "", strType)), false, ) stringFn := NewTCFunc(nil, "String", stringSig) if named.Methods == nil { named.Methods = []*TCFunc{:0:1} } named.AddMethod(stringFn) } func addBasicStringMethod(b *Basic, strType *Basic) { stringSig := NewSignature( NewTCVar(nil, "", b), nil, NewTuple(NewTCVar(nil, "", strType)), false, ) stringFn := NewTCFunc(nil, "String", stringSig) if b.Methods == nil { b.Methods = []*TCFunc{:0:1} } b.AddMethod(stringFn) } func defObj(obj Object) { Universe.Insert(obj) } func defConst(name string, typ Type, val ConstVal) { Universe.Insert(NewTCConst(nil, name, typ, val)) } func defNil() { Universe.Insert(NewTCConst(nil, "nil", Typ[UntypedNil], untypedNil())) } // Placeholder constant values. These wrap go/constant values. In B4 they // become native Moxie values. type ConstBool struct{ V bool } type ConstInt struct{ V int64 } type ConstFloat struct { V float64 Lit string } type ConstStr struct{ S string } type ConstNil struct{} func untypedBool(v bool) (c ConstVal) { return &ConstBool{V: v} } func untypedInt(v int64) (c ConstVal) { return &ConstInt{V: v} } func untypedFloat(v float64) (c ConstVal) { return &ConstFloat{V: v} } func untypedStr(s string) (c ConstVal) { return &ConstStr{S: s} } func untypedNil() (c ConstVal) { return &ConstNil{} } func (c *ConstBool) String() (s string) { if c.V { return "true" }; return "false" } func (c *ConstInt) String() (s string) { n := c.V if n == 0 { return "0" } neg := n < 0 if neg { n = -n if n < 0 { return "-9223372036854775808" } } buf := []byte{:0:20} for n > 0 { push(buf, byte('0'+n%10)) n /= 10 } if neg { push(buf, '-') } for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 { buf[i], buf[j] = buf[j], buf[i] } return string(buf) } func (c *ConstFloat) String() (s string) { if c.Lit != "" { return NormalizeLLVMFloat(c.Lit) } if c.V == 0.0 { return "0.0" } return "0.0" } func (c *ConstStr) String() (s string) { return c.S } func (c *ConstNil) String() (s string) { return "nil" } func NormalizeLLVMFloat(s string) (sv string) { if len(s) == 0 { return "0.0" } start := 0 if s[0] == '-' || s[0] == '+' { start = 1 } hasDot := false ePos := -1 for i := start; i < len(s); i++ { if s[i] == '.' { hasDot = true } if s[i] == 'e' || s[i] == 'E' { ePos = i break } } if !hasDot { if ePos >= 0 { return s[:ePos] | ".0" | s[ePos:] } return s | ".0" } dotIdx := -1 for i := start; i < len(s); i++ { if s[i] == '.' { dotIdx = i break } } if dotIdx == start { s = s[:start] | "0" | s[start:] dotIdx++ } afterDot := dotIdx + 1 if afterDot >= len(s) || s[afterDot] == 'e' || s[afterDot] == 'E' { s = s[:afterDot] | "0" | s[afterDot:] } return s } // Predeclared returns the universe-scope object with the given name, or nil. func Predeclared(name string) (o Object) { return Universe.Lookup(name) }