mono.go raw

   1  // Code generated by "go test -run=Generate -write=all"; DO NOT EDIT.
   2  // Source: ../../cmd/compile/internal/types2/mono.go
   3  
   4  // Copyright 2021 The Go Authors. All rights reserved.
   5  // Use of this source code is governed by a BSD-style
   6  // license that can be found in the LICENSE file.
   7  
   8  package types
   9  
  10  import (
  11  	"go/ast"
  12  	"go/token"
  13  	. "internal/types/errors"
  14  )
  15  
  16  // This file implements a check to validate that a Go package doesn't
  17  // have unbounded recursive instantiation, which is not compatible
  18  // with compilers using static instantiation (such as
  19  // monomorphization).
  20  //
  21  // It implements a sort of "type flow" analysis by detecting which
  22  // type parameters are instantiated with other type parameters (or
  23  // types derived thereof). A package cannot be statically instantiated
  24  // if the graph has any cycles involving at least one derived type.
  25  //
  26  // Concretely, we construct a directed, weighted graph. Vertices are
  27  // used to represent type parameters as well as some defined
  28  // types. Edges are used to represent how types depend on each other:
  29  //
  30  // * Everywhere a type-parameterized function or type is instantiated,
  31  //   we add edges to each type parameter from the vertices (if any)
  32  //   representing each type parameter or defined type referenced by
  33  //   the type argument. If the type argument is just the referenced
  34  //   type itself, then the edge has weight 0, otherwise 1.
  35  //
  36  // * For every defined type declared within a type-parameterized
  37  //   function or method, we add an edge of weight 1 to the defined
  38  //   type from each ambient type parameter.
  39  //
  40  // For example, given:
  41  //
  42  //	func f[A, B any]() {
  43  //		type T int
  44  //		f[T, map[A]B]()
  45  //	}
  46  //
  47  // we construct vertices representing types A, B, and T. Because of
  48  // declaration "type T int", we construct edges T<-A and T<-B with
  49  // weight 1; and because of instantiation "f[T, map[A]B]" we construct
  50  // edges A<-T with weight 0, and B<-A and B<-B with weight 1.
  51  //
  52  // Finally, we look for any positive-weight cycles. Zero-weight cycles
  53  // are allowed because static instantiation will reach a fixed point.
  54  
  55  type monoGraph struct {
  56  	vertices []monoVertex
  57  	edges    []monoEdge
  58  
  59  	// canon maps method receiver type parameters to their respective
  60  	// receiver type's type parameters.
  61  	canon map[*TypeParam]*TypeParam
  62  
  63  	// nameIdx maps a defined type or (canonical) type parameter to its
  64  	// vertex index.
  65  	nameIdx map[*TypeName]int
  66  }
  67  
  68  type monoVertex struct {
  69  	weight int // weight of heaviest known path to this vertex
  70  	pre    int // previous edge (if any) in the above path
  71  	len    int // length of the above path
  72  
  73  	// obj is the defined type or type parameter represented by this
  74  	// vertex.
  75  	obj *TypeName
  76  }
  77  
  78  type monoEdge struct {
  79  	dst, src int
  80  	weight   int
  81  
  82  	pos token.Pos
  83  	typ Type
  84  }
  85  
  86  func (check *Checker) monomorph() {
  87  	// We detect unbounded instantiation cycles using a variant of
  88  	// Bellman-Ford's algorithm. Namely, instead of always running |V|
  89  	// iterations, we run until we either reach a fixed point or we've
  90  	// found a path of length |V|. This allows us to terminate earlier
  91  	// when there are no cycles, which should be the common case.
  92  
  93  	again := true
  94  	for again {
  95  		again = false
  96  
  97  		for i, edge := range check.mono.edges {
  98  			src := &check.mono.vertices[edge.src]
  99  			dst := &check.mono.vertices[edge.dst]
 100  
 101  			// N.B., we're looking for the greatest weight paths, unlike
 102  			// typical Bellman-Ford.
 103  			w := src.weight + edge.weight
 104  			if w <= dst.weight {
 105  				continue
 106  			}
 107  
 108  			dst.pre = i
 109  			dst.len = src.len + 1
 110  			if dst.len == len(check.mono.vertices) {
 111  				check.reportInstanceLoop(edge.dst)
 112  				return
 113  			}
 114  
 115  			dst.weight = w
 116  			again = true
 117  		}
 118  	}
 119  }
 120  
 121  func (check *Checker) reportInstanceLoop(v int) {
 122  	var stack []int
 123  	seen := make([]bool, len(check.mono.vertices))
 124  
 125  	// We have a path that contains a cycle and ends at v, but v may
 126  	// only be reachable from the cycle, not on the cycle itself. We
 127  	// start by walking backwards along the path until we find a vertex
 128  	// that appears twice.
 129  	for !seen[v] {
 130  		stack = append(stack, v)
 131  		seen[v] = true
 132  		v = check.mono.edges[check.mono.vertices[v].pre].src
 133  	}
 134  
 135  	// Trim any vertices we visited before visiting v the first
 136  	// time. Since v is the first vertex we found within the cycle, any
 137  	// vertices we visited earlier cannot be part of the cycle.
 138  	for stack[0] != v {
 139  		stack = stack[1:]
 140  	}
 141  
 142  	// TODO(mdempsky): Pivot stack so we report the cycle from the top?
 143  
 144  	err := check.newError(InvalidInstanceCycle)
 145  	obj0 := check.mono.vertices[v].obj
 146  	err.addf(obj0, "instantiation cycle:")
 147  
 148  	qf := RelativeTo(check.pkg)
 149  	for _, v := range stack {
 150  		edge := check.mono.edges[check.mono.vertices[v].pre]
 151  		obj := check.mono.vertices[edge.dst].obj
 152  
 153  		switch obj.Type().(type) {
 154  		default:
 155  			panic("unexpected type")
 156  		case *Named:
 157  			err.addf(atPos(edge.pos), "%s implicitly parameterized by %s", obj.Name(), TypeString(edge.typ, qf)) // secondary error, \t indented
 158  		case *TypeParam:
 159  			err.addf(atPos(edge.pos), "%s instantiated as %s", obj.Name(), TypeString(edge.typ, qf)) // secondary error, \t indented
 160  		}
 161  	}
 162  	err.report()
 163  }
 164  
 165  // recordCanon records that tpar is the canonical type parameter
 166  // corresponding to method type parameter mpar.
 167  func (w *monoGraph) recordCanon(mpar, tpar *TypeParam) {
 168  	if w.canon == nil {
 169  		w.canon = make(map[*TypeParam]*TypeParam)
 170  	}
 171  	w.canon[mpar] = tpar
 172  }
 173  
 174  // recordInstance records that the given type parameters were
 175  // instantiated with the corresponding type arguments.
 176  func (w *monoGraph) recordInstance(pkg *Package, pos token.Pos, tparams []*TypeParam, targs []Type, xlist []ast.Expr) {
 177  	for i, tpar := range tparams {
 178  		pos := pos
 179  		if i < len(xlist) {
 180  			pos = startPos(xlist[i])
 181  		}
 182  		w.assign(pkg, pos, tpar, targs[i])
 183  	}
 184  }
 185  
 186  // assign records that tpar was instantiated as targ at pos.
 187  func (w *monoGraph) assign(pkg *Package, pos token.Pos, tpar *TypeParam, targ Type) {
 188  	// Go generics do not have an analog to C++`s template-templates,
 189  	// where a template parameter can itself be an instantiable
 190  	// template. So any instantiation cycles must occur within a single
 191  	// package. Accordingly, we can ignore instantiations of imported
 192  	// type parameters.
 193  	//
 194  	// TODO(mdempsky): Push this check up into recordInstance? All type
 195  	// parameters in a list will appear in the same package.
 196  	if tpar.Obj().Pkg() != pkg {
 197  		return
 198  	}
 199  
 200  	// flow adds an edge from vertex src representing that typ flows to tpar.
 201  	flow := func(src int, typ Type) {
 202  		weight := 1
 203  		if typ == targ {
 204  			weight = 0
 205  		}
 206  
 207  		w.addEdge(w.typeParamVertex(tpar), src, weight, pos, targ)
 208  	}
 209  
 210  	// Recursively walk the type argument to find any defined types or
 211  	// type parameters.
 212  	var do func(typ Type)
 213  	do = func(typ Type) {
 214  		switch typ := Unalias(typ).(type) {
 215  		default:
 216  			panic("unexpected type")
 217  
 218  		case *TypeParam:
 219  			assert(typ.Obj().Pkg() == pkg)
 220  			flow(w.typeParamVertex(typ), typ)
 221  
 222  		case *Named:
 223  			if src := w.localNamedVertex(pkg, typ.Origin()); src >= 0 {
 224  				flow(src, typ)
 225  			}
 226  
 227  			targs := typ.TypeArgs()
 228  			for i := 0; i < targs.Len(); i++ {
 229  				do(targs.At(i))
 230  			}
 231  
 232  		case *Array:
 233  			do(typ.Elem())
 234  		case *Basic:
 235  			// ok
 236  		case *Chan:
 237  			do(typ.Elem())
 238  		case *Map:
 239  			do(typ.Key())
 240  			do(typ.Elem())
 241  		case *Pointer:
 242  			do(typ.Elem())
 243  		case *Slice:
 244  			do(typ.Elem())
 245  
 246  		case *Interface:
 247  			for i := 0; i < typ.NumMethods(); i++ {
 248  				do(typ.Method(i).Type())
 249  			}
 250  		case *Signature:
 251  			tuple := func(tup *Tuple) {
 252  				for i := 0; i < tup.Len(); i++ {
 253  					do(tup.At(i).Type())
 254  				}
 255  			}
 256  			tuple(typ.Params())
 257  			tuple(typ.Results())
 258  		case *Struct:
 259  			for i := 0; i < typ.NumFields(); i++ {
 260  				do(typ.Field(i).Type())
 261  			}
 262  		}
 263  	}
 264  	do(targ)
 265  }
 266  
 267  // localNamedVertex returns the index of the vertex representing
 268  // named, or -1 if named doesn't need representation.
 269  func (w *monoGraph) localNamedVertex(pkg *Package, named *Named) int {
 270  	obj := named.Obj()
 271  	if obj.Pkg() != pkg {
 272  		return -1 // imported type
 273  	}
 274  
 275  	root := pkg.Scope()
 276  	if obj.Parent() == root {
 277  		return -1 // package scope, no ambient type parameters
 278  	}
 279  
 280  	if idx, ok := w.nameIdx[obj]; ok {
 281  		return idx
 282  	}
 283  
 284  	idx := -1
 285  
 286  	// Walk the type definition's scope to find any ambient type
 287  	// parameters that it's implicitly parameterized by.
 288  	for scope := obj.Parent(); scope != root; scope = scope.Parent() {
 289  		for _, elem := range scope.elems {
 290  			if elem, ok := elem.(*TypeName); ok && !elem.IsAlias() && cmpPos(elem.Pos(), obj.Pos()) < 0 {
 291  				if tpar, ok := elem.Type().(*TypeParam); ok {
 292  					if idx < 0 {
 293  						idx = len(w.vertices)
 294  						w.vertices = append(w.vertices, monoVertex{obj: obj})
 295  					}
 296  
 297  					w.addEdge(idx, w.typeParamVertex(tpar), 1, obj.Pos(), tpar)
 298  				}
 299  			}
 300  		}
 301  	}
 302  
 303  	if w.nameIdx == nil {
 304  		w.nameIdx = make(map[*TypeName]int)
 305  	}
 306  	w.nameIdx[obj] = idx
 307  	return idx
 308  }
 309  
 310  // typeParamVertex returns the index of the vertex representing tpar.
 311  func (w *monoGraph) typeParamVertex(tpar *TypeParam) int {
 312  	if x, ok := w.canon[tpar]; ok {
 313  		tpar = x
 314  	}
 315  
 316  	obj := tpar.Obj()
 317  
 318  	if idx, ok := w.nameIdx[obj]; ok {
 319  		return idx
 320  	}
 321  
 322  	if w.nameIdx == nil {
 323  		w.nameIdx = make(map[*TypeName]int)
 324  	}
 325  
 326  	idx := len(w.vertices)
 327  	w.vertices = append(w.vertices, monoVertex{obj: obj})
 328  	w.nameIdx[obj] = idx
 329  	return idx
 330  }
 331  
 332  func (w *monoGraph) addEdge(dst, src, weight int, pos token.Pos, typ Type) {
 333  	// TODO(mdempsky): Deduplicate redundant edges?
 334  	w.edges = append(w.edges, monoEdge{
 335  		dst:    dst,
 336  		src:    src,
 337  		weight: weight,
 338  
 339  		pos: pos,
 340  		typ: typ,
 341  	})
 342  }
 343