package gotypesimport ()// CmpType is a deterministic, environment-independent order over types, used to// sort type slices into a stable order, never for identity (that goes through// typeutil.Map) and never for output (that goes through a qualifier). Named types// are ordered by package path + name rather than pointer, and every level is// unaliased before comparison, so the order never depends on pointer addresses,// load order, or whether a type was first seen through an alias. It is the// ordering authority for types, so gotypes never has to serialize a type to a// string except when rendering output or diagnostics.//// It is a WEAK order, not a structural total order: types that are not// types.Identical can still compare equal. Named types are keyed on package// path + name + type args (not their full definition), and type parameters on// name + index (see the TypeParam case). Determinism therefore rests entirely on// the absence of ties, not on the sort: every sorted slice holds types drawn from// identity-keyed sets of provider-signature types that already differ in those// keys, so no two elements ever compare equal, and the determinism tests guard// that empirically: solve's TestNoCmpTypeTiesInSortedSlices asserts no tie// reaches those sort sites. A structural string tiebreaker cannot substitute for// this invariant: two distinct type parameters that share a name render// identically, so no deterministic key separates them (their only distinguisher// is object identity, which is exactly what this order avoids). Stability cannot// be the safeguard either: several of these slices are collected from// nondeterministically-ordered maps, where a stable sort would only preserve a// nondeterministic input order. Callers still use a stable sort, but for// consistency with plumb's other ordering sites rather than for determinism here.func (, types.Type) int { = types.Unalias() = types.Unalias()if , := typeRank(), typeRank(); != {returncmp.Compare(, ) }switch a := .(type) {case *types.Basic:returncmp.Compare(.Kind(), .(*types.Basic).Kind())case *types.Pointer:return (.Elem(), .(*types.Pointer).Elem())case *types.Slice:return (.Elem(), .(*types.Slice).Elem())case *types.Array: := .(*types.Array)if := cmp.Compare(.Len(), .Len()); != 0 {return }return (.Elem(), .Elem())case *types.Chan: := .(*types.Chan)if := cmp.Compare(.Dir(), .Dir()); != 0 {return }return (.Elem(), .Elem())case *types.Map: := .(*types.Map)if := (.Key(), .Key()); != 0 {return }return (.Elem(), .Elem())case *types.Signature: := .(*types.Signature)if := cmpBool(.Variadic(), .Variadic()); != 0 {return }if := cmpTuple(.Params(), .Params()); != 0 {return }returncmpTuple(.Results(), .Results())case *types.Tuple:returncmpTuple(, .(*types.Tuple))case *types.Struct: := .(*types.Struct)if := cmp.Compare(.NumFields(), .NumFields()); != 0 {return }for := range .NumFields() { , := .Field(), .Field()// Id() not Name() so an unexported field carries its package, matching // the identity semantics unify.go compares fields by.if := strings.Compare(.Id(), .Id()); != 0 {return }if := cmpBool(.Embedded(), .Embedded()); != 0 {return }if := strings.Compare(.Tag(), .Tag()); != 0 {return }if := (.Type(), .Type()); != 0 {return } }return0case *types.Interface: := .(*types.Interface)if := cmp.Compare(.NumMethods(), .NumMethods()); != 0 {return }for := range .NumMethods() { , := .Method(), .Method()if := strings.Compare(.Id(), .Id()); != 0 {return }if := (.Type(), .Type()); != 0 {return } }if := cmp.Compare(.NumEmbeddeds(), .NumEmbeddeds()); != 0 {return }for := range .NumEmbeddeds() {if := (.EmbeddedType(), .EmbeddedType()); != 0 {return } }return0case *types.Named: := .(*types.Named)if := cmpTypeName(.Obj(), .Obj()); != 0 {return }returncmpTypeList(.TypeArgs(), .TypeArgs())case *types.TypeParam: := .(*types.TypeParam)// Distinct parameters with the same name and index compare equal; a stable // sort leaves them in input order.if := strings.Compare(.Obj().Name(), .Obj().Name()); != 0 {return }returncmp.Compare(.Index(), .Index())case *types.Union: := .(*types.Union)if := cmp.Compare(.Len(), .Len()); != 0 {return }for := range .Len() { , := .Term(), .Term()if := cmpBool(.Tilde(), .Tilde()); != 0 {return }if := (.Type(), .Type()); != 0 {return } }return0 }// Unreachable: typeRank already rejected any kind absent from this switch.panic(fmt.Sprintf("plumb: CmpType on unsupported type %T", ))}// typeRank orders the type kinds, the primary key of CmpType.func ( types.Type) int {switch .(type) {case *types.Basic:return0case *types.Pointer:return1case *types.Slice:return2case *types.Array:return3case *types.Chan:return4case *types.Map:return5case *types.Signature:return6case *types.Tuple:return7case *types.Struct:return8case *types.Interface:return9case *types.Named:return10case *types.TypeParam:return11case *types.Union:return12 }panic(fmt.Sprintf("plumb: typeRank on unsupported type %T", ))}func (, bool) int {switch {case == :return0case !:return -1default:return1 }}func (, *types.Tuple) int {if := cmp.Compare(.Len(), .Len()); != 0 {return }for := range .Len() {if := CmpType(.At().Type(), .At().Type()); != 0 {return } }return0}// cmpTypeName orders named types' declaring objects by package path then name:// environment-independent, unlike pointer identity.func (, *types.TypeName) int { , := "", ""if .Pkg() != nil { = .Pkg().Path() }if .Pkg() != nil { = .Pkg().Path() }if := strings.Compare(, ); != 0 {return }returnstrings.Compare(.Name(), .Name())}func (, *types.TypeList) int {// TypeList.Len is safe on a nil receiver (returns 0). , := .Len(), .Len()if := cmp.Compare(, ); != 0 {return }for := range {if := CmpType(.At(), .At()); != 0 {return } }return0}
The pages are generated with Goldsv0.8.4. (GOOS=linux GOARCH=amd64)