instantiate.go raw

   1  // Copyright 2022 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  package ssa
   6  
   7  import (
   8  	"fmt"
   9  	"go/types"
  10  	"slices"
  11  	"sync"
  12  )
  13  
  14  // A generic records information about a generic origin function,
  15  // including a cache of existing instantiations.
  16  type generic struct {
  17  	instancesMu sync.Mutex
  18  	instances   map[*typeList]*Function // canonical type arguments to an instance.
  19  }
  20  
  21  // instance returns a Function that is the instantiation of generic
  22  // origin function fn with the type arguments targs.
  23  //
  24  // Any created instance is added to cr.
  25  //
  26  // Acquires fn.generic.instancesMu.
  27  func (fn *Function) instance(targs []types.Type, b *builder) *Function {
  28  	key := fn.Prog.canon.List(targs)
  29  
  30  	gen := fn.generic
  31  
  32  	gen.instancesMu.Lock()
  33  	defer gen.instancesMu.Unlock()
  34  	inst, ok := gen.instances[key]
  35  	if !ok {
  36  		inst = createInstance(fn, targs)
  37  		inst.buildshared = b.shared()
  38  		b.enqueue(inst)
  39  
  40  		if gen.instances == nil {
  41  			gen.instances = make(map[*typeList]*Function)
  42  		}
  43  		gen.instances[key] = inst
  44  	} else {
  45  		b.waitForSharedFunction(inst)
  46  	}
  47  	return inst
  48  }
  49  
  50  // createInstance returns the instantiation of generic function fn using targs.
  51  //
  52  // Requires fn.generic.instancesMu.
  53  func createInstance(fn *Function, targs []types.Type) *Function {
  54  	prog := fn.Prog
  55  
  56  	// Compute signature.
  57  	var sig *types.Signature
  58  	var obj *types.Func
  59  	if recv := fn.Signature.Recv(); recv != nil {
  60  		// method
  61  		obj = prog.canon.instantiateMethod(fn.object, targs, prog.ctxt)
  62  		sig = obj.Type().(*types.Signature)
  63  	} else {
  64  		// function
  65  		instSig, err := types.Instantiate(prog.ctxt, fn.Signature, targs, false)
  66  		if err != nil {
  67  			panic(err)
  68  		}
  69  		instance, ok := instSig.(*types.Signature)
  70  		if !ok {
  71  			panic("Instantiate of a Signature returned a non-signature")
  72  		}
  73  		obj = fn.object // instantiation does not exist yet
  74  		sig = prog.canon.Type(instance).(*types.Signature)
  75  	}
  76  
  77  	// Choose strategy (instance or wrapper).
  78  	var (
  79  		synthetic string
  80  		subst     *subster
  81  		build     buildFunc
  82  	)
  83  	if prog.mode&InstantiateGenerics != 0 && !prog.isParameterized(targs...) {
  84  		synthetic = fmt.Sprintf("instance of %s", fn.Name())
  85  		if fn.syntax != nil {
  86  			subst = makeSubster(prog.ctxt, obj, fn.typeparams, targs)
  87  			build = (*builder).buildFromSyntax
  88  		} else {
  89  			build = (*builder).buildParamsOnly
  90  		}
  91  	} else {
  92  		synthetic = fmt.Sprintf("instantiation wrapper of %s", fn.Name())
  93  		build = (*builder).buildInstantiationWrapper
  94  	}
  95  
  96  	/* generic instance or instantiation wrapper */
  97  	return &Function{
  98  		name:           fmt.Sprintf("%s%s", fn.Name(), targs), // may not be unique
  99  		object:         obj,
 100  		Signature:      sig,
 101  		Synthetic:      synthetic,
 102  		syntax:         fn.syntax,    // \
 103  		info:           fn.info,      //  } empty for non-created packages
 104  		goversion:      fn.goversion, // /
 105  		build:          build,
 106  		topLevelOrigin: fn,
 107  		pos:            obj.Pos(),
 108  		Pkg:            nil,
 109  		Prog:           fn.Prog,
 110  		typeparams:     fn.typeparams, // share with origin
 111  		typeargs:       targs,
 112  		subst:          subst,
 113  	}
 114  }
 115  
 116  // isParameterized reports whether any of the specified types contains
 117  // a free type parameter. It is safe to call concurrently.
 118  func (prog *Program) isParameterized(ts ...types.Type) bool {
 119  	prog.hasParamsMu.Lock()
 120  	defer prog.hasParamsMu.Unlock()
 121  
 122  	// TODO(adonovan): profile. If this operation is expensive,
 123  	// handle the most common but shallow cases such as T, pkg.T,
 124  	// *T without consulting the cache under the lock.
 125  
 126  	return slices.ContainsFunc(ts, prog.hasParams.Has)
 127  }
 128