1// Copyright 2018 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45// This file implements type parameter inference.67package types289import (10 "cmd/compile/internal/syntax"11 "fmt"12 "slices"13 "strings"14)1516// If enableReverseTypeInference is set, uninstantiated and17// partially instantiated generic functions may be assigned18// (incl. returned) to variables of function type and type19// inference will attempt to infer the missing type arguments.20// Available with go1.21.21const enableReverseTypeInference = true // disable for debugging2223// infer attempts to infer the complete set of type arguments for generic function instantiation/call24// based on the given type parameters tparams, type arguments targs, function parameters params, and25// function arguments args, if any. There must be at least one type parameter, no more type arguments26// than type parameters, and params and args must match in number (incl. zero).27// If reverse is set, an error message's contents are reversed for a better error message for some28// errors related to reverse type inference (where the function call is synthetic).29// If successful, infer returns the complete list of given and inferred type arguments, one for each30// type parameter. Otherwise the result is nil. Errors are reported through the err parameter.31// Note: infer may fail (return nil) due to invalid args operands without reporting additional errors.32func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type, params *Tuple, args []*operand, reverse bool, err *error_) (inferred []Type) {33 // Don't verify result conditions if there's no error handler installed:34 // in that case, an error leads to an exit panic and the result value may35 // be incorrect. But in that case it doesn't matter because callers won't36 // be able to use it either.37 if check.conf.Error != nil {38 defer func() {39 assert(inferred == nil || len(inferred) == len(tparams) && !slices.Contains(inferred, nil))40 }()41 }4243 if traceInference {44 check.dump("== infer : %s%s ➞ %s", tparams, params, targs) // aligned with rename print below45 defer func() {46 check.dump("=> %s ➞ %s\n", tparams, inferred)47 }()48 }4950 // There must be at least one type parameter, and no more type arguments than type parameters.51 n := len(tparams)52 assert(n > 0 && len(targs) <= n)5354 // Parameters and arguments must match in number.55 assert(params.Len() == len(args))5657 // If we already have all type arguments, we're done.58 if len(targs) == n && !slices.Contains(targs, nil) {59 return targs60 }6162 // If we have invalid (ordinary) arguments, an error was reported before.63 // Avoid additional inference errors and exit early (go.dev/issue/60434).64 for _, arg := range args {65 if !arg.isValid() {66 return nil67 }68 }6970 // Make sure we have a "full" list of type arguments, some of which may71 // be nil (unknown). Make a copy so as to not clobber the incoming slice.72 if len(targs) < n {73 targs2 := make([]Type, n)74 copy(targs2, targs)75 targs = targs276 }77 // len(targs) == n7879 // Continue with the type arguments we have. Avoid matching generic80 // parameters that already have type arguments against function arguments:81 // It may fail because matching uses type identity while parameter passing82 // uses assignment rules. Instantiate the parameter list with the type83 // arguments we have, and continue with that parameter list.8485 // Substitute type arguments for their respective type parameters in params,86 // if any. Note that nil targs entries are ignored by check.subst.87 // We do this for better error messages; it's not needed for correctness.88 // For instance, given:89 //90 // func f[P, Q any](P, Q) {}91 //92 // func _(s string) {93 // f[int](s, s) // ERROR94 // }95 //96 // With substitution, we get the error:97 // "cannot use s (variable of type string) as int value in argument to f[int]"98 //99 // Without substitution we get the (worse) error:100 // "type string of s does not match inferred type int for P"101 // even though the type int was provided (not inferred) for P.102 //103 // TODO(gri) We might be able to finesse this in the error message reporting104 // (which only happens in case of an error) and then avoid doing105 // the substitution (which always happens).106 if params.Len() > 0 {107 smap := makeSubstMap(tparams, targs)108 params = check.subst(nopos, params, smap, nil, check.context()).(*Tuple)109 }110111 // Unify parameter and argument types for generic parameters with typed arguments112 // and collect the indices of generic parameters with untyped arguments.113 // Terminology: generic parameter = function parameter with a type-parameterized type114 u := newUnifier(check, tparams, targs, check.allowVersion(go1_21))115116 errorf := func(tpar, targ Type, arg *operand) {117 // provide a better error message if we can118 targs := u.inferred(tparams)119 if targs[0] == nil {120 // The first type parameter couldn't be inferred.121 // If none of them could be inferred, don't try122 // to provide the inferred type in the error msg.123 allFailed := true124 for _, targ := range targs {125 if targ != nil {126 allFailed = false127 break128 }129 }130 if allFailed {131 err.addf(arg, "type %s of %s does not match %s (cannot infer %s)", targ, arg.expr, tpar, typeParamsString(tparams))132 return133 }134 }135 smap := makeSubstMap(tparams, targs)136 // TODO(gri): pass a poser here, rather than arg.Pos().137 inferred := check.subst(arg.Pos(), tpar, smap, nil, check.context())138 // CannotInferTypeArgs indicates a failure of inference, though the actual139 // error may be better attributed to a user-provided type argument (hence140 // InvalidTypeArg). We can't differentiate these cases, so fall back on141 // the more general CannotInferTypeArgs.142 if inferred != tpar {143 if reverse {144 err.addf(arg, "inferred type %s for %s does not match type %s of %s", inferred, tpar, targ, arg.expr)145 } else {146 err.addf(arg, "type %s of %s does not match inferred type %s for %s", targ, arg.expr, inferred, tpar)147 }148 } else {149 err.addf(arg, "type %s of %s does not match %s", targ, arg.expr, tpar)150 }151 }152153 // indices of generic parameters with untyped arguments, for later use154 var untyped []int155156 // --- 1 ---157 // use information from function arguments158159 if traceInference {160 u.tracef("== function parameters: %s", params)161 u.tracef("-- function arguments : %s", args)162 }163164 for i, arg := range args {165 if !arg.isValid() {166 // An error was reported earlier. Ignore this arg167 // and continue, we may still be able to infer all168 // targs resulting in fewer follow-on errors.169 // TODO(gri) determine if we still need this check170 continue171 }172 par := params.At(i)173 if isParameterized(tparams, par.typ) || isParameterized(tparams, arg.typ()) {174 // Function parameters are always typed. Arguments may be untyped.175 // Collect the indices of untyped arguments and handle them later.176 if isTyped(arg.typ()) {177 if !u.unify(par.typ, arg.typ(), assign) {178 errorf(par.typ, arg.typ(), arg)179 return nil180 }181 } else if _, ok := par.typ.(*TypeParam); ok && !arg.isNil() {182 // Since default types are all basic (i.e., non-composite) types, an183 // untyped argument will never match a composite parameter type; the184 // only parameter type it can possibly match against is a *TypeParam.185 // Thus, for untyped arguments we only need to look at parameter types186 // that are single type parameters.187 // Also, untyped nils don't have a default type and can be ignored.188 // Finally, it's not possible to have an alias type denoting a type189 // parameter declared by the current function and use it in the same190 // function signature; hence we don't need to Unalias before the191 // .(*TypeParam) type assertion above.192 untyped = append(untyped, i)193 }194 }195 }196197 if traceInference {198 inferred := u.inferred(tparams)199 u.tracef("=> %s ➞ %s\n", tparams, inferred)200 }201202 // --- 2 ---203 // use information from type parameter constraints204205 if traceInference {206 u.tracef("== type parameters: %s", tparams)207 }208209 // Unify type parameters with their constraints as long210 // as progress is being made.211 //212 // This is an O(n^2) algorithm where n is the number of213 // type parameters: if there is progress, at least one214 // type argument is inferred per iteration, and we have215 // a doubly nested loop.216 //217 // In practice this is not a problem because the number218 // of type parameters tends to be very small (< 5 or so).219 // (It should be possible for unification to efficiently220 // signal newly inferred type arguments; then the loops221 // here could handle the respective type parameters only,222 // but that will come at a cost of extra complexity which223 // may not be worth it.)224 for i := 0; ; i++ {225 nn := u.unknowns()226 if traceInference {227 if i > 0 {228 fmt.Println()229 }230 u.tracef("-- iteration %d", i)231 }232233 for _, tpar := range tparams {234 tx := u.at(tpar)235 core, single := coreTerm(tpar)236 if traceInference {237 u.tracef("-- type parameter %s = %s: core(%s) = %s, single = %v", tpar, tx, tpar, core, single)238 }239240 // If the type parameter's constraint has a core term (i.e., a core type with tilde information)241 // try to unify the type parameter with that core type.242 if core != nil {243 // A type parameter can be unified with its constraint's core type in two cases.244 switch {245 case tx != nil:246 if traceInference {247 u.tracef("-> unify type parameter %s (type %s) with constraint core type %s", tpar, tx, core.typ)248 }249 // The corresponding type argument tx is known. There are 2 cases:250 // 1) If the core type has a tilde, per spec requirement for tilde251 // elements, the core type is an underlying (literal) type.252 // And because of the tilde, the underlying type of tx must match253 // against the core type.254 // But because unify automatically matches a defined type against255 // an underlying literal type, we can simply unify tx with the256 // core type.257 // 2) If the core type doesn't have a tilde, we also must unify tx258 // with the core type.259 if !u.unify(tx, core.typ, 0) {260 // TODO(gri) Type parameters that appear in the constraint and261 // for which we have type arguments inferred should262 // use those type arguments for a better error message.263 err.addf(pos, "%s (type %s) does not satisfy %s", tpar, tx, tpar.Constraint())264 return nil265 }266 case single && !core.tilde:267 if traceInference {268 u.tracef("-> set type parameter %s to constraint's common underlying type %s", tpar, core.typ)269 }270 // The corresponding type argument tx is unknown and the core term271 // describes a single specific type and no tilde.272 // In this case the type argument must be that single type; set it.273 u.set(tpar, core.typ)274 }275 }276277 // Independent of whether there is a core term, if the type argument tx is known278 // it must implement the methods of the type constraint, possibly after unification279 // of the relevant method signatures, otherwise tx cannot satisfy the constraint.280 // This unification step may provide additional type arguments.281 //282 // Note: The type argument tx may be known but contain references to other type283 // parameters (i.e., tx may still be parameterized).284 // In this case the methods of tx don't correctly reflect the final method set285 // and we may get a missing method error below. Skip this step in this case.286 //287 // TODO(gri) We should be able continue even with a parameterized tx if we add288 // a simplify step beforehand (see below). This will require factoring out the289 // simplify phase so we can call it from here.290 if tx != nil && !isParameterized(tparams, tx) {291 if traceInference {292 u.tracef("-> unify type parameter %s (type %s) methods with constraint methods", tpar, tx)293 }294 // TODO(gri) Now that unification handles interfaces, this code can295 // be reduced to calling u.unify(tx, tpar.iface(), assign)296 // (which will compare signatures exactly as we do below).297 // We leave it as is for now because missingMethod provides298 // a failure cause which allows for a better error message.299 // Eventually, unify should return an error with cause.300 var cause string301 constraint := tpar.iface()302 if !check.hasAllMethods(tx, constraint, true, func(x, y Type) bool { return u.unify(x, y, exact) }, &cause) {303 // TODO(gri) better error message (see TODO above)304 err.addf(pos, "%s (type %s) does not satisfy %s %s", tpar, tx, tpar.Constraint(), cause)305 return nil306 }307 }308 }309310 if u.unknowns() == nn {311 break // no progress312 }313 }314315 if traceInference {316 inferred := u.inferred(tparams)317 u.tracef("=> %s ➞ %s\n", tparams, inferred)318 }319320 // --- 3 ---321 // use information from untyped constants322323 if traceInference {324 u.tracef("== untyped arguments: %v", untyped)325 }326327 // Some generic parameters with untyped arguments may have been given a type by now.328 // Collect all remaining parameters that don't have a type yet and determine the329 // maximum untyped type for each of those parameters, if possible.330 var maxUntyped map[*TypeParam]Type // lazily allocated (we may not need it)331 for _, index := range untyped {332 tpar := params.At(index).typ.(*TypeParam) // is type parameter (no alias) by construction of untyped333 if u.at(tpar) == nil {334 arg := args[index] // arg corresponding to tpar335 if maxUntyped == nil {336 maxUntyped = make(map[*TypeParam]Type)337 }338 max := maxUntyped[tpar]339 if max == nil {340 max = arg.typ()341 } else {342 m := maxType(max, arg.typ())343 if m == nil {344 err.addf(arg, "mismatched types %s and %s (cannot infer %s)", max, arg.typ(), tpar)345 return nil346 }347 max = m348 }349 maxUntyped[tpar] = max350 }351 }352 // maxUntyped contains the maximum untyped type for each type parameter353 // which doesn't have a type yet. Set the respective default types.354 for tpar, typ := range maxUntyped {355 d := Default(typ)356 assert(isTyped(d))357 u.set(tpar, d)358 }359360 // --- simplify ---361362 // u.inferred(tparams) now contains the incoming type arguments plus any additional type363 // arguments which were inferred. The inferred non-nil entries may still contain364 // references to other type parameters found in constraints.365 // For instance, for [A any, B interface{ []C }, C interface{ *A }], if A == int366 // was given, unification produced the type list [int, []C, *A]. We eliminate the367 // remaining type parameters by substituting the type parameters in this type list368 // until nothing changes anymore.369 inferred = u.inferred(tparams)370 if debug {371 for i, targ := range targs {372 assert(targ == nil || inferred[i] == targ)373 }374 }375376 // The data structure of each (provided or inferred) type represents a graph, where377 // each node corresponds to a type and each (directed) vertex points to a component378 // type. The substitution process described above repeatedly replaces type parameter379 // nodes in these graphs with the graphs of the types the type parameters stand for,380 // which creates a new (possibly bigger) graph for each type.381 // The substitution process will not stop if the replacement graph for a type parameter382 // also contains that type parameter.383 // For instance, for [A interface{ *A }], without any type argument provided for A,384 // unification produces the type list [*A]. Substituting A in *A with the value for385 // A will lead to infinite expansion by producing [**A], [****A], [********A], etc.,386 // because the graph A -> *A has a cycle through A.387 // Generally, cycles may occur across multiple type parameters and inferred types388 // (for instance, consider [P interface{ *Q }, Q interface{ func(P) }]).389 // We eliminate cycles by walking the graphs for all type parameters. If a cycle390 // through a type parameter is detected, killCycles nils out the respective type391 // (in the inferred list) which kills the cycle, and marks the corresponding type392 // parameter as not inferred.393 //394 // TODO(gri) If useful, we could report the respective cycle as an error. We don't395 // do this now because type inference will fail anyway, and furthermore,396 // constraints with cycles of this kind cannot currently be satisfied by397 // any user-supplied type. But should that change, reporting an error398 // would be wrong.399 killCycles(tparams, inferred)400401 // dirty tracks the indices of all types that may still contain type parameters.402 // We know that nil type entries and entries corresponding to provided (non-nil)403 // type arguments are clean, so exclude them from the start.404 var dirty []int405 for i, typ := range inferred {406 if typ != nil && (i >= len(targs) || targs[i] == nil) {407 dirty = append(dirty, i)408 }409 }410411 for len(dirty) > 0 {412 if traceInference {413 u.tracef("-- simplify %s ➞ %s", tparams, inferred)414 }415 // TODO(gri) Instead of creating a new substMap for each iteration,416 // provide an update operation for substMaps and only change when417 // needed. Optimization.418 smap := makeSubstMap(tparams, inferred)419 n := 0420 for _, index := range dirty {421 t0 := inferred[index]422 if t1 := check.subst(nopos, t0, smap, nil, check.context()); t1 != t0 {423 // t0 was simplified to t1.424 // If t0 was a generic function, but the simplified signature t1 does425 // not contain any type parameters anymore, the function is not generic426 // anymore. Remove its type parameters. (go.dev/issue/59953)427 // Note that if t0 was a signature, t1 must be a signature, and t1428 // can only be a generic signature if it originated from a generic429 // function argument. Those signatures are never defined types and430 // thus there is no need to call Underlying below.431 // TODO(gri) Consider doing this in Checker.subst.432 // Then this would fall out automatically here and also433 // in instantiation (where we also explicitly nil out434 // type parameters).435 if sig, _ := t1.(*Signature); sig != nil && sig.TypeParams().Len() > 0 && !isParameterized(tparams, sig) {436 sig.tparams = nil437 }438 inferred[index] = t1439 dirty[n] = index440 n++441 }442 }443 dirty = dirty[:n]444 }445446 // Once nothing changes anymore, we may still have type parameters left;447 // e.g., a constraint with core type *P may match a type parameter Q but448 // we don't have any type arguments to fill in for *P or Q (go.dev/issue/45548).449 // Don't let such inferences escape; instead treat them as unresolved.450 for i, typ := range inferred {451 if typ == nil || isParameterized(tparams, typ) {452 obj := tparams[i].obj453 err.addf(pos, "cannot infer %s (declared at %v)", obj.name, obj.pos)454 return nil455 }456 }457458 return459}460461// renameTParams renames the type parameters in the given type such that each type462// parameter is given a new identity. renameTParams returns the new type parameters463// and updated type. If the result type is unchanged from the argument type, none464// of the type parameters in tparams occurred in the type.465// If typ is a generic function, type parameters held with typ are not changed and466// must be updated separately if desired.467// The positions is only used for debug traces.468func (check *Checker) renameTParams(pos syntax.Pos, tparams []*TypeParam, typ Type) ([]*TypeParam, Type) {469 // For the purpose of type inference we must differentiate type parameters470 // occurring in explicit type or value function arguments from the type471 // parameters we are solving for via unification because they may be the472 // same in self-recursive calls:473 //474 // func f[P constraint](x P) {475 // f(x)476 // }477 //478 // In this example, without type parameter renaming, the P used in the479 // instantiation f[P] has the same pointer identity as the P we are trying480 // to solve for through type inference. This causes problems for type481 // unification. Because any such self-recursive call is equivalent to482 // a mutually recursive call, type parameter renaming can be used to483 // create separate, disentangled type parameters. The above example484 // can be rewritten into the following equivalent code:485 //486 // func f[P constraint](x P) {487 // f2(x)488 // }489 //490 // func f2[P2 constraint](x P2) {491 // f(x)492 // }493 //494 // Type parameter renaming turns the first example into the second495 // example by renaming the type parameter P into P2.496 if len(tparams) == 0 {497 return nil, typ // nothing to do498 }499500 tparams2 := make([]*TypeParam, len(tparams))501 for i, tparam := range tparams {502 tname := NewTypeName(tparam.Obj().Pos(), tparam.Obj().Pkg(), tparam.Obj().Name(), nil)503 tparams2[i] = NewTypeParam(tname, nil)504 tparams2[i].index = tparam.index // == i505 }506507 renameMap := makeRenameMap(tparams, tparams2)508 for i, tparam := range tparams {509 tparams2[i].bound = check.subst(pos, tparam.bound, renameMap, nil, check.context())510 }511512 return tparams2, check.subst(pos, typ, renameMap, nil, check.context())513}514515// typeParamsString produces a string containing all the type parameter names516// in list suitable for human consumption.517func typeParamsString(list []*TypeParam) string {518 // common cases519 n := len(list)520 switch n {521 case 0:522 return ""523 case 1:524 return list[0].obj.name525 case 2:526 return list[0].obj.name + " and " + list[1].obj.name527 }528529 // general case (n > 2)530 var buf strings.Builder531 for i, tname := range list[:n-1] {532 if i > 0 {533 buf.WriteString(", ")534 }535 buf.WriteString(tname.obj.name)536 }537 buf.WriteString(", and ")538 buf.WriteString(list[n-1].obj.name)539 return buf.String()540}541542// isParameterized reports whether typ contains any of the type parameters of tparams.543// If typ is a generic function, isParameterized ignores the type parameter declarations;544// it only considers the signature proper (incoming and result parameters).545func isParameterized(tparams []*TypeParam, typ Type) bool {546 w := tpWalker{547 tparams: tparams,548 seen: make(map[Type]bool),549 }550 return w.isParameterized(typ)551}552553type tpWalker struct {554 tparams []*TypeParam555 seen map[Type]bool556}557558func (w *tpWalker) isParameterized(typ Type) (res bool) {559 // detect cycles560 if x, ok := w.seen[typ]; ok {561 return x562 }563 w.seen[typ] = false564 defer func() {565 w.seen[typ] = res566 }()567568 switch t := typ.(type) {569 case *Basic:570 // nothing to do571572 case *Alias:573 return w.isParameterized(Unalias(t))574575 case *Array:576 return w.isParameterized(t.elem)577578 case *Slice:579 return w.isParameterized(t.elem)580581 case *Struct:582 return w.varList(t.fields)583584 case *Pointer:585 return w.isParameterized(t.base)586587 case *Tuple:588 // This case does not occur from within isParameterized589 // because tuples only appear in signatures where they590 // are handled explicitly. But isParameterized is also591 // called by Checker.callExpr with a function result tuple592 // if instantiation failed (go.dev/issue/59890).593 return t != nil && w.varList(t.vars)594595 case *Signature:596 // t.tparams may not be nil if we are looking at a signature597 // of a generic function type (or an interface method) that is598 // part of the type we're testing. We don't care about these type599 // parameters.600 // Similarly, the receiver of a method may declare (rather than601 // use) type parameters, we don't care about those either.602 // Thus, we only need to look at the input and result parameters.603 return t.params != nil && w.varList(t.params.vars) || t.results != nil && w.varList(t.results.vars)604605 case *Interface:606 tset := t.typeSet()607 for _, m := range tset.methods {608 if w.isParameterized(m.typ) {609 return true610 }611 }612 return tset.is(func(t *term) bool {613 return t != nil && w.isParameterized(t.typ)614 })615616 case *Map:617 return w.isParameterized(t.key) || w.isParameterized(t.elem)618619 case *Chan:620 return w.isParameterized(t.elem)621622 case *Named:623 for _, t := range t.TypeArgs().list() {624 if w.isParameterized(t) {625 return true626 }627 }628629 case *TypeParam:630 return slices.Index(w.tparams, t) >= 0631632 default:633 panic(fmt.Sprintf("unexpected %T", typ))634 }635636 return false637}638639func (w *tpWalker) varList(list []*Var) bool {640 for _, v := range list {641 if w.isParameterized(v.typ) {642 return true643 }644 }645 return false646}647648// If the type parameter has a single specific type S, coreTerm returns (S, true).649// Otherwise, if tpar has a core type T, it returns a term corresponding to that650// core type and false. In that case, if any term of tpar has a tilde, the core651// term has a tilde. In all other cases coreTerm returns (nil, false).652func coreTerm(tpar *TypeParam) (*term, bool) {653 n := 0654 var single *term // valid if n == 1655 var tilde bool656 tpar.is(func(t *term) bool {657 if t == nil {658 assert(n == 0)659 return false // no terms660 }661 n++662 single = t663 if t.tilde {664 tilde = true665 }666 return true667 })668 if n == 1 {669 if debug {670 u, _ := commonUnder(tpar, nil)671 assert(single.typ.Underlying() == u)672 }673 return single, true674 }675 if typ, _ := commonUnder(tpar, nil); typ != nil {676 // A core type is always an underlying type.677 // If any term of tpar has a tilde, we don't678 // have a precise core type and we must return679 // a tilde as well.680 return &term{tilde, typ}, false681 }682 return nil, false683}684685// killCycles walks through the given type parameters and looks for cycles686// created by type parameters whose inferred types refer back to that type687// parameter, either directly or indirectly. If such a cycle is detected,688// it is killed by setting the corresponding inferred type to nil.689//690// TODO(gri) Determine if we can simply abort inference as soon as we have691// found a single cycle.692func killCycles(tparams []*TypeParam, inferred []Type) {693 w := cycleFinder{tparams, inferred, make(map[Type]bool)}694 for _, t := range tparams {695 w.typ(t) // t != nil696 }697}698699type cycleFinder struct {700 tparams []*TypeParam701 inferred []Type702 seen map[Type]bool703}704705func (w *cycleFinder) typ(typ Type) {706 typ = Unalias(typ)707 if w.seen[typ] {708 // We have seen typ before. If it is one of the type parameters709 // in w.tparams, iterative substitution will lead to infinite expansion.710 // Nil out the corresponding type which effectively kills the cycle.711 if tpar, _ := typ.(*TypeParam); tpar != nil {712 if i := slices.Index(w.tparams, tpar); i >= 0 {713 // cycle through tpar714 w.inferred[i] = nil715 }716 }717 // If we don't have one of our type parameters, the cycle is due718 // to an ordinary recursive type and we can just stop walking it.719 return720 }721 w.seen[typ] = true722 defer delete(w.seen, typ)723724 switch t := typ.(type) {725 case *Basic:726 // nothing to do727728 // *Alias:729 // This case should not occur because of Unalias(typ) at the top.730731 case *Array:732 w.typ(t.elem)733734 case *Slice:735 w.typ(t.elem)736737 case *Struct:738 w.varList(t.fields)739740 case *Pointer:741 w.typ(t.base)742743 // case *Tuple:744 // This case should not occur because tuples only appear745 // in signatures where they are handled explicitly.746747 case *Signature:748 if t.params != nil {749 w.varList(t.params.vars)750 }751 if t.results != nil {752 w.varList(t.results.vars)753 }754755 case *Union:756 for _, t := range t.terms {757 w.typ(t.typ)758 }759760 case *Interface:761 for _, m := range t.methods {762 w.typ(m.typ)763 }764 for _, t := range t.embeddeds {765 w.typ(t)766 }767768 case *Map:769 w.typ(t.key)770 w.typ(t.elem)771772 case *Chan:773 w.typ(t.elem)774775 case *Named:776 for _, tpar := range t.TypeArgs().list() {777 w.typ(tpar)778 }779780 case *TypeParam:781 if i := slices.Index(w.tparams, t); i >= 0 && w.inferred[i] != nil {782 w.typ(w.inferred[i])783 }784785 default:786 panic(fmt.Sprintf("unexpected %T", typ))787 }788}789790func (w *cycleFinder) varList(list []*Var) {791 for _, v := range list {792 w.typ(v.typ)793 }794}
Findings
✓ No findings reported for this file.