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 typeparams 6 7 import ( 8 "go/types" 9 ) 10 11 // CoreType returns the core type of T or nil if T does not have a core type. 12 // 13 // See https://go.dev/ref/spec#Core_types for the definition of a core type. 14 func CoreType(T types.Type) types.Type { 15 U := T.Underlying() 16 if _, ok := U.(*types.Interface); !ok { 17 return U // for non-interface types, 18 } 19 20 terms, err := _NormalTerms(U) 21 if len(terms) == 0 || err != nil { 22 // len(terms) -> empty type set of interface. 23 // err != nil => U is invalid, exceeds complexity bounds, or has an empty type set. 24 return nil // no core type. 25 } 26 27 U = terms[0].Type().Underlying() 28 var identical int // i in [0,identical) => Identical(U, terms[i].Type().Underlying()) 29 for identical = 1; identical < len(terms); identical++ { 30 if !types.Identical(U, terms[identical].Type().Underlying()) { 31 break 32 } 33 } 34 35 if identical == len(terms) { 36 // https://go.dev/ref/spec#Core_types 37 // "There is a single type U which is the underlying type of all types in the type set of T" 38 return U 39 } 40 ch, ok := U.(*types.Chan) 41 if !ok { 42 return nil // no core type as identical < len(terms) and U is not a channel. 43 } 44 // https://go.dev/ref/spec#Core_types 45 // "the type chan E if T contains only bidirectional channels, or the type chan<- E or 46 // <-chan E depending on the direction of the directional channels present." 47 for chans := identical; chans < len(terms); chans++ { 48 curr, ok := terms[chans].Type().Underlying().(*types.Chan) 49 if !ok { 50 return nil 51 } 52 if !types.Identical(ch.Elem(), curr.Elem()) { 53 return nil // channel elements are not identical. 54 } 55 if ch.Dir() == types.SendRecv { 56 // ch is bidirectional. We can safely always use curr's direction. 57 ch = curr 58 } else if curr.Dir() != types.SendRecv && ch.Dir() != curr.Dir() { 59 // ch and curr are not bidirectional and not the same direction. 60 return nil 61 } 62 } 63 return ch 64 } 65 66 // _NormalTerms returns a slice of terms representing the normalized structural 67 // type restrictions of a type, if any. 68 // 69 // For all types other than *types.TypeParam, *types.Interface, and 70 // *types.Union, this is just a single term with Tilde() == false and 71 // Type() == typ. For *types.TypeParam, *types.Interface, and *types.Union, see 72 // below. 73 // 74 // Structural type restrictions of a type parameter are created via 75 // non-interface types embedded in its constraint interface (directly, or via a 76 // chain of interface embeddings). For example, in the declaration type 77 // T[P interface{~int; m()}] int the structural restriction of the type 78 // parameter P is ~int. 79 // 80 // With interface embedding and unions, the specification of structural type 81 // restrictions may be arbitrarily complex. For example, consider the 82 // following: 83 // 84 // type A interface{ ~string|~[]byte } 85 // 86 // type B interface{ int|string } 87 // 88 // type C interface { ~string|~int } 89 // 90 // type T[P interface{ A|B; C }] int 91 // 92 // In this example, the structural type restriction of P is ~string|int: A|B 93 // expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int, 94 // which when intersected with C (~string|~int) yields ~string|int. 95 // 96 // _NormalTerms computes these expansions and reductions, producing a 97 // "normalized" form of the embeddings. A structural restriction is normalized 98 // if it is a single union containing no interface terms, and is minimal in the 99 // sense that removing any term changes the set of types satisfying the 100 // constraint. It is left as a proof for the reader that, modulo sorting, there 101 // is exactly one such normalized form. 102 // 103 // Because the minimal representation always takes this form, _NormalTerms 104 // returns a slice of tilde terms corresponding to the terms of the union in 105 // the normalized structural restriction. An error is returned if the type is 106 // invalid, exceeds complexity bounds, or has an empty type set. In the latter 107 // case, _NormalTerms returns ErrEmptyTypeSet. 108 // 109 // _NormalTerms makes no guarantees about the order of terms, except that it 110 // is deterministic. 111 func _NormalTerms(typ types.Type) ([]*Term, error) { 112 switch typ := typ.(type) { 113 case *TypeParam: 114 return StructuralTerms(typ) 115 case *Union: 116 return UnionTermSet(typ) 117 case *types.Interface: 118 return InterfaceTermSet(typ) 119 default: 120 return []*Term{NewTerm(false, typ)}, nil 121 } 122 } 123