golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/internal/typeparams/normalize.go (about) 1 // Copyright 2021 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 typeparams 6 7 import ( 8 "errors" 9 "fmt" 10 "go/types" 11 "os" 12 "strings" 13 ) 14 15 //go:generate go run copytermlist.go 16 17 const debug = false 18 19 var ErrEmptyTypeSet = errors.New("empty type set") 20 21 // StructuralTerms returns a slice of terms representing the normalized 22 // structural type restrictions of a type parameter, if any. 23 // 24 // Structural type restrictions of a type parameter are created via 25 // non-interface types embedded in its constraint interface (directly, or via a 26 // chain of interface embeddings). For example, in the declaration 27 // 28 // type T[P interface{~int; m()}] int 29 // 30 // the structural restriction of the type parameter P is ~int. 31 // 32 // With interface embedding and unions, the specification of structural type 33 // restrictions may be arbitrarily complex. For example, consider the 34 // following: 35 // 36 // type A interface{ ~string|~[]byte } 37 // 38 // type B interface{ int|string } 39 // 40 // type C interface { ~string|~int } 41 // 42 // type T[P interface{ A|B; C }] int 43 // 44 // In this example, the structural type restriction of P is ~string|int: A|B 45 // expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int, 46 // which when intersected with C (~string|~int) yields ~string|int. 47 // 48 // StructuralTerms computes these expansions and reductions, producing a 49 // "normalized" form of the embeddings. A structural restriction is normalized 50 // if it is a single union containing no interface terms, and is minimal in the 51 // sense that removing any term changes the set of types satisfying the 52 // constraint. It is left as a proof for the reader that, modulo sorting, there 53 // is exactly one such normalized form. 54 // 55 // Because the minimal representation always takes this form, StructuralTerms 56 // returns a slice of tilde terms corresponding to the terms of the union in 57 // the normalized structural restriction. An error is returned if the 58 // constraint interface is invalid, exceeds complexity bounds, or has an empty 59 // type set. In the latter case, StructuralTerms returns ErrEmptyTypeSet. 60 // 61 // StructuralTerms makes no guarantees about the order of terms, except that it 62 // is deterministic. 63 func StructuralTerms(tparam *types.TypeParam) ([]*types.Term, error) { 64 constraint := tparam.Constraint() 65 if constraint == nil { 66 return nil, fmt.Errorf("%s has nil constraint", tparam) 67 } 68 iface, _ := constraint.Underlying().(*types.Interface) 69 if iface == nil { 70 return nil, fmt.Errorf("constraint is %T, not *types.Interface", constraint.Underlying()) 71 } 72 return InterfaceTermSet(iface) 73 } 74 75 // InterfaceTermSet computes the normalized terms for a constraint interface, 76 // returning an error if the term set cannot be computed or is empty. In the 77 // latter case, the error will be ErrEmptyTypeSet. 78 // 79 // See the documentation of StructuralTerms for more information on 80 // normalization. 81 func InterfaceTermSet(iface *types.Interface) ([]*types.Term, error) { 82 return computeTermSet(iface) 83 } 84 85 // UnionTermSet computes the normalized terms for a union, returning an error 86 // if the term set cannot be computed or is empty. In the latter case, the 87 // error will be ErrEmptyTypeSet. 88 // 89 // See the documentation of StructuralTerms for more information on 90 // normalization. 91 func UnionTermSet(union *types.Union) ([]*types.Term, error) { 92 return computeTermSet(union) 93 } 94 95 func computeTermSet(typ types.Type) ([]*types.Term, error) { 96 tset, err := computeTermSetInternal(typ, make(map[types.Type]*termSet), 0) 97 if err != nil { 98 return nil, err 99 } 100 if tset.terms.isEmpty() { 101 return nil, ErrEmptyTypeSet 102 } 103 if tset.terms.isAll() { 104 return nil, nil 105 } 106 var terms []*types.Term 107 for _, term := range tset.terms { 108 terms = append(terms, types.NewTerm(term.tilde, term.typ)) 109 } 110 return terms, nil 111 } 112 113 // A termSet holds the normalized set of terms for a given type. 114 // 115 // The name termSet is intentionally distinct from 'type set': a type set is 116 // all types that implement a type (and includes method restrictions), whereas 117 // a term set just represents the structural restrictions on a type. 118 type termSet struct { 119 complete bool 120 terms termlist 121 } 122 123 func indentf(depth int, format string, args ...interface{}) { 124 fmt.Fprintf(os.Stderr, strings.Repeat(".", depth)+format+"\n", args...) 125 } 126 127 func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth int) (res *termSet, err error) { 128 if t == nil { 129 panic("nil type") 130 } 131 132 if debug { 133 indentf(depth, "%s", t.String()) 134 defer func() { 135 if err != nil { 136 indentf(depth, "=> %s", err) 137 } else { 138 indentf(depth, "=> %s", res.terms.String()) 139 } 140 }() 141 } 142 143 const maxTermCount = 100 144 if tset, ok := seen[t]; ok { 145 if !tset.complete { 146 return nil, fmt.Errorf("cycle detected in the declaration of %s", t) 147 } 148 return tset, nil 149 } 150 151 // Mark the current type as seen to avoid infinite recursion. 152 tset := new(termSet) 153 defer func() { 154 tset.complete = true 155 }() 156 seen[t] = tset 157 158 switch u := t.Underlying().(type) { 159 case *types.Interface: 160 // The term set of an interface is the intersection of the term sets of its 161 // embedded types. 162 tset.terms = allTermlist 163 for i := 0; i < u.NumEmbeddeds(); i++ { 164 embedded := u.EmbeddedType(i) 165 if _, ok := embedded.Underlying().(*types.TypeParam); ok { 166 return nil, fmt.Errorf("invalid embedded type %T", embedded) 167 } 168 tset2, err := computeTermSetInternal(embedded, seen, depth+1) 169 if err != nil { 170 return nil, err 171 } 172 tset.terms = tset.terms.intersect(tset2.terms) 173 } 174 case *types.Union: 175 // The term set of a union is the union of term sets of its terms. 176 tset.terms = nil 177 for i := 0; i < u.Len(); i++ { 178 t := u.Term(i) 179 var terms termlist 180 switch t.Type().Underlying().(type) { 181 case *types.Interface: 182 tset2, err := computeTermSetInternal(t.Type(), seen, depth+1) 183 if err != nil { 184 return nil, err 185 } 186 terms = tset2.terms 187 case *types.TypeParam, *types.Union: 188 // A stand-alone type parameter or union is not permitted as union 189 // term. 190 return nil, fmt.Errorf("invalid union term %T", t) 191 default: 192 if t.Type() == types.Typ[types.Invalid] { 193 continue 194 } 195 terms = termlist{{t.Tilde(), t.Type()}} 196 } 197 tset.terms = tset.terms.union(terms) 198 if len(tset.terms) > maxTermCount { 199 return nil, fmt.Errorf("exceeded max term count %d", maxTermCount) 200 } 201 } 202 case *types.TypeParam: 203 panic("unreachable") 204 default: 205 // For all other types, the term set is just a single non-tilde term 206 // holding the type itself. 207 if u != types.Typ[types.Invalid] { 208 tset.terms = termlist{{false, t}} 209 } 210 } 211 return tset, nil 212 } 213 214 // under is a facade for the go/types internal function of the same name. It is 215 // used by typeterm.go. 216 func under(t types.Type) types.Type { 217 return t.Underlying() 218 }