Source file src/cmd/compile/internal/types2/typeparam.go
1 // Copyright 2011 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 types2 6 7 import "sync/atomic" 8 9 // Note: This is a uint32 rather than a uint64 because the 10 // respective 64 bit atomic instructions are not available 11 // on all platforms. 12 var lastID atomic.Uint32 13 14 // nextID returns a value increasing monotonically by 1 with 15 // each call, starting with 1. It may be called concurrently. 16 func nextID() uint64 { return uint64(lastID.Add(1)) } 17 18 // A TypeParam represents the type of a type parameter in a generic declaration. 19 // 20 // A TypeParam has a name; use the [TypeParam.Obj] method to access 21 // its [TypeName] object. 22 type TypeParam struct { 23 check *Checker // for lazy type bound completion 24 id uint64 // unique id, for debugging only 25 obj *TypeName // corresponding type name 26 index int // type parameter index in source order, starting at 0 27 bound Type // any type, but underlying is eventually *Interface for correct programs (see TypeParam.iface) 28 } 29 30 // NewTypeParam returns a new TypeParam. Type parameters may be set on a Named 31 // type by calling SetTypeParams. Setting a type parameter on more than one type 32 // will result in a panic. 33 // 34 // The constraint argument can be nil, and set later via SetConstraint. If the 35 // constraint is non-nil, it must be fully defined. 36 func NewTypeParam(obj *TypeName, constraint Type) *TypeParam { 37 return (*Checker)(nil).newTypeParam(obj, constraint) 38 } 39 40 // check may be nil 41 func (check *Checker) newTypeParam(obj *TypeName, constraint Type) *TypeParam { 42 // Always increment lastID, even if it is not used. 43 id := nextID() 44 if check != nil { 45 check.nextID++ 46 id = check.nextID 47 } 48 typ := &TypeParam{check: check, id: id, obj: obj, index: -1, bound: constraint} 49 if obj.typ == nil { 50 obj.typ = typ 51 } 52 // iface may mutate typ.bound, so we must ensure that iface() is called 53 // at least once before the resulting TypeParam escapes. 54 if check != nil { 55 check.needsCleanup(typ) 56 } else if constraint != nil { 57 typ.iface() 58 } 59 return typ 60 } 61 62 // Obj returns the type name for the type parameter t. 63 func (t *TypeParam) Obj() *TypeName { return t.obj } 64 65 // Index returns the index of the type param within its param list, or -1 if 66 // the type parameter has not yet been bound to a type. 67 func (t *TypeParam) Index() int { 68 return t.index 69 } 70 71 // Constraint returns the type constraint specified for t. 72 func (t *TypeParam) Constraint() Type { 73 return t.bound 74 } 75 76 // SetConstraint sets the type constraint for t. 77 // 78 // It must be called by users of NewTypeParam after the bound's underlying is 79 // fully defined, and before using the type parameter in any way other than to 80 // form other types. Once SetConstraint returns the receiver, t is safe for 81 // concurrent use. 82 func (t *TypeParam) SetConstraint(bound Type) { 83 if bound == nil { 84 panic("nil constraint") 85 } 86 t.bound = bound 87 // iface may mutate t.bound (if bound is not an interface), so ensure that 88 // this is done before returning. 89 t.iface() 90 } 91 92 // Underlying returns the [underlying type] of the type parameter t, which is 93 // the underlying type of its constraint. This type is always an interface. 94 // 95 // [underlying type]: https://go.dev/ref/spec#Underlying_types. 96 func (t *TypeParam) Underlying() Type { 97 return t.iface() 98 } 99 100 func (t *TypeParam) String() string { return TypeString(t, nil) } 101 102 // ---------------------------------------------------------------------------- 103 // Implementation 104 105 func (t *TypeParam) cleanup() { 106 t.iface() 107 t.check = nil 108 } 109 110 // iface returns the constraint interface of t. 111 func (t *TypeParam) iface() *Interface { 112 bound := t.bound 113 114 // determine constraint interface 115 var ityp *Interface 116 switch u := under(bound).(type) { 117 case *Basic: 118 if !isValid(u) { 119 // error is reported elsewhere 120 return &emptyInterface 121 } 122 case *Interface: 123 if isTypeParam(bound) { 124 // error is reported in Checker.collectTypeParams 125 return &emptyInterface 126 } 127 ityp = u 128 } 129 130 // If we don't have an interface, wrap constraint into an implicit interface. 131 if ityp == nil { 132 ityp = NewInterfaceType(nil, []Type{bound}) 133 ityp.implicit = true 134 t.bound = ityp // update t.bound for next time (optimization) 135 } 136 137 // compute type set if necessary 138 if ityp.tset == nil { 139 // pos is used for tracing output; start with the type parameter position. 140 pos := t.obj.pos 141 // use the (original or possibly instantiated) type bound position if we have one 142 if n := asNamed(bound); n != nil { 143 pos = n.obj.pos 144 } 145 computeInterfaceTypeSet(t.check, pos, ityp) 146 } 147 148 return ityp 149 } 150 151 // is calls f with the specific type terms of t's constraint and reports whether 152 // all calls to f returned true. If there are no specific terms, is 153 // returns the result of f(nil). 154 func (t *TypeParam) is(f func(*term) bool) bool { 155 return t.iface().typeSet().is(f) 156 } 157 158 // typeset is an iterator over the (type/underlying type) pairs of the 159 // specific type terms of t's constraint. 160 // If there are no specific terms, typeset calls yield with (nil, nil). 161 // In any case, typeset is guaranteed to call yield at least once. 162 func (t *TypeParam) typeset(yield func(t, u Type) bool) { 163 t.iface().typeSet().typeset(yield) 164 } 165