github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/ssa/instantiate_test.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ssa 6 7 // Note: Tests use unexported functions. 8 9 import ( 10 "bytes" 11 "go/types" 12 "reflect" 13 "sort" 14 "testing" 15 16 "github.com/powerman/golang-tools/go/loader" 17 "github.com/powerman/golang-tools/internal/typeparams" 18 ) 19 20 // TestNeedsInstance ensures that new method instances can be created via needsInstance, 21 // that TypeArgs are as expected, and can be accessed via _Instances. 22 func TestNeedsInstance(t *testing.T) { 23 if !typeparams.Enabled { 24 return 25 } 26 const input = ` 27 package p 28 29 import "unsafe" 30 31 type Pointer[T any] struct { 32 v unsafe.Pointer 33 } 34 35 func (x *Pointer[T]) Load() *T { 36 return (*T)(LoadPointer(&x.v)) 37 } 38 39 func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer) 40 ` 41 // The SSA members for this package should look something like this: 42 // func LoadPointer func(addr *unsafe.Pointer) (val unsafe.Pointer) 43 // type Pointer struct{v unsafe.Pointer} 44 // method (*Pointer[T any]) Load() *T 45 // func init func() 46 // var init$guard bool 47 48 // Parse 49 var conf loader.Config 50 f, err := conf.ParseFile("<input>", input) 51 if err != nil { 52 t.Fatalf("parse: %v", err) 53 } 54 conf.CreateFromFiles("p", f) 55 56 // Load 57 lprog, err := conf.Load() 58 if err != nil { 59 t.Fatalf("Load: %v", err) 60 } 61 62 // Create and build SSA 63 prog := NewProgram(lprog.Fset, 0) 64 65 for _, info := range lprog.AllPackages { 66 prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable) 67 } 68 69 p := prog.Package(lprog.Package("p").Pkg) 70 p.Build() 71 72 ptr := p.Type("Pointer").Type().(*types.Named) 73 if ptr.NumMethods() != 1 { 74 t.Fatalf("Expected Pointer to have 1 method. got %d", ptr.NumMethods()) 75 } 76 77 obj := ptr.Method(0) 78 if obj.Name() != "Load" { 79 t.Errorf("Expected Pointer to have method named 'Load'. got %q", obj.Name()) 80 } 81 82 meth := prog.FuncValue(obj) 83 84 var cr creator 85 intSliceTyp := types.NewSlice(types.Typ[types.Int]) 86 instance := prog.needsInstance(meth, []types.Type{intSliceTyp}, &cr) 87 if len(cr) != 1 { 88 t.Errorf("Expected first instance to create a function. got %d created functions", len(cr)) 89 } 90 if instance._Origin != meth { 91 t.Errorf("Expected Origin of %s to be %s. got %s", instance, meth, instance._Origin) 92 } 93 if len(instance._TypeArgs) != 1 || !types.Identical(instance._TypeArgs[0], intSliceTyp) { 94 t.Errorf("Expected TypeArgs of %s to be %v. got %v", instance, []types.Type{intSliceTyp}, instance._TypeArgs) 95 } 96 instances := prog._Instances(meth) 97 if want := []*Function{instance}; !reflect.DeepEqual(instances, want) { 98 t.Errorf("Expected instances of %s to be %v. got %v", meth, want, instances) 99 } 100 101 // A second request with an identical type returns the same Function. 102 second := prog.needsInstance(meth, []types.Type{types.NewSlice(types.Typ[types.Int])}, &cr) 103 if second != instance || len(cr) != 1 { 104 t.Error("Expected second identical instantiation to not create a function") 105 } 106 107 // Add a second instance. 108 inst2 := prog.needsInstance(meth, []types.Type{types.NewSlice(types.Typ[types.Uint])}, &cr) 109 instances = prog._Instances(meth) 110 111 // Note: instance.Name() < inst2.Name() 112 sort.Slice(instances, func(i, j int) bool { 113 return instances[i].Name() < instances[j].Name() 114 }) 115 if want := []*Function{instance, inst2}; !reflect.DeepEqual(instances, want) { 116 t.Errorf("Expected instances of %s to be %v. got %v", meth, want, instances) 117 } 118 119 // build and sanity check manually created instance. 120 var b builder 121 b.buildFunction(instance) 122 var buf bytes.Buffer 123 if !sanityCheck(instance, &buf) { 124 t.Errorf("sanityCheck of %s failed with: %s", instance, buf.String()) 125 } 126 }