mxtypes.go raw

   1  package loader
   2  
   3  // Moxie type unification.
   4  //
   5  // Moxie defines int as always 32 bits on all targets. This file patches
   6  // Go's type checker to agree: int becomes type-identical to int32, and
   7  // uint becomes type-identical to uint32. This eliminates the need for
   8  // int32(len(...)) casts throughout all Moxie code.
   9  //
  10  // The patch works by overwriting the kind field of types.Typ[Int] and
  11  // types.Typ[Uint] via unsafe. The Basic struct layout is:
  12  //   {kind BasicKind, info BasicInfo, name string}
  13  // with kind at offset 0. Type identity in go/types uses kind comparison
  14  // (x.kind == y.kind), so after patching, Identical(int, int32) returns true.
  15  //
  16  // On 64-bit targets, slice headers still use uintptr-sized (i64) len/cap
  17  // internally. The compiler inserts trunc/ext at the int boundary (len/cap
  18  // builtins, make, indexing). Max addressable length is 2^31-1.
  19  
  20  import (
  21  	"go/token"
  22  	"go/types"
  23  	"unsafe"
  24  )
  25  
  26  // patchIntTypes makes int≡int32 and uint≡uint32 in the type checker.
  27  // Applied unconditionally on all targets. Must be called before any type
  28  // checking begins. Idempotent - safe to call multiple times.
  29  //
  30  // Also registers push/pop/resize as universe-scope functions so go/types
  31  // accepts them in all packages including the runtime.
  32  func patchIntTypes() {
  33  	// Already patched - idempotent guard.
  34  	if types.Typ[types.Int].Kind() == types.Int32 {
  35  		return
  36  	}
  37  
  38  	// Patch int.kind from Int(2) to Int32(5).
  39  	kindPtr := (*types.BasicKind)(unsafe.Pointer(types.Typ[types.Int]))
  40  	*kindPtr = types.Int32
  41  
  42  	// Patch uint.kind from Uint(7) to Uint32(10).
  43  	kindPtr = (*types.BasicKind)(unsafe.Pointer(types.Typ[types.Uint]))
  44  	*kindPtr = types.Uint32
  45  
  46  	// Register push/pop/resize in go/types universe scope.
  47  	// push(s, v...) - variadic, mutates slice length, no return.
  48  	// pop(s) - returns element, mutates slice length.
  49  	// resize(s, n) - mutates slice length, no return.
  50  	// Typed as func(...any) any so go/types accepts any argument types.
  51  	// The real type checking happens in the Moxie type checker.
  52  	anySlice := types.NewSlice(types.Universe.Lookup("any").Type())
  53  	pushSig := types.NewSignatureType(nil, nil, nil,
  54  		types.NewTuple(types.NewVar(token.NoPos, nil, "args", anySlice)),
  55  		nil, true)
  56  	types.Universe.Insert(types.NewFunc(token.NoPos, nil, "push", pushSig))
  57  
  58  	popSig := types.NewSignatureType(nil, nil, nil,
  59  		types.NewTuple(types.NewVar(token.NoPos, nil, "s", anySlice)),
  60  		types.NewTuple(types.NewVar(token.NoPos, nil, "", types.Universe.Lookup("any").Type())),
  61  		false)
  62  	types.Universe.Insert(types.NewFunc(token.NoPos, nil, "pop", popSig))
  63  
  64  	resizeSig := types.NewSignatureType(nil, nil, nil,
  65  		types.NewTuple(
  66  			types.NewVar(token.NoPos, nil, "s", anySlice),
  67  			types.NewVar(token.NoPos, nil, "n", types.Typ[types.Int32]),
  68  		),
  69  		nil, false)
  70  	types.Universe.Insert(types.NewFunc(token.NoPos, nil, "resize", resizeSig))
  71  }
  72