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