github.com/gopherjs/gopherjs@v1.19.0-beta1.0.20240506212314-27071a8796e4/compiler/internal/typeparams/instance.go (about) 1 package typeparams 2 3 import ( 4 "fmt" 5 "go/types" 6 7 "github.com/gopherjs/gopherjs/compiler/internal/symbol" 8 "github.com/gopherjs/gopherjs/compiler/typesutil" 9 ) 10 11 // Instance of a generic type or function. 12 // 13 // Non-generic objects can be represented as an Instance with zero type params, 14 // they are instances of themselves. 15 type Instance struct { 16 Object types.Object // Object to be instantiated. 17 TArgs typesutil.TypeList // Type params to instantiate with. 18 } 19 20 // String returns a string representation of the Instance. 21 // 22 // Two semantically different instances may have the same string representation 23 // if the instantiated object or its type arguments shadow other types. 24 func (i *Instance) String() string { 25 sym := symbol.New(i.Object).String() 26 if len(i.TArgs) == 0 { 27 return sym 28 } 29 30 return fmt.Sprintf("%s<%s>", sym, i.TArgs) 31 } 32 33 // TypeString returns a Go type string representing the instance (suitable for %T verb). 34 func (i *Instance) TypeString() string { 35 tArgs := "" 36 if len(i.TArgs) > 0 { 37 tArgs = "[" + i.TArgs.String() + "]" 38 } 39 return fmt.Sprintf("%s.%s%s", i.Object.Pkg().Name(), i.Object.Name(), tArgs) 40 } 41 42 // IsTrivial returns true if this is an instance of a non-generic object. 43 func (i *Instance) IsTrivial() bool { 44 return len(i.TArgs) == 0 45 } 46 47 // Recv returns an instance of the receiver type of a method. 48 // 49 // Returns zero value if not a method. 50 func (i *Instance) Recv() Instance { 51 sig, ok := i.Object.Type().(*types.Signature) 52 if !ok { 53 return Instance{} 54 } 55 recv := typesutil.RecvType(sig) 56 if recv == nil { 57 return Instance{} 58 } 59 return Instance{ 60 Object: recv.Obj(), 61 TArgs: i.TArgs, 62 } 63 } 64 65 // InstanceSet allows collecting and processing unique Instances. 66 // 67 // Each Instance may be added to the set any number of times, but it will be 68 // returned for processing exactly once. Processing order is not specified. 69 type InstanceSet struct { 70 values []Instance 71 unprocessed int // Index in values for the next unprocessed element. 72 seen InstanceMap[int] // Maps instance to a unique numeric id. 73 } 74 75 // Add instances to the set. Instances that have been previously added to the 76 // set won't be requeued for processing regardless of whether they have been 77 // processed already. 78 func (iset *InstanceSet) Add(instances ...Instance) *InstanceSet { 79 for _, inst := range instances { 80 if iset.seen.Has(inst) { 81 continue 82 } 83 iset.seen.Set(inst, iset.seen.Len()) 84 iset.values = append(iset.values, inst) 85 } 86 return iset 87 } 88 89 // ID returns a unique numeric identifier assigned to an instance in the set. 90 // The ID is guaranteed to be unique among all instances of the same object 91 // within a given program. The ID will be consistent, as long as instances are 92 // added to the set in the same order. 93 // 94 // In order to have an ID assigned, the instance must have been previously added 95 // to the set. 96 // 97 // Note: these ids are used in the generated code as keys to the specific 98 // type/function instantiation in the type/function object. Using this has two 99 // advantages: 100 // 101 // - More compact generated code compared to string keys derived from type args. 102 // 103 // - Collision avoidance in case of two different types having the same name due 104 // to shadowing. 105 // 106 // Here's an example where it's very difficult to assign non-colliding 107 // name-based keys to the two different types T: 108 // 109 // func foo() { 110 // type T int 111 // { type T string } // Code block creates a new nested scope allowing for shadowing. 112 // } 113 func (iset *InstanceSet) ID(inst Instance) int { 114 id, ok := iset.seen.get(inst) 115 if !ok { 116 panic(fmt.Errorf("requesting ID of instance %v that hasn't been added to the set", inst)) 117 } 118 return id 119 } 120 121 // next returns the next Instance to be processed. 122 // 123 // If there are no unprocessed instances, the second returned value will be false. 124 func (iset *InstanceSet) next() (Instance, bool) { 125 if iset.exhausted() { 126 return Instance{}, false 127 } 128 next := iset.values[iset.unprocessed] 129 iset.unprocessed++ 130 return next, true 131 } 132 133 // exhausted returns true if there are no unprocessed instances in the set. 134 func (iset *InstanceSet) exhausted() bool { return len(iset.values) <= iset.unprocessed } 135 136 // Values returns instances that are currently in the set. Order is not specified. 137 func (iset *InstanceSet) Values() []Instance { 138 return iset.values 139 } 140 141 // PackageInstanceSets stores an InstanceSet for each package in a program, keyed 142 // by import path. 143 type PackageInstanceSets map[string]*InstanceSet 144 145 // Pkg returns InstanceSet for objects defined in the given package. 146 func (i PackageInstanceSets) Pkg(pkg *types.Package) *InstanceSet { 147 path := pkg.Path() 148 iset, ok := i[path] 149 if !ok { 150 iset = &InstanceSet{} 151 i[path] = iset 152 } 153 return iset 154 } 155 156 // Add instances to the appropriate package's set. Automatically initialized 157 // new per-package sets upon a first encounter. 158 func (i PackageInstanceSets) Add(instances ...Instance) { 159 for _, inst := range instances { 160 i.Pkg(inst.Object.Pkg()).Add(inst) 161 } 162 } 163 164 // ID returns a unique numeric identifier assigned to an instance in the set. 165 // 166 // See: InstanceSet.ID(). 167 func (i PackageInstanceSets) ID(inst Instance) int { 168 return i.Pkg(inst.Object.Pkg()).ID(inst) 169 }