github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/types2/subst.go (about)

     1  // Copyright 2018 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  // This file implements type parameter substitution.
     6  
     7  package types2
     8  
     9  import (
    10  	"github.com/go-asm/go/cmd/compile/syntax"
    11  )
    12  
    13  type substMap map[*TypeParam]Type
    14  
    15  // makeSubstMap creates a new substitution map mapping tpars[i] to targs[i].
    16  // If targs[i] is nil, tpars[i] is not substituted.
    17  func makeSubstMap(tpars []*TypeParam, targs []Type) substMap {
    18  	assert(len(tpars) == len(targs))
    19  	proj := make(substMap, len(tpars))
    20  	for i, tpar := range tpars {
    21  		proj[tpar] = targs[i]
    22  	}
    23  	return proj
    24  }
    25  
    26  // makeRenameMap is like makeSubstMap, but creates a map used to rename type
    27  // parameters in from with the type parameters in to.
    28  func makeRenameMap(from, to []*TypeParam) substMap {
    29  	assert(len(from) == len(to))
    30  	proj := make(substMap, len(from))
    31  	for i, tpar := range from {
    32  		proj[tpar] = to[i]
    33  	}
    34  	return proj
    35  }
    36  
    37  func (m substMap) empty() bool {
    38  	return len(m) == 0
    39  }
    40  
    41  func (m substMap) lookup(tpar *TypeParam) Type {
    42  	if t := m[tpar]; t != nil {
    43  		return t
    44  	}
    45  	return tpar
    46  }
    47  
    48  // subst returns the type typ with its type parameters tpars replaced by the
    49  // corresponding type arguments targs, recursively. subst doesn't modify the
    50  // incoming type. If a substitution took place, the result type is different
    51  // from the incoming type.
    52  //
    53  // If expanding is non-nil, it is the instance type currently being expanded.
    54  // One of expanding or ctxt must be non-nil.
    55  func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, expanding *Named, ctxt *Context) Type {
    56  	assert(expanding != nil || ctxt != nil)
    57  
    58  	if smap.empty() {
    59  		return typ
    60  	}
    61  
    62  	// common cases
    63  	switch t := typ.(type) {
    64  	case *Basic:
    65  		return typ // nothing to do
    66  	case *TypeParam:
    67  		return smap.lookup(t)
    68  	}
    69  
    70  	// general case
    71  	subst := subster{
    72  		pos:       pos,
    73  		smap:      smap,
    74  		check:     check,
    75  		expanding: expanding,
    76  		ctxt:      ctxt,
    77  	}
    78  	return subst.typ(typ)
    79  }
    80  
    81  type subster struct {
    82  	pos       syntax.Pos
    83  	smap      substMap
    84  	check     *Checker // nil if called via Instantiate
    85  	expanding *Named   // if non-nil, the instance that is being expanded
    86  	ctxt      *Context
    87  }
    88  
    89  func (subst *subster) typ(typ Type) Type {
    90  	switch t := typ.(type) {
    91  	case nil:
    92  		// Call typOrNil if it's possible that typ is nil.
    93  		panic("nil typ")
    94  
    95  	case *Basic:
    96  		// nothing to do
    97  
    98  	case *Array:
    99  		elem := subst.typOrNil(t.elem)
   100  		if elem != t.elem {
   101  			return &Array{len: t.len, elem: elem}
   102  		}
   103  
   104  	case *Slice:
   105  		elem := subst.typOrNil(t.elem)
   106  		if elem != t.elem {
   107  			return &Slice{elem: elem}
   108  		}
   109  
   110  	case *Struct:
   111  		if fields, copied := subst.varList(t.fields); copied {
   112  			s := &Struct{fields: fields, tags: t.tags}
   113  			s.markComplete()
   114  			return s
   115  		}
   116  
   117  	case *Pointer:
   118  		base := subst.typ(t.base)
   119  		if base != t.base {
   120  			return &Pointer{base: base}
   121  		}
   122  
   123  	case *Tuple:
   124  		return subst.tuple(t)
   125  
   126  	case *Signature:
   127  		// Preserve the receiver: it is handled during *Interface and *Named type
   128  		// substitution.
   129  		//
   130  		// Naively doing the substitution here can lead to an infinite recursion in
   131  		// the case where the receiver is an interface. For example, consider the
   132  		// following declaration:
   133  		//
   134  		//  type T[A any] struct { f interface{ m() } }
   135  		//
   136  		// In this case, the type of f is an interface that is itself the receiver
   137  		// type of all of its methods. Because we have no type name to break
   138  		// cycles, substituting in the recv results in an infinite loop of
   139  		// recv->interface->recv->interface->...
   140  		recv := t.recv
   141  
   142  		params := subst.tuple(t.params)
   143  		results := subst.tuple(t.results)
   144  		if params != t.params || results != t.results {
   145  			return &Signature{
   146  				rparams: t.rparams,
   147  				// TODO(gri) why can't we nil out tparams here, rather than in instantiate?
   148  				tparams: t.tparams,
   149  				// instantiated signatures have a nil scope
   150  				recv:     recv,
   151  				params:   params,
   152  				results:  results,
   153  				variadic: t.variadic,
   154  			}
   155  		}
   156  
   157  	case *Union:
   158  		terms, copied := subst.termlist(t.terms)
   159  		if copied {
   160  			// term list substitution may introduce duplicate terms (unlikely but possible).
   161  			// This is ok; lazy type set computation will determine the actual type set
   162  			// in normal form.
   163  			return &Union{terms}
   164  		}
   165  
   166  	case *Interface:
   167  		methods, mcopied := subst.funcList(t.methods)
   168  		embeddeds, ecopied := subst.typeList(t.embeddeds)
   169  		if mcopied || ecopied {
   170  			iface := subst.check.newInterface()
   171  			iface.embeddeds = embeddeds
   172  			iface.embedPos = t.embedPos
   173  			iface.implicit = t.implicit
   174  			assert(t.complete) // otherwise we are copying incomplete data
   175  			iface.complete = t.complete
   176  			// If we've changed the interface type, we may need to replace its
   177  			// receiver if the receiver type is the original interface. Receivers of
   178  			// *Named type are replaced during named type expansion.
   179  			//
   180  			// Notably, it's possible to reach here and not create a new *Interface,
   181  			// even though the receiver type may be parameterized. For example:
   182  			//
   183  			//  type T[P any] interface{ m() }
   184  			//
   185  			// In this case the interface will not be substituted here, because its
   186  			// method signatures do not depend on the type parameter P, but we still
   187  			// need to create new interface methods to hold the instantiated
   188  			// receiver. This is handled by Named.expandUnderlying.
   189  			iface.methods, _ = replaceRecvType(methods, t, iface)
   190  
   191  			// If check != nil, check.newInterface will have saved the interface for later completion.
   192  			if subst.check == nil { // golang/go#61561: all newly created interfaces must be completed
   193  				iface.typeSet()
   194  			}
   195  			return iface
   196  		}
   197  
   198  	case *Map:
   199  		key := subst.typ(t.key)
   200  		elem := subst.typ(t.elem)
   201  		if key != t.key || elem != t.elem {
   202  			return &Map{key: key, elem: elem}
   203  		}
   204  
   205  	case *Chan:
   206  		elem := subst.typ(t.elem)
   207  		if elem != t.elem {
   208  			return &Chan{dir: t.dir, elem: elem}
   209  		}
   210  
   211  	case *Named:
   212  		// dump is for debugging
   213  		dump := func(string, ...interface{}) {}
   214  		if subst.check != nil && subst.check.conf.Trace {
   215  			subst.check.indent++
   216  			defer func() {
   217  				subst.check.indent--
   218  			}()
   219  			dump = func(format string, args ...interface{}) {
   220  				subst.check.trace(subst.pos, format, args...)
   221  			}
   222  		}
   223  
   224  		// subst is called during expansion, so in this function we need to be
   225  		// careful not to call any methods that would cause t to be expanded: doing
   226  		// so would result in deadlock.
   227  		//
   228  		// So we call t.Origin().TypeParams() rather than t.TypeParams().
   229  		orig := t.Origin()
   230  		n := orig.TypeParams().Len()
   231  		if n == 0 {
   232  			dump(">>> %s is not parameterized", t)
   233  			return t // type is not parameterized
   234  		}
   235  
   236  		var newTArgs []Type
   237  		if t.TypeArgs().Len() != n {
   238  			return Typ[Invalid] // error reported elsewhere
   239  		}
   240  
   241  		// already instantiated
   242  		dump(">>> %s already instantiated", t)
   243  		// For each (existing) type argument targ, determine if it needs
   244  		// to be substituted; i.e., if it is or contains a type parameter
   245  		// that has a type argument for it.
   246  		for i, targ := range t.TypeArgs().list() {
   247  			dump(">>> %d targ = %s", i, targ)
   248  			new_targ := subst.typ(targ)
   249  			if new_targ != targ {
   250  				dump(">>> substituted %d targ %s => %s", i, targ, new_targ)
   251  				if newTArgs == nil {
   252  					newTArgs = make([]Type, n)
   253  					copy(newTArgs, t.TypeArgs().list())
   254  				}
   255  				newTArgs[i] = new_targ
   256  			}
   257  		}
   258  
   259  		if newTArgs == nil {
   260  			dump(">>> nothing to substitute in %s", t)
   261  			return t // nothing to substitute
   262  		}
   263  
   264  		// Create a new instance and populate the context to avoid endless
   265  		// recursion. The position used here is irrelevant because validation only
   266  		// occurs on t (we don't call validType on named), but we use subst.pos to
   267  		// help with debugging.
   268  		return subst.check.instance(subst.pos, orig, newTArgs, subst.expanding, subst.ctxt)
   269  
   270  	case *TypeParam:
   271  		return subst.smap.lookup(t)
   272  
   273  	default:
   274  		unreachable()
   275  	}
   276  
   277  	return typ
   278  }
   279  
   280  // typOrNil is like typ but if the argument is nil it is replaced with Typ[Invalid].
   281  // A nil type may appear in pathological cases such as type T[P any] []func(_ T([]_))
   282  // where an array/slice element is accessed before it is set up.
   283  func (subst *subster) typOrNil(typ Type) Type {
   284  	if typ == nil {
   285  		return Typ[Invalid]
   286  	}
   287  	return subst.typ(typ)
   288  }
   289  
   290  func (subst *subster) var_(v *Var) *Var {
   291  	if v != nil {
   292  		if typ := subst.typ(v.typ); typ != v.typ {
   293  			return substVar(v, typ)
   294  		}
   295  	}
   296  	return v
   297  }
   298  
   299  func substVar(v *Var, typ Type) *Var {
   300  	copy := *v
   301  	copy.typ = typ
   302  	copy.origin = v.Origin()
   303  	return &copy
   304  }
   305  
   306  func (subst *subster) tuple(t *Tuple) *Tuple {
   307  	if t != nil {
   308  		if vars, copied := subst.varList(t.vars); copied {
   309  			return &Tuple{vars: vars}
   310  		}
   311  	}
   312  	return t
   313  }
   314  
   315  func (subst *subster) varList(in []*Var) (out []*Var, copied bool) {
   316  	out = in
   317  	for i, v := range in {
   318  		if w := subst.var_(v); w != v {
   319  			if !copied {
   320  				// first variable that got substituted => allocate new out slice
   321  				// and copy all variables
   322  				new := make([]*Var, len(in))
   323  				copy(new, out)
   324  				out = new
   325  				copied = true
   326  			}
   327  			out[i] = w
   328  		}
   329  	}
   330  	return
   331  }
   332  
   333  func (subst *subster) func_(f *Func) *Func {
   334  	if f != nil {
   335  		if typ := subst.typ(f.typ); typ != f.typ {
   336  			return substFunc(f, typ)
   337  		}
   338  	}
   339  	return f
   340  }
   341  
   342  func substFunc(f *Func, typ Type) *Func {
   343  	copy := *f
   344  	copy.typ = typ
   345  	copy.origin = f.Origin()
   346  	return &copy
   347  }
   348  
   349  func (subst *subster) funcList(in []*Func) (out []*Func, copied bool) {
   350  	out = in
   351  	for i, f := range in {
   352  		if g := subst.func_(f); g != f {
   353  			if !copied {
   354  				// first function that got substituted => allocate new out slice
   355  				// and copy all functions
   356  				new := make([]*Func, len(in))
   357  				copy(new, out)
   358  				out = new
   359  				copied = true
   360  			}
   361  			out[i] = g
   362  		}
   363  	}
   364  	return
   365  }
   366  
   367  func (subst *subster) typeList(in []Type) (out []Type, copied bool) {
   368  	out = in
   369  	for i, t := range in {
   370  		if u := subst.typ(t); u != t {
   371  			if !copied {
   372  				// first function that got substituted => allocate new out slice
   373  				// and copy all functions
   374  				new := make([]Type, len(in))
   375  				copy(new, out)
   376  				out = new
   377  				copied = true
   378  			}
   379  			out[i] = u
   380  		}
   381  	}
   382  	return
   383  }
   384  
   385  func (subst *subster) termlist(in []*Term) (out []*Term, copied bool) {
   386  	out = in
   387  	for i, t := range in {
   388  		if u := subst.typ(t.typ); u != t.typ {
   389  			if !copied {
   390  				// first function that got substituted => allocate new out slice
   391  				// and copy all functions
   392  				new := make([]*Term, len(in))
   393  				copy(new, out)
   394  				out = new
   395  				copied = true
   396  			}
   397  			out[i] = NewTerm(t.tilde, u)
   398  		}
   399  	}
   400  	return
   401  }
   402  
   403  // replaceRecvType updates any function receivers that have type old to have
   404  // type new. It does not modify the input slice; if modifications are required,
   405  // the input slice and any affected signatures will be copied before mutating.
   406  //
   407  // The resulting out slice contains the updated functions, and copied reports
   408  // if anything was modified.
   409  func replaceRecvType(in []*Func, old, new Type) (out []*Func, copied bool) {
   410  	out = in
   411  	for i, method := range in {
   412  		sig := method.Type().(*Signature)
   413  		if sig.recv != nil && sig.recv.Type() == old {
   414  			if !copied {
   415  				// Allocate a new methods slice before mutating for the first time.
   416  				// This is defensive, as we may share methods across instantiations of
   417  				// a given interface type if they do not get substituted.
   418  				out = make([]*Func, len(in))
   419  				copy(out, in)
   420  				copied = true
   421  			}
   422  			newsig := *sig
   423  			newsig.recv = substVar(sig.recv, new)
   424  			out[i] = substFunc(method, &newsig)
   425  		}
   426  	}
   427  	return
   428  }