universe.go raw

   1  package typecheck
   2  
   3  // Universe is the outermost scope containing all predeclared identifiers.
   4  // All package scopes have Universe as their ultimate ancestor.
   5  var Universe *Scope
   6  
   7  // Predeclared types. Indices match BasicKind values.
   8  var Typ [UntypedNil + 1]*Basic
   9  
  10  // Predeclared named types (accessible as objects in Universe).
  11  var (
  12  	universeBool          *TypeName
  13  	universeError         *TypeName
  14  	universeByteAlias     *TypeName // byte = uint8
  15  	universeRuneAlias     *TypeName // rune = int32
  16  )
  17  
  18  func init() {
  19  	Universe = NewScope(nil)
  20  
  21  	// Initialize Basic types in Typ table.
  22  	infos := [...]struct {
  23  		kind BasicKind
  24  		info BasicInfo
  25  		name string
  26  	}{
  27  		{Invalid, 0, "invalid type"},
  28  
  29  		{Bool, IsBoolean, "bool"},
  30  
  31  		{Int8, IsInteger | IsOrdered | IsNumeric, "int8"},
  32  		{Int16, IsInteger | IsOrdered | IsNumeric, "int16"},
  33  		{Int32, IsInteger | IsOrdered | IsNumeric, "int32"},
  34  		{Int64, IsInteger | IsOrdered | IsNumeric, "int64"},
  35  		{Uint8, IsInteger | IsUnsigned | IsOrdered | IsNumeric, "uint8"},
  36  		{Uint16, IsInteger | IsUnsigned | IsOrdered | IsNumeric, "uint16"},
  37  		{Uint32, IsInteger | IsUnsigned | IsOrdered | IsNumeric, "uint32"},
  38  		{Uint64, IsInteger | IsUnsigned | IsOrdered | IsNumeric, "uint64"},
  39  
  40  		{Float32, IsFloat | IsOrdered | IsNumeric, "float32"},
  41  		{Float64, IsFloat | IsOrdered | IsNumeric, "float64"},
  42  
  43  		// string and []byte share kind String in Moxie.
  44  		{String, IsString | IsOrdered, "string"},
  45  
  46  		{UnsafePointer, 0, "Pointer"}, // lives in unsafe package
  47  
  48  		{UntypedBool, IsBoolean | IsUntyped, "untyped bool"},
  49  		{UntypedInt, IsInteger | IsNumeric | IsUntyped, "untyped int"},
  50  		{UntypedRune, IsInteger | IsNumeric | IsUntyped, "untyped rune"},
  51  		{UntypedFloat, IsFloat | IsNumeric | IsUntyped, "untyped float"},
  52  		{UntypedString, IsString | IsUntyped, "untyped string"},
  53  		{UntypedNil, IsUntyped, "untyped nil"},
  54  	}
  55  	for i, info := range infos {
  56  		Typ[i] = &Basic{kind: info.kind, info: info.info, name: info.name}
  57  	}
  58  
  59  	// In Moxie, int ≡ int32 and uint ≡ uint32. We expose "int" and "uint"
  60  	// as named aliases to int32/uint32 in the universe scope.
  61  	defTypeName("bool", Typ[Bool])
  62  	defTypeName("int8", Typ[Int8])
  63  	defTypeName("int16", Typ[Int16])
  64  	defTypeName("int32", Typ[Int32])
  65  	defTypeName("int64", Typ[Int64])
  66  	defTypeName("uint8", Typ[Uint8])
  67  	defTypeName("uint16", Typ[Uint16])
  68  	defTypeName("uint32", Typ[Uint32])
  69  	defTypeName("uint64", Typ[Uint64])
  70  	defTypeName("float32", Typ[Float32])
  71  	defTypeName("float64", Typ[Float64])
  72  	defTypeName("string", Typ[String])
  73  
  74  	// int = int32, uint = uint32 (Moxie always 32-bit)
  75  	intType := NewNamed(NewTypeName(nil, "int", nil), Typ[Int32])
  76  	uintType := NewNamed(NewTypeName(nil, "uint", nil), Typ[Uint32])
  77  	defObj(intType.obj)
  78  	defObj(uintType.obj)
  79  	intType.obj.typ = intType
  80  	uintType.obj.typ = uintType
  81  
  82  	// byte = uint8
  83  	byteType := NewNamed(NewTypeName(nil, "byte", nil), Typ[Uint8])
  84  	defObj(byteType.obj)
  85  	byteType.obj.typ = byteType
  86  	universeByteAlias = byteType.obj
  87  
  88  	// rune = int32
  89  	runeType := NewNamed(NewTypeName(nil, "rune", nil), Typ[Int32])
  90  	defObj(runeType.obj)
  91  	runeType.obj.typ = runeType
  92  	universeRuneAlias = runeType.obj
  93  
  94  	// uintptr (allowed only with import "unsafe")
  95  	uintptrType := NewNamed(NewTypeName(nil, "uintptr", nil), Typ[Uint64])
  96  	defObj(uintptrType.obj)
  97  	uintptrType.obj.typ = uintptrType
  98  
  99  	// error interface - includes String() so error values satisfy stringer
 100  	errStrSig := NewSignature(nil, nil, NewTuple(NewVar(nil, "", Typ[String])), false)
 101  	errMeth := &IfaceMethod{name: "Error", sig: errStrSig}
 102  	errStringMeth := &IfaceMethod{name: "String", sig: errStrSig}
 103  	errIface := NewInterface([]*IfaceMethod{errMeth, errStringMeth}, nil)
 104  	errIface.Complete()
 105  	universeError = NewTypeName(nil, "error", errIface)
 106  	defObj(universeError)
 107  
 108  	// complex64/128 are not valid in Moxie programs but appear in stdlib source.
 109  	// Register them as invalid basic types so the checker doesn't report "undefined".
 110  	complex64Type := NewNamed(NewTypeName(nil, "complex64", nil), Typ[Invalid])
 111  	defObj(complex64Type.obj)
 112  	complex128Type := NewNamed(NewTypeName(nil, "complex128", nil), Typ[Invalid])
 113  	defObj(complex128Type.obj)
 114  
 115  	// any is an alias for interface{} (Go 1.18+)
 116  	anyIface := NewInterface(nil, nil)
 117  	anyIface.Complete()
 118  	anyObj := NewTypeName(nil, "any", anyIface)
 119  	defObj(anyObj)
 120  
 121  	// comparable is a builtin interface satisfied by comparable types.
 122  	comparableIface := NewInterface(nil, nil)
 123  	comparableIface.Complete()
 124  	comparableObj := NewTypeName(nil, "comparable", comparableIface)
 125  	defObj(comparableObj)
 126  
 127  	// predeclared constants
 128  	defConst("true", Typ[UntypedBool], untypedBool(true))
 129  	defConst("false", Typ[UntypedBool], untypedBool(false))
 130  	defConst("iota", Typ[UntypedInt], untypedInt(0))
 131  	defNil()
 132  
 133  	// predeclared builtins
 134  	for id, name := range builtinNames {
 135  		Universe.Insert(newBuiltin(name, BuiltinID(id)))
 136  	}
 137  
 138  	// Go 1.21 builtins.
 139  	Universe.Insert(newBuiltin("min", BuiltinMin))
 140  	Universe.Insert(newBuiltin("max", BuiltinMax))
 141  	// Moxie arena-stack builtins.
 142  	Universe.Insert(newBuiltin("push", BuiltinPush))
 143  	Universe.Insert(newBuiltin("pop", BuiltinPop))
 144  	Universe.Insert(newBuiltin("resize", BuiltinResize))
 145  }
 146  
 147  var builtinNames = [...]string{
 148  	BuiltinAppend:  "append",
 149  	BuiltinCap:     "cap",
 150  	BuiltinClear:   "clear",
 151  	BuiltinClose:   "close",
 152  	BuiltinComplex: "complex",
 153  	BuiltinCopy:    "copy",
 154  	BuiltinDelete:  "delete",
 155  	BuiltinImag:    "imag",
 156  	BuiltinLen:     "len",
 157  	BuiltinMake:    "make",
 158  	BuiltinNew:     "new",
 159  	BuiltinPanic:   "panic",
 160  	BuiltinPrint:   "print",
 161  	BuiltinPrintln: "println",
 162  	BuiltinReal:    "real",
 163  	BuiltinRecover: "recover",
 164  	BuiltinSpawn:   "spawn",
 165  }
 166  
 167  func defTypeName(name string, typ Type) {
 168  	tn := NewTypeName(nil, name, typ)
 169  	Universe.Insert(tn)
 170  }
 171  
 172  func defObj(obj Object) {
 173  	Universe.Insert(obj)
 174  }
 175  
 176  func defConst(name string, typ Type, val ConstVal) {
 177  	Universe.Insert(NewConst(nil, name, typ, val))
 178  }
 179  
 180  func defNil() {
 181  	Universe.Insert(NewConst(nil, "nil", Typ[UntypedNil], untypedNil()))
 182  }
 183  
 184  // Placeholder constant values. These wrap go/constant values. In B4 they
 185  // become native Moxie values.
 186  type constBool struct{ v bool }
 187  type constInt struct{ v int64 }
 188  type constNil struct{}
 189  
 190  func untypedBool(v bool) ConstVal  { return constBool{v} }
 191  func untypedInt(v int64) ConstVal  { return constInt{v} }
 192  func untypedNil() ConstVal         { return constNil{} }
 193  
 194  func (c constBool) String() string { if c.v { return "true" }; return "false" }
 195  func (c constInt) String() string  { return "0" }
 196  func (c constNil) String() string  { return "nil" }
 197  
 198  // Predeclared returns the universe-scope object with the given name, or nil.
 199  func Predeclared(name string) Object {
 200  	return Universe.Lookup(name)
 201  }
 202