src/cmd/compile/internal/types2/infer.go GO 795 lines View on github.com → Search inside
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.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.